From 0381b3330db4b653772babd239aa1dcfb1fc9bfd Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 24 Dec 2022 10:13:56 -0800
Subject: [PATCH 001/393] Implement beginning of new logging framework.

Add MySqlDataSourceBuilder class to create a MySqlDataSource with a fluent API.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 ...icrosoftExtensionsLoggingLoggerProvider.cs |  4 +-
 ...ogging.Microsoft.Extensions.Logging.csproj |  2 +-
 src/MySqlConnector/Logging/EventIds.cs        | 24 ++++++++
 src/MySqlConnector/Logging/LogMessages.cs     | 55 +++++++++++++++++
 .../MySqlConnectorLoggingConfiguration.cs     | 21 +++++++
 src/MySqlConnector/MySqlConnection.cs         | 60 +++++++++----------
 src/MySqlConnector/MySqlConnector.csproj      |  4 ++
 src/MySqlConnector/MySqlDataSource.cs         | 26 ++++----
 src/MySqlConnector/MySqlDataSourceBuilder.cs  | 31 ++++++++++
 9 files changed, 182 insertions(+), 45 deletions(-)
 create mode 100644 src/MySqlConnector/Logging/EventIds.cs
 create mode 100644 src/MySqlConnector/Logging/LogMessages.cs
 create mode 100644 src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
 create mode 100644 src/MySqlConnector/MySqlDataSourceBuilder.cs

diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MicrosoftExtensionsLoggingLoggerProvider.cs b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MicrosoftExtensionsLoggingLoggerProvider.cs
index 0bd8f9989..9f2f19713 100644
--- a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MicrosoftExtensionsLoggingLoggerProvider.cs
+++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MicrosoftExtensionsLoggingLoggerProvider.cs
@@ -61,8 +61,8 @@ public void Log(MySqlConnectorLogLevel level, string message, object?[]? args =
 			_ => throw new ArgumentOutOfRangeException(nameof(level), level, "Invalid value for 'level'."),
 		};
 
-		private static readonly Func<string, Exception, string> s_getMessage = static (s, e) => s;
-		private static readonly Func<(string Message, object?[] Args), Exception, string> s_messageFormatter = static (s, e) => string.Format(CultureInfo.InvariantCulture, s.Message, s.Args);
+		private static readonly Func<string, Exception?, string> s_getMessage = static (s, e) => s;
+		private static readonly Func<(string Message, object?[] Args), Exception?, string> s_messageFormatter = static (s, e) => string.Format(CultureInfo.InvariantCulture, s.Message, s.Args);
 
 		private readonly ILogger m_logger;
 	}
diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
index 13d933964..418c0971d 100644
--- a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
+++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
@@ -12,7 +12,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.3" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
new file mode 100644
index 000000000..66cc0759d
--- /dev/null
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -0,0 +1,24 @@
+namespace MySqlConnector.Logging;
+
+internal static class EventIds
+{
+	public const int DataSourceCreatedWithPool = 1000;
+	public const int DataSourceCreatedWithoutPool = 1001;
+
+	public const int CreatedNonPooledSession = 2000;
+	public const int ResettingConnection = 2001;
+
+	public const int IgnoringCancellationForCommand = 2100;
+	public const int CancelingCommand = 2101;
+	public const int IgnoringCancellationForClosedConnection = 2102;
+	public const int CancelingCommandFailed = 2103;
+
+	public const int GettingCachedProcedure = 2200;
+	public const int PoolDoesNotHaveSharedProcedureCache = 2201;
+	public const int CouldNotNormalizeDatabaseAndName = 2202;
+	public const int FailedToCacheProcedure = 2203;
+	public const int CachingProcedure = 2204;
+	public const int ProcedureCacheCount = 2205;
+	public const int DidNotFindCachedProcedure = 2206;
+	public const int ReturningCachedProcedure = 2207;
+}
diff --git a/src/MySqlConnector/Logging/LogMessages.cs b/src/MySqlConnector/Logging/LogMessages.cs
new file mode 100644
index 000000000..8f65a2eed
--- /dev/null
+++ b/src/MySqlConnector/Logging/LogMessages.cs
@@ -0,0 +1,55 @@
+using System.Security;
+using Microsoft.Extensions.Logging;
+
+namespace MySqlConnector.Logging;
+
+internal static partial class LogMessages
+{
+	[LoggerMessage(EventIds.DataSourceCreatedWithPool, LogLevel.Information, "DataSource {DataSourceId} created with pool {PoolId}")]
+	public static partial void DataSourceCreatedWithPool(ILogger logger, int dataSourceId, int poolId);
+
+	[LoggerMessage(EventIds.DataSourceCreatedWithoutPool, LogLevel.Information, "DataSource {DataSourceId} created with no pool")]
+	public static partial void DataSourceCreatedWithoutPool(ILogger logger, int dataSourceId);
+
+	[LoggerMessage(EventIds.CreatedNonPooledSession, LogLevel.Debug, "Created new non-pooled session {SessionId}")]
+	public static partial void CreatedNonPooledSession(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.ResettingConnection, LogLevel.Debug, "Session {SessionId} resetting connection")]
+	public static partial void ResettingConnection(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.IgnoringCancellationForCommand, LogLevel.Trace, "Ignoring cancellation for closed connection or invalid command {CommandId}")]
+	public static partial void IgnoringCancellationForCommand(ILogger logger, int commandId);
+
+	[LoggerMessage(EventIds.CancelingCommand, LogLevel.Debug, "Command {CommandId} for session {SessionId} has been canceled via {CancellationSource}")]
+	public static partial void CancelingCommand(ILogger logger, int commandId, string sessionId, string cancellationSource);
+
+	[LoggerMessage(EventIds.IgnoringCancellationForClosedConnection, LogLevel.Information, "Session {SessionId} ignoring cancellation for closed connection")]
+	public static partial void IgnoringCancellationForClosedConnection(ILogger logger, Exception exception, string sessionId);
+
+	[LoggerMessage(EventIds.CancelingCommandFailed, LogLevel.Information, "Session {SessionId} cancelling command {CommandId} failed")]
+	public static partial void CancelingCommandFailed(ILogger logger, Exception exception, string sessionId, int commandId);
+
+	[LoggerMessage(EventIds.GettingCachedProcedure, LogLevel.Trace, "Session {SessionId} getting cached procedure named {ProcedureName}")]
+	public static partial void GettingCachedProcedure(ILogger logger, string sessionId, string procedureName);
+
+	[LoggerMessage(EventIds.PoolDoesNotHaveSharedProcedureCache, LogLevel.Information, "Session {SessionId} pool {PoolId} doesn't have a shared procedure cache; procedure will only be cached on this connection")]
+	public static partial void PoolDoesNotHaveSharedProcedureCache(ILogger logger, string sessionId, int? poolId);
+
+	[LoggerMessage(EventIds.CouldNotNormalizeDatabaseAndName, LogLevel.Information, "Session {SessionId} couldn't normalize the name '{ProcedureName}' in database {Database}; not caching procedure")]
+	public static partial void CouldNotNormalizeDatabaseAndName(ILogger logger, string sessionId, string procedureName, string database);
+
+	[LoggerMessage(EventIds.FailedToCacheProcedure, LogLevel.Information, "Session {SessionId} failed to cache procedure {Schema}.{Component}")]
+	public static partial void FailedToCacheProcedure(ILogger logger, string sessionId, string schema, string component);
+
+	[LoggerMessage(EventIds.CachingProcedure, LogLevel.Trace, "Session {SessionId} caching procedure {Schema}.{Component}")]
+	public static partial void CachingProcedure(ILogger logger, string sessionId, string schema, string component);
+
+	[LoggerMessage(EventIds.ProcedureCacheCount, LogLevel.Trace, "Session {SessionId} procedure cache count is {ProcedureCacheCount}")]
+	public static partial void ProcedureCacheCount(ILogger logger, string sessionId, int procedureCacheCount);
+
+	[LoggerMessage(EventIds.DidNotFindCachedProcedure, LogLevel.Information, "Session {SessionId} did not find cached procedure {Schema}.{Component}")]
+	public static partial void DidNotFindCachedProcedure(ILogger logger, string sessionId, string schema, string component);
+
+	[LoggerMessage(EventIds.ReturningCachedProcedure, LogLevel.Trace, "Session {SessionId} returning cached procedure {Schema}.{Component}")]
+	public static partial void ReturningCachedProcedure(ILogger logger, string sessionId, string schema, string component);
+}
diff --git a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
new file mode 100644
index 000000000..42aa3097c
--- /dev/null
+++ b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
@@ -0,0 +1,21 @@
+using Microsoft.Extensions.Logging;
+using Microsoft.Extensions.Logging.Abstractions;
+
+namespace MySqlConnector.Logging;
+
+internal sealed class MySqlConnectorLoggingConfiguration
+{
+	public MySqlConnectorLoggingConfiguration(ILoggerFactory loggerFactory)
+	{
+		DataSourceLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlDataSource");
+		ConnectionLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlConnection");
+		CommandLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlCommand");
+	}
+
+	public ILogger DataSourceLogger { get; }
+	public ILogger ConnectionLogger { get; }
+	public ILogger CommandLogger { get; }
+
+	public static MySqlConnectorLoggingConfiguration NullConfiguration { get; } = new MySqlConnectorLoggingConfiguration(NullLoggerFactory.Instance);
+	public static MySqlConnectorLoggingConfiguration GlobalConfiguration { get; set; } = NullConfiguration;
+}
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index fd744278b..fc7e1ae44 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -6,6 +6,7 @@
 using System.Net.Sockets;
 using System.Security.Authentication;
 using System.Security.Cryptography.X509Certificates;
+using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
 using MySqlConnector.Logging;
 using MySqlConnector.Protocol.Payloads;
@@ -29,19 +30,25 @@ public MySqlConnection()
 	}
 
 	public MySqlConnection(string? connectionString)
+		: this(connectionString ?? "", MySqlConnectorLoggingConfiguration.GlobalConfiguration.ConnectionLogger)
 	{
-		GC.SuppressFinalize(this);
-		m_connectionString = connectionString ?? "";
 	}
 
 #if NET7_0_OR_GREATER
 	internal MySqlConnection(MySqlDataSource dataSource)
-		: this(dataSource.ConnectionString)
+		: this(dataSource.ConnectionString, dataSource.LoggingConfiguration.ConnectionLogger)
 	{
 		m_dataSource = dataSource;
 	}
 #endif
 
+	private MySqlConnection(string connectionString, ILogger logger)
+	{
+		GC.SuppressFinalize(this);
+		m_connectionString = connectionString;
+		m_logger = logger;
+	}
+
 #pragma warning disable CA2012 // Safe because method completes synchronously
 	/// <summary>
 	/// Begins a database transaction.
@@ -467,7 +474,7 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 	public async ValueTask ResetConnectionAsync(CancellationToken cancellationToken = default)
 	{
 		var session = Session;
-		Log.Debug("Session{0} resetting connection", session.Id);
+		LogMessages.ResettingConnection(m_logger, session.Id);
 		await session.SendAsync(ResetConnectionPayload.Instance, AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
 		var payload = await session.ReceiveReplyAsync(AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
 		OkPayload.Create(payload.Span, session.SupportsDeprecateEof, session.SupportsSessionTrack);
@@ -753,12 +760,11 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 	{
 		if (m_session?.Id is not string sessionId || State != ConnectionState.Open || m_session?.TryStartCancel(command) is not true)
 		{
-			Log.Trace("Ignoring cancellation for closed connection or invalid CommandId {0}", commandId);
+			LogMessages.IgnoringCancellationForCommand(m_logger, commandId);
 			return;
 		}
 
-		Log.Debug("CommandId {0} for Session{1} has been canceled via {2}.", commandId, sessionId, isCancel ? "Cancel()" : "command timeout");
-
+		LogMessages.CancelingCommand(m_logger, commandId, sessionId, isCancel ? "Cancel()" : "command timeout");
 		try
 		{
 			// open a dedicated connection to the server to kill the active query
@@ -787,35 +793,34 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 		{
 			// ignore a rare race condition where the connection is open at the beginning of the method, but closed by the time
 			// KILL QUERY is executed: https://github.com/mysql-net/MySqlConnector/issues/1002
-			Log.Info(ex, "Session{0} ignoring cancellation for closed connection.", sessionId);
+			LogMessages.IgnoringCancellationForClosedConnection(m_logger, ex, sessionId);
 			m_session?.AbortCancel(command);
 		}
 		catch (MySqlException ex)
 		{
 			// cancelling the query failed; setting the state back to 'Querying' will allow another call to 'Cancel' to try again
-			Log.Info(ex, "Session{0} cancelling CommandId {1} failed", sessionId, command.CommandId);
+			LogMessages.CancelingCommandFailed(m_logger, ex, sessionId, command.CommandId);
 			m_session?.AbortCancel(command);
 		}
 	}
 
 	internal async Task<CachedProcedure?> GetCachedProcedure(string name, bool revalidateMissing, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		if (Log.IsTraceEnabled())
-			Log.Trace("Session{0} getting cached procedure Name={1}", m_session!.Id, name);
+		LogMessages.GettingCachedProcedure(m_logger, m_session!.Id, name);
 		if (State != ConnectionState.Open)
 			throw new InvalidOperationException("Connection is not open.");
 
 		var cachedProcedures = m_session!.Pool?.GetProcedureCache() ?? m_cachedProcedures;
 		if (cachedProcedures is null)
 		{
-			Log.Info("Session{0} pool Pool{1} doesn't have a shared procedure cache; procedure will only be cached on this connection", m_session.Id, m_session.Pool?.Id);
+			LogMessages.PoolDoesNotHaveSharedProcedureCache(m_logger, m_session.Id, m_session.Pool?.Id);
 			cachedProcedures = m_cachedProcedures = new();
 		}
 
 		var normalized = NormalizedSchema.MustNormalize(name, Database);
 		if (string.IsNullOrEmpty(normalized.Schema))
 		{
-			Log.Info("Session{0} couldn't normalize Database={1} Name={2}; not caching procedure", m_session.Id, Database, name);
+			LogMessages.CouldNotNormalizeDatabaseAndName(m_logger, m_session.Id, name, Database);
 			return null;
 		}
 
@@ -826,30 +831,23 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 		if (!foundProcedure || (cachedProcedure is null && revalidateMissing))
 		{
 			cachedProcedure = await CachedProcedure.FillAsync(ioBehavior, this, normalized.Schema!, normalized.Component!, cancellationToken).ConfigureAwait(false);
-			if (Log.IsInfoEnabled())
-			{
-				if (cachedProcedure is null)
-					Log.Info("Session{0} failed to cache procedure Schema={1} Component={2}", m_session.Id, normalized.Schema, normalized.Component);
-				else
-					Log.Trace("Session{0} caching procedure Schema={1} Component={2}", m_session.Id, normalized.Schema, normalized.Component);
-			}
+			if (cachedProcedure is null)
+				LogMessages.FailedToCacheProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
+			else
+				LogMessages.CachingProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
 			int count;
 			lock (cachedProcedures)
 			{
 				cachedProcedures[normalized.FullyQualified] = cachedProcedure;
 				count = cachedProcedures.Count;
 			}
-			if (Log.IsTraceEnabled())
-				Log.Trace("Session{0} procedure cache Count={1}", m_session.Id, count);
+			LogMessages.ProcedureCacheCount(m_logger, m_session.Id, count);
 		}
 
-		if (Log.IsInfoEnabled())
-		{
-			if (cachedProcedure is null)
-				Log.Info("Session{0} did not find cached procedure Schema={1} Component={2}", m_session.Id, normalized.Schema, normalized.Component);
-			else
-				Log.Trace("Session{0} returning cached procedure Schema={1} Component={2}", m_session.Id, normalized.Schema, normalized.Component);
-		}
+		if (cachedProcedure is null)
+			LogMessages.DidNotFindCachedProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
+		else
+			LogMessages.ReturningCachedProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
 		return cachedProcedure;
 	}
 
@@ -936,7 +934,7 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 
 				var session = new ServerSession();
 				session.OwningConnection = new WeakReference<MySqlConnection>(this);
-				Log.Debug("Created new non-pooled Session{0}", session.Id);
+				LogMessages.CreatedNonPooledSession(m_logger, session.Id);
 				try
 				{
 					await session.ConnectAsync(connectionSettings, this, startTickCount, loadBalancer, activity, actualIOBehavior, connectToken).ConfigureAwait(false);
@@ -1112,7 +1110,6 @@ private ConnectionSettings GetConnectionSettings() =>
 	// This method may be called when it's known that the connection settings have been initialized.
 	private ConnectionSettings GetInitializedConnectionSettings() => m_connectionSettings!;
 
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(MySqlConnection));
 	private static readonly StateChangeEventArgs s_stateChangeClosedConnecting = new(ConnectionState.Closed, ConnectionState.Connecting);
 	private static readonly StateChangeEventArgs s_stateChangeConnectingOpen = new(ConnectionState.Connecting, ConnectionState.Open);
 	private static readonly StateChangeEventArgs s_stateChangeOpenClosed = new(ConnectionState.Open, ConnectionState.Closed);
@@ -1122,6 +1119,7 @@ private ConnectionSettings GetConnectionSettings() =>
 #if NET7_0_OR_GREATER
 	private readonly MySqlDataSource? m_dataSource;
 #endif
+	private readonly ILogger m_logger;
 	private string m_connectionString;
 	private ConnectionSettings? m_connectionSettings;
 	private ServerSession? m_session;
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index a002bc0ff..62722c977 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -15,6 +15,10 @@
     <ImplicitUsings>enable</ImplicitUsings>
   </PropertyGroup>
 
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.3" />
+  </ItemGroup>
+
   <ItemGroup Condition=" '$(TargetFramework)' == 'net461' OR '$(TargetFramework)' == 'net471' ">
     <Reference Include="System.Transactions" />
     <Using Remove="System.Net.Http" />
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index 492206e73..608c7c49d 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -1,4 +1,5 @@
 #if NET7_0_OR_GREATER
+using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
 using MySqlConnector.Logging;
 using MySqlConnector.Protocol.Serialization;
@@ -16,20 +17,22 @@ public sealed class MySqlDataSource : DbDataSource
 	/// <param name="connectionString">The connection string for the MySQL Server. This parameter is required.</param>
 	/// <exception cref="ArgumentNullException">Thrown if <paramref name="connectionString"/> is <c>null</c>.</exception>
 	public MySqlDataSource(string connectionString)
+		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration)
 	{
-		m_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString));
+	}
+
+	internal MySqlDataSource(string connectionString, MySqlConnectorLoggingConfiguration loggingConfiguration)
+	{
+		m_connectionString = connectionString;
+		LoggingConfiguration = loggingConfiguration;
+		m_logger = loggingConfiguration.DataSourceLogger;
+
 		Pool = ConnectionPool.CreatePool(m_connectionString);
 		m_id = Interlocked.Increment(ref s_lastId);
-		m_logArguments = new object?[2] { m_id, null };
 		if (Pool is not null)
-		{
-			m_logArguments[1] = Pool.Id;
-			Log.Info("DataSource{0} created with Pool {1}", m_logArguments);
-		}
+			LogMessages.DataSourceCreatedWithPool(m_logger, m_id, Pool.Id);
 		else
-		{
-			Log.Info("DataSource{0} created with no pool", m_logArguments);
-		}
+			LogMessages.DataSourceCreatedWithoutPool(m_logger, m_id);
 	}
 
 	/// <summary>
@@ -102,11 +105,12 @@ private async ValueTask DisposeAsync(IOBehavior ioBehavior)
 
 	internal ConnectionPool? Pool { get; }
 
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(MySqlDataSource));
+	internal MySqlConnectorLoggingConfiguration LoggingConfiguration { get; }
+
 	private static int s_lastId;
 
+	private readonly ILogger m_logger;
 	private readonly int m_id;
-	private readonly object?[] m_logArguments;
 	private readonly string m_connectionString;
 	private bool m_isDisposed;
 }
diff --git a/src/MySqlConnector/MySqlDataSourceBuilder.cs b/src/MySqlConnector/MySqlDataSourceBuilder.cs
new file mode 100644
index 000000000..456990327
--- /dev/null
+++ b/src/MySqlConnector/MySqlDataSourceBuilder.cs
@@ -0,0 +1,31 @@
+using Microsoft.Extensions.Logging;
+using MySqlConnector.Logging;
+
+namespace MySqlConnector;
+
+#if NET7_0_OR_GREATER
+public sealed class MySqlDataSourceBuilder
+{
+	public MySqlDataSourceBuilder(string? connectionString = null)
+	{
+		ConnectionStringBuilder = new(connectionString ?? "");
+	}
+
+	public MySqlDataSourceBuilder UseLoggerFactory(ILoggerFactory? loggerFactory)
+	{
+		m_loggerFactory = loggerFactory;
+		return this;
+	}
+
+	public MySqlDataSource Build()
+	{
+		var loggingConfiguration = m_loggerFactory is null ? MySqlConnectorLoggingConfiguration.NullConfiguration : new(m_loggerFactory);
+		return new(ConnectionStringBuilder.ConnectionString,
+			loggingConfiguration);
+	}
+
+	public MySqlConnectionStringBuilder ConnectionStringBuilder { get; }
+
+	private ILoggerFactory? m_loggerFactory;
+}
+#endif

From 4b84364fa8f430ea6cfc3c97517494889dd48786 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 24 Dec 2022 16:06:14 -0800
Subject: [PATCH 002/393] Connect new logging framework to old logger provider.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Logging/MySqlConnectorLogManager.cs       | 52 +++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
index 2e09bf963..77516b356 100644
--- a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
+++ b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
@@ -1,3 +1,5 @@
+using Microsoft.Extensions.Logging;
+
 namespace MySqlConnector.Logging;
 
 /// <summary>
@@ -23,6 +25,7 @@ internal get
 				throw new InvalidOperationException("The logging provider must be set before any MySqlConnector methods are called.");
 
 			s_provider = value;
+			MySqlConnectorLoggingConfiguration.GlobalConfiguration = new(new MySqlConnectorLoggerFactor(value));
 		}
 	}
 
@@ -30,4 +33,53 @@ internal get
 
 	private static IMySqlConnectorLoggerProvider s_provider = new NoOpLoggerProvider();
 	private static bool s_providerRetrieved;
+
+	// A helper class that adapts ILoggerFactory to the old-style IMySqlConnectorLoggerProvider interface.
+	private sealed class MySqlConnectorLoggerFactor : ILoggerFactory
+	{
+		public MySqlConnectorLoggerFactor(IMySqlConnectorLoggerProvider loggerProvider) =>
+			m_loggerProvider = loggerProvider;
+
+		public void AddProvider(ILoggerProvider provider) => throw new NotSupportedException();
+
+		public ILogger CreateLogger(string categoryName)
+		{
+			// assume all logger names start with "MySqlConnector." but the old API didn't expect that prefix
+			return new MySqlConnectorLogger(m_loggerProvider.CreateLogger(categoryName[15..]));
+		}
+
+		public void Dispose()
+		{
+		}
+
+		private readonly IMySqlConnectorLoggerProvider m_loggerProvider;
+	}
+
+	// A helper class that adapts ILogger to the old-style IMySqlConnectorLogger interface.
+	private sealed class MySqlConnectorLogger : ILogger
+	{
+		public MySqlConnectorLogger(IMySqlConnectorLogger logger) =>
+			m_logger = logger;
+
+		public IDisposable BeginScope<TState>(TState state) => throw new NotSupportedException();
+
+		public bool IsEnabled(LogLevel logLevel) => m_logger.IsEnabled(ConvertLogLevel(logLevel));
+
+		public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) =>
+			m_logger.Log(ConvertLogLevel(logLevel), formatter(state, exception), exception: exception);
+
+		private static MySqlConnectorLogLevel ConvertLogLevel(LogLevel logLevel) =>
+			logLevel switch
+			{
+				LogLevel.Trace => MySqlConnectorLogLevel.Trace,
+				LogLevel.Debug => MySqlConnectorLogLevel.Debug,
+				LogLevel.Information => MySqlConnectorLogLevel.Info,
+				LogLevel.Warning => MySqlConnectorLogLevel.Warn,
+				LogLevel.Error => MySqlConnectorLogLevel.Error,
+				LogLevel.Critical => MySqlConnectorLogLevel.Fatal,
+				_ => MySqlConnectorLogLevel.Info,
+			};
+
+		private readonly IMySqlConnectorLogger m_logger;
+	}
 }

From 3cab55c3da5f958c9a6bdadd7e2cf34f04362954 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 26 Dec 2022 11:31:27 -0800
Subject: [PATCH 003/393] Plumb loggers through to ServerSession and
 ConnectionPool.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ConnectionPool.cs     | 31 ++++++++++++-------
 src/MySqlConnector/Core/ServerSession.cs      | 11 ++++---
 src/MySqlConnector/Logging/EventIds.cs        |  4 +++
 src/MySqlConnector/Logging/LogMessages.cs     |  9 ++++++
 .../MySqlConnectorLoggingConfiguration.cs     |  2 ++
 src/MySqlConnector/MySqlConnection.cs         | 16 +++++-----
 src/MySqlConnector/MySqlDataSource.cs         |  2 +-
 7 files changed, 51 insertions(+), 24 deletions(-)

diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index c081bfb50..e17d6729f 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -3,6 +3,7 @@
 using System.Globalization;
 using System.Net;
 using System.Security.Authentication;
+using Microsoft.Extensions.Logging;
 using MySqlConnector.Logging;
 using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
@@ -34,7 +35,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			await CreateMinimumPooledSessions(connection, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 		// wait for an open slot (until the cancellationToken is cancelled, which is typically due to timeout)
-		Log.Trace("Pool{0} waiting for an available session", m_logArguments);
+		LogMessages.WaitingForAvailableSession(m_logger, Id);
 		if (ioBehavior == IOBehavior.Asynchronous)
 			await m_sessionSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
 		else
@@ -394,7 +395,7 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh
 
 	private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection connection, string logMessage, int startTickCount, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		var session = new ServerSession(this, m_generation, Interlocked.Increment(ref m_lastSessionId));
+		var session = new ServerSession(m_connectionLogger, this, m_generation, Interlocked.Increment(ref m_lastSessionId));
 		if (Log.IsDebugEnabled())
 			Log.Debug(logMessage, m_logArguments[0], session.Id);
 		string? statusInfo;
@@ -424,7 +425,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 				{
 					var redirectedSettings = ConnectionSettings.CloneWith(host, port, user);
 					Log.Debug("Pool{0} opening new connection to Host={1}; Port={2}; User={3}", m_logArguments[0], host, port, user);
-					var redirectedSession = new ServerSession(this, m_generation, Interlocked.Increment(ref m_lastSessionId));
+					var redirectedSession = new ServerSession(m_connectionLogger, this, m_generation, Interlocked.Increment(ref m_lastSessionId));
 					try
 					{
 						await redirectedSession.ConnectAsync(redirectedSettings, connection, startTickCount, m_loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
@@ -467,24 +468,24 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 		return session;
 	}
 
-	public static ConnectionPool? CreatePool(string connectionString)
+	public static ConnectionPool? CreatePool(string connectionString, MySqlConnectorLoggingConfiguration loggingConfiguration)
 	{
 		// parse connection string and check for 'Pooling' setting; return 'null' if pooling is disabled
 		var connectionStringBuilder = new MySqlConnectionStringBuilder(connectionString);
 		if (!connectionStringBuilder.Pooling)
-		{
 			return null;
-		}
 
 		// force a new pool to be created, ignoring the cache
 		var connectionSettings = new ConnectionSettings(connectionStringBuilder);
-		var pool = new ConnectionPool(connectionSettings);
+		var pool = new ConnectionPool(loggingConfiguration, connectionSettings);
 		pool.StartReaperTask();
 		pool.StartDnsCheckTimer();
 		return pool;
 	}
 
-	public static ConnectionPool? GetPool(string connectionString)
+	// Gets an existing ConnectionPool, creating it if it's missing. If 'createIfNotFound' is false, then 'loggingConfiguration'
+	// may be set to null; otherwise, it must be provided.
+	public static ConnectionPool? GetPool(string connectionString, MySqlConnectorLoggingConfiguration? loggingConfiguration, bool createIfNotFound = true)
 	{
 		// check single-entry MRU cache for this exact connection string; most applications have just one
 		// connection string and will get a cache hit here
@@ -519,9 +520,12 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 			return pool;
 		}
 
+		if (!createIfNotFound)
+			return null;
+
 		// create a new pool and attempt to insert it; if someone else beats us to it, just use their value
 		var connectionSettings = new ConnectionSettings(connectionStringBuilder);
-		var newPool = new ConnectionPool(connectionSettings);
+		var newPool = new ConnectionPool(loggingConfiguration!, connectionSettings);
 		pool = s_pools.GetOrAdd(normalizedConnectionString, newPool);
 
 		if (pool == newPool)
@@ -560,8 +564,10 @@ private static IReadOnlyList<ConnectionPool> GetAllPools()
 		return pools;
 	}
 
-	private ConnectionPool(ConnectionSettings cs)
+	private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration, ConnectionSettings cs)
 	{
+		m_logger = loggingConfiguration.PoolLogger;
+		m_connectionLogger = loggingConfiguration.ConnectionLogger;
 		ConnectionSettings = cs;
 		SslProtocols = cs.TlsVersions;
 		m_generation = 0;
@@ -584,8 +590,7 @@ private ConnectionPool(ConnectionSettings cs)
 
 		Id = Interlocked.Increment(ref s_poolId);
 		m_logArguments = new object[] { Id.ToString(CultureInfo.InvariantCulture) };
-		if (Log.IsInfoEnabled())
-			Log.Info("Pool{0} creating new connection pool for ConnectionString: {1}", m_logArguments[0], cs.ConnectionStringBuilder.GetConnectionString(includePassword: false));
+		LogMessages.CreatingNewConnectionPool(m_logger, Id, cs.ConnectionStringBuilder.GetConnectionString(includePassword: false));
 	}
 
 	private void StartReaperTask()
@@ -771,6 +776,8 @@ private static void OnAppDomainShutDown(object? sender, EventArgs e) =>
 	private static int s_poolId;
 	private static ConnectionStringPool? s_mruCache;
 
+	private readonly ILogger m_logger;
+	private readonly ILogger m_connectionLogger;
 	private readonly SemaphoreSlim m_cleanSemaphore;
 	private readonly SemaphoreSlim m_sessionSemaphore;
 	private readonly LinkedList<ServerSession> m_sessions;
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 87320fbbd..a0e516fb9 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -11,6 +11,7 @@
 using System.Security.Cryptography;
 using System.Security.Cryptography.X509Certificates;
 using System.Text;
+using Microsoft.Extensions.Logging;
 using MySqlConnector.Authentication;
 using MySqlConnector.Logging;
 using MySqlConnector.Protocol;
@@ -24,13 +25,14 @@ namespace MySqlConnector.Core;
 
 internal sealed class ServerSession
 {
-	public ServerSession()
-		: this(null, 0, Interlocked.Increment(ref s_lastId))
+	public ServerSession(ILogger logger)
+		: this(logger, null, 0, Interlocked.Increment(ref s_lastId))
 	{
 	}
 
-	public ServerSession(ConnectionPool? pool, int poolGeneration, int id)
+	public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, int id)
 	{
+		m_logger = logger;
 		m_lock = new();
 		m_payloadCache = new();
 		Id = (pool?.Id ?? 0) + "." + id;
@@ -41,7 +43,7 @@ public ServerSession(ConnectionPool? pool, int poolGeneration, int id)
 		HostName = "";
 		m_logArguments = new object?[] { Id.ToString(CultureInfo.InvariantCulture), null };
 		m_activityTags = new ActivityTagsCollection();
-		Log.Trace("Session{0} created new session", m_logArguments);
+		LogMessages.CreatedNewSession(m_logger, Id);
 	}
 
 	public string Id { get; }
@@ -1974,6 +1976,7 @@ protected override void OnStatementBegin(int index)
 	private static readonly PayloadData s_selectConnectionIdVersionWithAttributesPayload = QueryPayload.Create(true, "SELECT CONNECTION_ID(), VERSION();"u8);
 	private static int s_lastId;
 
+	private readonly ILogger m_logger;
 	private readonly object m_lock;
 	private readonly object?[] m_logArguments;
 	private readonly ArraySegmentHolder<byte> m_payloadCache;
diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index 66cc0759d..c39324c53 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -7,6 +7,7 @@ internal static class EventIds
 
 	public const int CreatedNonPooledSession = 2000;
 	public const int ResettingConnection = 2001;
+	public const int CreatedNewSession = 2002;
 
 	public const int IgnoringCancellationForCommand = 2100;
 	public const int CancelingCommand = 2101;
@@ -21,4 +22,7 @@ internal static class EventIds
 	public const int ProcedureCacheCount = 2205;
 	public const int DidNotFindCachedProcedure = 2206;
 	public const int ReturningCachedProcedure = 2207;
+
+	public const int WaitingForAvailableSession = 3000;
+	public const int CreatingNewConnectionPool = 3001;
 }
diff --git a/src/MySqlConnector/Logging/LogMessages.cs b/src/MySqlConnector/Logging/LogMessages.cs
index 8f65a2eed..0e78716d5 100644
--- a/src/MySqlConnector/Logging/LogMessages.cs
+++ b/src/MySqlConnector/Logging/LogMessages.cs
@@ -52,4 +52,13 @@ internal static partial class LogMessages
 
 	[LoggerMessage(EventIds.ReturningCachedProcedure, LogLevel.Trace, "Session {SessionId} returning cached procedure {Schema}.{Component}")]
 	public static partial void ReturningCachedProcedure(ILogger logger, string sessionId, string schema, string component);
+
+	[LoggerMessage(EventIds.CreatedNewSession, LogLevel.Trace, "Session {SessionId} created new session")]
+	public static partial void CreatedNewSession(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.WaitingForAvailableSession, LogLevel.Trace, "Pool {PoolId} waiting for an available session")]
+	public static partial void WaitingForAvailableSession(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.CreatingNewConnectionPool, LogLevel.Information, "Pool {PoolId} creating new connection pool for {ConnectionString}")]
+	public static partial void CreatingNewConnectionPool(ILogger logger, int poolId, string connectionString);
 }
diff --git a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
index 42aa3097c..033e27fd9 100644
--- a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
+++ b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
@@ -10,11 +10,13 @@ public MySqlConnectorLoggingConfiguration(ILoggerFactory loggerFactory)
 		DataSourceLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlDataSource");
 		ConnectionLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlConnection");
 		CommandLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlCommand");
+		PoolLogger = loggerFactory.CreateLogger("MySqlConnector.ConnectionPool");
 	}
 
 	public ILogger DataSourceLogger { get; }
 	public ILogger ConnectionLogger { get; }
 	public ILogger CommandLogger { get; }
+	public ILogger PoolLogger { get; }
 
 	public static MySqlConnectorLoggingConfiguration NullConfiguration { get; } = new MySqlConnectorLoggingConfiguration(NullLoggerFactory.Instance);
 	public static MySqlConnectorLoggingConfiguration GlobalConfiguration { get; set; } = NullConfiguration;
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index fc7e1ae44..30ba8f2ae 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -30,23 +30,24 @@ public MySqlConnection()
 	}
 
 	public MySqlConnection(string? connectionString)
-		: this(connectionString ?? "", MySqlConnectorLoggingConfiguration.GlobalConfiguration.ConnectionLogger)
+		: this(connectionString ?? "", MySqlConnectorLoggingConfiguration.GlobalConfiguration)
 	{
 	}
 
 #if NET7_0_OR_GREATER
 	internal MySqlConnection(MySqlDataSource dataSource)
-		: this(dataSource.ConnectionString, dataSource.LoggingConfiguration.ConnectionLogger)
+		: this(dataSource.ConnectionString, dataSource.LoggingConfiguration)
 	{
 		m_dataSource = dataSource;
 	}
 #endif
 
-	private MySqlConnection(string connectionString, ILogger logger)
+	private MySqlConnection(string connectionString, MySqlConnectorLoggingConfiguration loggingConfiguration)
 	{
 		GC.SuppressFinalize(this);
 		m_connectionString = connectionString;
-		m_logger = logger;
+		m_loggingConfiguration = loggingConfiguration;
+		m_logger = loggingConfiguration.ConnectionLogger;
 	}
 
 #pragma warning disable CA2012 // Safe because method completes synchronously
@@ -408,7 +409,7 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 #if NET7_0_OR_GREATER
 				m_dataSource?.Pool ??
 #endif
-				ConnectionPool.GetPool(m_connectionString);
+				ConnectionPool.GetPool(m_connectionString, m_loggingConfiguration, createIfNotFound: true);
 			m_connectionSettings ??= pool?.ConnectionSettings ?? new ConnectionSettings(new MySqlConnectionStringBuilder(m_connectionString));
 
 			// check if there is an open session (in the current transaction) that can be adopted
@@ -577,7 +578,7 @@ private static async Task ClearPoolAsync(MySqlConnection connection, IOBehavior
 		if (connection is null)
 			throw new ArgumentNullException(nameof(connection));
 
-		var pool = ConnectionPool.GetPool(connection.m_connectionString);
+		var pool = ConnectionPool.GetPool(connection.m_connectionString, null, createIfNotFound: false);
 		if (pool is not null)
 			await pool.ClearAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 	}
@@ -932,7 +933,7 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 				var loadBalancer = connectionSettings.LoadBalance == MySqlLoadBalance.Random && connectionSettings.HostNames!.Count > 1 ?
 					RandomLoadBalancer.Instance : FailOverLoadBalancer.Instance;
 
-				var session = new ServerSession();
+				var session = new ServerSession(m_logger);
 				session.OwningConnection = new WeakReference<MySqlConnection>(this);
 				LogMessages.CreatedNonPooledSession(m_logger, session.Id);
 				try
@@ -1119,6 +1120,7 @@ private ConnectionSettings GetConnectionSettings() =>
 #if NET7_0_OR_GREATER
 	private readonly MySqlDataSource? m_dataSource;
 #endif
+	private readonly MySqlConnectorLoggingConfiguration m_loggingConfiguration;
 	private readonly ILogger m_logger;
 	private string m_connectionString;
 	private ConnectionSettings? m_connectionSettings;
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index 608c7c49d..20c499d8e 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -27,7 +27,7 @@ internal MySqlDataSource(string connectionString, MySqlConnectorLoggingConfigura
 		LoggingConfiguration = loggingConfiguration;
 		m_logger = loggingConfiguration.DataSourceLogger;
 
-		Pool = ConnectionPool.CreatePool(m_connectionString);
+		Pool = ConnectionPool.CreatePool(m_connectionString, LoggingConfiguration);
 		m_id = Interlocked.Increment(ref s_lastId);
 		if (Pool is not null)
 			LogMessages.DataSourceCreatedWithPool(m_logger, m_id, Pool.Id);

From 79e340511b132258ce3d7119c4cb250baf724f02 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 26 Dec 2022 13:32:18 -0800
Subject: [PATCH 004/393] Migrate all ConnectionPool logging statements.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ConnectionPool.cs | 91 +++++++++++------------
 src/MySqlConnector/Logging/EventIds.cs    | 33 ++++++++
 src/MySqlConnector/Logging/LogMessages.cs | 87 ++++++++++++++++++++++
 3 files changed, 165 insertions(+), 46 deletions(-)

diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index e17d6729f..f00713fa0 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -27,7 +27,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 		// on the lock in RecoverLeakedSessions in high-concurrency situations
 		if (IsEmpty && unchecked(((uint) Environment.TickCount) - m_lastRecoveryTime) >= 1000u)
 		{
-			Log.Debug("Pool{0} is empty; scanning for any leaked sessions", m_logArguments);
+			LogMessages.ScanningForLeakedSessions(m_logger, Id);
 			await RecoverLeakedSessionsAsync(ioBehavior).ConfigureAwait(false);
 		}
 
@@ -55,12 +55,12 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			}
 			if (session is not null)
 			{
-				Log.Trace("Pool{0} found an existing session; checking it for validity", m_logArguments);
+				LogMessages.FoundExistingSession(m_logger, Id);
 				bool reuseSession;
 
 				if (session.PoolGeneration != m_generation)
 				{
-					Log.Trace("Pool{0} discarding session due to wrong generation", m_logArguments);
+					LogMessages.DiscardingSessionDueToWrongGeneration(m_logger, Id);
 					reuseSession = false;
 				}
 				else
@@ -74,7 +74,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 				if (!reuseSession)
 				{
 					// session is either old or cannot communicate with the server
-					Log.Info("Pool{0} Session{1} is unusable; destroying it", m_logArguments[0], session.Id);
+					LogMessages.SessionIsUnusable(m_logger, Id, session.Id);
 					AdjustHostConnectionCount(session, -1);
 					await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 				}
@@ -89,14 +89,13 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 						leasedSessionsCountPooled = m_leasedSessions.Count;
 					}
 					ActivitySourceHelper.CopyTags(session.ActivityTags, activity);
-					if (Log.IsTraceEnabled())
-						Log.Trace("Pool{0} returning pooled Session{1} to caller; LeasedSessionsCount={2}", m_logArguments[0], session.Id, leasedSessionsCountPooled);
+					LogMessages.ReturningPooledSession(m_logger, Id, session.Id, leasedSessionsCountPooled);
 					return session;
 				}
 			}
 
 			// create a new session
-			session = await ConnectSessionAsync(connection, "Pool{0} no pooled session available; created new Session{1}", startTickCount, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+			session = await ConnectSessionAsync(connection, s_createdNewSession, startTickCount, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
 			AdjustHostConnectionCount(session, 1);
 			session.OwningConnection = new(connection);
 			int leasedSessionsCountNew;
@@ -105,8 +104,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 				m_leasedSessions.Add(session.Id, session);
 				leasedSessionsCountNew = m_leasedSessions.Count;
 			}
-			if (Log.IsTraceEnabled())
-				Log.Trace("Pool{0} returning new Session{1} to caller; LeasedSessionsCount={2}", m_logArguments[0], session.Id, leasedSessionsCountNew);
+			LogMessages.ReturningNewSession(m_logger, Id, session.Id, leasedSessionsCountNew);
 			return session;
 		}
 		catch (Exception ex)
@@ -115,13 +113,13 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			{
 				try
 				{
-					Log.Debug(ex, "Pool{0} disposing created Session{1} due to exception: {2}", m_logArguments[0], session.Id, ex.Message);
+					LogMessages.DisposingCreatedSessionDueToException(m_logger, ex, Id, session.Id, ex.Message);
 					AdjustHostConnectionCount(session, -1);
 					await session.DisposeAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 				}
 				catch (Exception unexpectedException)
 				{
-					Log.Warn(unexpectedException, "Pool{0} unexpected error in GetSessionAsync: {1}", m_logArguments[0], unexpectedException.Message);
+					LogMessages.UnexpectedErrorInGetSessionAsync(m_logger, unexpectedException, Id, unexpectedException.Message);
 				}
 			}
 
@@ -152,8 +150,7 @@ private int GetSessionHealth(ServerSession session)
 
 	public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 	{
-		if (Log.IsTraceEnabled())
-			Log.Trace("Pool{0} receiving Session{1} back", m_logArguments[0], session.Id);
+		LogMessages.ReceivingSessionBack(m_logger, Id, session.Id);
 
 		try
 		{
@@ -169,9 +166,9 @@ public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 			else
 			{
 				if (sessionHealth == 1)
-					Log.Info("Pool{0} received invalid Session{1}; destroying it", m_logArguments[0], session.Id);
+					LogMessages.ReceivedInvalidSession(m_logger, Id, session.Id);
 				else
-					Log.Debug("Pool{0} received expired Session{1}; destroying it", m_logArguments[0], session.Id);
+					LogMessages.ReceivedExpiredSession(m_logger, Id, session.Id);
 				AdjustHostConnectionCount(session, -1);
 				await session.DisposeAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 			}
@@ -185,7 +182,7 @@ public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 	public async Task ClearAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		// increment the generation of the connection pool
-		Log.Info("Pool{0} clearing connection pool", m_logArguments);
+		LogMessages.ClearingConnectionPool(m_logger, Id);
 		Interlocked.Increment(ref m_generation);
 		m_procedureCache = null;
 		await RecoverLeakedSessionsAsync(ioBehavior).ConfigureAwait(false);
@@ -194,7 +191,7 @@ public async Task ClearAsync(IOBehavior ioBehavior, CancellationToken cancellati
 
 	public async Task ReapAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		Log.Trace("Pool{0} reaping connection pool", m_logArguments);
+		LogMessages.ReapingConnectionPool(m_logger, Id);
 		await RecoverLeakedSessionsAsync(ioBehavior).ConfigureAwait(false);
 		await CleanPoolAsync(ioBehavior, session => (unchecked((uint) Environment.TickCount) - session.LastReturnedTicks) / 1000 >= ConnectionSettings.ConnectionIdleTimeout, true, cancellationToken).ConfigureAwait(false);
 	}
@@ -218,7 +215,7 @@ public async Task ReapAsync(IOBehavior ioBehavior, CancellationToken cancellatio
 
 	public void Dispose()
 	{
-		Log.Debug("Pool{0} disposing connection pool", m_logArguments);
+		LogMessages.DisposingConnectionPool(m_logger, Id);
 #if NET6_0_OR_GREATER
 		m_dnsCheckTimer?.Dispose();
 		m_dnsCheckTimer = null;
@@ -263,9 +260,9 @@ private async Task RecoverLeakedSessionsAsync(IOBehavior ioBehavior)
 			}
 		}
 		if (recoveredSessions.Count == 0)
-			Log.Trace("Pool{0} recovered no sessions", m_logArguments);
+			LogMessages.RecoveredNoSessions(m_logger, Id);
 		else
-			Log.Warn("Pool{0}: RecoveredSessionCount={1}", m_logArguments[0], recoveredSessions.Count);
+			LogMessages.RecoveredSessionCount(m_logger, Id, recoveredSessions.Count);
 
 		foreach (var (session, connection) in recoveredSessions)
 		{
@@ -331,7 +328,7 @@ private async Task CleanPoolAsync(IOBehavior ioBehavior, Func<ServerSession, boo
 					if (shouldCleanFn(session))
 					{
 						// session should be cleaned; dispose it and keep iterating
-						Log.Debug("Pool{0} found Session{1} to clean up", m_logArguments[0], session.Id);
+						LogMessages.FoundSessionToCleanUp(m_logger, Id, session.Id);
 						await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 					}
 					else
@@ -380,7 +377,7 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh
 
 			try
 			{
-				var session = await ConnectSessionAsync(connection, "Pool{0} created Session{1} to reach minimum pool size", Environment.TickCount, null, ioBehavior, cancellationToken).ConfigureAwait(false);
+				var session = await ConnectSessionAsync(connection, s_createdToReachMinimumPoolSize, Environment.TickCount, null, ioBehavior, cancellationToken).ConfigureAwait(false);
 				AdjustHostConnectionCount(session, 1);
 				lock (m_sessions)
 					m_sessions.AddFirst(session);
@@ -393,11 +390,11 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh
 		}
 	}
 
-	private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection connection, string logMessage, int startTickCount, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection connection, Action<ILogger, int, string, Exception?> logMessage, int startTickCount, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		var session = new ServerSession(m_connectionLogger, this, m_generation, Interlocked.Increment(ref m_lastSessionId));
-		if (Log.IsDebugEnabled())
-			Log.Debug(logMessage, m_logArguments[0], session.Id);
+		if (m_logger.IsEnabled(LogLevel.Debug))
+			logMessage(m_logger, Id, session.Id, null);
 		string? statusInfo;
 		try
 		{
@@ -413,18 +410,18 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 		if (statusInfo is not null && statusInfo.StartsWith("Location: mysql://", StringComparison.Ordinal))
 		{
 			// server redirection string has the format "Location: mysql://{host}:{port}/user={userId}[&ttl={ttl}]"
-			Log.Trace("Session{0} has server redirection header {1}", session.Id, statusInfo);
+			LogMessages.HasServerRedirectionHeader(m_logger, session.Id, statusInfo);
 
 			if (ConnectionSettings.ServerRedirectionMode == MySqlServerRedirectionMode.Disabled)
 			{
-				Log.Trace("Pool{0} server redirection is disabled; ignoring redirection", m_logArguments);
+				LogMessages.ServerRedirectionIsDisabled(m_logger, Id);
 			}
 			else if (Utility.TryParseRedirectionHeader(statusInfo, out var host, out var port, out var user))
 			{
 				if (host != ConnectionSettings.HostNames![0] || port != ConnectionSettings.Port || user != ConnectionSettings.UserID)
 				{
 					var redirectedSettings = ConnectionSettings.CloneWith(host, port, user);
-					Log.Debug("Pool{0} opening new connection to Host={1}; Port={2}; User={3}", m_logArguments[0], host, port, user);
+					LogMessages.OpeningNewConnection(m_logger, Id, host, port, user);
 					var redirectedSession = new ServerSession(m_connectionLogger, this, m_generation, Interlocked.Increment(ref m_lastSessionId));
 					try
 					{
@@ -432,13 +429,13 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 					}
 					catch (Exception ex)
 					{
-						Log.Info(ex, "Pool{0} failed to connect redirected Session{1}", m_logArguments[0], redirectedSession.Id);
+						LogMessages.FailedToConnectRedirectedSession(m_logger, ex, Id, redirectedSession.Id);
 						redirectionException = ex;
 					}
 
 					if (redirectionException is null)
 					{
-						Log.Trace("Pool{0} closing Session{1} to use redirected Session{2} instead", m_logArguments[0], session.Id, redirectedSession.Id);
+						LogMessages.ClosingSessionToUseRedirectedSession(m_logger, Id, session.Id, redirectedSession.Id);
 						await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 						return redirectedSession;
 					}
@@ -455,14 +452,14 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 				}
 				else
 				{
-					Log.Trace("Session{0} is already connected to this server; ignoring redirection", session.Id);
+					LogMessages.SessionAlreadyConnectedToServer(m_logger, session.Id);
 				}
 			}
 		}
 
 		if (ConnectionSettings.ServerRedirectionMode == MySqlServerRedirectionMode.Required)
 		{
-			Log.Error("Pool{0} requires server redirection but server doesn't support it", m_logArguments);
+			LogMessages.RequiresServerRedirection(m_logger, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support redirection", redirectionException);
 		}
 		return session;
@@ -538,9 +535,9 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 			if (connectionString != normalizedConnectionString)
 				s_pools.GetOrAdd(connectionString, pool);
 		}
-		else if (pool != newPool && Log.IsDebugEnabled())
+		else if (pool != newPool)
 		{
-			Log.Debug("Pool{0} was created but will not be used (due to race)", newPool.m_logArguments);
+			LogMessages.CreatedPoolWillNotBeUsed(newPool.m_logger, newPool.Id);
 		}
 
 		return pool;
@@ -589,7 +586,6 @@ private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration,
 			(ILoadBalancer) new RoundRobinLoadBalancer();
 
 		Id = Interlocked.Increment(ref s_poolId);
-		m_logArguments = new object[] { Id.ToString(CultureInfo.InvariantCulture) };
 		LogMessages.CreatingNewConnectionPool(m_logger, Id, cs.ConnectionStringBuilder.GetConnectionString(includePassword: false));
 	}
 
@@ -657,7 +653,7 @@ async Task RunTimer()
 		{
 			while (await m_dnsCheckTimer.WaitForNextTickAsync().ConfigureAwait(false))
 			{
-				Log.Trace("Pool{0} checking for DNS changes", m_logArguments);
+				LogMessages.CheckingForDnsChanges(m_logger, Id);
 				var hostNamesChanged = false;
 				for (var hostNameIndex = 0; hostNameIndex < hostNames.Count; hostNameIndex++)
 				{
@@ -670,7 +666,7 @@ async Task RunTimer()
 						}
 						else if (hostAddresses[hostNameIndex].Except(ipAddresses).Any())
 						{
-							Log.Debug("Pool{0} detected DNS change for HostName '{1}': {2} to {3}", m_logArguments[0], hostNames[hostNameIndex], string.Join<IPAddress>(',', hostAddresses[hostNameIndex]), string.Join<IPAddress>(',', ipAddresses));
+							LogMessages.DetectedDnsChange(m_logger, Id, hostNames[hostNameIndex], string.Join<IPAddress>(',', hostAddresses[hostNameIndex]), string.Join<IPAddress>(',', ipAddresses));
 							hostAddresses[hostNameIndex] = ipAddresses;
 							hostNamesChanged = true;
 						}
@@ -678,12 +674,12 @@ async Task RunTimer()
 					catch (Exception ex)
 					{
 						// do nothing; we'll try again later
-						Log.Debug("Pool{0} DNS check failed; ignoring HostName '{1}': {2}", m_logArguments[0], hostNames[hostNameIndex], ex.Message);
+						LogMessages.DnsCheckFailed(m_logger, ex, Id, hostNames[hostNameIndex], ex.Message);
 					}
 				}
 				if (hostNamesChanged)
 				{
-					Log.Info("Pool{0} clearing pool due to DNS changes", m_logArguments);
+					LogMessages.ClearingPoolDueToDnsChanges(m_logger, Id);
 					await ClearAsync(IOBehavior.Asynchronous, CancellationToken.None).ConfigureAwait(false);
 				}
 			}
@@ -692,7 +688,7 @@ async Task RunTimer()
 		var interval = Math.Min(int.MaxValue / 1000, ConnectionSettings.DnsCheckInterval) * 1000;
 		m_dnsCheckTimer = new Timer(t =>
 		{
-			Log.Trace("Pool{0} checking for DNS changes", m_logArguments);
+			LogMessages.CheckingForDnsChanges(m_logger, Id);
 			var hostNamesChanged = false;
 			for (var hostNameIndex = 0; hostNameIndex < hostNames.Count; hostNameIndex++)
 			{
@@ -705,7 +701,7 @@ async Task RunTimer()
 					}
 					else if (hostAddresses[hostNameIndex].Except(ipAddresses).Any())
 					{
-						Log.Debug("Pool{0} detected DNS change for HostName '{1}': {2} to {3}", m_logArguments[0], hostNames[hostNameIndex], string.Join<IPAddress>(",", hostAddresses[hostNameIndex]), string.Join<IPAddress>(",", ipAddresses));
+						LogMessages.DetectedDnsChange(m_logger, Id, hostNames[hostNameIndex], string.Join<IPAddress>(",", hostAddresses[hostNameIndex]), string.Join<IPAddress>(",", ipAddresses));
 						hostAddresses[hostNameIndex] = ipAddresses;
 						hostNamesChanged = true;
 					}
@@ -713,12 +709,12 @@ async Task RunTimer()
 				catch (Exception ex)
 				{
 					// do nothing; we'll try again later
-					Log.Debug("Pool{0} DNS check failed; ignoring HostName '{1}': {2}", m_logArguments[0], hostNames[hostNameIndex], ex.Message);
+					LogMessages.DnsCheckFailed(m_logger, ex, Id, hostNames[hostNameIndex], ex.Message);
 				}
 			}
 			if (hostNamesChanged)
 			{
-				Log.Info("Pool{0} clearing pool due to DNS changes", m_logArguments);
+				LogMessages.ClearingPoolDueToDnsChanges(m_logger, Id);
 				ClearAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 			}
 			((Timer) t!).Change(interval, -1);
@@ -770,9 +766,13 @@ static ConnectionPool()
 	private static void OnAppDomainShutDown(object? sender, EventArgs e) =>
 		ClearPoolsAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(ConnectionPool));
 	private static readonly ConcurrentDictionary<string, ConnectionPool?> s_pools = new();
-
+	private static readonly Action<ILogger, int, string, Exception?> s_createdNewSession = LoggerMessage.Define<int, string>(
+		LogLevel.Debug, new EventId(EventIds.PoolCreatedNewSession, nameof(EventIds.PoolCreatedNewSession)),
+		"Pool {PoolId} has no pooled session available; created new session {SessionId}");
+	private static readonly Action<ILogger, int, string, Exception?> s_createdToReachMinimumPoolSize = LoggerMessage.Define<int, string>(
+		LogLevel.Debug, new EventId(EventIds.CreatedSessionToReachMinimumPoolCount, nameof(EventIds.CreatedSessionToReachMinimumPoolCount)),
+		"Pool {PoolId} created session {SessionId} to reach minimum pool size");
 	private static int s_poolId;
 	private static ConnectionStringPool? s_mruCache;
 
@@ -784,7 +784,6 @@ private static void OnAppDomainShutDown(object? sender, EventArgs e) =>
 	private readonly Dictionary<string, ServerSession> m_leasedSessions;
 	private readonly ILoadBalancer? m_loadBalancer;
 	private readonly Dictionary<string, int>? m_hostSessions;
-	private readonly object[] m_logArguments;
 	private int m_generation;
 	private uint m_lastRecoveryTime;
 	private int m_lastSessionId;
diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index c39324c53..7e1d80678 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -25,4 +25,37 @@ internal static class EventIds
 
 	public const int WaitingForAvailableSession = 3000;
 	public const int CreatingNewConnectionPool = 3001;
+	public const int ScanningForLeakedSessions = 3002;
+	public const int FoundExistingSession = 3003;
+	public const int DiscardingSessionDueToWrongGeneration = 3004;
+	public const int SessionIsUnusable = 3005;
+	public const int ReturningPooledSession = 3006;
+	public const int ReturningNewSession = 3007;
+	public const int DisposingCreatedSessionDueToException = 3008;
+	public const int UnexpectedErrorInGetSessionAsync = 3009;
+	public const int ReceivingSessionBack = 3010;
+	public const int ReceivedInvalidSession = 3011;
+	public const int ReceivedExpiredSession = 3012;
+	public const int ClearingConnectionPool = 3013;
+	public const int DisposingConnectionPool = 3014;
+	public const int RecoveredNoSessions = 3015;
+	public const int RecoveredSessionCount = 3016;
+	public const int FoundSessionToCleanUp = 3017;
+	public const int PoolCreatedNewSession = 3018;
+	public const int CreatedSessionToReachMinimumPoolCount = 3019;
+	public const int CreatedPoolWillNotBeUsed = 3020;
+
+	public const int HasServerRedirectionHeader = 3100;
+	public const int ServerRedirectionIsDisabled = 3101;
+	public const int OpeningNewConnection = 3102;
+	public const int FailedToConnectRedirectedSession = 3103;
+	public const int ClosingSessionToUseRedirectedSession = 3104;
+	public const int SessionAlreadyConnectedToServer = 3105;
+	public const int RequiresServerRedirection = 3106;
+
+	public const int ReapingConnectionPool = 3200;
+	public const int CheckingForDnsChanges = 3201;
+	public const int DetectedDnsChange = 3202;
+	public const int DnsCheckFailed = 3203;
+	public const int ClearingPoolDueToDnsChanges = 3204;
 }
diff --git a/src/MySqlConnector/Logging/LogMessages.cs b/src/MySqlConnector/Logging/LogMessages.cs
index 0e78716d5..c9c61426d 100644
--- a/src/MySqlConnector/Logging/LogMessages.cs
+++ b/src/MySqlConnector/Logging/LogMessages.cs
@@ -61,4 +61,91 @@ internal static partial class LogMessages
 
 	[LoggerMessage(EventIds.CreatingNewConnectionPool, LogLevel.Information, "Pool {PoolId} creating new connection pool for {ConnectionString}")]
 	public static partial void CreatingNewConnectionPool(ILogger logger, int poolId, string connectionString);
+
+	[LoggerMessage(EventIds.ScanningForLeakedSessions, LogLevel.Debug, "Pool {PoolId} is empty; scanning for any leaked sessions")]
+	public static partial void ScanningForLeakedSessions(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.FoundExistingSession, LogLevel.Trace, "Pool {PoolId} found an existing session; checking it for validity")]
+	public static partial void FoundExistingSession(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.DiscardingSessionDueToWrongGeneration, LogLevel.Trace, "Pool {PoolId} discarding session due to wrong generation")]
+	public static partial void DiscardingSessionDueToWrongGeneration(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.SessionIsUnusable, LogLevel.Information, "Pool {PoolId} session {SessionId} is unusable; destroying it")]
+	public static partial void SessionIsUnusable(ILogger logger, int poolId, string sessionId);
+
+	[LoggerMessage(EventIds.ReturningPooledSession, LogLevel.Trace, "Pool {PoolId} returning pooled session {SessionId} to caller; {LeasedSessionsCount} leased sessions")]
+	public static partial void ReturningPooledSession(ILogger logger, int poolId, string sessionId, int leasedSessionsCount);
+
+	[LoggerMessage(EventIds.ReturningNewSession, LogLevel.Trace, "Pool {PoolId} returning new session {SessionId} to caller; {LeasedSessionsCount} leased sessions")]
+	public static partial void ReturningNewSession(ILogger logger, int poolId, string sessionId, int leasedSessionsCount);
+
+	[LoggerMessage(EventIds.DisposingCreatedSessionDueToException, LogLevel.Debug, "Pool {PoolId} disposing created session {SessionId} due to exception: {ExceptionMessage}")]
+	public static partial void DisposingCreatedSessionDueToException(ILogger logger, Exception exception, int poolId, string sessionId, string exceptionMessage);
+
+	[LoggerMessage(EventIds.UnexpectedErrorInGetSessionAsync, LogLevel.Warning, "Pool {PoolId} unexpected error in GetSessionAsync: {ExceptionMessage}")]
+	public static partial void UnexpectedErrorInGetSessionAsync(ILogger logger, Exception exception, int poolId, string exceptionMessage);
+
+	[LoggerMessage(EventIds.ReceivingSessionBack, LogLevel.Trace, "Pool {PoolId} receiving session {SessionId} back")]
+	public static partial void ReceivingSessionBack(ILogger logger, int poolId, string sessionId);
+
+	[LoggerMessage(EventIds.ReceivedInvalidSession, LogLevel.Information, "Pool {PoolId} received invalid session {SessionId}; destroying it")]
+	public static partial void ReceivedInvalidSession(ILogger logger, int poolId, string sessionId);
+
+	[LoggerMessage(EventIds.ReceivedExpiredSession, LogLevel.Debug, "Pool {PoolId} received expired session {SessionId}; destroying it")]
+	public static partial void ReceivedExpiredSession(ILogger logger, int poolId, string sessionId);
+
+	[LoggerMessage(EventIds.ClearingConnectionPool, LogLevel.Information, "Pool {PoolId} clearing connection pool")]
+	public static partial void ClearingConnectionPool(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.DisposingConnectionPool, LogLevel.Debug, "Pool {PoolId} disposing connection pool")]
+	public static partial void DisposingConnectionPool(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.RecoveredNoSessions, LogLevel.Trace, "Pool {PoolId} recovered no sessions")]
+	public static partial void RecoveredNoSessions(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.RecoveredSessionCount, LogLevel.Warning, "Pool {PoolId} recovered {SessionCount} sessions")]
+	public static partial void RecoveredSessionCount(ILogger logger, int poolId, int sessionCount);
+
+	[LoggerMessage(EventIds.FoundSessionToCleanUp, LogLevel.Debug, "Pool {PoolId} found session {SessionId} to clean up")]
+	public static partial void FoundSessionToCleanUp(ILogger logger, int poolId, string sessionId);
+
+	[LoggerMessage(EventIds.HasServerRedirectionHeader, LogLevel.Trace, "Session {SessionId} has server redirection header {Header}")]
+	public static partial void HasServerRedirectionHeader(ILogger logger, string sessionId, string header);
+
+	[LoggerMessage(EventIds.ServerRedirectionIsDisabled, LogLevel.Trace, "Pool {PoolId} server redirection is disabled; ignoring redirection")]
+	public static partial void ServerRedirectionIsDisabled(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.OpeningNewConnection, LogLevel.Debug, "Pool {PoolId} opening new connection to {Host}:{Port} as {User}")]
+	public static partial void OpeningNewConnection(ILogger logger, int poolId, string host, int port, string user);
+
+	[LoggerMessage(EventIds.FailedToConnectRedirectedSession, LogLevel.Information, "Pool {PoolId} failed to connect redirected session {SessionId}")]
+	public static partial void FailedToConnectRedirectedSession(ILogger logger, Exception ex, int poolId, string sessionId);
+
+	[LoggerMessage(EventIds.ClosingSessionToUseRedirectedSession, LogLevel.Trace, "Pool {PoolId} closing session {SessionId} to use redirected session {RedirectedSessionId} instead")]
+	public static partial void ClosingSessionToUseRedirectedSession(ILogger logger, int poolId, string sessionId, string redirectedSessionId);
+
+	[LoggerMessage(EventIds.SessionAlreadyConnectedToServer, LogLevel.Trace, "Session {SessionId} is already connected to this server; ignoring redirection")]
+	public static partial void SessionAlreadyConnectedToServer(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.RequiresServerRedirection, LogLevel.Error, "Pool {PoolId} requires server redirection but server doesn't support it")]
+	public static partial void RequiresServerRedirection(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.CreatedPoolWillNotBeUsed, LogLevel.Debug, "Pool {PoolId} was created but will not be used (due to race)")]
+	public static partial void CreatedPoolWillNotBeUsed(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.ReapingConnectionPool, LogLevel.Trace, "Pool {PoolId} reaping connection pool")]
+	public static partial void ReapingConnectionPool(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.CheckingForDnsChanges, LogLevel.Trace, "Pool {PoolId} checking for DNS changes")]
+	public static partial void CheckingForDnsChanges(ILogger logger, int poolId);
+
+	[LoggerMessage(EventIds.DetectedDnsChange, LogLevel.Debug, "Pool {PoolId} detected DNS change for '{HostName}': {OldAddresses} to {NewAddresses}")]
+	public static partial void DetectedDnsChange(ILogger logger, int poolId, string hostName, string oldAddresses, string newAddresses);
+
+	[LoggerMessage(EventIds.DnsCheckFailed, LogLevel.Debug, "Pool {PoolId} DNS check failed; ignoring '{HostName}': {ExceptionMessage}")]
+	public static partial void DnsCheckFailed(ILogger logger, Exception exception, int poolId, string hostName, string exceptionMessage);
+
+	[LoggerMessage(EventIds.ClearingPoolDueToDnsChanges, LogLevel.Information, "Pool {PoolId} clearing pool due to DNS changes")]
+	public static partial void ClearingPoolDueToDnsChanges(ILogger logger, int poolId);
 }

From d6699993d1fc34c1f0751b7d8a7588a137b54e2e Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 08:02:32 -0800
Subject: [PATCH 005/393] Migrate all ServerSession logging statements.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ServerSession.cs  | 250 ++++++++++------------
 src/MySqlConnector/Logging/EventIds.cs    |  80 ++++++-
 src/MySqlConnector/Logging/LogMessages.cs | 221 ++++++++++++++++++-
 src/MySqlConnector/MySqlConnection.cs     |   2 +-
 4 files changed, 408 insertions(+), 145 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index a0e516fb9..cb10a9931 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -23,7 +23,7 @@ namespace MySqlConnector.Core;
 
 #pragma warning disable CA1001 // Types that own disposable fields should be disposable
 
-internal sealed class ServerSession
+internal sealed partial class ServerSession
 {
 	public ServerSession(ILogger logger)
 		: this(logger, null, 0, Interlocked.Increment(ref s_lastId))
@@ -41,7 +41,6 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 		Pool = pool;
 		PoolGeneration = poolGeneration;
 		HostName = "";
-		m_logArguments = new object?[] { Id.ToString(CultureInfo.InvariantCulture), null };
 		m_activityTags = new ActivityTagsCollection();
 		LogMessages.CreatedNewSession(m_logger, Id);
 	}
@@ -69,11 +68,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 
 	public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? owningConnection)
 	{
-		if (Log.IsTraceEnabled())
-		{
-			m_logArguments[1] = Pool?.Id;
-			Log.Trace("Session{0} returning to Pool{1}", m_logArguments);
-		}
+		LogMessages.ReturningToPool(m_logger, Id, Pool?.Id ?? 0);
 		LastReturnedTicks = unchecked((uint) Environment.TickCount);
 		return Pool is null ? default : Pool.ReturnAsync(ioBehavior, this);
 	}
@@ -101,18 +96,18 @@ public bool TryStartCancel(ICancellableCommand command)
 			m_state = State.CancelingQuery;
 		}
 
-		Log.Debug("Session{0} will cancel CommandId: {1} (CancelledAttempts={2}) CommandText: {3}", m_logArguments[0], command.CommandId, command.CancelAttemptCount, (command as MySqlCommand)?.CommandText);
+		LogMessages.WillCancelCommand(m_logger, Id, command.CommandId, command.CancelAttemptCount, (command as MySqlCommand)?.CommandText);
 		return true;
 	}
 
 	public void DoCancel(ICancellableCommand commandToCancel, MySqlCommand killCommand)
 	{
-		Log.Info("Session{0} canceling CommandId {1} from Session{2}; CommandText: {3}", m_logArguments[0], commandToCancel.CommandId, killCommand.Connection!.Session.Id, (commandToCancel as MySqlCommand)?.CommandText);
+		LogMessages.CancelingCommandFromSession(m_logger, Id, commandToCancel.CommandId, killCommand.Connection!.Session.Id, (commandToCancel as MySqlCommand)?.CommandText);
 		lock (m_lock)
 		{
 			if (ActiveCommandId != commandToCancel.CommandId)
 			{
-				Log.Debug("Session{0} ActiveCommandId {1} is not the CommandId {2} being canceled; ignoring cancellation.", m_logArguments[0], ActiveCommandId, commandToCancel.CommandId);
+				LogMessages.IgnoringCancellationForInactiveCommand(m_logger, Id, ActiveCommandId, commandToCancel.CommandId);
 				return;
 			}
 
@@ -122,7 +117,7 @@ public void DoCancel(ICancellableCommand commandToCancel, MySqlCommand killComma
 			// command would be killed (because "KILL QUERY" specifies the connection whose command should be killed, not
 			// a unique identifier of the command itself). As a mitigation, we set the CommandTimeout to a low value to avoid
 			// blocking the other thread for an extended duration.
-			Log.Debug("Session{0} canceling CommandId {1} with CommandText {2}", killCommand.Connection!.Session.Id, commandToCancel.CommandId, killCommand.CommandText);
+			LogMessages.CancelingCommand(m_logger, killCommand.Connection!.Session.Id, commandToCancel.CommandId, killCommand.CommandText);
 			killCommand.ExecuteNonQuery();
 		}
 	}
@@ -280,8 +275,7 @@ public void StartQuerying(ICancellableCommand command)
 		{
 			if (m_state is State.Querying or State.CancelingQuery)
 			{
-				m_logArguments[1] = m_state;
-				Log.Error("Session{0} can't execute new command when in SessionState: {1}", m_logArguments[0], m_state);
+				CannotExecuteNewCommandInState(m_logger, Id, m_state);
 				throw new InvalidOperationException("This MySqlConnection is already in use. See https://fl.vu/mysql-conn-reuse");
 			}
 
@@ -295,8 +289,7 @@ public void StartQuerying(ICancellableCommand command)
 
 	public void FinishQuerying()
 	{
-		m_logArguments[1] = m_state;
-		Log.Trace("Session{0} entering FinishQuerying; SessionState={1}", m_logArguments);
+		EnteringFinishQuerying(m_logger, Id, m_state);
 		bool clearConnection = false;
 		lock (m_lock)
 		{
@@ -312,7 +305,7 @@ public void FinishQuerying()
 			// KILL QUERY will kill a subsequent query if the command it was intended to cancel has already completed.
 			// In order to handle this case, we issue a dummy query that will consume the pending cancellation.
 			// See https://bugs.mysql.com/bug.php?id=45679
-			Log.Debug("Session{0} sending 'SLEEP(0)' command to clear pending cancellation", m_logArguments);
+			LogMessages.SendingSleepToClearPendingCancellation(m_logger, Id);
 			var payload = SupportsQueryAttributes ? s_sleepWithAttributesPayload : s_sleepNoAttributesPayload;
 #pragma warning disable CA2012 // Safe because method completes synchronously
 			SendAsync(payload, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
@@ -363,7 +356,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			{
 				try
 				{
-					Log.Trace("Session{0} sending QUIT command", m_logArguments);
+					LogMessages.SendingQuitCommand(m_logger, Id);
 					m_payloadHandler.StartNewConversation();
 					await m_payloadHandler.WritePayloadAsync(QuitPayload.Instance.Memory, ioBehavior).ConfigureAwait(false);
 				}
@@ -446,7 +439,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				{
 					lock (m_lock)
 						m_state = State.Failed;
-					Log.Error("Session{0} connecting failed", m_logArguments);
+					LogMessages.ConnectingFailed(m_logger, Id);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Unable to connect to any of the specified MySQL hosts.");
 				}
 
@@ -464,11 +457,10 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 					authPluginName = initialHandshake.AuthPluginName!;
 				else
 					authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password";
-				m_logArguments[1] = authPluginName;
-				Log.Trace("Session{0} server sent AuthPluginName={1}", m_logArguments);
+				LogMessages.ServerSentAuthPluginName(m_logger, Id, authPluginName);
 				if (authPluginName != "mysql_native_password" && authPluginName != "sha256_password" && authPluginName != "caching_sha2_password")
 				{
-					Log.Error("Session{0} unsupported authentication method AuthPluginName={1}", m_logArguments);
+					LogMessages.UnsupportedAuthenticationMethod(m_logger, Id, authPluginName);
 					throw new NotSupportedException($"Authentication method '{initialHandshake.AuthPluginName}' is not supported.");
 				}
 
@@ -500,8 +492,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				// disable pipelining for RDS MySQL 5.7 (assuming Aurora); otherwise take it from the connection string or default to true
 				if (!cs.Pipelining.HasValue && ServerVersion.Version.Major == 5 && ServerVersion.Version.Minor == 7 && HostName.EndsWith(".rds.amazonaws.com", StringComparison.OrdinalIgnoreCase))
 				{
-					m_logArguments[1] = HostName;
-					Log.Debug("Session{0} auto-detected Aurora 5.7 at '{1}'; disabling pipelining", m_logArguments);
+					LogMessages.AutoDetectedAurora57(m_logger, Id, HostName);
 					m_supportsPipelining = false;
 				}
 				else
@@ -524,15 +515,13 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 					}
 				}
 
-				Log.Debug("Session{0} made connection; ServerVersion={1}; ConnectionId={2}; Compression={3}; Attributes={4}; DeprecateEof={5}; Ssl={6}; SessionTrack={7}; Pipelining={8}; QueryAttributes={9}",
-					m_logArguments[0], ServerVersion.OriginalString, ConnectionId,
-					m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
+				LogMessages.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
 
 				if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
 				{
 					if (!serverSupportsSsl)
 					{
-						Log.Error("Session{0} requires SSL but server doesn't support it", m_logArguments);
+						LogMessages.ServerDoesNotSupportSsl(m_logger, Id);
 						throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support SSL");
 					}
 
@@ -543,13 +532,13 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 					}
 					catch (ArgumentException ex) when (ex.ParamName == "sslProtocolType" && sslProtocols == SslProtocols.None)
 					{
-						Log.Debug(ex, "Session{0} doesn't support SslProtocols.None; falling back to explicitly specifying SslProtocols", m_logArguments);
+						LogMessages.SessionDoesNotSupportSslProtocolsNone(m_logger, ex, Id);
 						sslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
 					}
 					catch (Exception ex) when (shouldRetrySsl && ((ex is MySqlException && ex.InnerException is IOException) || ex is IOException))
 					{
 						// negotiating TLS 1.2 with a yaSSL-based server throws an exception on Windows, see comment at top of method
-						Log.Debug(ex, "Session{0} failed negotiating TLS; falling back to TLS 1.1", m_logArguments);
+						LogMessages.FailedNegotiatingTls(m_logger, ex, Id);
 						sslProtocols = sslProtocols == SslProtocols.None ? SslProtocols.Tls | SslProtocols.Tls11 : (SslProtocols.Tls | SslProtocols.Tls11) & sslProtocols;
 						if (Pool is not null)
 							Pool.SslProtocols = sslProtocols;
@@ -593,12 +582,12 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 		}
 		catch (ArgumentException ex)
 		{
-			Log.Error(ex, "Session{0} couldn't connect to server", m_logArguments);
+			LogMessages.CouldNotConnectToServer(m_logger, ex, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Couldn't connect to server", ex);
 		}
 		catch (IOException ex)
 		{
-			Log.Error(ex, "Session{0} couldn't connect to server", m_logArguments);
+			LogMessages.CouldNotConnectToServer(m_logger, ex, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Couldn't connect to server", ex);
 		}
 
@@ -617,11 +606,9 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 			PayloadData payload;
 			if (DatabaseOverride is null && (ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0 || ServerVersion.MariaDbVersion?.CompareTo(ServerVersions.MariaDbSupportsResetConnection) >= 0))
 			{
-				m_logArguments[1] = ServerVersion.OriginalString;
-
 				if (m_supportsPipelining)
 				{
-					Log.Trace("Session{0} ServerVersion={1} supports reset connection and pipelining; sending pipelined reset connection request", m_logArguments);
+					LogMessages.SendingPipelinedResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
 
 					// send both packets at once
 					await m_payloadHandler!.ByteHandler.WriteBytesAsync(m_pipelinedResetConnectionBytes!, ioBehavior).ConfigureAwait(false);
@@ -638,7 +625,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 					return true;
 				}
 
-				Log.Trace("Session{0} ServerVersion={1} supports reset connection; sending reset connection request", m_logArguments);
+				LogMessages.SendingResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
 				await SendAsync(ResetConnectionPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 				OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
@@ -648,13 +635,11 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 				// optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL)
 				if (DatabaseOverride is null)
 				{
-					m_logArguments[1] = ServerVersion.OriginalString;
-					Log.Trace("Session{0} ServerVersion={1} doesn't support reset connection; sending change user request", m_logArguments);
+					LogMessages.SendingChangeUserRequest(m_logger, Id, ServerVersion.OriginalString);
 				}
 				else
 				{
-					m_logArguments[1] = DatabaseOverride;
-					Log.Debug("Session{0} sending change user request due to changed Database={1}", m_logArguments);
+					LogMessages.SendingChangeUserRequestDueToChangedDatabase(m_logger, Id, DatabaseOverride);
 					DatabaseOverride = null;
 				}
 				var password = GetPassword(cs, connection);
@@ -664,7 +649,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 				if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature)
 				{
-					Log.Trace("Session{0} optimistic reauthentication failed; logging in again", m_logArguments);
+					LogMessages.OptimisticReauthenticationFailed(m_logger, Id);
 					payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				}
 				OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
@@ -679,19 +664,19 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 		}
 		catch (IOException ex)
 		{
-			Log.Trace(ex, "Session{0} ignoring IOException in TryResetConnectionAsync", m_logArguments);
+			LogMessages.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "IOException");
 		}
 		catch (MySqlException ex) when (ex.ErrorCode == MySqlErrorCode.ClientInteractionTimeout)
 		{
-			Log.Trace(ex, "Session{0} ignoring ClientInteractionTimeout MySqlException in TryResetConnectionAsync", m_logArguments);
+			LogMessages.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "ClientInteractionTimeout MySqlException");
 		}
 		catch (ObjectDisposedException ex)
 		{
-			Log.Trace(ex, "Session{0} ignoring ObjectDisposedException in TryResetConnectionAsync", m_logArguments);
+			LogMessages.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "ObjectDisposedException");
 		}
 		catch (SocketException ex)
 		{
-			Log.Trace(ex, "Session{0} ignoring SocketException in TryResetConnectionAsync", m_logArguments);
+			LogMessages.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "SocketException");
 		}
 
 		return false;
@@ -701,8 +686,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 	{
 		// if the server didn't support the hashed password; rehash with the new challenge
 		var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload.Span);
-		m_logArguments[1] = switchRequest.Name;
-		Log.Trace("Session{0} switching to AuthenticationMethod '{1}'", m_logArguments);
+		LogMessages.SwitchingToAuthenticationMethod(m_logger, Id, switchRequest.Name);
 		switch (switchRequest.Name)
 		{
 		case "mysql_native_password":
@@ -715,7 +699,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 		case "mysql_clear_password":
 			if (!m_isSecureConnection)
 			{
-				Log.Error("Session{0} needs a secure connection to use AuthenticationMethod '{1}'", m_logArguments);
+				LogMessages.NeedsSecureConnection(m_logger, Id, switchRequest.Name);
 				throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, $"Authentication method '{switchRequest.Name}' requires a secure connection.");
 			}
 
@@ -757,7 +741,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 			return await AuthGSSAPI.AuthenticateAsync(cs, switchRequest.Data, this, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 		case "mysql_old_password":
-			Log.Error("Session{0} is requesting AuthenticationMethod '{1}' which is not supported", m_logArguments);
+			LogMessages.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
 			throw new NotSupportedException("'MySQL Server is requesting the insecure pre-4.1 auth mechanism (mysql_old_password). The user password must be upgraded; see https://dev.mysql.com/doc/refman/5.7/en/account-upgrades.html.");
 
 		case "client_ed25519":
@@ -768,7 +752,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 			return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 		default:
-			Log.Error("Session{0} is requesting AuthenticationMethod '{1}' which is not supported", m_logArguments);
+			LogMessages.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
 			throw new NotSupportedException($"Authentication method '{switchRequest.Name}' is not supported.");
 		}
 	}
@@ -800,7 +784,7 @@ private async Task<PayloadData> SendEncryptedPasswordAsync(
 		}
 		catch (Exception ex)
 		{
-			Log.Error(ex, "Session{0} couldn't load server's RSA public key", m_logArguments);
+			LogMessages.CouldNotLoadServerRsaPublicKey(m_logger, ex, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Couldn't load server's RSA public key; try using a secure connection instead.", ex);
 		}
 #else
@@ -812,7 +796,7 @@ private async Task<PayloadData> SendEncryptedPasswordAsync(
 		}
 		catch (Exception ex)
 		{
-			Log.Error(ex, "Session{0} couldn't load server's RSA public key", m_logArguments);
+			LogMessages.CouldNotLoadServerRsaPublicKey(m_logger, ex, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Couldn't load server's RSA public key; try using a secure connection instead.", ex);
 		}
 
@@ -846,8 +830,7 @@ private async Task<string> GetRsaPublicKeyAsync(string switchRequestName, Connec
 			}
 			catch (IOException ex)
 			{
-				m_logArguments[1] = cs.ServerRsaPublicKeyFile;
-				Log.Error(ex, "Session{0} couldn't load server's RSA public key from PublicKeyFile '{1}'", m_logArguments);
+				LogMessages.CouldNotLoadServerRsaPublicKeyFromFile(m_logger, ex, Id, cs.ServerRsaPublicKeyFile);
 				throw new MySqlException($"Couldn't load server's RSA public key from '{cs.ServerRsaPublicKeyFile}'", ex);
 			}
 		}
@@ -862,8 +845,7 @@ private async Task<string> GetRsaPublicKeyAsync(string switchRequestName, Connec
 			return Encoding.ASCII.GetString(publicKeyPayload.Data);
 		}
 
-		m_logArguments[1] = switchRequestName;
-		Log.Error("Session{0} couldn't use AuthenticationMethod '{1}' because RSA key wasn't specified or couldn't be retrieved", m_logArguments);
+		LogMessages.CouldNotUseAuthenticationMethodForRsa(m_logger, Id, switchRequestName);
 		throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, $"Authentication method '{switchRequestName}' failed. Either use a secure connection, specify the server's RSA public key with ServerRSAPublicKeyFile, or set AllowPublicKeyRetrieval=True.");
 	}
 
@@ -874,27 +856,24 @@ public async ValueTask<bool> TryPingAsync(bool logInfo, IOBehavior ioBehavior, C
 		// send ping payload to verify client and server socket are still connected
 		try
 		{
-			Log.Trace("Session{0} pinging server", m_logArguments);
+			LogMessages.PingingServer(m_logger, Id);
 			await SendAsync(PingPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
 			var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 			OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
-			if (logInfo)
-				Log.Info("Session{0} successfully pinged server", m_logArguments);
-			else
-				Log.Trace("Session{0} successfully pinged server", m_logArguments);
+			LogMessages.SuccessfullyPingedServer(m_logger, logInfo ? LogLevel.Information : LogLevel.Trace, Id);
 			return true;
 		}
 		catch (IOException ex)
 		{
-			Log.Trace(ex, "Session{0} ping failed due to IOException", m_logArguments);
+			LogMessages.PingFailed(m_logger, ex, Id, "IOException");
 		}
 		catch (MySqlException ex) when (ex.ErrorCode == MySqlErrorCode.ClientInteractionTimeout)
 		{
-			Log.Trace(ex, "Session{0} ping failed due to ClientInteractionTimeout MySqlException", m_logArguments);
+			LogMessages.PingFailed(m_logger, ex, Id, "ClientInteractionTimeout MySqlException");
 		}
 		catch (SocketException ex)
 		{
-			Log.Trace(ex, "Session{0} ping failed due to SocketException", m_logArguments);
+			LogMessages.PingFailed(m_logger, ex, Id, "SocketException");
 		}
 
 		VerifyState(State.Failed);
@@ -926,7 +905,7 @@ public ValueTask<PayloadData> ReceiveReplyAsync(IOBehavior ioBehavior, Cancellat
 		}
 		catch (Exception ex)
 		{
-			Log.Debug(ex, "Session{0} failed in ReceiveReplyAsync", m_logArguments);
+			LogMessages.FailedInReceiveReplyAsync(m_logger, ex, Id);
 			if ((ex as MySqlException)?.ErrorCode == MySqlErrorCode.CommandTimeoutExpired)
 				HandleTimeout();
 			task = ValueTaskExtensions.FromException<ArraySegment<byte>>(ex);
@@ -976,7 +955,7 @@ public ValueTask SendReplyAsync(PayloadData payload, IOBehavior ioBehavior, Canc
 		}
 		catch (Exception ex)
 		{
-			Log.Debug(ex, "Session{0} failed in SendReplyAsync", m_logArguments);
+			LogMessages.FailedInSendReplyAsync(m_logger, ex, Id);
 			task = ValueTaskExtensions.FromException(ex);
 		}
 
@@ -1062,7 +1041,7 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 			catch (SocketException ex)
 			{
 				// name couldn't be resolved
-				Log.Warn("Session{0} failed to resolve HostName '{1}' ({2} of {3}): {4}", m_logArguments[0], hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
+				LogMessages.FailedToResolveHostName(m_logger, ex, Id, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
 				continue;
 			}
 
@@ -1071,8 +1050,7 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 			{
 				var ipAddress = ipAddresses[ipAddressIndex];
 				var ipAddressString = ipAddress.ToString();
-				if (Log.IsTraceEnabled())
-					Log.Trace("Session{0} connecting to IpAddress {1} ({2} of {3}) for HostName '{4}' ({5} of {6})", m_logArguments[0], ipAddressString, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count);
+				LogMessages.ConnectingToIpAddress(m_logger, Id, ipAddressString, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count);
 
 				// set activity tags for the current IP address/hostname
 				{
@@ -1127,7 +1105,7 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 						catch (Exception ex) when (cancellationToken.IsCancellationRequested && ex is ObjectDisposedException or SocketException)
 						{
 							SafeDispose(ref tcpClient);
-							Log.Info("Session{0} connect timeout expired connecting to IpAddress {1} for HostName '{2}'", m_logArguments[0], ipAddress, hostName);
+							LogMessages.ConnectTimeoutExpired(m_logger, ex, Id, ipAddressString, hostName);
 							throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.");
 						}
 					}
@@ -1142,20 +1120,20 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 						lock (m_lock)
 							m_state = State.Failed;
 						if (hostNames.Count == 1 && ipAddresses.Length == 1)
-							Log.Info("Session{0} failed to connect to IpAddress {1} for HostName '{2}': {3}", m_logArguments[0], ipAddress, hostName, ex.Message);
+							LogMessages.FailedToConnectToSingleIpAddress(m_logger, ex, Id, ipAddressString, hostName, ex.Message);
 						else
-							Log.Info("Session{0} failed to connect to IpAddress {1} ({2} of {3}) for HostName '{4}' ({5} of {6}): {7}", m_logArguments[0], ipAddress, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
+							LogMessages.FailedToConnectToIpAddress(m_logger, ex, LogLevel.Information, Id, ipAddressString, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
 						throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Unable to connect to any of the specified MySQL hosts.");
 					}
 
-					Log.Trace("Session{0} failed to connect to IpAddress {1} ({2} of {3}) for HostName '{4}' ({5} of {6}): {7}", m_logArguments[0], ipAddress, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
+					LogMessages.FailedToConnectToIpAddress(m_logger, ex, LogLevel.Trace, Id, ipAddressString, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
 					continue;
 				}
 
 				if (!tcpClient.Connected && cancellationToken.IsCancellationRequested)
 				{
 					SafeDispose(ref tcpClient);
-					Log.Info("Session{0} connect timeout expired connecting to IpAddress {1} for HostName '{2}'", m_logArguments[0], ipAddress, hostName);
+					LogMessages.ConnectTimeoutExpired(m_logger, null, Id, ipAddressString, hostName);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.");
 				}
 
@@ -1173,13 +1151,13 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 					Utility.Dispose(ref m_stream);
 					SafeDispose(ref m_tcpClient);
 					SafeDispose(ref m_socket);
-					Log.Info("Session{0} connect timeout expired connecting to IpAddress {1} for HostName '{2}'", m_logArguments[0], ipAddress, hostName);
+					LogMessages.ConnectTimeoutExpired(m_logger, null, Id, ipAddressString, hostName);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.");
 				}
 
 				lock (m_lock)
 					m_state = State.Connected;
-				Log.Trace("Session{0} connected to IpAddress {1} for HostName '{2}' with local Port {3}", m_logArguments[0], ipAddress, hostName, (m_socket.LocalEndPoint as IPEndPoint)?.Port);
+				LogMessages.ConnectedToIpAddress(m_logger, Id, ipAddressString, hostName, (m_socket.LocalEndPoint as IPEndPoint)?.Port);
 				return true;
 			}
 		}
@@ -1188,8 +1166,7 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 
 	private async Task<bool> OpenUnixSocketAsync(ConnectionSettings cs, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		m_logArguments[1] = cs.UnixSocket;
-		Log.Trace("Session{0} connecting to UNIX Socket '{1}'", m_logArguments);
+		LogMessages.ConnectingToUnixSocket(m_logger, Id, cs.UnixSocket!);
 
 		// set activity tags
 		{
@@ -1221,7 +1198,7 @@ private async Task<bool> OpenUnixSocketAsync(ConnectionSettings cs, Activity? ac
 				}
 				catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested)
 				{
-					Log.Info("Session{0} connect timeout expired connecting to UNIX Socket '{1}'", m_logArguments);
+					LogMessages.ConnectTimeoutExpiredForUnixSocket(m_logger, Id, cs.UnixSocket!);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.");
 				}
 			}
@@ -1246,13 +1223,12 @@ private async Task<bool> OpenUnixSocketAsync(ConnectionSettings cs, Activity? ac
 
 	private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, int startTickCount, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		if (Log.IsTraceEnabled())
-			Log.Trace("Session{0} connecting to NamedPipe '{1}' on Server '{2}'", m_logArguments[0], cs.PipeName, cs.HostNames![0]);
+		LogMessages.ConnectingToNamedPipe(m_logger, Id, cs.PipeName, cs.HostNames![0]);
 
 		// set activity tags
 		{
 			// see https://docs.microsoft.com/en-us/windows/win32/ipc/pipe-names for pipe name format
-			var pipeName = @"\\" + cs.HostNames![0] + @"\pipe\" + cs.PipeName;
+			var pipeName = $@"\\{cs.HostNames![0]}\pipe\{cs.PipeName}";
 			m_activityTags.Add(ActivitySourceHelper.NetTransportTagName, ActivitySourceHelper.NetTransportNamedPipeValue);
 			m_activityTags.Add(ActivitySourceHelper.NetPeerNameTagName, pipeName);
 			if (activity is { IsAllDataRequested: true })
@@ -1266,7 +1242,7 @@ private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, int startTick
 		var timeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - unchecked(Environment.TickCount - startTickCount));
 		try
 		{
-			using (cancellationToken.Register(() => namedPipeStream.Dispose()))
+			using (cancellationToken.Register(namedPipeStream.Dispose))
 			{
 				try
 				{
@@ -1277,8 +1253,7 @@ private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, int startTick
 				}
 				catch (Exception ex) when ((ex is ObjectDisposedException && cancellationToken.IsCancellationRequested) || ex is TimeoutException)
 				{
-					m_logArguments[1] = cs.PipeName;
-					Log.Info("Session{0} connect timeout expired connecting to named pipe '{1}'", m_logArguments);
+					LogMessages.ConnectTimeoutExpiredForNamedPipe(m_logger, ex, Id, cs.PipeName, cs.HostNames![0]);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.");
 				}
 			}
@@ -1302,7 +1277,7 @@ private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, int startTick
 
 	private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, MySqlConnection connection, SslProtocols sslProtocols, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		Log.Trace("Session{0} initializing TLS connection", m_logArguments);
+		LogMessages.InitializingTlsConnection(m_logger, Id);
 		X509CertificateCollection? clientCertificates = null;
 
 		if (cs.CertificateStoreLocation != MySqlCertificateStoreLocation.None)
@@ -1317,7 +1292,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 				{
 					if (store.Certificates.Count == 0)
 					{
-						Log.Error("Session{0} no certificates were found in the certificate store", m_logArguments);
+						LogMessages.NoCertificatesFound(m_logger, Id);
 						throw new MySqlException("No certificates were found in the certificate store");
 					}
 
@@ -1329,8 +1304,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 					var foundCertificates = store.Certificates.Find(X509FindType.FindByThumbprint, cs.CertificateThumbprint, requireValid);
 					if (foundCertificates.Count == 0)
 					{
-						m_logArguments[1] = cs.CertificateThumbprint;
-						Log.Error("Session{0} certificate with Thumbprint={1} not found in store", m_logArguments);
+						LogMessages.CertificateNotFoundInStore(m_logger, Id, cs.CertificateThumbprint);
 						throw new MySqlException($"Certificate with Thumbprint {cs.CertificateThumbprint} not found");
 					}
 
@@ -1339,8 +1313,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 			}
 			catch (CryptographicException ex)
 			{
-				m_logArguments[1] = cs.CertificateStoreLocation;
-				Log.Error(ex, "Session{0} couldn't load certificate from CertificateStoreLocation={1}", m_logArguments);
+				LogMessages.CouldNotLoadCertificate(m_logger, ex, Id, cs.CertificateStoreLocation);
 				throw new MySqlException("Certificate couldn't be loaded from the CertificateStoreLocation", ex);
 			}
 		}
@@ -1358,8 +1331,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 				{
 					certificate.Dispose();
 
-					m_logArguments[1] = cs.CertificateFile;
-					Log.Error("Session{0} no private key included with CertificateFile '{1}'", m_logArguments);
+					LogMessages.NoPrivateKeyIncludedWithCertificateFile(m_logger, Id, cs.CertificateFile);
 					throw new MySqlException("CertificateFile does not contain a private key. " +
 						"CertificateFile should be in PKCS #12 (.pfx) format and contain both a Certificate and Private Key");
 				}
@@ -1368,8 +1340,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 			}
 			catch (CryptographicException ex)
 			{
-				m_logArguments[1] = cs.CertificateFile;
-				Log.Error(ex, "Session{0} couldn't load certificate from CertificateFile '{1}'", m_logArguments);
+				LogMessages.CouldNotLoadCertificateFromFile(m_logger, ex, Id, cs.CertificateFile);
 				if (!File.Exists(cs.CertificateFile))
 					throw new MySqlException("Cannot find Certificate File", ex);
 				throw new MySqlException("Either the Certificate Password is incorrect or the Certificate File is invalid", ex);
@@ -1385,8 +1356,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 			}
 			catch (Exception ex)
 			{
-				m_logArguments[1] = ex.Message;
-				Log.Error(ex, "Session{0} failed to obtain client certificates via ProvideClientCertificatesCallback: {1}", m_logArguments);
+				LogMessages.FailedToObtainClientCertificates(m_logger, ex, Id, ex.Message);
 				throw new MySqlException("Failed to obtain client certificates via ProvideClientCertificatesCallback", ex);
 			}
 		}
@@ -1406,8 +1376,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 			try
 			{
 				// read the CA Certificate File
-				m_logArguments[1] = cs.CACertificateFile;
-				Log.Trace("Session{0} loading CA certificate(s) from CertificateFile '{1}'", m_logArguments);
+				LogMessages.LoadingCaCertificatesFromFile(m_logger, Id, cs.CACertificateFile);
 				byte[] certificateBytes;
 				try
 				{
@@ -1415,7 +1384,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 				}
 				catch (Exception ex)
 				{
-					Log.Error(ex, "Session{0} couldn't load CA certificate from CertificateFile '{1}'", m_logArguments);
+					LogMessages.CouldNotLoadCaCertificateFromFile(m_logger, ex, LogLevel.Error, Id, cs.CACertificateFile);
 					if (!File.Exists(cs.CACertificateFile))
 						throw new MySqlException("Cannot find CA Certificate File: " + cs.CACertificateFile, ex);
 					throw new MySqlException("Could not load CA Certificate File: " + cs.CACertificateFile, ex);
@@ -1428,8 +1397,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 					try
 					{
 						// load the certificate at this index; note that 'new X509Certificate' stops at the end of the first certificate it loads
-						m_logArguments[1] = index;
-						Log.Trace("Session{0} loading certificate at Index {1} in the CA certificate file.", m_logArguments);
+						LogMessages.LoadingCaCertificate(m_logger, Id, index);
 #if NET5_0_OR_GREATER
 						var caCertificate = new X509Certificate2(certificateBytes.AsSpan(index, (nextIndex == -1 ? certificateBytes.Length : nextIndex) - index), default(ReadOnlySpan<char>), X509KeyStorageFlags.MachineKeySet);
 #else
@@ -1439,15 +1407,13 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 					}
 					catch (CryptographicException ex)
 					{
-						m_logArguments[1] = cs.CACertificateFile;
-						Log.Warn(ex, "Session{0} couldn't load CA certificate from CertificateFile '{1}'", m_logArguments);
+						LogMessages.CouldNotLoadCaCertificateFromFile(m_logger, ex, LogLevel.Warning, Id, cs.CACertificateFile);
 					}
 					index = nextIndex;
 				}
 
 				// success
-				if (Log.IsTraceEnabled())
-					Log.Trace("Session{0} loaded certificates from CertificateFile '{1}'; CertificateCount: {2}", m_logArguments[0], cs.CACertificateFile, certificateChain.ChainPolicy.ExtraStore.Count);
+				LogMessages.LoadedCaCertificatesFromFile(m_logger, Id, certificateChain.ChainPolicy.ExtraStore.Count, cs.CACertificateFile);
 				caCertificateChain = certificateChain;
 				certificateChain = null;
 			}
@@ -1487,16 +1453,15 @@ caCertificateChain is not null &&
 		{
 			if (caCertificateChain is not null)
 			{
-				Log.Warn("Session{0} not using client-provided RemoteCertificateValidationCallback because SslCA is specified", m_logArguments);
+				LogMessages.NotUsingRemoteCertificateValidationCallbackDueToSslCa(m_logger, Id);
 			}
 			else if (cs.SslMode is not MySqlSslMode.Preferred and not MySqlSslMode.Required)
 			{
-				m_logArguments[1] = cs.SslMode;
-				Log.Warn("Session{0} not using client-provided RemoteCertificateValidationCallback because SslMode is {1}", m_logArguments);
+				LogMessages.NotUsingRemoteCertificateValidationCallbackDueToSslMode(m_logger, Id, cs.SslMode);
 			}
 			else
 			{
-				Log.Debug("Session{0} using client-provided RemoteCertificateValidationCallback", m_logArguments);
+				LogMessages.UsingRemoteCertificateValidationCallback(m_logger, Id);
 				validateRemoteCertificate = connection.RemoteCertificateValidationCallback;
 			}
 		}
@@ -1550,18 +1515,15 @@ await sslStream.AuthenticateAsClientAsync(clientAuthenticationOptions.TargetHost
 			m_payloadHandler!.ByteHandler = sslByteHandler;
 			m_isSecureConnection = true;
 			m_sslStream = sslStream;
-			if (Log.IsDebugEnabled())
-			{
 #if NETCOREAPP3_0_OR_GREATER
-				Log.Debug("Session{0} connected TLS with SslProtocol={1}, NegotiatedCipherSuite={2}", m_logArguments[0], sslStream.SslProtocol, sslStream.NegotiatedCipherSuite);
+			LogMessages.ConnectedTlsBasic(m_logger, Id, sslStream.SslProtocol, sslStream.NegotiatedCipherSuite);
 #else
-				Log.Debug("Session{0} connected TLS with SslProtocol={1}, CipherAlgorithm={2}, HashAlgorithm={3}, KeyExchangeAlgorithm={4}, KeyExchangeStrength={5}", m_logArguments[0], sslStream.SslProtocol, sslStream.CipherAlgorithm, sslStream.HashAlgorithm, sslStream.KeyExchangeAlgorithm, sslStream.KeyExchangeStrength);
+			LogMessages.ConnectedTlsDetailed(m_logger, Id, sslStream.SslProtocol, sslStream.CipherAlgorithm, sslStream.HashAlgorithm, sslStream.KeyExchangeAlgorithm, sslStream.KeyExchangeStrength);
 #endif
-			}
 		}
 		catch (Exception ex)
 		{
-			Log.Error(ex, "Session{0} couldn't initialize TLS connection", m_logArguments);
+			LogMessages.CouldNotInitializeTlsConnection(m_logger, ex, Id);
 			sslStream.Dispose();
 			ShutdownSocket();
 			HostName = "";
@@ -1589,8 +1551,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 			m_clientCertificate = X509Certificate2.CreateFromPemFile(sslCertificateFile, sslKeyFile);
 			return new() { m_clientCertificate };
 #else
-			m_logArguments[1] = sslKeyFile;
-			Log.Trace("Session{0} loading client key from KeyFile '{1}'", m_logArguments);
+			LogMessages.LoadingClientKeyFromKeyFile(m_logger, Id, sslKeyFile);
 			string keyPem;
 			try
 			{
@@ -1598,8 +1559,8 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 			}
 			catch (Exception ex)
 			{
-				Log.Error(ex, "Session{0} couldn't load client key from KeyFile '{1}'", m_logArguments);
-				throw new MySqlException("Could not load client key file: " + sslKeyFile, ex);
+				LogMessages.CouldNotLoadClientKeyFromKeyFile(m_logger, ex, Id, sslKeyFile);
+				throw new MySqlException($"Could not load the client key from '{sslKeyFile}'", ex);
 			}
 
 			RSAParameters rsaParameters;
@@ -1609,8 +1570,8 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 			}
 			catch (FormatException ex)
 			{
-				Log.Error(ex, "Session{0} couldn't load client key from KeyFile '{1}'", m_logArguments);
-				throw new MySqlException("Could not load the client key from " + sslKeyFile, ex);
+				LogMessages.CouldNotLoadClientKeyFromKeyFile(m_logger, ex, Id, sslKeyFile);
+				throw new MySqlException($"Could not load the client key from '{sslKeyFile}'", ex);
 			}
 
 			try
@@ -1652,7 +1613,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 			}
 			catch (CryptographicException ex)
 			{
-				Log.Error(ex, "Session{0} couldn't load client key from KeyFile '{1}'", m_logArguments);
+				LogMessages.CouldNotLoadClientKeyFromKeyFile(m_logger, ex, Id, sslCertificateFile);
 				if (!File.Exists(sslCertificateFile))
 					throw new MySqlException("Cannot find client certificate file: " + sslCertificateFile, ex);
 				throw new MySqlException("Could not load the client key from " + sslCertificateFile, ex);
@@ -1694,7 +1655,7 @@ private bool ShouldGetRealServerDetails(ConnectionSettings cs)
 
 	private async Task GetRealServerDetailsAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		Log.Debug("Session{0} detected proxy; getting CONNECTION_ID(), VERSION() from server", m_logArguments);
+		LogMessages.DetectedProxy(m_logger, Id);
 		try
 		{
 			var payload = SupportsQueryAttributes ? s_selectConnectionIdVersionWithAttributesPayload : s_selectConnectionIdVersionNoAttributesPayload;
@@ -1732,22 +1693,22 @@ static void ReadRow(ReadOnlySpan<byte> span, out int? connectionId, out ServerVe
 			else
 				EofPayload.Create(payload.Span);
 
-			if (connectionId.HasValue && serverVersion is not null)
+			if (connectionId is int newConnectionId && serverVersion is not null)
 			{
-				Log.Debug("Session{0} changing ConnectionIdOld {1} to ConnectionId {2} and ServerVersionOld {3} to ServerVersion {4}", m_logArguments[0], ConnectionId, connectionId.Value, ServerVersion.OriginalString, serverVersion.OriginalString);
-				ConnectionId = connectionId.Value;
+				LogMessages.ChangingConnectionId(m_logger, Id, ConnectionId, newConnectionId, ServerVersion.OriginalString, serverVersion.OriginalString);
+				ConnectionId = newConnectionId;
 				ServerVersion = serverVersion;
 			}
 		}
 		catch (MySqlException ex)
 		{
-			Log.Info(ex, "Session{0} failed to get CONNECTION_ID(), VERSION()", m_logArguments);
+			LogMessages.FailedToGetConnectionId(m_logger, ex, Id);
 		}
 	}
 
 	private void ShutdownSocket()
 	{
-		Log.Debug("Session{0} closing stream/socket", m_logArguments);
+		LogMessages.ClosingStreamSocket(m_logger, Id);
 		Utility.Dispose(ref m_payloadHandler);
 		Utility.Dispose(ref m_stream);
 		SafeDispose(ref m_tcpClient);
@@ -1783,7 +1744,7 @@ private static void SafeDispose<T>(ref T? disposable)
 
 	internal void SetFailed(Exception exception)
 	{
-		Log.Debug(exception, "Session{0} setting state to Failed", m_logArguments);
+		LogMessages.SettingStateToFailed(m_logger, exception, Id);
 		lock (m_lock)
 			m_state = State.Failed;
 		if (OwningConnection is not null && OwningConnection.TryGetTarget(out var connection))
@@ -1794,7 +1755,7 @@ private void VerifyState(State state)
 	{
 		if (m_state != state)
 		{
-			Log.Error("Session{0} should have SessionStateExpected {1} but was SessionState {2}", m_logArguments[0], state, m_state);
+			ExpectedSessionState1(m_logger, Id, state, m_state);
 			throw new InvalidOperationException($"Expected state to be {state} but was {m_state}.");
 		}
 	}
@@ -1803,8 +1764,7 @@ private void VerifyState(State state1, State state2, State state3, State state4,
 	{
 		if (m_state != state1 && m_state != state2 && m_state != state3 && m_state != state4 && m_state != state5 && m_state != state6)
 		{
-			Log.Error("Session{0} should have SessionStateExpected {1} or SessionStateExpected2 {2} or SessionStateExpected3 {3} or SessionStateExpected4 {4} or SessionStateExpected5 {5} or SessionStateExpected6 {6} but was SessionState {7}",
-				m_logArguments[0], state1, state2, state3, state4, state5, state6, m_state);
+			ExpectedSessionState6(m_logger, Id, state1, state2, state3, state4, state5, state5, m_state);
 			throw new InvalidOperationException($"Expected state to be ({state1}|{state2}|{state3}|{state4}|{state5}|{state6}) but was {m_state}.");
 		}
 	}
@@ -1821,7 +1781,7 @@ private void VerifyState(State state1, State state2, State state3, State state4,
 
 	private byte[] CreateConnectionAttributes(string programName)
 	{
-		Log.Trace("Session{0} creating connection attributes", m_logArguments);
+		LogMessages.CreatingConnectionAttributes(m_logger, Id);
 		var attributesWriter = new ByteBufferWriter();
 		attributesWriter.WriteLengthEncodedString("_client_name");
 		attributesWriter.WriteLengthEncodedString("MySqlConnector");
@@ -1830,7 +1790,7 @@ private byte[] CreateConnectionAttributes(string programName)
 		var version = typeof(ServerSession).GetTypeInfo().Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()!.InformationalVersion;
 		var plusIndex = version.IndexOf('+');
 		if (plusIndex != -1)
-			version = version.Substring(0, plusIndex);
+			version = version[..plusIndex];
 		attributesWriter.WriteLengthEncodedString(version);
 
 		try
@@ -1874,8 +1834,7 @@ private byte[] CreateConnectionAttributes(string programName)
 	private Exception CreateExceptionForErrorPayload(ReadOnlySpan<byte> span)
 	{
 		var errorPayload = ErrorPayload.Create(span);
-		if (Log.IsDebugEnabled())
-			Log.Debug("Session{0} got error payload: Code={1}, State={2}, Message={3}", m_logArguments[0], errorPayload.ErrorCode, errorPayload.State, errorPayload.Message);
+		LogMessages.ErrorPayload(m_logger, Id, errorPayload.ErrorCode, errorPayload.State, errorPayload.Message);
 		var exception = errorPayload.ToException();
 		if (exception.ErrorCode is MySqlErrorCode.ClientInteractionTimeout)
 			SetFailed(exception);
@@ -1901,13 +1860,12 @@ private string GetPassword(ConnectionSettings cs, MySqlConnection connection)
 		{
 			try
 			{
-				Log.Trace("Session{0} obtaining password via ProvidePasswordCallback", m_logArguments);
+				LogMessages.ObtainingPasswordViaProvidePasswordCallback(m_logger, Id);
 				return passwordProvider(new(HostName, cs.Port, cs.UserID, cs.Database));
 			}
 			catch (Exception ex)
 			{
-				m_logArguments[1] = ex.Message;
-				Log.Error(ex, "Session{0} failed to obtain password via ProvidePasswordCallback: {1}", m_logArguments);
+				LogMessages.FailedToObtainPassword(m_logger, ex, Id, ex.Message);
 				throw new MySqlException(MySqlErrorCode.ProvidePasswordCallbackFailed, "Failed to obtain password via ProvidePasswordCallback", ex);
 			}
 		}
@@ -1964,8 +1922,19 @@ protected override void OnStatementBegin(int index)
 		private readonly string m_sql;
 	}
 
+	[LoggerMessage(EventIds.CannotExecuteNewCommandInState, LogLevel.Error, "Session {SessionId} can't execute new command when in state {SessionState}")]
+	private static partial void CannotExecuteNewCommandInState(ILogger logger, string sessionId, State sessionState);
+
+	[LoggerMessage(EventIds.EnteringFinishQuerying, LogLevel.Trace, "Session {SessionId} entering FinishQuerying; state is {SessionState}")]
+	private static partial void EnteringFinishQuerying(ILogger logger, string sessionId, State sessionState);
+
+	[LoggerMessage(EventIds.ExpectedSessionState1, LogLevel.Error, "Session {SessionId} should have state {ExpectedState1} but was {SessionState}")]
+	private static partial void ExpectedSessionState1(ILogger logger, string sessionId, State expectedState1, State sessionState);
+
+	[LoggerMessage(EventIds.ExpectedSessionState6, LogLevel.Error, "Session {SessionId} should have state {ExpectedState1} or {ExpectedState2} or {ExpectedState3} or {ExpectedState4} or {ExpectedState5} or {ExpectedState6} but was {SessionState}")]
+	private static partial void ExpectedSessionState6(ILogger logger, string sessionId, State expectedState1, State expectedState2, State expectedState3, State expectedState4, State expectedState5, State expectedState6, State sessionState);
+
 	private static ReadOnlySpan<byte> BeginCertificateBytes => "-----BEGIN CERTIFICATE-----"u8;
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(ServerSession));
 	private static readonly PayloadData s_setNamesUtf8NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8;"u8);
 	private static readonly PayloadData s_setNamesUtf8mb4NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8mb4;"u8);
 	private static readonly PayloadData s_setNamesUtf8WithAttributesPayload = QueryPayload.Create(true, "SET NAMES utf8;"u8);
@@ -1978,7 +1947,6 @@ protected override void OnStatementBegin(int index)
 
 	private readonly ILogger m_logger;
 	private readonly object m_lock;
-	private readonly object?[] m_logArguments;
 	private readonly ArraySegmentHolder<byte> m_payloadCache;
 	private readonly ActivityTagsCollection m_activityTags;
 	private State m_state;
diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index 7e1d80678..3f3014b1e 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -8,11 +8,75 @@ internal static class EventIds
 	public const int CreatedNonPooledSession = 2000;
 	public const int ResettingConnection = 2001;
 	public const int CreatedNewSession = 2002;
+	public const int ReturningToPool = 2003;
+	public const int SendingQuitCommand = 2004;
+	public const int ConnectingFailed = 2005;
+	public const int ServerSentAuthPluginName = 2006;
+	public const int UnsupportedAuthenticationMethod = 2007;
+	public const int AutoDetectedAurora57 = 2008;
+	public const int SessionMadeConnection = 2009;
+	public const int ServerDoesNotSupportSsl = 2010;
+	public const int SessionDoesNotSupportSslProtocolsNone = 2011;
+	public const int FailedNegotiatingTls = 2012;
+	public const int CouldNotConnectToServer = 2013;
+	public const int SendingPipelinedResetConnectionRequest = 2014;
+	public const int SendingResetConnectionRequest = 2015;
+	public const int SendingChangeUserRequest = 2016;
+	public const int SendingChangeUserRequestDueToChangedDatabase = 2017;
+	public const int OptimisticReauthenticationFailed = 2018;
+	public const int IgnoringFailureInTryResetConnectionAsync = 2019;
+	public const int SwitchingToAuthenticationMethod = 2020;
+	public const int NeedsSecureConnection = 2021;
+	public const int AuthenticationMethodNotSupported = 2022;
+	public const int CouldNotLoadServerRsaPublicKey = 2023;
+	public const int CouldNotLoadServerRsaPublicKeyFromFile = 2024;
+	public const int CouldNotUseAuthenticationMethodForRsa = 2025;
+	public const int FailedToResolveHostName = 2026;
+	public const int ConnectingToIpAddress = 2027;
+	public const int ConnectTimeoutExpired = 2028;
+	public const int FailedToConnectToSingleIpAddress = 2029;
+	public const int FailedToConnectToIpAddress = 2030;
+	public const int ConnectedToIpAddress = 2031;
+	public const int ConnectingToUnixSocket = 2032;
+	public const int ConnectTimeoutExpiredForUnixSocket = 2033;
+	public const int ConnectingToNamedPipe = 2034;
+	public const int ConnectTimeoutExpiredForNamedPipe = 2035;
+	public const int InitializingTlsConnection = 2036;
+	public const int NoCertificatesFound = 2037;
+	public const int CertificateNotFoundInStore = 2038;
+	public const int CouldNotLoadCertificate = 2039;
+	public const int NoPrivateKeyIncludedWithCertificateFile = 2040;
+	public const int CouldNotLoadCertificateFromFile = 2041;
+	public const int FailedToObtainClientCertificates = 2042;
+	public const int LoadingCaCertificatesFromFile = 2043;
+	public const int CouldNotLoadCaCertificateFromFile = 2044;
+	public const int LoadingCaCertificate = 2045;
+	public const int LoadedCaCertificatesFromFile = 2046;
+	public const int NotUsingRemoteCertificateValidationCallbackDueToSslCa = 2047;
+	public const int NotUsingRemoteCertificateValidationCallbackDueToSslMode = 2048;
+	public const int UsingRemoteCertificateValidationCallback = 2049;
+	public const int ConnectedTlsBasic = 2050;
+	public const int ConnectedTlsDetailed = 2051;
+	public const int CouldNotInitializeTlsConnection = 2052;
+	public const int LoadingClientKeyFromKeyFile = 2053;
+	public const int CouldNotLoadClientKeyFromKeyFile = 2054;
+	public const int DetectedProxy = 2055;
+	public const int ChangingConnectionId = 2056;
+	public const int FailedToGetConnectionId = 2057;
+	public const int ClosingStreamSocket = 2058;
+	public const int CreatingConnectionAttributes = 2059;
+	public const int ObtainingPasswordViaProvidePasswordCallback = 2060;
+	public const int FailedToObtainPassword = 2061;
 
 	public const int IgnoringCancellationForCommand = 2100;
-	public const int CancelingCommand = 2101;
+	public const int CommandHasBeenCanceled = 2101;
 	public const int IgnoringCancellationForClosedConnection = 2102;
 	public const int CancelingCommandFailed = 2103;
+	public const int WillCancelCommand = 2104;
+	public const int CancelingCommandFromSession = 2105;
+	public const int IgnoringCancellationForInactiveCommand = 2106;
+	public const int CancelingCommand = 2107;
+	public const int SendingSleepToClearPendingCancellation = 2108;
 
 	public const int GettingCachedProcedure = 2200;
 	public const int PoolDoesNotHaveSharedProcedureCache = 2201;
@@ -23,6 +87,20 @@ internal static class EventIds
 	public const int DidNotFindCachedProcedure = 2206;
 	public const int ReturningCachedProcedure = 2207;
 
+	public const int CannotExecuteNewCommandInState = 2300;
+	public const int EnteringFinishQuerying = 2301;
+
+	public const int PingingServer = 2400;
+	public const int SuccessfullyPingedServer = 2401;
+	public const int PingFailed = 2402;
+
+	public const int FailedInReceiveReplyAsync = 2500;
+	public const int ExpectedSessionState1 = 2501;
+	public const int ExpectedSessionState6 = 2506;
+	public const int FailedInSendReplyAsync = 2510;
+	public const int SettingStateToFailed = 2511;
+	public const int ErrorPayload = 2512;
+
 	public const int WaitingForAvailableSession = 3000;
 	public const int CreatingNewConnectionPool = 3001;
 	public const int ScanningForLeakedSessions = 3002;
diff --git a/src/MySqlConnector/Logging/LogMessages.cs b/src/MySqlConnector/Logging/LogMessages.cs
index c9c61426d..0a51e31bf 100644
--- a/src/MySqlConnector/Logging/LogMessages.cs
+++ b/src/MySqlConnector/Logging/LogMessages.cs
@@ -1,4 +1,6 @@
+using System.Net.Security;
 using System.Security;
+using System.Security.Authentication;
 using Microsoft.Extensions.Logging;
 
 namespace MySqlConnector.Logging;
@@ -17,11 +19,190 @@ internal static partial class LogMessages
 	[LoggerMessage(EventIds.ResettingConnection, LogLevel.Debug, "Session {SessionId} resetting connection")]
 	public static partial void ResettingConnection(ILogger logger, string sessionId);
 
+	[LoggerMessage(EventIds.ReturningToPool, LogLevel.Trace, "Session {SessionId} returning to pool {PoolId}")]
+	public static partial void ReturningToPool(ILogger logger, string sessionId, int poolId);
+
+	[LoggerMessage(EventIds.SendingQuitCommand, LogLevel.Trace, "Session {SessionId} sending QUIT command")]
+	public static partial void SendingQuitCommand(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.ConnectingFailed, LogLevel.Error, "Session {SessionId} connecting failed")]
+	public static partial void ConnectingFailed(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.ServerSentAuthPluginName, LogLevel.Trace, "Session {SessionId} server sent auth plugin name {AuthPluginName}")]
+	public static partial void ServerSentAuthPluginName(ILogger logger, string sessionId, string authPluginName);
+
+	[LoggerMessage(EventIds.UnsupportedAuthenticationMethod, LogLevel.Error, "Session {SessionId} unsupported authentication method {AuthPluginName}")]
+	public static partial void UnsupportedAuthenticationMethod(ILogger logger, string sessionId, string authPluginName);
+
+	[LoggerMessage(EventIds.AutoDetectedAurora57, LogLevel.Debug, "Session {SessionId} auto-detected Aurora 5.7 at '{HostName}'; disabling pipelining")]
+	public static partial void AutoDetectedAurora57(ILogger logger, string sessionId, string hostName);
+
+	[LoggerMessage(EventIds.SessionMadeConnection, LogLevel.Debug, "Session {SessionId} made connection; server version {ServerVersion}; connection ID {ConnectionId}; supports: compression {SupportsCompression}, attributes {SupportsAttributes}, deprecate EOF {SupportsDeprecateEof}, SSL {SupportsSsl}, session track {SupportsSessionTrack}, pipelining {SupportsPipelining}, query attributes {SupportsQueryAttributes}")]
+	public static partial void SessionMadeConnection(ILogger logger, string sessionId, string serverVersion, int connectionId, bool supportsCompression, bool supportsAttributes, bool supportsDeprecateEof, bool supportsSsl, bool supportsSessionTrack, bool supportsPipelining, bool supportsQueryAttributes);
+
+	[LoggerMessage(EventIds.ServerDoesNotSupportSsl, LogLevel.Error, "Session {SessionId} requires SSL but server doesn't support it")]
+	public static partial void ServerDoesNotSupportSsl(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.SessionDoesNotSupportSslProtocolsNone, LogLevel.Debug, "Session {SessionId} doesn't support SslProtocols.None; falling back to explicitly specifying SslProtocols")]
+	public static partial void SessionDoesNotSupportSslProtocolsNone(ILogger logger, Exception exception, string sessionId);
+
+	[LoggerMessage(EventIds.FailedNegotiatingTls, LogLevel.Debug, "Session {SessionId} failed negotiating TLS; falling back to TLS 1.1")]
+	public static partial void FailedNegotiatingTls(ILogger logger, Exception exception, string sessionId);
+
+	[LoggerMessage(EventIds.CouldNotConnectToServer, LogLevel.Error, "Session {SessionId} couldn't connect to server")]
+	public static partial void CouldNotConnectToServer(ILogger logger, Exception exception, string sessionId);
+
+	[LoggerMessage(EventIds.SendingPipelinedResetConnectionRequest, LogLevel.Trace, "Session {SessionId} server version {ServerVersion} supports reset connection and pipelining; sending pipelined reset connection request")]
+	public static partial void SendingPipelinedResetConnectionRequest(ILogger logger, string sessionId, string serverVersion);
+
+	[LoggerMessage(EventIds.SendingResetConnectionRequest, LogLevel.Trace, "Session {SessionId} server version {ServerVersion} supports reset connection; sending reset connection request")]
+	public static partial void SendingResetConnectionRequest(ILogger logger, string sessionId, string serverVersion);
+
+	[LoggerMessage(EventIds.SendingChangeUserRequest, LogLevel.Trace, "Session {SessionId} server version {ServerVersion} doesn't support reset connection; sending change user request")]
+	public static partial void SendingChangeUserRequest(ILogger logger, string sessionId, string serverVersion);
+
+	[LoggerMessage(EventIds.SendingChangeUserRequestDueToChangedDatabase, LogLevel.Debug, "Session {SessionId} sending change user request due to changed database {Database}")]
+	public static partial void SendingChangeUserRequestDueToChangedDatabase(ILogger logger, string sessionId, string database);
+
+	[LoggerMessage(EventIds.OptimisticReauthenticationFailed, LogLevel.Trace, "Session {SessionId} optimistic reauthentication failed; logging in again")]
+	public static partial void OptimisticReauthenticationFailed(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.IgnoringFailureInTryResetConnectionAsync, LogLevel.Trace, "Session {SessionId} ignoring {Failure} in TryResetConnectionAsync")]
+	public static partial void IgnoringFailureInTryResetConnectionAsync(ILogger logger, Exception exception, string sessionId, string failure);
+
+	[LoggerMessage(EventIds.SwitchingToAuthenticationMethod, LogLevel.Trace, "Session {SessionId} switching to authentication method {AuthenticationMethod}")]
+	public static partial void SwitchingToAuthenticationMethod(ILogger logger, string sessionId, string authenticationMethod);
+
+	[LoggerMessage(EventIds.NeedsSecureConnection, LogLevel.Error, "Session {SessionId} needs a secure connection to use authentication method {AuthenticationMethod}")]
+	public static partial void NeedsSecureConnection(ILogger logger, string sessionId, string authenticationMethod);
+
+	[LoggerMessage(EventIds.AuthenticationMethodNotSupported, LogLevel.Error, "Session {SessionId} is requesting authentication method {AuthenticationMethod} which is not supported")]
+	public static partial void AuthenticationMethodNotSupported(ILogger logger, string sessionId, string authenticationMethod);
+
+	[LoggerMessage(EventIds.CouldNotLoadServerRsaPublicKey, LogLevel.Error, "Session {SessionId} couldn't load server's RSA public key")]
+	public static partial void CouldNotLoadServerRsaPublicKey(ILogger logger, Exception exception, string sessionId);
+
+	[LoggerMessage(EventIds.CouldNotLoadServerRsaPublicKeyFromFile, LogLevel.Error, "Session {SessionId} couldn't load server's RSA public key from '{PublicKeyFilePath}'")]
+	public static partial void CouldNotLoadServerRsaPublicKeyFromFile(ILogger logger, Exception exception, string sessionId, string publicKeyFilePath);
+
+	[LoggerMessage(EventIds.CouldNotUseAuthenticationMethodForRsa, LogLevel.Error, "Session {SessionId} couldn't use authentication method {AuthenticationMethod} because RSA key wasn't specified or couldn't be retrieved")]
+	public static partial void CouldNotUseAuthenticationMethodForRsa(ILogger logger, string sessionId, string authenticationMethod);
+
+	[LoggerMessage(EventIds.FailedToResolveHostName, LogLevel.Warning, "Session {SessionId} failed to resolve host name {HostName} ({HostNameIndex} of {HostNameCount}): {ExceptionMessage}")]
+	public static partial void FailedToResolveHostName(ILogger logger, Exception exception, string sessionId, string hostName, int hostNameIndex, int hostNameCount, string exceptionMessage);
+
+	[LoggerMessage(EventIds.ConnectingToIpAddress, LogLevel.Trace, "Session {SessionId} connecting to IP address {IpAddress} ({IpAddressIndex} of {IpAddressCount}) for host name {HostName} ({HostNameIndex} of {HostNameCount})")]
+	public static partial void ConnectingToIpAddress(ILogger logger, string sessionId, string ipAddress, int ipAddressIndex, int ipAddressCount, string hostName, int hostNameIndex, int hostNameCount);
+
+	[LoggerMessage(EventIds.ConnectTimeoutExpired, LogLevel.Information, "Session {SessionId} connect timeout expired connecting to IP address {IpAddress} for host name {HostName}")]
+	public static partial void ConnectTimeoutExpired(ILogger logger, Exception? exception, string sessionId, string ipAddress, string hostName);
+
+	[LoggerMessage(EventIds.FailedToConnectToSingleIpAddress, LogLevel.Information, "Session {SessionId} failed to connect to IP address {IpAddress} for host name {HostName}: {ExceptionMessage}")]
+	public static partial void FailedToConnectToSingleIpAddress(ILogger logger, Exception exception, string sessionId, string ipAddress, string hostName, string exceptionMessage);
+
+	[LoggerMessage(EventId = EventIds.FailedToConnectToIpAddress, Message = "Session {SessionId} failed to connect to IP address {IpAddress} ({IpAddressIndex} of {IpAddressCount}) for host name {HostName} ({HostNameIndex} of {HostNameCount}): {ExceptionMessage}")]
+	public static partial void FailedToConnectToIpAddress(ILogger logger, Exception exception, LogLevel logLevel, string sessionId, string ipAddress, int ipAddressIndex, int ipAddressCount, string hostName, int hostNameIndex, int hostNameCount, string exceptionMessage);
+
+	[LoggerMessage(EventIds.ConnectedToIpAddress, LogLevel.Trace, "Session {SessionId} connected to IP address {IpAddress} for host name {HostName} with local port {LocalPort}")]
+	public static partial void ConnectedToIpAddress(ILogger logger, string sessionId, string ipAddress, string hostName, int? localPort);
+
+	[LoggerMessage(EventIds.ConnectingToUnixSocket, LogLevel.Trace, "Session {SessionId} connecting to UNIX socket {SocketPath}")]
+	public static partial void ConnectingToUnixSocket(ILogger logger, string sessionId, string socketPath);
+
+	[LoggerMessage(EventIds.ConnectTimeoutExpiredForUnixSocket, LogLevel.Information, "Session {SessionId} connect timeout expired connecting to UNIX socket {SocketPath}")]
+	public static partial void ConnectTimeoutExpiredForUnixSocket(ILogger logger, string sessionId, string socketPath);
+
+	[LoggerMessage(EventIds.ConnectingToNamedPipe, LogLevel.Trace, "Session {SessionId} connecting to named pipe {PipeName} on server {HostName}")]
+	public static partial void ConnectingToNamedPipe(ILogger logger, string sessionId, string pipeName, string hostName);
+
+	[LoggerMessage(EventIds.ConnectTimeoutExpiredForNamedPipe, LogLevel.Information, "Session {SessionId} connect timeout expired connecting to named pipe {PipeName} on server {HostName}")]
+	public static partial void ConnectTimeoutExpiredForNamedPipe(ILogger logger, Exception exception, string sessionId, string pipeName, string hostName);
+
+	[LoggerMessage(EventIds.InitializingTlsConnection, LogLevel.Trace, "Session {SessionId} initializing TLS connection")]
+	public static partial void InitializingTlsConnection(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.NoCertificatesFound, LogLevel.Error, "Session {SessionId} found no certificates in the certificate store")]
+	public static partial void NoCertificatesFound(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.CertificateNotFoundInStore, LogLevel.Error, "Session {SessionId} certificate with thumbprint {Thumbprint} not found in store")]
+	public static partial void CertificateNotFoundInStore(ILogger logger, string sessionId, string thumbprint);
+
+	[LoggerMessage(EventIds.CouldNotLoadCertificate, LogLevel.Error, "Session {SessionId} couldn't load certificate from {CertificateStoreLocation}")]
+	public static partial void CouldNotLoadCertificate(ILogger logger, Exception exception, string sessionId, MySqlCertificateStoreLocation certificateStoreLocation);
+
+	[LoggerMessage(EventIds.NoPrivateKeyIncludedWithCertificateFile, LogLevel.Error, "Session {SessionId} no private key included with certificate file '{CertificateFile}'")]
+	public static partial void NoPrivateKeyIncludedWithCertificateFile(ILogger logger, string sessionId, string certificateFile);
+
+	[LoggerMessage(EventIds.CouldNotLoadCertificateFromFile, LogLevel.Error, "Session {SessionId} couldn't load certificate from '{CertificateFile}'")]
+	public static partial void CouldNotLoadCertificateFromFile(ILogger logger, Exception exception, string sessionId, string certificateFile);
+
+	[LoggerMessage(EventIds.FailedToObtainClientCertificates, LogLevel.Error, "Session {SessionId} failed to obtain client certificates via ProvideClientCertificatesCallback: {ExceptionMessage}")]
+	public static partial void FailedToObtainClientCertificates(ILogger logger, Exception exception, string sessionId, string exceptionMessage);
+
+	[LoggerMessage(EventIds.LoadingCaCertificatesFromFile, LogLevel.Trace, "Session {SessionId} loading CA certificate(s) from '{CACertificateFile}'")]
+	public static partial void LoadingCaCertificatesFromFile(ILogger logger, string sessionId, string caCertificateFile);
+
+	[LoggerMessage(EventId = EventIds.CouldNotLoadCaCertificateFromFile, Message = "Session {SessionId} couldn't load CA certificate from '{CACertificateFile}'")]
+	public static partial void CouldNotLoadCaCertificateFromFile(ILogger logger, Exception exception, LogLevel logLevel, string sessionId, string caCertificateFile);
+
+	[LoggerMessage(EventIds.LoadingCaCertificate, LogLevel.Trace, "Session {SessionId} loading certificate at index {Index} in the CA certificate file.")]
+	public static partial void LoadingCaCertificate(ILogger logger, string sessionId, int index);
+
+	[LoggerMessage(EventIds.LoadedCaCertificatesFromFile, LogLevel.Trace, "Session {SessionId} loaded {CertificateCount} certificate(s) from '{CACertificateFile}'")]
+	public static partial void LoadedCaCertificatesFromFile(ILogger logger, string sessionId, int certificateCount, string caCertificateFile);
+
+	[LoggerMessage(EventIds.NotUsingRemoteCertificateValidationCallbackDueToSslCa, LogLevel.Warning, "Session {SessionId} not using client-provided RemoteCertificateValidationCallback because SslCA is specified")]
+	public static partial void NotUsingRemoteCertificateValidationCallbackDueToSslCa(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.NotUsingRemoteCertificateValidationCallbackDueToSslMode, LogLevel.Warning, "Session {SessionId} not using client-provided RemoteCertificateValidationCallback because SslMode is {SslMode}")]
+	public static partial void NotUsingRemoteCertificateValidationCallbackDueToSslMode(ILogger logger, string sessionId, MySqlSslMode sslMode);
+
+	[LoggerMessage(EventIds.UsingRemoteCertificateValidationCallback, LogLevel.Debug, "Session {SessionId} using client-provided RemoteCertificateValidationCallback")]
+	public static partial void UsingRemoteCertificateValidationCallback(ILogger logger, string sessionId);
+
+#if NETCOREAPP3_0_OR_GREATER
+	[LoggerMessage(EventIds.ConnectedTlsBasic, LogLevel.Debug, "Session {SessionId} connected TLS with {SslProtocol}, {NegotiatedCipherSuite}")]
+	public static partial void ConnectedTlsBasic(ILogger logger, string sessionId, SslProtocols sslProtocol, TlsCipherSuite negotiatedCipherSuite);
+#endif
+
+	[LoggerMessage(EventIds.ConnectedTlsDetailed, LogLevel.Debug, "Session {SessionId} connected TLS with {SslProtocol}, {CipherAlgorithm}, {HashAlgorithm}, {KeyExchangeAlgorithm}, {KeyExchangeStrength}")]
+	public static partial void ConnectedTlsDetailed(ILogger logger, string sessionId, SslProtocols sslProtocol, CipherAlgorithmType cipherAlgorithm, HashAlgorithmType hashAlgorithm, ExchangeAlgorithmType keyExchangeAlgorithm, int keyExchangeStrength);
+
+	[LoggerMessage(EventIds.CouldNotInitializeTlsConnection, LogLevel.Error, "Session {SessionId} couldn't initialize TLS connection")]
+	public static partial void CouldNotInitializeTlsConnection(ILogger logger, Exception exception, string sessionId);
+
+	[LoggerMessage(EventIds.LoadingClientKeyFromKeyFile, LogLevel.Trace, "Session {SessionId} loading client key from '{ClientKeyFilePath}'")]
+	public static partial void LoadingClientKeyFromKeyFile(ILogger logger, string sessionId, string clientKeyFilePath);
+
+	[LoggerMessage(EventIds.CouldNotLoadClientKeyFromKeyFile, LogLevel.Error, "Session {SessionId} couldn't load client key from '{ClientKeyFilePath}'")]
+	public static partial void CouldNotLoadClientKeyFromKeyFile(ILogger logger, Exception exception, string sessionId, string clientKeyFilePath);
+
+	[LoggerMessage(EventIds.DetectedProxy, LogLevel.Debug, "Session {SessionId} detected proxy; getting CONNECTION_ID(), VERSION() from server")]
+	public static partial void DetectedProxy(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.ChangingConnectionId, LogLevel.Debug, "Session {SessionId} changing connection id from {OldConnectionId} to {ConnectionId} and server version from {OldServerVersion} to {ServerVersion}")]
+	public static partial void ChangingConnectionId(ILogger logger, string sessionId, int oldConnectionId, int connectionId, string oldServerVersion, string serverVersion);
+
+	[LoggerMessage(EventIds.FailedToGetConnectionId, LogLevel.Information, "Session {SessionId} failed to get CONNECTION_ID(), VERSION()")]
+	public static partial void FailedToGetConnectionId(ILogger logger, Exception exception, string sessionId);
+
+	[LoggerMessage(EventIds.ClosingStreamSocket, LogLevel.Debug, "Session {SessionId} closing stream/socket")]
+	public static partial void ClosingStreamSocket(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.CreatingConnectionAttributes, LogLevel.Trace, "Session {SessionId} creating connection attributes")]
+	public static partial void CreatingConnectionAttributes(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.ObtainingPasswordViaProvidePasswordCallback, LogLevel.Trace, "Session {SessionId} obtaining password via ProvidePasswordCallback")]
+	public static partial void ObtainingPasswordViaProvidePasswordCallback(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.FailedToObtainPassword, LogLevel.Error, "Session {SessionId} failed to obtain password via ProvidePasswordCallback: {ExceptionMessage}")]
+	public static partial void FailedToObtainPassword(ILogger logger, Exception exception, string sessionId, string exceptionMessage);
+
 	[LoggerMessage(EventIds.IgnoringCancellationForCommand, LogLevel.Trace, "Ignoring cancellation for closed connection or invalid command {CommandId}")]
 	public static partial void IgnoringCancellationForCommand(ILogger logger, int commandId);
 
-	[LoggerMessage(EventIds.CancelingCommand, LogLevel.Debug, "Command {CommandId} for session {SessionId} has been canceled via {CancellationSource}")]
-	public static partial void CancelingCommand(ILogger logger, int commandId, string sessionId, string cancellationSource);
+	[LoggerMessage(EventIds.CommandHasBeenCanceled, LogLevel.Debug, "Command {CommandId} for session {SessionId} has been canceled via {CancellationSource}")]
+	public static partial void CommandHasBeenCanceled(ILogger logger, int commandId, string sessionId, string cancellationSource);
 
 	[LoggerMessage(EventIds.IgnoringCancellationForClosedConnection, LogLevel.Information, "Session {SessionId} ignoring cancellation for closed connection")]
 	public static partial void IgnoringCancellationForClosedConnection(ILogger logger, Exception exception, string sessionId);
@@ -29,6 +210,21 @@ internal static partial class LogMessages
 	[LoggerMessage(EventIds.CancelingCommandFailed, LogLevel.Information, "Session {SessionId} cancelling command {CommandId} failed")]
 	public static partial void CancelingCommandFailed(ILogger logger, Exception exception, string sessionId, int commandId);
 
+	[LoggerMessage(EventIds.WillCancelCommand, LogLevel.Debug, "Session {SessionId} will cancel command {CommandId} ({CancelAttemptCount} attempts); CommandText: {CommandText}")]
+	public static partial void WillCancelCommand(ILogger logger, string sessionId, int commandId, int cancelAttemptCount, string? commandText);
+
+	[LoggerMessage(EventIds.CancelingCommandFromSession, LogLevel.Information, "Session {SessionId} canceling command {CommandId} from session {CancelingSessionId}; CommandText: {CommandText}")]
+	public static partial void CancelingCommandFromSession(ILogger logger, string sessionId, int commandId, string cancelingSessionId, string? commandText);
+
+	[LoggerMessage(EventIds.IgnoringCancellationForInactiveCommand, LogLevel.Debug, "Session {SessionId} active command {ActiveCommandId} is not the command {CommandId} being canceled; ignoring cancellation.")]
+	public static partial void IgnoringCancellationForInactiveCommand(ILogger logger, string sessionId, int activeCommandId, int commandId);
+
+	[LoggerMessage(EventIds.CancelingCommand, LogLevel.Debug, "Session {SessionId} canceling command {CommandId} with text {CommandText}")]
+	public static partial void CancelingCommand(ILogger logger, string sessionId, int commandId, string commandText);
+
+	[LoggerMessage(EventIds.SendingSleepToClearPendingCancellation, LogLevel.Debug, "Session {SessionId} sending 'SLEEP(0)' command to clear pending cancellation")]
+	public static partial void SendingSleepToClearPendingCancellation(ILogger logger, string sessionId);
+
 	[LoggerMessage(EventIds.GettingCachedProcedure, LogLevel.Trace, "Session {SessionId} getting cached procedure named {ProcedureName}")]
 	public static partial void GettingCachedProcedure(ILogger logger, string sessionId, string procedureName);
 
@@ -56,9 +252,30 @@ internal static partial class LogMessages
 	[LoggerMessage(EventIds.CreatedNewSession, LogLevel.Trace, "Session {SessionId} created new session")]
 	public static partial void CreatedNewSession(ILogger logger, string sessionId);
 
+	[LoggerMessage(EventIds.PingingServer, LogLevel.Trace, "Session {SessionId} pinging server")]
+	public static partial void PingingServer(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventId = EventIds.SuccessfullyPingedServer, Message = "Session {SessionId} successfully pinged server")]
+	public static partial void SuccessfullyPingedServer(ILogger logger, LogLevel logLevel, string sessionId);
+
+	[LoggerMessage(EventIds.PingFailed, LogLevel.Trace, "Session {SessionId} ping failed due to {Failure}")]
+	public static partial void PingFailed(ILogger logger, Exception exception, string sessionId, string failure);
+
+	[LoggerMessage(EventIds.SettingStateToFailed, LogLevel.Debug, "Session {SessionId} setting state to Failed")]
+	public static partial void SettingStateToFailed(ILogger logger, Exception exception, string sessionId);
+
+	[LoggerMessage(EventIds.ErrorPayload, LogLevel.Debug, "Session {SessionId} got error payload: {ErrorCode}, {State}, {Message}")]
+	public static partial void ErrorPayload(ILogger logger, string sessionId, int errorCode, string state, string message);
+
 	[LoggerMessage(EventIds.WaitingForAvailableSession, LogLevel.Trace, "Pool {PoolId} waiting for an available session")]
 	public static partial void WaitingForAvailableSession(ILogger logger, int poolId);
 
+	[LoggerMessage(EventIds.FailedInReceiveReplyAsync, LogLevel.Debug, "Session {SessionId} failed in ReceiveReplyAsync")]
+	public static partial void FailedInReceiveReplyAsync(ILogger logger, Exception exception, string sessionId);
+
+	[LoggerMessage(EventIds.FailedInSendReplyAsync, LogLevel.Debug, "Session {SessionId} failed in SendReplyAsync")]
+	public static partial void FailedInSendReplyAsync(ILogger logger, Exception exception, string sessionId);
+
 	[LoggerMessage(EventIds.CreatingNewConnectionPool, LogLevel.Information, "Pool {PoolId} creating new connection pool for {ConnectionString}")]
 	public static partial void CreatingNewConnectionPool(ILogger logger, int poolId, string connectionString);
 
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 30ba8f2ae..9dcd179ce 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -765,7 +765,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 			return;
 		}
 
-		LogMessages.CancelingCommand(m_logger, commandId, sessionId, isCancel ? "Cancel()" : "command timeout");
+		LogMessages.CommandHasBeenCanceled(m_logger, commandId, sessionId, isCancel ? "Cancel()" : "command timeout");
 		try
 		{
 			// open a dedicated connection to the server to kill the active query

From 0668d1d6c1313765a728cc59c58f57b27ec5dc66 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 08:14:28 -0800
Subject: [PATCH 006/393] Copy the logging configuration when cloning
 connection.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/MySqlConnection.cs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 9dcd179ce..f751b7571 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -711,7 +711,7 @@ public async Task DisposeAsync()
 		}
 	}
 
-	public MySqlConnection Clone() => new(m_connectionString, m_hasBeenOpened)
+	public MySqlConnection Clone() => new(m_connectionString, m_loggingConfiguration, m_hasBeenOpened)
 	{
 		ProvideClientCertificatesCallback = ProvideClientCertificatesCallback,
 		ProvidePasswordCallback = ProvidePasswordCallback,
@@ -736,7 +736,7 @@ public MySqlConnection CloneWith(string connectionString)
 		var shouldCopyPassword = newBuilder.Password.Length == 0 && (!newBuilder.PersistSecurityInfo || currentBuilder.PersistSecurityInfo);
 		if (shouldCopyPassword)
 			newBuilder.Password = currentBuilder.Password;
-		return new MySqlConnection(newBuilder.ConnectionString, m_hasBeenOpened && shouldCopyPassword && !currentBuilder.PersistSecurityInfo)
+		return new MySqlConnection(newBuilder.ConnectionString, m_loggingConfiguration, m_hasBeenOpened && shouldCopyPassword && !currentBuilder.PersistSecurityInfo)
 		{
 			ProvideClientCertificatesCallback = ProvideClientCertificatesCallback,
 			ProvidePasswordCallback = ProvidePasswordCallback,
@@ -989,8 +989,8 @@ internal void SetState(ConnectionState newState)
 		}
 	}
 
-	private MySqlConnection(string connectionString, bool hasBeenOpened)
-		: this(connectionString)
+	private MySqlConnection(string connectionString, MySqlConnectorLoggingConfiguration loggingConfiguration, bool hasBeenOpened)
+		: this(connectionString, loggingConfiguration)
 	{
 		m_hasBeenOpened = hasBeenOpened;
 	}

From d9e442cb3f392ca9cc209646d80568bbdbfcd3b6 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 12:44:28 -0800
Subject: [PATCH 007/393] Migrate remaining log statements.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/CachedProcedure.cs    | 11 ++--
 src/MySqlConnector/Core/CommandExecutor.cs    |  7 +--
 .../Core/ConcatenatedCommandPayloadCreator.cs |  5 +-
 src/MySqlConnector/Core/ConnectionSettings.cs |  5 --
 src/MySqlConnector/Core/IMySqlCommand.cs      |  3 ++
 .../Core/SingleCommandPayloadCreator.cs       | 12 ++---
 src/MySqlConnector/Logging/EventIds.cs        | 19 +++++++
 src/MySqlConnector/Logging/LogMessages.cs     | 52 +++++++++++++++++++
 .../Logging/LoggerExtensions.cs               | 25 ---------
 .../Logging/MySqlConnectorLogManager.cs       | 14 -----
 .../MySqlConnectorLoggingConfiguration.cs     |  2 +
 src/MySqlConnector/MySqlBatchCommand.cs       |  2 +
 src/MySqlConnector/MySqlBulkCopy.cs           | 26 +++++-----
 src/MySqlConnector/MySqlCommand.cs            |  2 +
 src/MySqlConnector/MySqlConnection.cs         | 12 ++---
 src/MySqlConnector/MySqlDataReader.cs         |  4 +-
 16 files changed, 113 insertions(+), 88 deletions(-)
 delete mode 100644 src/MySqlConnector/Logging/LoggerExtensions.cs

diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index 5ae206037..e800c30b3 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -1,6 +1,7 @@
 using System.Globalization;
 using System.Text;
 using System.Text.RegularExpressions;
+using Microsoft.Extensions.Logging;
 using MySqlConnector.Logging;
 using MySqlConnector.Protocol.Serialization;
 
@@ -8,7 +9,7 @@ namespace MySqlConnector.Core;
 
 internal sealed class CachedProcedure
 {
-	public static async Task<CachedProcedure?> FillAsync(IOBehavior ioBehavior, MySqlConnection connection, string schema, string component, CancellationToken cancellationToken)
+	public static async Task<CachedProcedure?> FillAsync(IOBehavior ioBehavior, MySqlConnection connection, string schema, string component, ILogger logger, CancellationToken cancellationToken)
 	{
 		// try to use mysql.proc first, as it is much faster
 		if (connection.Session.ServerVersion.Version < ServerVersions.RemovesMySqlProcTable && !connection.Session.ProcAccessDenied)
@@ -44,7 +45,7 @@ internal sealed class CachedProcedure
 			}
 			catch (MySqlException ex)
 			{
-				Log.Info("Session{0} failed to retrieve metadata for Schema={1} Component={2}; falling back to INFORMATION_SCHEMA. Error: {3}", connection.Session.Id, schema, component, ex.Message);
+				LogMessages.FailedToRetrieveProcedureMetadata(logger, ex, connection.Session.Id, schema, component, ex.Message);
 				if (ex.ErrorCode == MySqlErrorCode.TableAccessDenied)
 					connection.Session.ProcAccessDenied = true;
 			}
@@ -52,7 +53,7 @@ internal sealed class CachedProcedure
 
 		if (connection.Session.ServerVersion.Version < ServerVersions.SupportsProcedureCache)
 		{
-			Log.Info("Session{0} ServerVersion={1} does not support cached procedures", connection.Session.Id, connection.Session.ServerVersion.OriginalString);
+			LogMessages.ServerDoesNotSupportCachedProcedures(logger, connection.Session.Id, connection.Session.ServerVersion.OriginalString);
 			return null;
 		}
 
@@ -90,8 +91,7 @@ FROM information_schema.parameters
 			}
 		}
 
-		if (Log.IsTraceEnabled())
-			Log.Trace("Procedure for Schema={0} Component={1} has RoutineCount={2}, ParameterCount={3}", schema, component, routineCount, parameters.Count);
+		LogMessages.ProcedureHasRoutineCount(logger, schema, component, routineCount, parameters.Count);
 		return routineCount == 0 ? null : new CachedProcedure(schema, component, parameters);
 	}
 
@@ -231,7 +231,6 @@ private static CachedParameter CreateCachedParameter(int ordinal, string? direct
 
 	private string FullyQualified => $"`{m_schema}`.`{m_component}`";
 
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(CachedProcedure));
 	private static readonly IReadOnlyDictionary<string, string> s_typeMapping = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
 	{
 		{ "BOOL", "TINYINT" },
diff --git a/src/MySqlConnector/Core/CommandExecutor.cs b/src/MySqlConnector/Core/CommandExecutor.cs
index 53cc0185e..90668873a 100644
--- a/src/MySqlConnector/Core/CommandExecutor.cs
+++ b/src/MySqlConnector/Core/CommandExecutor.cs
@@ -19,8 +19,7 @@ public static async Task<MySqlDataReader> ExecuteReaderAsync(IReadOnlyList<IMySq
 			// pre-requisite: Connection is non-null must be checked before calling this method
 			var connection = command.Connection!;
 
-			if (Log.IsTraceEnabled())
-				Log.Trace("Session{0} ExecuteReader {1} CommandCount: {2}", connection.Session.Id, ioBehavior, commands.Count);
+			LogMessages.CommandExecutorExecuteReader(command.Logger, connection.Session.Id, ioBehavior, commands.Count);
 
 			Dictionary<string, CachedProcedure?>? cachedProcedures = null;
 			foreach (var command2 in commands)
@@ -57,7 +56,7 @@ public static async Task<MySqlDataReader> ExecuteReaderAsync(IReadOnlyList<IMySq
 			}
 			catch (MySqlException ex) when (ex.ErrorCode == MySqlErrorCode.QueryInterrupted && cancellationToken.IsCancellationRequested)
 			{
-				Log.Info("Session{0} query was interrupted", connection.Session.Id);
+				LogMessages.QueryWasInterrupted(command.Logger, connection.Session.Id);
 				throw new OperationCanceledException(ex.Message, ex, cancellationToken);
 			}
 			catch (Exception ex) when (payload.Span.Length > 4_194_304 && (ex is SocketException or IOException or MySqlProtocolException))
@@ -75,6 +74,4 @@ public static async Task<MySqlDataReader> ExecuteReaderAsync(IReadOnlyList<IMySq
 			throw;
 		}
 	}
-
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(CommandExecutor));
 }
diff --git a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
index 498f78da8..a87b5899e 100644
--- a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
@@ -30,8 +30,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 		do
 		{
 			var command = commandListPosition.Commands[commandListPosition.CommandIndex];
-			if (Log.IsTraceEnabled())
-				Log.Trace("Session{0} Preparing command payload; CommandText: {1}", command.Connection!.Session.Id, command.CommandText);
+			LogMessages.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 
 			isComplete = SingleCommandPayloadCreator.WriteQueryPayload(command, cachedProcedures, writer, commandListPosition.CommandIndex < commandListPosition.Commands.Count - 1 || appendSemicolon);
 			commandListPosition.CommandIndex++;
@@ -40,6 +39,4 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 
 		return true;
 	}
-
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(ConcatenatedCommandPayloadCreator));
 }
diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index ba1c34c0d..f2f0aaf20 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -111,10 +111,6 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
 		ConnectionLifeTime = Math.Min(csb.ConnectionLifeTime, uint.MaxValue / 1000) * 1000;
 		ConnectionReset = csb.ConnectionReset;
 		ConnectionIdleTimeout = (int) csb.ConnectionIdleTimeout;
-#pragma warning disable 618
-		if (!csb.DeferConnectionReset)
-			Log.Warn("DeferConnectionReset=false is not supported; using regular connection reset behavior.");
-#pragma warning restore 618
 		if (csb.MinimumPoolSize > csb.MaximumPoolSize)
 			throw new MySqlException("MaximumPoolSize must be greater than or equal to MinimumPoolSize");
 		MinimumPoolSize = ToSigned(csb.MinimumPoolSize);
@@ -331,6 +327,5 @@ private ConnectionSettings(ConnectionSettings other, string host, int port, stri
 		UseXaTransactions = other.UseXaTransactions;
 	}
 
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(ConnectionSettings));
 	private static readonly string[] s_localhostPipeServer = { "." };
 }
diff --git a/src/MySqlConnector/Core/IMySqlCommand.cs b/src/MySqlConnector/Core/IMySqlCommand.cs
index 768c7dbaa..5d4663438 100644
--- a/src/MySqlConnector/Core/IMySqlCommand.cs
+++ b/src/MySqlConnector/Core/IMySqlCommand.cs
@@ -1,3 +1,5 @@
+using Microsoft.Extensions.Logging;
+
 namespace MySqlConnector.Core;
 
 /// <summary>
@@ -18,6 +20,7 @@ internal interface IMySqlCommand
 	MySqlParameterCollection? OutParameters { get; set; }
 	MySqlParameter? ReturnParameter { get; set; }
 	ICancellableCommand CancellableCommand { get; }
+	ILogger Logger { get; }
 }
 
 internal static class IMySqlCommandExtensions
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index ef9e0c426..6457f09ba 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -22,8 +22,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 		var preparedStatements = command.TryGetPreparedStatements();
 		if (preparedStatements is null)
 		{
-			if (Log.IsTraceEnabled())
-				Log.Trace("Session{0} Preparing command payload; CommandText: {1}", command.Connection!.Session.Id, command.CommandText);
+			LogMessages.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 
 			writer.Write((byte) CommandKind.Query);
 			var supportsQueryAttributes = command.Connection!.Session.SupportsQueryAttributes;
@@ -41,7 +40,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 			}
 			else if (command.RawAttributes?.Count > 0)
 			{
-				Log.Warn("Session{0} has query attributes but server doesn't support them; CommandText: {1}", command.Connection!.Session.Id, command.CommandText);
+				LogMessages.QueryAttributesNotSupported(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 			}
 
 			WriteQueryPayload(command, cachedProcedures, writer, appendSemicolon);
@@ -78,8 +77,7 @@ private static void WritePreparedStatement(IMySqlCommand command, PreparedStatem
 	{
 		var parameterCollection = command.RawParameters;
 
-		if (Log.IsTraceEnabled())
-			Log.Trace("Session{0} Preparing command payload; CommandId: {1}; CommandText: {2}", command.Connection!.Session.Id, preparedStatement.StatementId, command.CommandText);
+		LogMessages.PreparingCommandPayloadWithId(command.Logger, command.Connection!.Session.Id, preparedStatement.StatementId, command.CommandText!);
 
 		var attributes = command.RawAttributes;
 		var supportsQueryAttributes = command.Connection!.Session.SupportsQueryAttributes;
@@ -103,7 +101,7 @@ private static void WritePreparedStatement(IMySqlCommand command, PreparedStatem
 				writer.WriteLengthEncodedInteger((uint) commandParameterCount);
 			if (attributeCount > 0)
 			{
-				Log.Warn("Session{0} has attributes for CommandId {1} but the server does not support them", command.Connection!.Session.Id, preparedStatement.StatementId);
+				LogMessages.QueryAttributesNotSupportedWithId(command.Logger, command.Connection!.Session.Id, preparedStatement.StatementId);
 				attributeCount = 0;
 			}
 		}
@@ -266,6 +264,4 @@ private static bool WriteCommand(IMySqlCommand command, ByteBufferWriter writer,
 		}
 		return isComplete;
 	}
-
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(SingleCommandPayloadCreator));
 }
diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index 3f3014b1e..a7225a70f 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -86,6 +86,9 @@ internal static class EventIds
 	public const int ProcedureCacheCount = 2205;
 	public const int DidNotFindCachedProcedure = 2206;
 	public const int ReturningCachedProcedure = 2207;
+	public const int FailedToRetrieveProcedureMetadata = 2208;
+	public const int ServerDoesNotSupportCachedProcedures = 2209;
+	public const int ProcedureHasRoutineCount = 2210;
 
 	public const int CannotExecuteNewCommandInState = 2300;
 	public const int EnteringFinishQuerying = 2301;
@@ -101,6 +104,22 @@ internal static class EventIds
 	public const int SettingStateToFailed = 2511;
 	public const int ErrorPayload = 2512;
 
+	public const int CommandExecutorExecuteReader = 2600;
+	public const int QueryWasInterrupted = 2601;
+	public const int PreparingCommandPayload = 2602;
+	public const int PreparingCommandPayloadWithId = 2603;
+	public const int QueryAttributesNotSupported = 2604;
+	public const int QueryAttributesNotSupportedWithId = 2605;
+	public const int IgnoringExceptionInDisposeAsync = 2606;
+
+	public const int StartingBulkCopy = 2700;
+	public const int AddingDefaultColumnMapping = 2701;
+	public const int IgnoringColumn = 2702;
+	public const int FinishedBulkCopy = 2703;
+	public const int BulkCopyFailed = 2704;
+	public const int ColumnMappingAlreadyHasExpression = 2705;
+	public const int SettingExpressionToMapColumn = 2706;
+
 	public const int WaitingForAvailableSession = 3000;
 	public const int CreatingNewConnectionPool = 3001;
 	public const int ScanningForLeakedSessions = 3002;
diff --git a/src/MySqlConnector/Logging/LogMessages.cs b/src/MySqlConnector/Logging/LogMessages.cs
index 0a51e31bf..6b006e4a1 100644
--- a/src/MySqlConnector/Logging/LogMessages.cs
+++ b/src/MySqlConnector/Logging/LogMessages.cs
@@ -2,6 +2,7 @@
 using System.Security;
 using System.Security.Authentication;
 using Microsoft.Extensions.Logging;
+using MySqlConnector.Protocol.Serialization;
 
 namespace MySqlConnector.Logging;
 
@@ -249,6 +250,15 @@ internal static partial class LogMessages
 	[LoggerMessage(EventIds.ReturningCachedProcedure, LogLevel.Trace, "Session {SessionId} returning cached procedure {Schema}.{Component}")]
 	public static partial void ReturningCachedProcedure(ILogger logger, string sessionId, string schema, string component);
 
+	[LoggerMessage(EventIds.FailedToRetrieveProcedureMetadata, LogLevel.Information, "Session {SessionId} failed to retrieve metadata for {Schema}.{Component}; falling back to INFORMATION_SCHEMA: {ExceptionMessage}")]
+	public static partial void FailedToRetrieveProcedureMetadata(ILogger logger, Exception exception, string sessionId, string schema, string component, string exceptionMessage);
+
+	[LoggerMessage(EventIds.ServerDoesNotSupportCachedProcedures, LogLevel.Information, "Session {SessionId} server version {ServerVersion} does not support cached procedures")]
+	public static partial void ServerDoesNotSupportCachedProcedures(ILogger logger, string sessionId, string serverVersion);
+
+	[LoggerMessage(EventIds.ProcedureHasRoutineCount, LogLevel.Trace, "Procedure for {Schema}.{Component} has {RoutineCount} routines and {ParameterCount} parameters")]
+	public static partial void ProcedureHasRoutineCount(ILogger logger, string schema, string component, int routineCount, int parameterCount);
+
 	[LoggerMessage(EventIds.CreatedNewSession, LogLevel.Trace, "Session {SessionId} created new session")]
 	public static partial void CreatedNewSession(ILogger logger, string sessionId);
 
@@ -267,6 +277,48 @@ internal static partial class LogMessages
 	[LoggerMessage(EventIds.ErrorPayload, LogLevel.Debug, "Session {SessionId} got error payload: {ErrorCode}, {State}, {Message}")]
 	public static partial void ErrorPayload(ILogger logger, string sessionId, int errorCode, string state, string message);
 
+	[LoggerMessage(EventIds.CommandExecutorExecuteReader, LogLevel.Trace, "Session {SessionId} ExecuteReader {IOBehavior} for {CommandCount} command(s)")]
+	public static partial void CommandExecutorExecuteReader(ILogger logger, string sessionId, IOBehavior ioBehavior, int commandCount);
+
+	[LoggerMessage(EventIds.QueryWasInterrupted, LogLevel.Information, "Session {SessionId} query was interrupted")]
+	public static partial void QueryWasInterrupted(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.PreparingCommandPayload, LogLevel.Trace, "Session {SessionId} preparing command payload for {CommandText}")]
+	public static partial void PreparingCommandPayload(ILogger logger, string sessionId, string commandText);
+
+	[LoggerMessage(EventIds.PreparingCommandPayloadWithId, LogLevel.Trace, "Session {SessionId} preparing statement payload with ID {StatementId} for {CommandText}")]
+	public static partial void PreparingCommandPayloadWithId(ILogger logger, string sessionId, int statementId, string commandText);
+
+	[LoggerMessage(EventIds.QueryAttributesNotSupported, LogLevel.Warning, "Session {SessionId} has query attributes but server doesn't support them; CommandText: {CommandText}")]
+	public static partial void QueryAttributesNotSupported(ILogger logger, string sessionId, string commandText);
+
+	[LoggerMessage(EventIds.QueryAttributesNotSupportedWithId, LogLevel.Warning, "Session {SessionId} has attributes for statement {StatementId} but the server does not support them")]
+	public static partial void QueryAttributesNotSupportedWithId(ILogger logger, string sessionId, int statementId);
+
+	[LoggerMessage(EventIds.IgnoringExceptionInDisposeAsync, LogLevel.Warning, "Session {SessionId} ignoring exception in MySqlDataReader.DisposeAsync. Message: {ExceptionMessage}. CommandText: {CommandText}")]
+	public static partial void IgnoringExceptionInDisposeAsync(ILogger logger, Exception exception, string sessionId, string exceptionMessage, string commandText);
+
+	[LoggerMessage(EventIds.StartingBulkCopy, LogLevel.Debug, "Starting bulk copy to {TableName}")]
+	public static partial void StartingBulkCopy(ILogger logger, string tableName);
+
+	[LoggerMessage(EventIds.AddingDefaultColumnMapping, LogLevel.Debug, "Adding default column mapping from {SourceOrdinal} to {DestinationColumn}")]
+	public static partial void AddingDefaultColumnMapping(ILogger logger, int sourceOrdinal, string destinationColumn);
+
+	[LoggerMessage(EventIds.IgnoringColumn, LogLevel.Debug, "Ignoring column with source ordinal {SourceOrdinal}")]
+	public static partial void IgnoringColumn(ILogger logger, int sourceOrdinal);
+
+	[LoggerMessage(EventIds.FinishedBulkCopy, LogLevel.Debug, "Finished bulk copy to {TableName}")]
+	public static partial void FinishedBulkCopy(ILogger logger, string tableName);
+
+	[LoggerMessage(EventIds.BulkCopyFailed, LogLevel.Error, "Bulk copy to {TableName} failed: {RowsCopied} row(s) copied; {RowsInserted} row(s) inserted")]
+	public static partial void BulkCopyFailed(ILogger logger, string tableName, int rowsCopied, int rowsInserted);
+
+	[LoggerMessage(EventIds.ColumnMappingAlreadyHasExpression, LogLevel.Information, "Column mapping for {SourceOrdinal} to {DestinationColumn} already has expression {Expression}")]
+	public static partial void ColumnMappingAlreadyHasExpression(ILogger logger, int sourceOrdinal, string destinationColumn, string expression);
+
+	[LoggerMessage(EventIds.SettingExpressionToMapColumn, LogLevel.Trace, "Setting expression to map column {SourceOrdinal} to {DestinationColumn}: {Expression}")]
+	public static partial void SettingExpressionToMapColumn(ILogger logger, int sourceOrdinal, string destinationColumn, string expression);
+
 	[LoggerMessage(EventIds.WaitingForAvailableSession, LogLevel.Trace, "Pool {PoolId} waiting for an available session")]
 	public static partial void WaitingForAvailableSession(ILogger logger, int poolId);
 
diff --git a/src/MySqlConnector/Logging/LoggerExtensions.cs b/src/MySqlConnector/Logging/LoggerExtensions.cs
deleted file mode 100644
index 79373429d..000000000
--- a/src/MySqlConnector/Logging/LoggerExtensions.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace MySqlConnector.Logging;
-
-internal static class LoggerExtensions
-{
-	public static bool IsTraceEnabled(this IMySqlConnectorLogger log) => log.IsEnabled(MySqlConnectorLogLevel.Trace);
-	public static bool IsDebugEnabled(this IMySqlConnectorLogger log) => log.IsEnabled(MySqlConnectorLogLevel.Debug);
-	public static bool IsInfoEnabled(this IMySqlConnectorLogger log) => log.IsEnabled(MySqlConnectorLogLevel.Info);
-	public static bool IsWarnEnabled(this IMySqlConnectorLogger log) => log.IsEnabled(MySqlConnectorLogLevel.Warn);
-	public static bool IsErrorEnabled(this IMySqlConnectorLogger log) => log.IsEnabled(MySqlConnectorLogLevel.Error);
-	public static bool IsFatalEnabled(this IMySqlConnectorLogger log) => log.IsEnabled(MySqlConnectorLogLevel.Fatal);
-
-	public static void Trace(this IMySqlConnectorLogger log, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Trace, message, args, null);
-	public static void Debug(this IMySqlConnectorLogger log, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Debug, message, args, null);
-	public static void Info(this IMySqlConnectorLogger log, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Info, message, args, null);
-	public static void Warn(this IMySqlConnectorLogger log, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Warn, message, args, null);
-	public static void Error(this IMySqlConnectorLogger log, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Error, message, args, null);
-	public static void Fatal(this IMySqlConnectorLogger log, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Fatal, message, args, null);
-
-	public static void Trace(this IMySqlConnectorLogger log, Exception exception, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Trace, message, args, exception);
-	public static void Debug(this IMySqlConnectorLogger log, Exception exception, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Debug, message, args, exception);
-	public static void Info(this IMySqlConnectorLogger log, Exception exception, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Info, message, args, exception);
-	public static void Warn(this IMySqlConnectorLogger log, Exception exception, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Warn, message, args, exception);
-	public static void Error(this IMySqlConnectorLogger log, Exception exception, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Error, message, args, exception);
-	public static void Fatal(this IMySqlConnectorLogger log, Exception exception, string message, params object?[] args) => log.Log(MySqlConnectorLogLevel.Fatal, message, args, exception);
-}
diff --git a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
index 77516b356..226b2cb51 100644
--- a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
+++ b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
@@ -14,26 +14,12 @@ public static class MySqlConnectorLogManager
 #pragma warning disable CA1044 // Properties should not be write only
 	public static IMySqlConnectorLoggerProvider Provider
 	{
-		internal get
-		{
-			s_providerRetrieved = true;
-			return s_provider;
-		}
 		set
 		{
-			if (s_providerRetrieved)
-				throw new InvalidOperationException("The logging provider must be set before any MySqlConnector methods are called.");
-
-			s_provider = value;
 			MySqlConnectorLoggingConfiguration.GlobalConfiguration = new(new MySqlConnectorLoggerFactor(value));
 		}
 	}
 
-	internal static IMySqlConnectorLogger CreateLogger(string name) => Provider.CreateLogger(name);
-
-	private static IMySqlConnectorLoggerProvider s_provider = new NoOpLoggerProvider();
-	private static bool s_providerRetrieved;
-
 	// A helper class that adapts ILoggerFactory to the old-style IMySqlConnectorLoggerProvider interface.
 	private sealed class MySqlConnectorLoggerFactor : ILoggerFactory
 	{
diff --git a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
index 033e27fd9..294eff95f 100644
--- a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
+++ b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
@@ -11,12 +11,14 @@ public MySqlConnectorLoggingConfiguration(ILoggerFactory loggerFactory)
 		ConnectionLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlConnection");
 		CommandLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlCommand");
 		PoolLogger = loggerFactory.CreateLogger("MySqlConnector.ConnectionPool");
+		BulkCopyLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlBulkCopy");
 	}
 
 	public ILogger DataSourceLogger { get; }
 	public ILogger ConnectionLogger { get; }
 	public ILogger CommandLogger { get; }
 	public ILogger PoolLogger { get; }
+	public ILogger BulkCopyLogger { get; }
 
 	public static MySqlConnectorLoggingConfiguration NullConfiguration { get; } = new MySqlConnectorLoggingConfiguration(NullLoggerFactory.Instance);
 	public static MySqlConnectorLoggingConfiguration GlobalConfiguration { get; set; } = NullConfiguration;
diff --git a/src/MySqlConnector/MySqlBatchCommand.cs b/src/MySqlConnector/MySqlBatchCommand.cs
index b0aebc84c..1fc7393d7 100644
--- a/src/MySqlConnector/MySqlBatchCommand.cs
+++ b/src/MySqlConnector/MySqlBatchCommand.cs
@@ -1,3 +1,4 @@
+using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
 
 namespace MySqlConnector;
@@ -72,6 +73,7 @@ public MySqlBatchCommand(string? commandText)
 	MySqlParameter? IMySqlCommand.ReturnParameter { get; set; }
 
 	ICancellableCommand IMySqlCommand.CancellableCommand => Batch!;
+	ILogger IMySqlCommand.Logger => Batch!.Connection!.LoggingConfiguration.CommandLogger;
 
 	internal MySqlBatch? Batch { get; set; }
 
diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs
index 779dd2233..0e83fff0e 100644
--- a/src/MySqlConnector/MySqlBulkCopy.cs
+++ b/src/MySqlConnector/MySqlBulkCopy.cs
@@ -3,6 +3,7 @@
 using System.Globalization;
 using System.Numerics;
 using System.Text;
+using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
 using MySqlConnector.Logging;
 using MySqlConnector.Protocol;
@@ -53,6 +54,7 @@ public MySqlBulkCopy(MySqlConnection connection, MySqlTransaction? transaction =
 	{
 		m_connection = connection ?? throw new ArgumentNullException(nameof(connection));
 		m_transaction = transaction;
+		m_logger = m_connection.LoggingConfiguration.BulkCopyLogger;
 		ColumnMappings = new();
 	}
 
@@ -238,7 +240,7 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 		var tableName = DestinationTableName ?? throw new InvalidOperationException("DestinationTableName must be set before calling WriteToServer");
 		m_wasAborted = false;
 
-		Log.Debug("Starting bulk copy to {0}", tableName);
+		LogMessages.StartingBulkCopy(m_logger, tableName);
 		var bulkLoader = new MySqlBulkLoader(m_connection)
 		{
 			CharacterSet = "utf8mb4",
@@ -274,7 +276,7 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 				var destinationColumn = reader.GetName(i);
 				if (schema[i].DataTypeName == "BIT")
 				{
-					AddColumnMapping(columnMappings, addDefaultMappings, i, destinationColumn, $"@`\uE002\bcol{i}`", $"%COL% = CAST(%VAR% AS UNSIGNED)");
+					AddColumnMapping(m_logger, columnMappings, addDefaultMappings, i, destinationColumn, $"@`\uE002\bcol{i}`", $"%COL% = CAST(%VAR% AS UNSIGNED)");
 				}
 				else if (schema[i].DataTypeName == "YEAR")
 				{
@@ -286,11 +288,11 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 					var type = schema[i].DataType;
 					if (type == typeof(byte[]) || (type == typeof(Guid) && (m_connection.GuidFormat is MySqlGuidFormat.Binary16 or MySqlGuidFormat.LittleEndianBinary16 or MySqlGuidFormat.TimeSwapBinary16)))
 					{
-						AddColumnMapping(columnMappings, addDefaultMappings, i, destinationColumn, $"@`\uE002\bcol{i}`", $"%COL% = UNHEX(%VAR%)");
+						AddColumnMapping(m_logger, columnMappings, addDefaultMappings, i, destinationColumn, $"@`\uE002\bcol{i}`", $"%COL% = UNHEX(%VAR%)");
 					}
 					else if (addDefaultMappings)
 					{
-						Log.Debug("Adding default column mapping from SourceOrdinal {0} to DestinationColumn {1}", i, destinationColumn);
+						LogMessages.AddingDefaultColumnMapping(m_logger, i, destinationColumn);
 						columnMappings.Add(new(i, destinationColumn));
 					}
 				}
@@ -303,7 +305,7 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 			var columnMapping = columnMappings.FirstOrDefault(x => x.SourceOrdinal == i);
 			if (columnMapping is null)
 			{
-				Log.Debug("Ignoring column with SourceOrdinal {0}", i);
+				LogMessages.IgnoringColumn(m_logger, i);
 				bulkLoader.Columns.Add("@`\uE002\bignore`");
 			}
 			else
@@ -342,11 +344,11 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 		if (closeConnection)
 			m_connection.Close();
 
-		Log.Debug("Finished bulk copy to {0}", tableName);
+		LogMessages.FinishedBulkCopy(m_logger, tableName);
 
 		if (!m_wasAborted && rowsInserted != m_rowsCopied && ConflictOption is MySqlBulkLoaderConflictOption.None)
 		{
-			Log.Error("Bulk copy to DestinationTableName={0} failed; RowsCopied={1}; RowsInserted={2}", tableName, m_rowsCopied, rowsInserted);
+			LogMessages.BulkCopyFailed(m_logger, tableName, m_rowsCopied, rowsInserted);
 			throw new MySqlException(MySqlErrorCode.BulkCopyFailed, $"{m_rowsCopied} row{(m_rowsCopied == 1 ? " was" : "s were")} copied to {tableName} but only {rowsInserted} {(rowsInserted == 1 ? "was" : "were")} inserted.");
 		}
 
@@ -354,7 +356,7 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 
 		static string QuoteIdentifier(string identifier) => "`" + identifier.Replace("`", "``") + "`";
 
-		static void AddColumnMapping(List<MySqlBulkCopyColumnMapping> columnMappings, bool addDefaultMappings, int destinationOrdinal, string destinationColumn, string variableName, string expression)
+		static void AddColumnMapping(ILogger logger, List<MySqlBulkCopyColumnMapping> columnMappings, bool addDefaultMappings, int destinationOrdinal, string destinationColumn, string variableName, string expression)
 		{
 			expression = expression.Replace("%COL%", "`" + destinationColumn + "`").Replace("%VAR%", variableName);
 			var columnMapping = columnMappings.FirstOrDefault(x => destinationColumn.Equals(x.DestinationColumn, StringComparison.OrdinalIgnoreCase));
@@ -362,18 +364,18 @@ static void AddColumnMapping(List<MySqlBulkCopyColumnMapping> columnMappings, bo
 			{
 				if (columnMapping.Expression is not null)
 				{
-					Log.Info("Column mapping for SourceOrdinal {0}, DestinationColumn {1} already has Expression {2}", columnMapping.SourceOrdinal, destinationColumn, columnMapping.Expression);
+					LogMessages.ColumnMappingAlreadyHasExpression(logger, columnMapping.SourceOrdinal, destinationColumn, columnMapping.Expression);
 				}
 				else
 				{
-					Log.Trace("Setting expression to map SourceOrdinal {0} to DestinationColumn {1}", columnMapping.SourceOrdinal, destinationColumn);
+					LogMessages.SettingExpressionToMapColumn(logger, columnMapping.SourceOrdinal, destinationColumn, expression);
 					columnMappings.Remove(columnMapping);
 					columnMappings.Add(new(columnMapping.SourceOrdinal, variableName, expression));
 				}
 			}
 			else if (addDefaultMappings)
 			{
-				Log.Debug("Adding default column mapping from SourceOrdinal {0} to DestinationColumn {1}", destinationOrdinal, destinationColumn);
+				LogMessages.AddingDefaultColumnMapping(logger, destinationOrdinal, destinationColumn);
 				columnMappings.Add(new(destinationOrdinal, variableName, expression));
 			}
 		}
@@ -704,11 +706,11 @@ static bool WriteBytes(ReadOnlySpan<byte> value, ref int inputIndex, Span<byte>
 	}
 
 	private static readonly char[] s_specialCharacters = new char[] { '\t', '\\', '\n' };
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(MySqlBulkCopy));
 	private static readonly Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
 
 	private readonly MySqlConnection m_connection;
 	private readonly MySqlTransaction? m_transaction;
+	private readonly ILogger m_logger;
 	private int m_rowsCopied;
 	private IValuesEnumerator? m_valuesEnumerator;
 	private bool m_wasAborted;
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index 77745ea4f..b98c96b44 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -1,5 +1,6 @@
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
 using MySqlConnector.Logging;
 using MySqlConnector.Protocol.Serialization;
@@ -445,6 +446,7 @@ private bool IsValid([NotNullWhen(false)] out Exception? exception)
 	CommandBehavior IMySqlCommand.CommandBehavior => m_commandBehavior;
 	MySqlParameterCollection? IMySqlCommand.OutParameters { get; set; }
 	MySqlParameter? IMySqlCommand.ReturnParameter { get; set; }
+	ILogger IMySqlCommand.Logger => Connection!.LoggingConfiguration.CommandLogger;
 
 	private readonly int m_commandId;
 	private bool m_isDisposed;
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index f751b7571..8e9a99402 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -46,7 +46,7 @@ private MySqlConnection(string connectionString, MySqlConnectorLoggingConfigurat
 	{
 		GC.SuppressFinalize(this);
 		m_connectionString = connectionString;
-		m_loggingConfiguration = loggingConfiguration;
+		LoggingConfiguration = loggingConfiguration;
 		m_logger = loggingConfiguration.ConnectionLogger;
 	}
 
@@ -409,7 +409,7 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 #if NET7_0_OR_GREATER
 				m_dataSource?.Pool ??
 #endif
-				ConnectionPool.GetPool(m_connectionString, m_loggingConfiguration, createIfNotFound: true);
+				ConnectionPool.GetPool(m_connectionString, LoggingConfiguration, createIfNotFound: true);
 			m_connectionSettings ??= pool?.ConnectionSettings ?? new ConnectionSettings(new MySqlConnectionStringBuilder(m_connectionString));
 
 			// check if there is an open session (in the current transaction) that can be adopted
@@ -711,7 +711,7 @@ public async Task DisposeAsync()
 		}
 	}
 
-	public MySqlConnection Clone() => new(m_connectionString, m_loggingConfiguration, m_hasBeenOpened)
+	public MySqlConnection Clone() => new(m_connectionString, LoggingConfiguration, m_hasBeenOpened)
 	{
 		ProvideClientCertificatesCallback = ProvideClientCertificatesCallback,
 		ProvidePasswordCallback = ProvidePasswordCallback,
@@ -736,7 +736,7 @@ public MySqlConnection CloneWith(string connectionString)
 		var shouldCopyPassword = newBuilder.Password.Length == 0 && (!newBuilder.PersistSecurityInfo || currentBuilder.PersistSecurityInfo);
 		if (shouldCopyPassword)
 			newBuilder.Password = currentBuilder.Password;
-		return new MySqlConnection(newBuilder.ConnectionString, m_loggingConfiguration, m_hasBeenOpened && shouldCopyPassword && !currentBuilder.PersistSecurityInfo)
+		return new MySqlConnection(newBuilder.ConnectionString, LoggingConfiguration, m_hasBeenOpened && shouldCopyPassword && !currentBuilder.PersistSecurityInfo)
 		{
 			ProvideClientCertificatesCallback = ProvideClientCertificatesCallback,
 			ProvidePasswordCallback = ProvidePasswordCallback,
@@ -831,7 +831,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 			foundProcedure = cachedProcedures.TryGetValue(normalized.FullyQualified, out cachedProcedure);
 		if (!foundProcedure || (cachedProcedure is null && revalidateMissing))
 		{
-			cachedProcedure = await CachedProcedure.FillAsync(ioBehavior, this, normalized.Schema!, normalized.Component!, cancellationToken).ConfigureAwait(false);
+			cachedProcedure = await CachedProcedure.FillAsync(ioBehavior, this, normalized.Schema!, normalized.Component!, m_logger, cancellationToken).ConfigureAwait(false);
 			if (cachedProcedure is null)
 				LogMessages.FailedToCacheProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
 			else
@@ -853,6 +853,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 	}
 
 	internal MySqlTransaction? CurrentTransaction { get; set; }
+	internal MySqlConnectorLoggingConfiguration LoggingConfiguration { get; }
 	internal bool AllowLoadLocalInfile => GetInitializedConnectionSettings().AllowLoadLocalInfile;
 	internal bool AllowUserVariables => GetInitializedConnectionSettings().AllowUserVariables;
 	internal bool AllowZeroDateTime => GetInitializedConnectionSettings().AllowZeroDateTime;
@@ -1120,7 +1121,6 @@ private ConnectionSettings GetConnectionSettings() =>
 #if NET7_0_OR_GREATER
 	private readonly MySqlDataSource? m_dataSource;
 #endif
-	private readonly MySqlConnectorLoggingConfiguration m_loggingConfiguration;
 	private readonly ILogger m_logger;
 	private string m_connectionString;
 	private ConnectionSettings? m_connectionSettings;
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 957b110e5..2ec190d30 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -617,7 +617,7 @@ internal async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancel
 				{
 					// ignore "Query execution was interrupted" exceptions when closing a data reader; log other exceptions
 					if (ex.ErrorCode != MySqlErrorCode.QueryInterrupted)
-						Log.Warn("Session{0} ignoring exception in MySqlDataReader.DisposeAsync. Message: {1}. CommandText: {2}", Command.Connection.Session.Id, ex.Message, Command.CommandText);
+						LogMessages.IgnoringExceptionInDisposeAsync(Command.Logger, ex, Command.Connection.Session.Id, ex.Message, Command.CommandText!);
 				}
 				m_resultSet = null;
 			}
@@ -683,8 +683,6 @@ private ResultSet GetResultSet()
 		return m_resultSet;
 	}
 
-	private static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(MySqlDataReader));
-
 	private readonly CommandBehavior m_behavior;
 	private readonly ICommandPayloadCreator m_payloadCreator;
 	private readonly IDictionary<string, CachedProcedure?>? m_cachedProcedures;

From 4acf1331de49c97f4572a826e41d42230b65d802 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 13:51:17 -0800
Subject: [PATCH 008/393] Rename LogMessages to Log.

Now that there is no conflict with a static Log field in each class, the logging statements read better (as a verb) this way. (The other alternative would be making them extension methods, but that might read less clearly.)

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/CachedProcedure.cs    |   6 +-
 src/MySqlConnector/Core/CommandExecutor.cs    |   4 +-
 .../Core/ConcatenatedCommandPayloadCreator.cs |   2 +-
 src/MySqlConnector/Core/ConnectionPool.cs     |  70 +++----
 src/MySqlConnector/Core/ServerSession.cs      | 172 +++++++++---------
 .../Core/SingleCommandPayloadCreator.cs       |   8 +-
 .../Logging/{LogMessages.cs => Log.cs}        |   3 +-
 src/MySqlConnector/MySqlBulkCopy.cs           |  16 +-
 src/MySqlConnector/MySqlConnection.cs         |  28 +--
 src/MySqlConnector/MySqlDataReader.cs         |   2 +-
 src/MySqlConnector/MySqlDataSource.cs         |   4 +-
 11 files changed, 157 insertions(+), 158 deletions(-)
 rename src/MySqlConnector/Logging/{LogMessages.cs => Log.cs} (99%)

diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index e800c30b3..f070384cb 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -45,7 +45,7 @@ internal sealed class CachedProcedure
 			}
 			catch (MySqlException ex)
 			{
-				LogMessages.FailedToRetrieveProcedureMetadata(logger, ex, connection.Session.Id, schema, component, ex.Message);
+				Log.FailedToRetrieveProcedureMetadata(logger, ex, connection.Session.Id, schema, component, ex.Message);
 				if (ex.ErrorCode == MySqlErrorCode.TableAccessDenied)
 					connection.Session.ProcAccessDenied = true;
 			}
@@ -53,7 +53,7 @@ internal sealed class CachedProcedure
 
 		if (connection.Session.ServerVersion.Version < ServerVersions.SupportsProcedureCache)
 		{
-			LogMessages.ServerDoesNotSupportCachedProcedures(logger, connection.Session.Id, connection.Session.ServerVersion.OriginalString);
+			Log.ServerDoesNotSupportCachedProcedures(logger, connection.Session.Id, connection.Session.ServerVersion.OriginalString);
 			return null;
 		}
 
@@ -91,7 +91,7 @@ FROM information_schema.parameters
 			}
 		}
 
-		LogMessages.ProcedureHasRoutineCount(logger, schema, component, routineCount, parameters.Count);
+		Log.ProcedureHasRoutineCount(logger, schema, component, routineCount, parameters.Count);
 		return routineCount == 0 ? null : new CachedProcedure(schema, component, parameters);
 	}
 
diff --git a/src/MySqlConnector/Core/CommandExecutor.cs b/src/MySqlConnector/Core/CommandExecutor.cs
index 90668873a..3c20a63f9 100644
--- a/src/MySqlConnector/Core/CommandExecutor.cs
+++ b/src/MySqlConnector/Core/CommandExecutor.cs
@@ -19,7 +19,7 @@ public static async Task<MySqlDataReader> ExecuteReaderAsync(IReadOnlyList<IMySq
 			// pre-requisite: Connection is non-null must be checked before calling this method
 			var connection = command.Connection!;
 
-			LogMessages.CommandExecutorExecuteReader(command.Logger, connection.Session.Id, ioBehavior, commands.Count);
+			Log.CommandExecutorExecuteReader(command.Logger, connection.Session.Id, ioBehavior, commands.Count);
 
 			Dictionary<string, CachedProcedure?>? cachedProcedures = null;
 			foreach (var command2 in commands)
@@ -56,7 +56,7 @@ public static async Task<MySqlDataReader> ExecuteReaderAsync(IReadOnlyList<IMySq
 			}
 			catch (MySqlException ex) when (ex.ErrorCode == MySqlErrorCode.QueryInterrupted && cancellationToken.IsCancellationRequested)
 			{
-				LogMessages.QueryWasInterrupted(command.Logger, connection.Session.Id);
+				Log.QueryWasInterrupted(command.Logger, connection.Session.Id);
 				throw new OperationCanceledException(ex.Message, ex, cancellationToken);
 			}
 			catch (Exception ex) when (payload.Span.Length > 4_194_304 && (ex is SocketException or IOException or MySqlProtocolException))
diff --git a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
index a87b5899e..51617c2b0 100644
--- a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
@@ -30,7 +30,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 		do
 		{
 			var command = commandListPosition.Commands[commandListPosition.CommandIndex];
-			LogMessages.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
+			Log.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 
 			isComplete = SingleCommandPayloadCreator.WriteQueryPayload(command, cachedProcedures, writer, commandListPosition.CommandIndex < commandListPosition.Commands.Count - 1 || appendSemicolon);
 			commandListPosition.CommandIndex++;
diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index f00713fa0..7d94a6f6d 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -27,7 +27,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 		// on the lock in RecoverLeakedSessions in high-concurrency situations
 		if (IsEmpty && unchecked(((uint) Environment.TickCount) - m_lastRecoveryTime) >= 1000u)
 		{
-			LogMessages.ScanningForLeakedSessions(m_logger, Id);
+			Log.ScanningForLeakedSessions(m_logger, Id);
 			await RecoverLeakedSessionsAsync(ioBehavior).ConfigureAwait(false);
 		}
 
@@ -35,7 +35,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			await CreateMinimumPooledSessions(connection, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 		// wait for an open slot (until the cancellationToken is cancelled, which is typically due to timeout)
-		LogMessages.WaitingForAvailableSession(m_logger, Id);
+		Log.WaitingForAvailableSession(m_logger, Id);
 		if (ioBehavior == IOBehavior.Asynchronous)
 			await m_sessionSemaphore.WaitAsync(cancellationToken).ConfigureAwait(false);
 		else
@@ -55,12 +55,12 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			}
 			if (session is not null)
 			{
-				LogMessages.FoundExistingSession(m_logger, Id);
+				Log.FoundExistingSession(m_logger, Id);
 				bool reuseSession;
 
 				if (session.PoolGeneration != m_generation)
 				{
-					LogMessages.DiscardingSessionDueToWrongGeneration(m_logger, Id);
+					Log.DiscardingSessionDueToWrongGeneration(m_logger, Id);
 					reuseSession = false;
 				}
 				else
@@ -74,7 +74,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 				if (!reuseSession)
 				{
 					// session is either old or cannot communicate with the server
-					LogMessages.SessionIsUnusable(m_logger, Id, session.Id);
+					Log.SessionIsUnusable(m_logger, Id, session.Id);
 					AdjustHostConnectionCount(session, -1);
 					await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 				}
@@ -89,7 +89,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 						leasedSessionsCountPooled = m_leasedSessions.Count;
 					}
 					ActivitySourceHelper.CopyTags(session.ActivityTags, activity);
-					LogMessages.ReturningPooledSession(m_logger, Id, session.Id, leasedSessionsCountPooled);
+					Log.ReturningPooledSession(m_logger, Id, session.Id, leasedSessionsCountPooled);
 					return session;
 				}
 			}
@@ -104,7 +104,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 				m_leasedSessions.Add(session.Id, session);
 				leasedSessionsCountNew = m_leasedSessions.Count;
 			}
-			LogMessages.ReturningNewSession(m_logger, Id, session.Id, leasedSessionsCountNew);
+			Log.ReturningNewSession(m_logger, Id, session.Id, leasedSessionsCountNew);
 			return session;
 		}
 		catch (Exception ex)
@@ -113,13 +113,13 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			{
 				try
 				{
-					LogMessages.DisposingCreatedSessionDueToException(m_logger, ex, Id, session.Id, ex.Message);
+					Log.DisposingCreatedSessionDueToException(m_logger, ex, Id, session.Id, ex.Message);
 					AdjustHostConnectionCount(session, -1);
 					await session.DisposeAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 				}
 				catch (Exception unexpectedException)
 				{
-					LogMessages.UnexpectedErrorInGetSessionAsync(m_logger, unexpectedException, Id, unexpectedException.Message);
+					Log.UnexpectedErrorInGetSessionAsync(m_logger, unexpectedException, Id, unexpectedException.Message);
 				}
 			}
 
@@ -150,7 +150,7 @@ private int GetSessionHealth(ServerSession session)
 
 	public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 	{
-		LogMessages.ReceivingSessionBack(m_logger, Id, session.Id);
+		Log.ReceivingSessionBack(m_logger, Id, session.Id);
 
 		try
 		{
@@ -166,9 +166,9 @@ public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 			else
 			{
 				if (sessionHealth == 1)
-					LogMessages.ReceivedInvalidSession(m_logger, Id, session.Id);
+					Log.ReceivedInvalidSession(m_logger, Id, session.Id);
 				else
-					LogMessages.ReceivedExpiredSession(m_logger, Id, session.Id);
+					Log.ReceivedExpiredSession(m_logger, Id, session.Id);
 				AdjustHostConnectionCount(session, -1);
 				await session.DisposeAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 			}
@@ -182,7 +182,7 @@ public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 	public async Task ClearAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		// increment the generation of the connection pool
-		LogMessages.ClearingConnectionPool(m_logger, Id);
+		Log.ClearingConnectionPool(m_logger, Id);
 		Interlocked.Increment(ref m_generation);
 		m_procedureCache = null;
 		await RecoverLeakedSessionsAsync(ioBehavior).ConfigureAwait(false);
@@ -191,7 +191,7 @@ public async Task ClearAsync(IOBehavior ioBehavior, CancellationToken cancellati
 
 	public async Task ReapAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		LogMessages.ReapingConnectionPool(m_logger, Id);
+		Log.ReapingConnectionPool(m_logger, Id);
 		await RecoverLeakedSessionsAsync(ioBehavior).ConfigureAwait(false);
 		await CleanPoolAsync(ioBehavior, session => (unchecked((uint) Environment.TickCount) - session.LastReturnedTicks) / 1000 >= ConnectionSettings.ConnectionIdleTimeout, true, cancellationToken).ConfigureAwait(false);
 	}
@@ -215,7 +215,7 @@ public async Task ReapAsync(IOBehavior ioBehavior, CancellationToken cancellatio
 
 	public void Dispose()
 	{
-		LogMessages.DisposingConnectionPool(m_logger, Id);
+		Log.DisposingConnectionPool(m_logger, Id);
 #if NET6_0_OR_GREATER
 		m_dnsCheckTimer?.Dispose();
 		m_dnsCheckTimer = null;
@@ -260,9 +260,9 @@ private async Task RecoverLeakedSessionsAsync(IOBehavior ioBehavior)
 			}
 		}
 		if (recoveredSessions.Count == 0)
-			LogMessages.RecoveredNoSessions(m_logger, Id);
+			Log.RecoveredNoSessions(m_logger, Id);
 		else
-			LogMessages.RecoveredSessionCount(m_logger, Id, recoveredSessions.Count);
+			Log.RecoveredSessionCount(m_logger, Id, recoveredSessions.Count);
 
 		foreach (var (session, connection) in recoveredSessions)
 		{
@@ -328,7 +328,7 @@ private async Task CleanPoolAsync(IOBehavior ioBehavior, Func<ServerSession, boo
 					if (shouldCleanFn(session))
 					{
 						// session should be cleaned; dispose it and keep iterating
-						LogMessages.FoundSessionToCleanUp(m_logger, Id, session.Id);
+						Log.FoundSessionToCleanUp(m_logger, Id, session.Id);
 						await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 					}
 					else
@@ -410,18 +410,18 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 		if (statusInfo is not null && statusInfo.StartsWith("Location: mysql://", StringComparison.Ordinal))
 		{
 			// server redirection string has the format "Location: mysql://{host}:{port}/user={userId}[&ttl={ttl}]"
-			LogMessages.HasServerRedirectionHeader(m_logger, session.Id, statusInfo);
+			Log.HasServerRedirectionHeader(m_logger, session.Id, statusInfo);
 
 			if (ConnectionSettings.ServerRedirectionMode == MySqlServerRedirectionMode.Disabled)
 			{
-				LogMessages.ServerRedirectionIsDisabled(m_logger, Id);
+				Log.ServerRedirectionIsDisabled(m_logger, Id);
 			}
 			else if (Utility.TryParseRedirectionHeader(statusInfo, out var host, out var port, out var user))
 			{
 				if (host != ConnectionSettings.HostNames![0] || port != ConnectionSettings.Port || user != ConnectionSettings.UserID)
 				{
 					var redirectedSettings = ConnectionSettings.CloneWith(host, port, user);
-					LogMessages.OpeningNewConnection(m_logger, Id, host, port, user);
+					Log.OpeningNewConnection(m_logger, Id, host, port, user);
 					var redirectedSession = new ServerSession(m_connectionLogger, this, m_generation, Interlocked.Increment(ref m_lastSessionId));
 					try
 					{
@@ -429,13 +429,13 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 					}
 					catch (Exception ex)
 					{
-						LogMessages.FailedToConnectRedirectedSession(m_logger, ex, Id, redirectedSession.Id);
+						Log.FailedToConnectRedirectedSession(m_logger, ex, Id, redirectedSession.Id);
 						redirectionException = ex;
 					}
 
 					if (redirectionException is null)
 					{
-						LogMessages.ClosingSessionToUseRedirectedSession(m_logger, Id, session.Id, redirectedSession.Id);
+						Log.ClosingSessionToUseRedirectedSession(m_logger, Id, session.Id, redirectedSession.Id);
 						await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 						return redirectedSession;
 					}
@@ -452,14 +452,14 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 				}
 				else
 				{
-					LogMessages.SessionAlreadyConnectedToServer(m_logger, session.Id);
+					Log.SessionAlreadyConnectedToServer(m_logger, session.Id);
 				}
 			}
 		}
 
 		if (ConnectionSettings.ServerRedirectionMode == MySqlServerRedirectionMode.Required)
 		{
-			LogMessages.RequiresServerRedirection(m_logger, Id);
+			Log.RequiresServerRedirection(m_logger, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support redirection", redirectionException);
 		}
 		return session;
@@ -537,7 +537,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 		}
 		else if (pool != newPool)
 		{
-			LogMessages.CreatedPoolWillNotBeUsed(newPool.m_logger, newPool.Id);
+			Log.CreatedPoolWillNotBeUsed(newPool.m_logger, newPool.Id);
 		}
 
 		return pool;
@@ -586,7 +586,7 @@ private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration,
 			(ILoadBalancer) new RoundRobinLoadBalancer();
 
 		Id = Interlocked.Increment(ref s_poolId);
-		LogMessages.CreatingNewConnectionPool(m_logger, Id, cs.ConnectionStringBuilder.GetConnectionString(includePassword: false));
+		Log.CreatingNewConnectionPool(m_logger, Id, cs.ConnectionStringBuilder.GetConnectionString(includePassword: false));
 	}
 
 	private void StartReaperTask()
@@ -653,7 +653,7 @@ async Task RunTimer()
 		{
 			while (await m_dnsCheckTimer.WaitForNextTickAsync().ConfigureAwait(false))
 			{
-				LogMessages.CheckingForDnsChanges(m_logger, Id);
+				Log.CheckingForDnsChanges(m_logger, Id);
 				var hostNamesChanged = false;
 				for (var hostNameIndex = 0; hostNameIndex < hostNames.Count; hostNameIndex++)
 				{
@@ -666,7 +666,7 @@ async Task RunTimer()
 						}
 						else if (hostAddresses[hostNameIndex].Except(ipAddresses).Any())
 						{
-							LogMessages.DetectedDnsChange(m_logger, Id, hostNames[hostNameIndex], string.Join<IPAddress>(',', hostAddresses[hostNameIndex]), string.Join<IPAddress>(',', ipAddresses));
+							Log.DetectedDnsChange(m_logger, Id, hostNames[hostNameIndex], string.Join<IPAddress>(',', hostAddresses[hostNameIndex]), string.Join<IPAddress>(',', ipAddresses));
 							hostAddresses[hostNameIndex] = ipAddresses;
 							hostNamesChanged = true;
 						}
@@ -674,12 +674,12 @@ async Task RunTimer()
 					catch (Exception ex)
 					{
 						// do nothing; we'll try again later
-						LogMessages.DnsCheckFailed(m_logger, ex, Id, hostNames[hostNameIndex], ex.Message);
+						Log.DnsCheckFailed(m_logger, ex, Id, hostNames[hostNameIndex], ex.Message);
 					}
 				}
 				if (hostNamesChanged)
 				{
-					LogMessages.ClearingPoolDueToDnsChanges(m_logger, Id);
+					Log.ClearingPoolDueToDnsChanges(m_logger, Id);
 					await ClearAsync(IOBehavior.Asynchronous, CancellationToken.None).ConfigureAwait(false);
 				}
 			}
@@ -688,7 +688,7 @@ async Task RunTimer()
 		var interval = Math.Min(int.MaxValue / 1000, ConnectionSettings.DnsCheckInterval) * 1000;
 		m_dnsCheckTimer = new Timer(t =>
 		{
-			LogMessages.CheckingForDnsChanges(m_logger, Id);
+			Log.CheckingForDnsChanges(m_logger, Id);
 			var hostNamesChanged = false;
 			for (var hostNameIndex = 0; hostNameIndex < hostNames.Count; hostNameIndex++)
 			{
@@ -701,7 +701,7 @@ async Task RunTimer()
 					}
 					else if (hostAddresses[hostNameIndex].Except(ipAddresses).Any())
 					{
-						LogMessages.DetectedDnsChange(m_logger, Id, hostNames[hostNameIndex], string.Join<IPAddress>(",", hostAddresses[hostNameIndex]), string.Join<IPAddress>(",", ipAddresses));
+						Log.DetectedDnsChange(m_logger, Id, hostNames[hostNameIndex], string.Join<IPAddress>(",", hostAddresses[hostNameIndex]), string.Join<IPAddress>(",", ipAddresses));
 						hostAddresses[hostNameIndex] = ipAddresses;
 						hostNamesChanged = true;
 					}
@@ -709,12 +709,12 @@ async Task RunTimer()
 				catch (Exception ex)
 				{
 					// do nothing; we'll try again later
-					LogMessages.DnsCheckFailed(m_logger, ex, Id, hostNames[hostNameIndex], ex.Message);
+					Log.DnsCheckFailed(m_logger, ex, Id, hostNames[hostNameIndex], ex.Message);
 				}
 			}
 			if (hostNamesChanged)
 			{
-				LogMessages.ClearingPoolDueToDnsChanges(m_logger, Id);
+				Log.ClearingPoolDueToDnsChanges(m_logger, Id);
 				ClearAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 			}
 			((Timer) t!).Change(interval, -1);
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index cb10a9931..9a6a92aa3 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -42,7 +42,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 		PoolGeneration = poolGeneration;
 		HostName = "";
 		m_activityTags = new ActivityTagsCollection();
-		LogMessages.CreatedNewSession(m_logger, Id);
+		Log.CreatedNewSession(m_logger, Id);
 	}
 
 	public string Id { get; }
@@ -68,7 +68,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 
 	public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? owningConnection)
 	{
-		LogMessages.ReturningToPool(m_logger, Id, Pool?.Id ?? 0);
+		Log.ReturningToPool(m_logger, Id, Pool?.Id ?? 0);
 		LastReturnedTicks = unchecked((uint) Environment.TickCount);
 		return Pool is null ? default : Pool.ReturnAsync(ioBehavior, this);
 	}
@@ -96,18 +96,18 @@ public bool TryStartCancel(ICancellableCommand command)
 			m_state = State.CancelingQuery;
 		}
 
-		LogMessages.WillCancelCommand(m_logger, Id, command.CommandId, command.CancelAttemptCount, (command as MySqlCommand)?.CommandText);
+		Log.WillCancelCommand(m_logger, Id, command.CommandId, command.CancelAttemptCount, (command as MySqlCommand)?.CommandText);
 		return true;
 	}
 
 	public void DoCancel(ICancellableCommand commandToCancel, MySqlCommand killCommand)
 	{
-		LogMessages.CancelingCommandFromSession(m_logger, Id, commandToCancel.CommandId, killCommand.Connection!.Session.Id, (commandToCancel as MySqlCommand)?.CommandText);
+		Log.CancelingCommandFromSession(m_logger, Id, commandToCancel.CommandId, killCommand.Connection!.Session.Id, (commandToCancel as MySqlCommand)?.CommandText);
 		lock (m_lock)
 		{
 			if (ActiveCommandId != commandToCancel.CommandId)
 			{
-				LogMessages.IgnoringCancellationForInactiveCommand(m_logger, Id, ActiveCommandId, commandToCancel.CommandId);
+				Log.IgnoringCancellationForInactiveCommand(m_logger, Id, ActiveCommandId, commandToCancel.CommandId);
 				return;
 			}
 
@@ -117,7 +117,7 @@ public void DoCancel(ICancellableCommand commandToCancel, MySqlCommand killComma
 			// command would be killed (because "KILL QUERY" specifies the connection whose command should be killed, not
 			// a unique identifier of the command itself). As a mitigation, we set the CommandTimeout to a low value to avoid
 			// blocking the other thread for an extended duration.
-			LogMessages.CancelingCommand(m_logger, killCommand.Connection!.Session.Id, commandToCancel.CommandId, killCommand.CommandText);
+			Log.CancelingCommand(m_logger, killCommand.Connection!.Session.Id, commandToCancel.CommandId, killCommand.CommandText);
 			killCommand.ExecuteNonQuery();
 		}
 	}
@@ -305,7 +305,7 @@ public void FinishQuerying()
 			// KILL QUERY will kill a subsequent query if the command it was intended to cancel has already completed.
 			// In order to handle this case, we issue a dummy query that will consume the pending cancellation.
 			// See https://bugs.mysql.com/bug.php?id=45679
-			LogMessages.SendingSleepToClearPendingCancellation(m_logger, Id);
+			Log.SendingSleepToClearPendingCancellation(m_logger, Id);
 			var payload = SupportsQueryAttributes ? s_sleepWithAttributesPayload : s_sleepNoAttributesPayload;
 #pragma warning disable CA2012 // Safe because method completes synchronously
 			SendAsync(payload, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
@@ -356,7 +356,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			{
 				try
 				{
-					LogMessages.SendingQuitCommand(m_logger, Id);
+					Log.SendingQuitCommand(m_logger, Id);
 					m_payloadHandler.StartNewConversation();
 					await m_payloadHandler.WritePayloadAsync(QuitPayload.Instance.Memory, ioBehavior).ConfigureAwait(false);
 				}
@@ -439,7 +439,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				{
 					lock (m_lock)
 						m_state = State.Failed;
-					LogMessages.ConnectingFailed(m_logger, Id);
+					Log.ConnectingFailed(m_logger, Id);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Unable to connect to any of the specified MySQL hosts.");
 				}
 
@@ -457,10 +457,10 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 					authPluginName = initialHandshake.AuthPluginName!;
 				else
 					authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password";
-				LogMessages.ServerSentAuthPluginName(m_logger, Id, authPluginName);
+				Log.ServerSentAuthPluginName(m_logger, Id, authPluginName);
 				if (authPluginName != "mysql_native_password" && authPluginName != "sha256_password" && authPluginName != "caching_sha2_password")
 				{
-					LogMessages.UnsupportedAuthenticationMethod(m_logger, Id, authPluginName);
+					Log.UnsupportedAuthenticationMethod(m_logger, Id, authPluginName);
 					throw new NotSupportedException($"Authentication method '{initialHandshake.AuthPluginName}' is not supported.");
 				}
 
@@ -492,7 +492,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				// disable pipelining for RDS MySQL 5.7 (assuming Aurora); otherwise take it from the connection string or default to true
 				if (!cs.Pipelining.HasValue && ServerVersion.Version.Major == 5 && ServerVersion.Version.Minor == 7 && HostName.EndsWith(".rds.amazonaws.com", StringComparison.OrdinalIgnoreCase))
 				{
-					LogMessages.AutoDetectedAurora57(m_logger, Id, HostName);
+					Log.AutoDetectedAurora57(m_logger, Id, HostName);
 					m_supportsPipelining = false;
 				}
 				else
@@ -515,13 +515,13 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 					}
 				}
 
-				LogMessages.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
+				Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
 
 				if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
 				{
 					if (!serverSupportsSsl)
 					{
-						LogMessages.ServerDoesNotSupportSsl(m_logger, Id);
+						Log.ServerDoesNotSupportSsl(m_logger, Id);
 						throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support SSL");
 					}
 
@@ -532,13 +532,13 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 					}
 					catch (ArgumentException ex) when (ex.ParamName == "sslProtocolType" && sslProtocols == SslProtocols.None)
 					{
-						LogMessages.SessionDoesNotSupportSslProtocolsNone(m_logger, ex, Id);
+						Log.SessionDoesNotSupportSslProtocolsNone(m_logger, ex, Id);
 						sslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
 					}
 					catch (Exception ex) when (shouldRetrySsl && ((ex is MySqlException && ex.InnerException is IOException) || ex is IOException))
 					{
 						// negotiating TLS 1.2 with a yaSSL-based server throws an exception on Windows, see comment at top of method
-						LogMessages.FailedNegotiatingTls(m_logger, ex, Id);
+						Log.FailedNegotiatingTls(m_logger, ex, Id);
 						sslProtocols = sslProtocols == SslProtocols.None ? SslProtocols.Tls | SslProtocols.Tls11 : (SslProtocols.Tls | SslProtocols.Tls11) & sslProtocols;
 						if (Pool is not null)
 							Pool.SslProtocols = sslProtocols;
@@ -582,12 +582,12 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 		}
 		catch (ArgumentException ex)
 		{
-			LogMessages.CouldNotConnectToServer(m_logger, ex, Id);
+			Log.CouldNotConnectToServer(m_logger, ex, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Couldn't connect to server", ex);
 		}
 		catch (IOException ex)
 		{
-			LogMessages.CouldNotConnectToServer(m_logger, ex, Id);
+			Log.CouldNotConnectToServer(m_logger, ex, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Couldn't connect to server", ex);
 		}
 
@@ -608,7 +608,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 			{
 				if (m_supportsPipelining)
 				{
-					LogMessages.SendingPipelinedResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
+					Log.SendingPipelinedResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
 
 					// send both packets at once
 					await m_payloadHandler!.ByteHandler.WriteBytesAsync(m_pipelinedResetConnectionBytes!, ioBehavior).ConfigureAwait(false);
@@ -625,7 +625,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 					return true;
 				}
 
-				LogMessages.SendingResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
+				Log.SendingResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
 				await SendAsync(ResetConnectionPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 				OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
@@ -635,11 +635,11 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 				// optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL)
 				if (DatabaseOverride is null)
 				{
-					LogMessages.SendingChangeUserRequest(m_logger, Id, ServerVersion.OriginalString);
+					Log.SendingChangeUserRequest(m_logger, Id, ServerVersion.OriginalString);
 				}
 				else
 				{
-					LogMessages.SendingChangeUserRequestDueToChangedDatabase(m_logger, Id, DatabaseOverride);
+					Log.SendingChangeUserRequestDueToChangedDatabase(m_logger, Id, DatabaseOverride);
 					DatabaseOverride = null;
 				}
 				var password = GetPassword(cs, connection);
@@ -649,7 +649,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 				if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature)
 				{
-					LogMessages.OptimisticReauthenticationFailed(m_logger, Id);
+					Log.OptimisticReauthenticationFailed(m_logger, Id);
 					payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				}
 				OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
@@ -664,19 +664,19 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 		}
 		catch (IOException ex)
 		{
-			LogMessages.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "IOException");
+			Log.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "IOException");
 		}
 		catch (MySqlException ex) when (ex.ErrorCode == MySqlErrorCode.ClientInteractionTimeout)
 		{
-			LogMessages.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "ClientInteractionTimeout MySqlException");
+			Log.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "ClientInteractionTimeout MySqlException");
 		}
 		catch (ObjectDisposedException ex)
 		{
-			LogMessages.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "ObjectDisposedException");
+			Log.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "ObjectDisposedException");
 		}
 		catch (SocketException ex)
 		{
-			LogMessages.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "SocketException");
+			Log.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "SocketException");
 		}
 
 		return false;
@@ -686,7 +686,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 	{
 		// if the server didn't support the hashed password; rehash with the new challenge
 		var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload.Span);
-		LogMessages.SwitchingToAuthenticationMethod(m_logger, Id, switchRequest.Name);
+		Log.SwitchingToAuthenticationMethod(m_logger, Id, switchRequest.Name);
 		switch (switchRequest.Name)
 		{
 		case "mysql_native_password":
@@ -699,7 +699,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 		case "mysql_clear_password":
 			if (!m_isSecureConnection)
 			{
-				LogMessages.NeedsSecureConnection(m_logger, Id, switchRequest.Name);
+				Log.NeedsSecureConnection(m_logger, Id, switchRequest.Name);
 				throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, $"Authentication method '{switchRequest.Name}' requires a secure connection.");
 			}
 
@@ -741,7 +741,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 			return await AuthGSSAPI.AuthenticateAsync(cs, switchRequest.Data, this, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 		case "mysql_old_password":
-			LogMessages.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
+			Log.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
 			throw new NotSupportedException("'MySQL Server is requesting the insecure pre-4.1 auth mechanism (mysql_old_password). The user password must be upgraded; see https://dev.mysql.com/doc/refman/5.7/en/account-upgrades.html.");
 
 		case "client_ed25519":
@@ -752,7 +752,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 			return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 		default:
-			LogMessages.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
+			Log.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
 			throw new NotSupportedException($"Authentication method '{switchRequest.Name}' is not supported.");
 		}
 	}
@@ -784,7 +784,7 @@ private async Task<PayloadData> SendEncryptedPasswordAsync(
 		}
 		catch (Exception ex)
 		{
-			LogMessages.CouldNotLoadServerRsaPublicKey(m_logger, ex, Id);
+			Log.CouldNotLoadServerRsaPublicKey(m_logger, ex, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Couldn't load server's RSA public key; try using a secure connection instead.", ex);
 		}
 #else
@@ -796,7 +796,7 @@ private async Task<PayloadData> SendEncryptedPasswordAsync(
 		}
 		catch (Exception ex)
 		{
-			LogMessages.CouldNotLoadServerRsaPublicKey(m_logger, ex, Id);
+			Log.CouldNotLoadServerRsaPublicKey(m_logger, ex, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Couldn't load server's RSA public key; try using a secure connection instead.", ex);
 		}
 
@@ -830,7 +830,7 @@ private async Task<string> GetRsaPublicKeyAsync(string switchRequestName, Connec
 			}
 			catch (IOException ex)
 			{
-				LogMessages.CouldNotLoadServerRsaPublicKeyFromFile(m_logger, ex, Id, cs.ServerRsaPublicKeyFile);
+				Log.CouldNotLoadServerRsaPublicKeyFromFile(m_logger, ex, Id, cs.ServerRsaPublicKeyFile);
 				throw new MySqlException($"Couldn't load server's RSA public key from '{cs.ServerRsaPublicKeyFile}'", ex);
 			}
 		}
@@ -845,7 +845,7 @@ private async Task<string> GetRsaPublicKeyAsync(string switchRequestName, Connec
 			return Encoding.ASCII.GetString(publicKeyPayload.Data);
 		}
 
-		LogMessages.CouldNotUseAuthenticationMethodForRsa(m_logger, Id, switchRequestName);
+		Log.CouldNotUseAuthenticationMethodForRsa(m_logger, Id, switchRequestName);
 		throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, $"Authentication method '{switchRequestName}' failed. Either use a secure connection, specify the server's RSA public key with ServerRSAPublicKeyFile, or set AllowPublicKeyRetrieval=True.");
 	}
 
@@ -856,24 +856,24 @@ public async ValueTask<bool> TryPingAsync(bool logInfo, IOBehavior ioBehavior, C
 		// send ping payload to verify client and server socket are still connected
 		try
 		{
-			LogMessages.PingingServer(m_logger, Id);
+			Log.PingingServer(m_logger, Id);
 			await SendAsync(PingPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
 			var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 			OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
-			LogMessages.SuccessfullyPingedServer(m_logger, logInfo ? LogLevel.Information : LogLevel.Trace, Id);
+			Log.SuccessfullyPingedServer(m_logger, logInfo ? LogLevel.Information : LogLevel.Trace, Id);
 			return true;
 		}
 		catch (IOException ex)
 		{
-			LogMessages.PingFailed(m_logger, ex, Id, "IOException");
+			Log.PingFailed(m_logger, ex, Id, "IOException");
 		}
 		catch (MySqlException ex) when (ex.ErrorCode == MySqlErrorCode.ClientInteractionTimeout)
 		{
-			LogMessages.PingFailed(m_logger, ex, Id, "ClientInteractionTimeout MySqlException");
+			Log.PingFailed(m_logger, ex, Id, "ClientInteractionTimeout MySqlException");
 		}
 		catch (SocketException ex)
 		{
-			LogMessages.PingFailed(m_logger, ex, Id, "SocketException");
+			Log.PingFailed(m_logger, ex, Id, "SocketException");
 		}
 
 		VerifyState(State.Failed);
@@ -905,7 +905,7 @@ public ValueTask<PayloadData> ReceiveReplyAsync(IOBehavior ioBehavior, Cancellat
 		}
 		catch (Exception ex)
 		{
-			LogMessages.FailedInReceiveReplyAsync(m_logger, ex, Id);
+			Log.FailedInReceiveReplyAsync(m_logger, ex, Id);
 			if ((ex as MySqlException)?.ErrorCode == MySqlErrorCode.CommandTimeoutExpired)
 				HandleTimeout();
 			task = ValueTaskExtensions.FromException<ArraySegment<byte>>(ex);
@@ -955,7 +955,7 @@ public ValueTask SendReplyAsync(PayloadData payload, IOBehavior ioBehavior, Canc
 		}
 		catch (Exception ex)
 		{
-			LogMessages.FailedInSendReplyAsync(m_logger, ex, Id);
+			Log.FailedInSendReplyAsync(m_logger, ex, Id);
 			task = ValueTaskExtensions.FromException(ex);
 		}
 
@@ -1041,7 +1041,7 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 			catch (SocketException ex)
 			{
 				// name couldn't be resolved
-				LogMessages.FailedToResolveHostName(m_logger, ex, Id, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
+				Log.FailedToResolveHostName(m_logger, ex, Id, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
 				continue;
 			}
 
@@ -1050,7 +1050,7 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 			{
 				var ipAddress = ipAddresses[ipAddressIndex];
 				var ipAddressString = ipAddress.ToString();
-				LogMessages.ConnectingToIpAddress(m_logger, Id, ipAddressString, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count);
+				Log.ConnectingToIpAddress(m_logger, Id, ipAddressString, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count);
 
 				// set activity tags for the current IP address/hostname
 				{
@@ -1105,7 +1105,7 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 						catch (Exception ex) when (cancellationToken.IsCancellationRequested && ex is ObjectDisposedException or SocketException)
 						{
 							SafeDispose(ref tcpClient);
-							LogMessages.ConnectTimeoutExpired(m_logger, ex, Id, ipAddressString, hostName);
+							Log.ConnectTimeoutExpired(m_logger, ex, Id, ipAddressString, hostName);
 							throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.");
 						}
 					}
@@ -1120,20 +1120,20 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 						lock (m_lock)
 							m_state = State.Failed;
 						if (hostNames.Count == 1 && ipAddresses.Length == 1)
-							LogMessages.FailedToConnectToSingleIpAddress(m_logger, ex, Id, ipAddressString, hostName, ex.Message);
+							Log.FailedToConnectToSingleIpAddress(m_logger, ex, Id, ipAddressString, hostName, ex.Message);
 						else
-							LogMessages.FailedToConnectToIpAddress(m_logger, ex, LogLevel.Information, Id, ipAddressString, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
+							Log.FailedToConnectToIpAddress(m_logger, ex, LogLevel.Information, Id, ipAddressString, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
 						throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Unable to connect to any of the specified MySQL hosts.");
 					}
 
-					LogMessages.FailedToConnectToIpAddress(m_logger, ex, LogLevel.Trace, Id, ipAddressString, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
+					Log.FailedToConnectToIpAddress(m_logger, ex, LogLevel.Trace, Id, ipAddressString, ipAddressIndex + 1, ipAddresses.Length, hostName, hostNameIndex + 1, hostNames.Count, ex.Message);
 					continue;
 				}
 
 				if (!tcpClient.Connected && cancellationToken.IsCancellationRequested)
 				{
 					SafeDispose(ref tcpClient);
-					LogMessages.ConnectTimeoutExpired(m_logger, null, Id, ipAddressString, hostName);
+					Log.ConnectTimeoutExpired(m_logger, null, Id, ipAddressString, hostName);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.");
 				}
 
@@ -1151,13 +1151,13 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 					Utility.Dispose(ref m_stream);
 					SafeDispose(ref m_tcpClient);
 					SafeDispose(ref m_socket);
-					LogMessages.ConnectTimeoutExpired(m_logger, null, Id, ipAddressString, hostName);
+					Log.ConnectTimeoutExpired(m_logger, null, Id, ipAddressString, hostName);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.");
 				}
 
 				lock (m_lock)
 					m_state = State.Connected;
-				LogMessages.ConnectedToIpAddress(m_logger, Id, ipAddressString, hostName, (m_socket.LocalEndPoint as IPEndPoint)?.Port);
+				Log.ConnectedToIpAddress(m_logger, Id, ipAddressString, hostName, (m_socket.LocalEndPoint as IPEndPoint)?.Port);
 				return true;
 			}
 		}
@@ -1166,7 +1166,7 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 
 	private async Task<bool> OpenUnixSocketAsync(ConnectionSettings cs, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		LogMessages.ConnectingToUnixSocket(m_logger, Id, cs.UnixSocket!);
+		Log.ConnectingToUnixSocket(m_logger, Id, cs.UnixSocket!);
 
 		// set activity tags
 		{
@@ -1198,7 +1198,7 @@ private async Task<bool> OpenUnixSocketAsync(ConnectionSettings cs, Activity? ac
 				}
 				catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested)
 				{
-					LogMessages.ConnectTimeoutExpiredForUnixSocket(m_logger, Id, cs.UnixSocket!);
+					Log.ConnectTimeoutExpiredForUnixSocket(m_logger, Id, cs.UnixSocket!);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.");
 				}
 			}
@@ -1223,7 +1223,7 @@ private async Task<bool> OpenUnixSocketAsync(ConnectionSettings cs, Activity? ac
 
 	private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, int startTickCount, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		LogMessages.ConnectingToNamedPipe(m_logger, Id, cs.PipeName, cs.HostNames![0]);
+		Log.ConnectingToNamedPipe(m_logger, Id, cs.PipeName, cs.HostNames![0]);
 
 		// set activity tags
 		{
@@ -1253,7 +1253,7 @@ private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, int startTick
 				}
 				catch (Exception ex) when ((ex is ObjectDisposedException && cancellationToken.IsCancellationRequested) || ex is TimeoutException)
 				{
-					LogMessages.ConnectTimeoutExpiredForNamedPipe(m_logger, ex, Id, cs.PipeName, cs.HostNames![0]);
+					Log.ConnectTimeoutExpiredForNamedPipe(m_logger, ex, Id, cs.PipeName, cs.HostNames![0]);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.");
 				}
 			}
@@ -1277,7 +1277,7 @@ private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, int startTick
 
 	private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, MySqlConnection connection, SslProtocols sslProtocols, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		LogMessages.InitializingTlsConnection(m_logger, Id);
+		Log.InitializingTlsConnection(m_logger, Id);
 		X509CertificateCollection? clientCertificates = null;
 
 		if (cs.CertificateStoreLocation != MySqlCertificateStoreLocation.None)
@@ -1292,7 +1292,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 				{
 					if (store.Certificates.Count == 0)
 					{
-						LogMessages.NoCertificatesFound(m_logger, Id);
+						Log.NoCertificatesFound(m_logger, Id);
 						throw new MySqlException("No certificates were found in the certificate store");
 					}
 
@@ -1304,7 +1304,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 					var foundCertificates = store.Certificates.Find(X509FindType.FindByThumbprint, cs.CertificateThumbprint, requireValid);
 					if (foundCertificates.Count == 0)
 					{
-						LogMessages.CertificateNotFoundInStore(m_logger, Id, cs.CertificateThumbprint);
+						Log.CertificateNotFoundInStore(m_logger, Id, cs.CertificateThumbprint);
 						throw new MySqlException($"Certificate with Thumbprint {cs.CertificateThumbprint} not found");
 					}
 
@@ -1313,7 +1313,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 			}
 			catch (CryptographicException ex)
 			{
-				LogMessages.CouldNotLoadCertificate(m_logger, ex, Id, cs.CertificateStoreLocation);
+				Log.CouldNotLoadCertificate(m_logger, ex, Id, cs.CertificateStoreLocation);
 				throw new MySqlException("Certificate couldn't be loaded from the CertificateStoreLocation", ex);
 			}
 		}
@@ -1331,7 +1331,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 				{
 					certificate.Dispose();
 
-					LogMessages.NoPrivateKeyIncludedWithCertificateFile(m_logger, Id, cs.CertificateFile);
+					Log.NoPrivateKeyIncludedWithCertificateFile(m_logger, Id, cs.CertificateFile);
 					throw new MySqlException("CertificateFile does not contain a private key. " +
 						"CertificateFile should be in PKCS #12 (.pfx) format and contain both a Certificate and Private Key");
 				}
@@ -1340,7 +1340,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 			}
 			catch (CryptographicException ex)
 			{
-				LogMessages.CouldNotLoadCertificateFromFile(m_logger, ex, Id, cs.CertificateFile);
+				Log.CouldNotLoadCertificateFromFile(m_logger, ex, Id, cs.CertificateFile);
 				if (!File.Exists(cs.CertificateFile))
 					throw new MySqlException("Cannot find Certificate File", ex);
 				throw new MySqlException("Either the Certificate Password is incorrect or the Certificate File is invalid", ex);
@@ -1356,7 +1356,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 			}
 			catch (Exception ex)
 			{
-				LogMessages.FailedToObtainClientCertificates(m_logger, ex, Id, ex.Message);
+				Log.FailedToObtainClientCertificates(m_logger, ex, Id, ex.Message);
 				throw new MySqlException("Failed to obtain client certificates via ProvideClientCertificatesCallback", ex);
 			}
 		}
@@ -1376,7 +1376,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 			try
 			{
 				// read the CA Certificate File
-				LogMessages.LoadingCaCertificatesFromFile(m_logger, Id, cs.CACertificateFile);
+				Log.LoadingCaCertificatesFromFile(m_logger, Id, cs.CACertificateFile);
 				byte[] certificateBytes;
 				try
 				{
@@ -1384,7 +1384,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 				}
 				catch (Exception ex)
 				{
-					LogMessages.CouldNotLoadCaCertificateFromFile(m_logger, ex, LogLevel.Error, Id, cs.CACertificateFile);
+					Log.CouldNotLoadCaCertificateFromFile(m_logger, ex, LogLevel.Error, Id, cs.CACertificateFile);
 					if (!File.Exists(cs.CACertificateFile))
 						throw new MySqlException("Cannot find CA Certificate File: " + cs.CACertificateFile, ex);
 					throw new MySqlException("Could not load CA Certificate File: " + cs.CACertificateFile, ex);
@@ -1397,7 +1397,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 					try
 					{
 						// load the certificate at this index; note that 'new X509Certificate' stops at the end of the first certificate it loads
-						LogMessages.LoadingCaCertificate(m_logger, Id, index);
+						Log.LoadingCaCertificate(m_logger, Id, index);
 #if NET5_0_OR_GREATER
 						var caCertificate = new X509Certificate2(certificateBytes.AsSpan(index, (nextIndex == -1 ? certificateBytes.Length : nextIndex) - index), default(ReadOnlySpan<char>), X509KeyStorageFlags.MachineKeySet);
 #else
@@ -1407,13 +1407,13 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 					}
 					catch (CryptographicException ex)
 					{
-						LogMessages.CouldNotLoadCaCertificateFromFile(m_logger, ex, LogLevel.Warning, Id, cs.CACertificateFile);
+						Log.CouldNotLoadCaCertificateFromFile(m_logger, ex, LogLevel.Warning, Id, cs.CACertificateFile);
 					}
 					index = nextIndex;
 				}
 
 				// success
-				LogMessages.LoadedCaCertificatesFromFile(m_logger, Id, certificateChain.ChainPolicy.ExtraStore.Count, cs.CACertificateFile);
+				Log.LoadedCaCertificatesFromFile(m_logger, Id, certificateChain.ChainPolicy.ExtraStore.Count, cs.CACertificateFile);
 				caCertificateChain = certificateChain;
 				certificateChain = null;
 			}
@@ -1453,15 +1453,15 @@ caCertificateChain is not null &&
 		{
 			if (caCertificateChain is not null)
 			{
-				LogMessages.NotUsingRemoteCertificateValidationCallbackDueToSslCa(m_logger, Id);
+				Log.NotUsingRemoteCertificateValidationCallbackDueToSslCa(m_logger, Id);
 			}
 			else if (cs.SslMode is not MySqlSslMode.Preferred and not MySqlSslMode.Required)
 			{
-				LogMessages.NotUsingRemoteCertificateValidationCallbackDueToSslMode(m_logger, Id, cs.SslMode);
+				Log.NotUsingRemoteCertificateValidationCallbackDueToSslMode(m_logger, Id, cs.SslMode);
 			}
 			else
 			{
-				LogMessages.UsingRemoteCertificateValidationCallback(m_logger, Id);
+				Log.UsingRemoteCertificateValidationCallback(m_logger, Id);
 				validateRemoteCertificate = connection.RemoteCertificateValidationCallback;
 			}
 		}
@@ -1516,14 +1516,14 @@ await sslStream.AuthenticateAsClientAsync(clientAuthenticationOptions.TargetHost
 			m_isSecureConnection = true;
 			m_sslStream = sslStream;
 #if NETCOREAPP3_0_OR_GREATER
-			LogMessages.ConnectedTlsBasic(m_logger, Id, sslStream.SslProtocol, sslStream.NegotiatedCipherSuite);
+			Log.ConnectedTlsBasic(m_logger, Id, sslStream.SslProtocol, sslStream.NegotiatedCipherSuite);
 #else
-			LogMessages.ConnectedTlsDetailed(m_logger, Id, sslStream.SslProtocol, sslStream.CipherAlgorithm, sslStream.HashAlgorithm, sslStream.KeyExchangeAlgorithm, sslStream.KeyExchangeStrength);
+			Log.ConnectedTlsDetailed(m_logger, Id, sslStream.SslProtocol, sslStream.CipherAlgorithm, sslStream.HashAlgorithm, sslStream.KeyExchangeAlgorithm, sslStream.KeyExchangeStrength);
 #endif
 		}
 		catch (Exception ex)
 		{
-			LogMessages.CouldNotInitializeTlsConnection(m_logger, ex, Id);
+			Log.CouldNotInitializeTlsConnection(m_logger, ex, Id);
 			sslStream.Dispose();
 			ShutdownSocket();
 			HostName = "";
@@ -1551,7 +1551,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 			m_clientCertificate = X509Certificate2.CreateFromPemFile(sslCertificateFile, sslKeyFile);
 			return new() { m_clientCertificate };
 #else
-			LogMessages.LoadingClientKeyFromKeyFile(m_logger, Id, sslKeyFile);
+			Log.LoadingClientKeyFromKeyFile(m_logger, Id, sslKeyFile);
 			string keyPem;
 			try
 			{
@@ -1559,7 +1559,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 			}
 			catch (Exception ex)
 			{
-				LogMessages.CouldNotLoadClientKeyFromKeyFile(m_logger, ex, Id, sslKeyFile);
+				Log.CouldNotLoadClientKeyFromKeyFile(m_logger, ex, Id, sslKeyFile);
 				throw new MySqlException($"Could not load the client key from '{sslKeyFile}'", ex);
 			}
 
@@ -1570,7 +1570,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 			}
 			catch (FormatException ex)
 			{
-				LogMessages.CouldNotLoadClientKeyFromKeyFile(m_logger, ex, Id, sslKeyFile);
+				Log.CouldNotLoadClientKeyFromKeyFile(m_logger, ex, Id, sslKeyFile);
 				throw new MySqlException($"Could not load the client key from '{sslKeyFile}'", ex);
 			}
 
@@ -1613,7 +1613,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 			}
 			catch (CryptographicException ex)
 			{
-				LogMessages.CouldNotLoadClientKeyFromKeyFile(m_logger, ex, Id, sslCertificateFile);
+				Log.CouldNotLoadClientKeyFromKeyFile(m_logger, ex, Id, sslCertificateFile);
 				if (!File.Exists(sslCertificateFile))
 					throw new MySqlException("Cannot find client certificate file: " + sslCertificateFile, ex);
 				throw new MySqlException("Could not load the client key from " + sslCertificateFile, ex);
@@ -1655,7 +1655,7 @@ private bool ShouldGetRealServerDetails(ConnectionSettings cs)
 
 	private async Task GetRealServerDetailsAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		LogMessages.DetectedProxy(m_logger, Id);
+		Log.DetectedProxy(m_logger, Id);
 		try
 		{
 			var payload = SupportsQueryAttributes ? s_selectConnectionIdVersionWithAttributesPayload : s_selectConnectionIdVersionNoAttributesPayload;
@@ -1695,20 +1695,20 @@ static void ReadRow(ReadOnlySpan<byte> span, out int? connectionId, out ServerVe
 
 			if (connectionId is int newConnectionId && serverVersion is not null)
 			{
-				LogMessages.ChangingConnectionId(m_logger, Id, ConnectionId, newConnectionId, ServerVersion.OriginalString, serverVersion.OriginalString);
+				Log.ChangingConnectionId(m_logger, Id, ConnectionId, newConnectionId, ServerVersion.OriginalString, serverVersion.OriginalString);
 				ConnectionId = newConnectionId;
 				ServerVersion = serverVersion;
 			}
 		}
 		catch (MySqlException ex)
 		{
-			LogMessages.FailedToGetConnectionId(m_logger, ex, Id);
+			Log.FailedToGetConnectionId(m_logger, ex, Id);
 		}
 	}
 
 	private void ShutdownSocket()
 	{
-		LogMessages.ClosingStreamSocket(m_logger, Id);
+		Log.ClosingStreamSocket(m_logger, Id);
 		Utility.Dispose(ref m_payloadHandler);
 		Utility.Dispose(ref m_stream);
 		SafeDispose(ref m_tcpClient);
@@ -1744,7 +1744,7 @@ private static void SafeDispose<T>(ref T? disposable)
 
 	internal void SetFailed(Exception exception)
 	{
-		LogMessages.SettingStateToFailed(m_logger, exception, Id);
+		Log.SettingStateToFailed(m_logger, exception, Id);
 		lock (m_lock)
 			m_state = State.Failed;
 		if (OwningConnection is not null && OwningConnection.TryGetTarget(out var connection))
@@ -1781,7 +1781,7 @@ private void VerifyState(State state1, State state2, State state3, State state4,
 
 	private byte[] CreateConnectionAttributes(string programName)
 	{
-		LogMessages.CreatingConnectionAttributes(m_logger, Id);
+		Log.CreatingConnectionAttributes(m_logger, Id);
 		var attributesWriter = new ByteBufferWriter();
 		attributesWriter.WriteLengthEncodedString("_client_name");
 		attributesWriter.WriteLengthEncodedString("MySqlConnector");
@@ -1834,7 +1834,7 @@ private byte[] CreateConnectionAttributes(string programName)
 	private Exception CreateExceptionForErrorPayload(ReadOnlySpan<byte> span)
 	{
 		var errorPayload = ErrorPayload.Create(span);
-		LogMessages.ErrorPayload(m_logger, Id, errorPayload.ErrorCode, errorPayload.State, errorPayload.Message);
+		Log.ErrorPayload(m_logger, Id, errorPayload.ErrorCode, errorPayload.State, errorPayload.Message);
 		var exception = errorPayload.ToException();
 		if (exception.ErrorCode is MySqlErrorCode.ClientInteractionTimeout)
 			SetFailed(exception);
@@ -1860,12 +1860,12 @@ private string GetPassword(ConnectionSettings cs, MySqlConnection connection)
 		{
 			try
 			{
-				LogMessages.ObtainingPasswordViaProvidePasswordCallback(m_logger, Id);
+				Log.ObtainingPasswordViaProvidePasswordCallback(m_logger, Id);
 				return passwordProvider(new(HostName, cs.Port, cs.UserID, cs.Database));
 			}
 			catch (Exception ex)
 			{
-				LogMessages.FailedToObtainPassword(m_logger, ex, Id, ex.Message);
+				Log.FailedToObtainPassword(m_logger, ex, Id, ex.Message);
 				throw new MySqlException(MySqlErrorCode.ProvidePasswordCallbackFailed, "Failed to obtain password via ProvidePasswordCallback", ex);
 			}
 		}
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index 6457f09ba..0abe65b4d 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -22,7 +22,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 		var preparedStatements = command.TryGetPreparedStatements();
 		if (preparedStatements is null)
 		{
-			LogMessages.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
+			Log.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 
 			writer.Write((byte) CommandKind.Query);
 			var supportsQueryAttributes = command.Connection!.Session.SupportsQueryAttributes;
@@ -40,7 +40,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 			}
 			else if (command.RawAttributes?.Count > 0)
 			{
-				LogMessages.QueryAttributesNotSupported(command.Logger, command.Connection!.Session.Id, command.CommandText!);
+				Log.QueryAttributesNotSupported(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 			}
 
 			WriteQueryPayload(command, cachedProcedures, writer, appendSemicolon);
@@ -77,7 +77,7 @@ private static void WritePreparedStatement(IMySqlCommand command, PreparedStatem
 	{
 		var parameterCollection = command.RawParameters;
 
-		LogMessages.PreparingCommandPayloadWithId(command.Logger, command.Connection!.Session.Id, preparedStatement.StatementId, command.CommandText!);
+		Log.PreparingCommandPayloadWithId(command.Logger, command.Connection!.Session.Id, preparedStatement.StatementId, command.CommandText!);
 
 		var attributes = command.RawAttributes;
 		var supportsQueryAttributes = command.Connection!.Session.SupportsQueryAttributes;
@@ -101,7 +101,7 @@ private static void WritePreparedStatement(IMySqlCommand command, PreparedStatem
 				writer.WriteLengthEncodedInteger((uint) commandParameterCount);
 			if (attributeCount > 0)
 			{
-				LogMessages.QueryAttributesNotSupportedWithId(command.Logger, command.Connection!.Session.Id, preparedStatement.StatementId);
+				Log.QueryAttributesNotSupportedWithId(command.Logger, command.Connection!.Session.Id, preparedStatement.StatementId);
 				attributeCount = 0;
 			}
 		}
diff --git a/src/MySqlConnector/Logging/LogMessages.cs b/src/MySqlConnector/Logging/Log.cs
similarity index 99%
rename from src/MySqlConnector/Logging/LogMessages.cs
rename to src/MySqlConnector/Logging/Log.cs
index 6b006e4a1..c3e5062bb 100644
--- a/src/MySqlConnector/Logging/LogMessages.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -1,12 +1,11 @@
 using System.Net.Security;
-using System.Security;
 using System.Security.Authentication;
 using Microsoft.Extensions.Logging;
 using MySqlConnector.Protocol.Serialization;
 
 namespace MySqlConnector.Logging;
 
-internal static partial class LogMessages
+internal static partial class Log
 {
 	[LoggerMessage(EventIds.DataSourceCreatedWithPool, LogLevel.Information, "DataSource {DataSourceId} created with pool {PoolId}")]
 	public static partial void DataSourceCreatedWithPool(ILogger logger, int dataSourceId, int poolId);
diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs
index 0e83fff0e..2c0c52dd5 100644
--- a/src/MySqlConnector/MySqlBulkCopy.cs
+++ b/src/MySqlConnector/MySqlBulkCopy.cs
@@ -240,7 +240,7 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 		var tableName = DestinationTableName ?? throw new InvalidOperationException("DestinationTableName must be set before calling WriteToServer");
 		m_wasAborted = false;
 
-		LogMessages.StartingBulkCopy(m_logger, tableName);
+		Log.StartingBulkCopy(m_logger, tableName);
 		var bulkLoader = new MySqlBulkLoader(m_connection)
 		{
 			CharacterSet = "utf8mb4",
@@ -292,7 +292,7 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 					}
 					else if (addDefaultMappings)
 					{
-						LogMessages.AddingDefaultColumnMapping(m_logger, i, destinationColumn);
+						Log.AddingDefaultColumnMapping(m_logger, i, destinationColumn);
 						columnMappings.Add(new(i, destinationColumn));
 					}
 				}
@@ -305,7 +305,7 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 			var columnMapping = columnMappings.FirstOrDefault(x => x.SourceOrdinal == i);
 			if (columnMapping is null)
 			{
-				LogMessages.IgnoringColumn(m_logger, i);
+				Log.IgnoringColumn(m_logger, i);
 				bulkLoader.Columns.Add("@`\uE002\bignore`");
 			}
 			else
@@ -344,11 +344,11 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 		if (closeConnection)
 			m_connection.Close();
 
-		LogMessages.FinishedBulkCopy(m_logger, tableName);
+		Log.FinishedBulkCopy(m_logger, tableName);
 
 		if (!m_wasAborted && rowsInserted != m_rowsCopied && ConflictOption is MySqlBulkLoaderConflictOption.None)
 		{
-			LogMessages.BulkCopyFailed(m_logger, tableName, m_rowsCopied, rowsInserted);
+			Log.BulkCopyFailed(m_logger, tableName, m_rowsCopied, rowsInserted);
 			throw new MySqlException(MySqlErrorCode.BulkCopyFailed, $"{m_rowsCopied} row{(m_rowsCopied == 1 ? " was" : "s were")} copied to {tableName} but only {rowsInserted} {(rowsInserted == 1 ? "was" : "were")} inserted.");
 		}
 
@@ -364,18 +364,18 @@ static void AddColumnMapping(ILogger logger, List<MySqlBulkCopyColumnMapping> co
 			{
 				if (columnMapping.Expression is not null)
 				{
-					LogMessages.ColumnMappingAlreadyHasExpression(logger, columnMapping.SourceOrdinal, destinationColumn, columnMapping.Expression);
+					Log.ColumnMappingAlreadyHasExpression(logger, columnMapping.SourceOrdinal, destinationColumn, columnMapping.Expression);
 				}
 				else
 				{
-					LogMessages.SettingExpressionToMapColumn(logger, columnMapping.SourceOrdinal, destinationColumn, expression);
+					Log.SettingExpressionToMapColumn(logger, columnMapping.SourceOrdinal, destinationColumn, expression);
 					columnMappings.Remove(columnMapping);
 					columnMappings.Add(new(columnMapping.SourceOrdinal, variableName, expression));
 				}
 			}
 			else if (addDefaultMappings)
 			{
-				LogMessages.AddingDefaultColumnMapping(logger, destinationOrdinal, destinationColumn);
+				Log.AddingDefaultColumnMapping(logger, destinationOrdinal, destinationColumn);
 				columnMappings.Add(new(destinationOrdinal, variableName, expression));
 			}
 		}
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 8e9a99402..40d06c187 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -475,7 +475,7 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 	public async ValueTask ResetConnectionAsync(CancellationToken cancellationToken = default)
 	{
 		var session = Session;
-		LogMessages.ResettingConnection(m_logger, session.Id);
+		Log.ResettingConnection(m_logger, session.Id);
 		await session.SendAsync(ResetConnectionPayload.Instance, AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
 		var payload = await session.ReceiveReplyAsync(AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
 		OkPayload.Create(payload.Span, session.SupportsDeprecateEof, session.SupportsSessionTrack);
@@ -761,11 +761,11 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 	{
 		if (m_session?.Id is not string sessionId || State != ConnectionState.Open || m_session?.TryStartCancel(command) is not true)
 		{
-			LogMessages.IgnoringCancellationForCommand(m_logger, commandId);
+			Log.IgnoringCancellationForCommand(m_logger, commandId);
 			return;
 		}
 
-		LogMessages.CommandHasBeenCanceled(m_logger, commandId, sessionId, isCancel ? "Cancel()" : "command timeout");
+		Log.CommandHasBeenCanceled(m_logger, commandId, sessionId, isCancel ? "Cancel()" : "command timeout");
 		try
 		{
 			// open a dedicated connection to the server to kill the active query
@@ -794,34 +794,34 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 		{
 			// ignore a rare race condition where the connection is open at the beginning of the method, but closed by the time
 			// KILL QUERY is executed: https://github.com/mysql-net/MySqlConnector/issues/1002
-			LogMessages.IgnoringCancellationForClosedConnection(m_logger, ex, sessionId);
+			Log.IgnoringCancellationForClosedConnection(m_logger, ex, sessionId);
 			m_session?.AbortCancel(command);
 		}
 		catch (MySqlException ex)
 		{
 			// cancelling the query failed; setting the state back to 'Querying' will allow another call to 'Cancel' to try again
-			LogMessages.CancelingCommandFailed(m_logger, ex, sessionId, command.CommandId);
+			Log.CancelingCommandFailed(m_logger, ex, sessionId, command.CommandId);
 			m_session?.AbortCancel(command);
 		}
 	}
 
 	internal async Task<CachedProcedure?> GetCachedProcedure(string name, bool revalidateMissing, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		LogMessages.GettingCachedProcedure(m_logger, m_session!.Id, name);
+		Log.GettingCachedProcedure(m_logger, m_session!.Id, name);
 		if (State != ConnectionState.Open)
 			throw new InvalidOperationException("Connection is not open.");
 
 		var cachedProcedures = m_session!.Pool?.GetProcedureCache() ?? m_cachedProcedures;
 		if (cachedProcedures is null)
 		{
-			LogMessages.PoolDoesNotHaveSharedProcedureCache(m_logger, m_session.Id, m_session.Pool?.Id);
+			Log.PoolDoesNotHaveSharedProcedureCache(m_logger, m_session.Id, m_session.Pool?.Id);
 			cachedProcedures = m_cachedProcedures = new();
 		}
 
 		var normalized = NormalizedSchema.MustNormalize(name, Database);
 		if (string.IsNullOrEmpty(normalized.Schema))
 		{
-			LogMessages.CouldNotNormalizeDatabaseAndName(m_logger, m_session.Id, name, Database);
+			Log.CouldNotNormalizeDatabaseAndName(m_logger, m_session.Id, name, Database);
 			return null;
 		}
 
@@ -833,22 +833,22 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 		{
 			cachedProcedure = await CachedProcedure.FillAsync(ioBehavior, this, normalized.Schema!, normalized.Component!, m_logger, cancellationToken).ConfigureAwait(false);
 			if (cachedProcedure is null)
-				LogMessages.FailedToCacheProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
+				Log.FailedToCacheProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
 			else
-				LogMessages.CachingProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
+				Log.CachingProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
 			int count;
 			lock (cachedProcedures)
 			{
 				cachedProcedures[normalized.FullyQualified] = cachedProcedure;
 				count = cachedProcedures.Count;
 			}
-			LogMessages.ProcedureCacheCount(m_logger, m_session.Id, count);
+			Log.ProcedureCacheCount(m_logger, m_session.Id, count);
 		}
 
 		if (cachedProcedure is null)
-			LogMessages.DidNotFindCachedProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
+			Log.DidNotFindCachedProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
 		else
-			LogMessages.ReturningCachedProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
+			Log.ReturningCachedProcedure(m_logger, m_session.Id, normalized.Schema!, normalized.Component!);
 		return cachedProcedure;
 	}
 
@@ -936,7 +936,7 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 
 				var session = new ServerSession(m_logger);
 				session.OwningConnection = new WeakReference<MySqlConnection>(this);
-				LogMessages.CreatedNonPooledSession(m_logger, session.Id);
+				Log.CreatedNonPooledSession(m_logger, session.Id);
 				try
 				{
 					await session.ConnectAsync(connectionSettings, this, startTickCount, loadBalancer, activity, actualIOBehavior, connectToken).ConfigureAwait(false);
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 2ec190d30..a196dbd5a 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -617,7 +617,7 @@ internal async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancel
 				{
 					// ignore "Query execution was interrupted" exceptions when closing a data reader; log other exceptions
 					if (ex.ErrorCode != MySqlErrorCode.QueryInterrupted)
-						LogMessages.IgnoringExceptionInDisposeAsync(Command.Logger, ex, Command.Connection.Session.Id, ex.Message, Command.CommandText!);
+						Log.IgnoringExceptionInDisposeAsync(Command.Logger, ex, Command.Connection.Session.Id, ex.Message, Command.CommandText!);
 				}
 				m_resultSet = null;
 			}
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index 20c499d8e..84442ce0d 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -30,9 +30,9 @@ internal MySqlDataSource(string connectionString, MySqlConnectorLoggingConfigura
 		Pool = ConnectionPool.CreatePool(m_connectionString, LoggingConfiguration);
 		m_id = Interlocked.Increment(ref s_lastId);
 		if (Pool is not null)
-			LogMessages.DataSourceCreatedWithPool(m_logger, m_id, Pool.Id);
+			Log.DataSourceCreatedWithPool(m_logger, m_id, Pool.Id);
 		else
-			LogMessages.DataSourceCreatedWithoutPool(m_logger, m_id);
+			Log.DataSourceCreatedWithoutPool(m_logger, m_id);
 	}
 
 	/// <summary>

From 3e2f6e48e9db1ce83eab29c2787124461bb1a5ef Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 14:19:58 -0800
Subject: [PATCH 009/393] Reorder and renumber log event IDs.

Attempt to get the initial numbering semantically grouped.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Logging/EventIds.cs | 246 +++++++++++++------------
 1 file changed, 128 insertions(+), 118 deletions(-)

diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index a7225a70f..3d9204e25 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -2,124 +2,132 @@ namespace MySqlConnector.Logging;
 
 internal static class EventIds
 {
+	// DataSource events, 1000-1999
 	public const int DataSourceCreatedWithPool = 1000;
 	public const int DataSourceCreatedWithoutPool = 1001;
 
-	public const int CreatedNonPooledSession = 2000;
-	public const int ResettingConnection = 2001;
-	public const int CreatedNewSession = 2002;
+	// Core Session events, 2000-2099
+	public const int CreatedNewSession = 2000;
+	public const int CreatedNonPooledSession = 2001;
+	public const int ResettingConnection = 2002;
 	public const int ReturningToPool = 2003;
 	public const int SendingQuitCommand = 2004;
-	public const int ConnectingFailed = 2005;
-	public const int ServerSentAuthPluginName = 2006;
-	public const int UnsupportedAuthenticationMethod = 2007;
-	public const int AutoDetectedAurora57 = 2008;
-	public const int SessionMadeConnection = 2009;
-	public const int ServerDoesNotSupportSsl = 2010;
-	public const int SessionDoesNotSupportSslProtocolsNone = 2011;
-	public const int FailedNegotiatingTls = 2012;
-	public const int CouldNotConnectToServer = 2013;
-	public const int SendingPipelinedResetConnectionRequest = 2014;
-	public const int SendingResetConnectionRequest = 2015;
-	public const int SendingChangeUserRequest = 2016;
-	public const int SendingChangeUserRequestDueToChangedDatabase = 2017;
-	public const int OptimisticReauthenticationFailed = 2018;
-	public const int IgnoringFailureInTryResetConnectionAsync = 2019;
-	public const int SwitchingToAuthenticationMethod = 2020;
-	public const int NeedsSecureConnection = 2021;
-	public const int AuthenticationMethodNotSupported = 2022;
-	public const int CouldNotLoadServerRsaPublicKey = 2023;
-	public const int CouldNotLoadServerRsaPublicKeyFromFile = 2024;
-	public const int CouldNotUseAuthenticationMethodForRsa = 2025;
-	public const int FailedToResolveHostName = 2026;
-	public const int ConnectingToIpAddress = 2027;
-	public const int ConnectTimeoutExpired = 2028;
-	public const int FailedToConnectToSingleIpAddress = 2029;
-	public const int FailedToConnectToIpAddress = 2030;
-	public const int ConnectedToIpAddress = 2031;
-	public const int ConnectingToUnixSocket = 2032;
-	public const int ConnectTimeoutExpiredForUnixSocket = 2033;
-	public const int ConnectingToNamedPipe = 2034;
-	public const int ConnectTimeoutExpiredForNamedPipe = 2035;
-	public const int InitializingTlsConnection = 2036;
-	public const int NoCertificatesFound = 2037;
-	public const int CertificateNotFoundInStore = 2038;
-	public const int CouldNotLoadCertificate = 2039;
-	public const int NoPrivateKeyIncludedWithCertificateFile = 2040;
-	public const int CouldNotLoadCertificateFromFile = 2041;
-	public const int FailedToObtainClientCertificates = 2042;
-	public const int LoadingCaCertificatesFromFile = 2043;
-	public const int CouldNotLoadCaCertificateFromFile = 2044;
-	public const int LoadingCaCertificate = 2045;
-	public const int LoadedCaCertificatesFromFile = 2046;
-	public const int NotUsingRemoteCertificateValidationCallbackDueToSslCa = 2047;
-	public const int NotUsingRemoteCertificateValidationCallbackDueToSslMode = 2048;
-	public const int UsingRemoteCertificateValidationCallback = 2049;
-	public const int ConnectedTlsBasic = 2050;
-	public const int ConnectedTlsDetailed = 2051;
-	public const int CouldNotInitializeTlsConnection = 2052;
-	public const int LoadingClientKeyFromKeyFile = 2053;
-	public const int CouldNotLoadClientKeyFromKeyFile = 2054;
-	public const int DetectedProxy = 2055;
-	public const int ChangingConnectionId = 2056;
-	public const int FailedToGetConnectionId = 2057;
-	public const int ClosingStreamSocket = 2058;
-	public const int CreatingConnectionAttributes = 2059;
-	public const int ObtainingPasswordViaProvidePasswordCallback = 2060;
-	public const int FailedToObtainPassword = 2061;
+	public const int ClosingStreamSocket = 2005;
+	public const int ErrorPayload = 2006;
+	public const int FailedInSendReplyAsync = 2007;
+	public const int FailedInReceiveReplyAsync = 2008;
+	public const int SettingStateToFailed = 2009;
+	public const int ExpectedSessionState1 = 2011;
+	public const int ExpectedSessionState6 = 2016;
 
-	public const int IgnoringCancellationForCommand = 2100;
-	public const int CommandHasBeenCanceled = 2101;
-	public const int IgnoringCancellationForClosedConnection = 2102;
-	public const int CancelingCommandFailed = 2103;
-	public const int WillCancelCommand = 2104;
-	public const int CancelingCommandFromSession = 2105;
-	public const int IgnoringCancellationForInactiveCommand = 2106;
-	public const int CancelingCommand = 2107;
-	public const int SendingSleepToClearPendingCancellation = 2108;
+	// Session connecting events, 2100-2199
+	public const int ConnectingFailed = 2100;
+	public const int ServerSentAuthPluginName = 2101;
+	public const int UnsupportedAuthenticationMethod = 2102;
+	public const int AutoDetectedAurora57 = 2103;
+	public const int SessionMadeConnection = 2104;
+	public const int ServerDoesNotSupportSsl = 2105;
+	public const int SessionDoesNotSupportSslProtocolsNone = 2106;
+	public const int FailedNegotiatingTls = 2107;
+	public const int CouldNotConnectToServer = 2108;
+	public const int SendingPipelinedResetConnectionRequest = 2109;
+	public const int SendingResetConnectionRequest = 2110;
+	public const int SendingChangeUserRequest = 2111;
+	public const int SendingChangeUserRequestDueToChangedDatabase = 2112;
+	public const int OptimisticReauthenticationFailed = 2113;
+	public const int IgnoringFailureInTryResetConnectionAsync = 2114;
+	public const int SwitchingToAuthenticationMethod = 2115;
+	public const int NeedsSecureConnection = 2116;
+	public const int AuthenticationMethodNotSupported = 2117;
+	public const int CouldNotLoadServerRsaPublicKey = 2118;
+	public const int CouldNotLoadServerRsaPublicKeyFromFile = 2119;
+	public const int CouldNotUseAuthenticationMethodForRsa = 2120;
+	public const int FailedToResolveHostName = 2121;
+	public const int ConnectingToIpAddress = 2122;
+	public const int ConnectTimeoutExpired = 2123;
+	public const int FailedToConnectToSingleIpAddress = 2124;
+	public const int FailedToConnectToIpAddress = 2125;
+	public const int ConnectedToIpAddress = 2126;
+	public const int ConnectingToUnixSocket = 2127;
+	public const int ConnectTimeoutExpiredForUnixSocket = 2128;
+	public const int ConnectingToNamedPipe = 2129;
+	public const int ConnectTimeoutExpiredForNamedPipe = 2130;
+	public const int InitializingTlsConnection = 2131;
+	public const int NoCertificatesFound = 2132;
+	public const int CertificateNotFoundInStore = 2133;
+	public const int CouldNotLoadCertificate = 2134;
+	public const int NoPrivateKeyIncludedWithCertificateFile = 2135;
+	public const int CouldNotLoadCertificateFromFile = 2136;
+	public const int FailedToObtainClientCertificates = 2137;
+	public const int LoadingCaCertificatesFromFile = 2138;
+	public const int CouldNotLoadCaCertificateFromFile = 2139;
+	public const int LoadingCaCertificate = 2140;
+	public const int LoadedCaCertificatesFromFile = 2141;
+	public const int NotUsingRemoteCertificateValidationCallbackDueToSslCa = 2142;
+	public const int NotUsingRemoteCertificateValidationCallbackDueToSslMode = 2143;
+	public const int UsingRemoteCertificateValidationCallback = 2144;
+	public const int ConnectedTlsBasic = 2145;
+	public const int ConnectedTlsDetailed = 2146;
+	public const int CouldNotInitializeTlsConnection = 2147;
+	public const int LoadingClientKeyFromKeyFile = 2148;
+	public const int CouldNotLoadClientKeyFromKeyFile = 2149;
+	public const int DetectedProxy = 2150;
+	public const int ChangingConnectionId = 2151;
+	public const int FailedToGetConnectionId = 2152;
+	public const int CreatingConnectionAttributes = 2153;
+	public const int ObtainingPasswordViaProvidePasswordCallback = 2154;
+	public const int FailedToObtainPassword = 2155;
 
-	public const int GettingCachedProcedure = 2200;
-	public const int PoolDoesNotHaveSharedProcedureCache = 2201;
-	public const int CouldNotNormalizeDatabaseAndName = 2202;
-	public const int FailedToCacheProcedure = 2203;
-	public const int CachingProcedure = 2204;
-	public const int ProcedureCacheCount = 2205;
-	public const int DidNotFindCachedProcedure = 2206;
-	public const int ReturningCachedProcedure = 2207;
-	public const int FailedToRetrieveProcedureMetadata = 2208;
-	public const int ServerDoesNotSupportCachedProcedures = 2209;
-	public const int ProcedureHasRoutineCount = 2210;
+	// Command execution events, 2200-2299
+	public const int CannotExecuteNewCommandInState = 2200;
+	public const int EnteringFinishQuerying = 2201;
+	public const int CommandExecutorExecuteReader = 2202;
+	public const int QueryWasInterrupted = 2203;
+	public const int PreparingCommandPayload = 2204;
+	public const int PreparingCommandPayloadWithId = 2205;
+	public const int QueryAttributesNotSupported = 2206;
+	public const int QueryAttributesNotSupportedWithId = 2207;
+	public const int IgnoringExceptionInDisposeAsync = 2208;
 
-	public const int CannotExecuteNewCommandInState = 2300;
-	public const int EnteringFinishQuerying = 2301;
+	// Command cancellation events, 2300-2399
+	public const int IgnoringCancellationForCommand = 2300;
+	public const int CommandHasBeenCanceled = 2301;
+	public const int IgnoringCancellationForClosedConnection = 2302;
+	public const int CancelingCommandFailed = 2303;
+	public const int WillCancelCommand = 2304;
+	public const int CancelingCommandFromSession = 2305;
+	public const int IgnoringCancellationForInactiveCommand = 2306;
+	public const int CancelingCommand = 2307;
+	public const int SendingSleepToClearPendingCancellation = 2308;
 
-	public const int PingingServer = 2400;
-	public const int SuccessfullyPingedServer = 2401;
-	public const int PingFailed = 2402;
+	// Cached procedure events, 2400-2499
+	public const int GettingCachedProcedure = 2400;
+	public const int PoolDoesNotHaveSharedProcedureCache = 2401;
+	public const int CouldNotNormalizeDatabaseAndName = 2402;
+	public const int FailedToCacheProcedure = 2403;
+	public const int CachingProcedure = 2404;
+	public const int ProcedureCacheCount = 2405;
+	public const int DidNotFindCachedProcedure = 2406;
+	public const int ReturningCachedProcedure = 2407;
+	public const int FailedToRetrieveProcedureMetadata = 2408;
+	public const int ServerDoesNotSupportCachedProcedures = 2409;
+	public const int ProcedureHasRoutineCount = 2410;
 
-	public const int FailedInReceiveReplyAsync = 2500;
-	public const int ExpectedSessionState1 = 2501;
-	public const int ExpectedSessionState6 = 2506;
-	public const int FailedInSendReplyAsync = 2510;
-	public const int SettingStateToFailed = 2511;
-	public const int ErrorPayload = 2512;
+	// Ping events, 2500-2599
+	public const int PingingServer = 2500;
+	public const int SuccessfullyPingedServer = 2501;
+	public const int PingFailed = 2502;
 
-	public const int CommandExecutorExecuteReader = 2600;
-	public const int QueryWasInterrupted = 2601;
-	public const int PreparingCommandPayload = 2602;
-	public const int PreparingCommandPayloadWithId = 2603;
-	public const int QueryAttributesNotSupported = 2604;
-	public const int QueryAttributesNotSupportedWithId = 2605;
-	public const int IgnoringExceptionInDisposeAsync = 2606;
-
-	public const int StartingBulkCopy = 2700;
-	public const int AddingDefaultColumnMapping = 2701;
-	public const int IgnoringColumn = 2702;
-	public const int FinishedBulkCopy = 2703;
-	public const int BulkCopyFailed = 2704;
-	public const int ColumnMappingAlreadyHasExpression = 2705;
-	public const int SettingExpressionToMapColumn = 2706;
+	// Bulk copy events, 2600-2699
+	public const int StartingBulkCopy = 2503;
+	public const int AddingDefaultColumnMapping = 2504;
+	public const int IgnoringColumn = 2505;
+	public const int FinishedBulkCopy = 2506;
+	public const int BulkCopyFailed = 2507;
+	public const int ColumnMappingAlreadyHasExpression = 2508;
+	public const int SettingExpressionToMapColumn = 2509;
 
+	// Connection pool events, 3000-3099
 	public const int WaitingForAvailableSession = 3000;
 	public const int CreatingNewConnectionPool = 3001;
 	public const int ScanningForLeakedSessions = 3002;
@@ -142,17 +150,19 @@ internal static class EventIds
 	public const int CreatedSessionToReachMinimumPoolCount = 3019;
 	public const int CreatedPoolWillNotBeUsed = 3020;
 
-	public const int HasServerRedirectionHeader = 3100;
-	public const int ServerRedirectionIsDisabled = 3101;
-	public const int OpeningNewConnection = 3102;
-	public const int FailedToConnectRedirectedSession = 3103;
-	public const int ClosingSessionToUseRedirectedSession = 3104;
-	public const int SessionAlreadyConnectedToServer = 3105;
-	public const int RequiresServerRedirection = 3106;
+	// Connection pool background events, 3100-3199
+	public const int ReapingConnectionPool = 3100;
+	public const int CheckingForDnsChanges = 3101;
+	public const int DetectedDnsChange = 3102;
+	public const int DnsCheckFailed = 3103;
+	public const int ClearingPoolDueToDnsChanges = 3104;
 
-	public const int ReapingConnectionPool = 3200;
-	public const int CheckingForDnsChanges = 3201;
-	public const int DetectedDnsChange = 3202;
-	public const int DnsCheckFailed = 3203;
-	public const int ClearingPoolDueToDnsChanges = 3204;
+	// Server redirection events, 3200-3299
+	public const int HasServerRedirectionHeader = 3200;
+	public const int ServerRedirectionIsDisabled = 3201;
+	public const int OpeningNewConnection = 3202;
+	public const int FailedToConnectRedirectedSession = 3203;
+	public const int ClosingSessionToUseRedirectedSession = 3204;
+	public const int SessionAlreadyConnectedToServer = 3205;
+	public const int RequiresServerRedirection = 3206;
 }

From 9f03ec747642059285b785dd98b28eb3e5d74a04 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 14:34:59 -0800
Subject: [PATCH 010/393] Reword the TLS connection log message.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Logging/Log.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index c3e5062bb..1051db5bf 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -161,11 +161,11 @@ internal static partial class Log
 	public static partial void UsingRemoteCertificateValidationCallback(ILogger logger, string sessionId);
 
 #if NETCOREAPP3_0_OR_GREATER
-	[LoggerMessage(EventIds.ConnectedTlsBasic, LogLevel.Debug, "Session {SessionId} connected TLS with {SslProtocol}, {NegotiatedCipherSuite}")]
+	[LoggerMessage(EventIds.ConnectedTlsBasic, LogLevel.Debug, "Session {SessionId} connected TLS using {SslProtocol} and {NegotiatedCipherSuite}")]
 	public static partial void ConnectedTlsBasic(ILogger logger, string sessionId, SslProtocols sslProtocol, TlsCipherSuite negotiatedCipherSuite);
 #endif
 
-	[LoggerMessage(EventIds.ConnectedTlsDetailed, LogLevel.Debug, "Session {SessionId} connected TLS with {SslProtocol}, {CipherAlgorithm}, {HashAlgorithm}, {KeyExchangeAlgorithm}, {KeyExchangeStrength}")]
+	[LoggerMessage(EventIds.ConnectedTlsDetailed, LogLevel.Debug, "Session {SessionId} connected TLS using {SslProtocol}, {CipherAlgorithm}, {HashAlgorithm}, {KeyExchangeAlgorithm}, {KeyExchangeStrength}")]
 	public static partial void ConnectedTlsDetailed(ILogger logger, string sessionId, SslProtocols sslProtocol, CipherAlgorithmType cipherAlgorithm, HashAlgorithmType hashAlgorithm, ExchangeAlgorithmType keyExchangeAlgorithm, int keyExchangeStrength);
 
 	[LoggerMessage(EventIds.CouldNotInitializeTlsConnection, LogLevel.Error, "Session {SessionId} couldn't initialize TLS connection")]

From a4f6449a2069c14b381c136b6cdb8965e0899b1a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 14:57:03 -0800
Subject: [PATCH 011/393] Add an implementation of DbDataSource.

This was derived from the source code at https://github.com/npgsql/npgsql/blob/main/src/Npgsql/Shims/DbDataSource.cs.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Shims/DbDataSource.cs | 60 ++++++++++++++++++++++++
 1 file changed, 60 insertions(+)
 create mode 100644 src/MySqlConnector/Shims/DbDataSource.cs

diff --git a/src/MySqlConnector/Shims/DbDataSource.cs b/src/MySqlConnector/Shims/DbDataSource.cs
new file mode 100644
index 000000000..97f13acbd
--- /dev/null
+++ b/src/MySqlConnector/Shims/DbDataSource.cs
@@ -0,0 +1,60 @@
+#if !NET7_0_OR_GREATER
+namespace System.Data.Common;
+
+public abstract class DbDataSource
+#if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+	: IDisposable, IAsyncDisposable
+#else
+	: IDisposable
+#endif
+{
+	public abstract string ConnectionString { get; }
+
+	public DbConnection CreateConnection() => CreateDbConnection();
+
+	public DbConnection OpenConnection() => OpenDbConnection();
+
+	public ValueTask<DbConnection> OpenConnectionAsync(CancellationToken cancellationToken = default) =>
+		OpenDbConnectionAsync(cancellationToken);
+
+	public DbCommand CreateCommand(string? commandText = null) => CreateDbCommand(commandText);
+
+#if NET6_0_OR_GREATER
+	public DbBatch CreateBatch() => CreateDbBatch();
+#endif
+
+	public void Dispose()
+	{
+		Dispose(disposing: true);
+		GC.SuppressFinalize(this);
+	}
+
+	public async ValueTask DisposeAsync()
+	{
+		await DisposeAsyncCore().ConfigureAwait(false);
+
+		Dispose(disposing: false);
+#pragma warning disable CA1816 // Call GC.SuppressFinalize correctly
+		GC.SuppressFinalize(this);
+#pragma warning restore CA1816
+	}
+
+	protected abstract DbConnection CreateDbConnection();
+
+	// No need for actual implementations in this compat shim - its only implementation will be MySqlDataSource, which overrides these.
+	protected virtual DbConnection OpenDbConnection() => throw new NotSupportedException();
+	protected virtual ValueTask<DbConnection> OpenDbConnectionAsync(CancellationToken cancellationToken = default) =>
+		throw new NotSupportedException();
+	protected virtual DbCommand CreateDbCommand(string? commandText = null) => throw new NotSupportedException();
+
+#if NET6_0_OR_GREATER
+	protected virtual DbBatch CreateDbBatch() => throw new NotSupportedException();
+#endif
+
+	protected virtual void Dispose(bool disposing)
+	{
+	}
+
+	protected virtual ValueTask DisposeAsyncCore() => default;
+}
+#endif

From 7f4e497305000cb70710d5203708b46f40f744c7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 15:26:25 -0800
Subject: [PATCH 012/393] Compile MySqlDataSource for all TFMs.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/MySqlConnection.cs         |  9 +---
 src/MySqlConnector/MySqlConnectorFactory.cs   |  8 ++--
 src/MySqlConnector/MySqlDataSource.cs         |  2 -
 src/MySqlConnector/Shims/DbDataSource.cs      | 41 +++++++++++++++++--
 .../IntegrationTests/MySqlDataSourceTests.cs  |  8 ----
 5 files changed, 43 insertions(+), 25 deletions(-)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 40d06c187..3e6c6f631 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -34,13 +34,11 @@ public MySqlConnection(string? connectionString)
 	{
 	}
 
-#if NET7_0_OR_GREATER
 	internal MySqlConnection(MySqlDataSource dataSource)
 		: this(dataSource.ConnectionString, dataSource.LoggingConfiguration)
 	{
 		m_dataSource = dataSource;
 	}
-#endif
 
 	private MySqlConnection(string connectionString, MySqlConnectorLoggingConfiguration loggingConfiguration)
 	{
@@ -405,10 +403,7 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 
 			SetState(ConnectionState.Connecting);
 
-			var pool =
-#if NET7_0_OR_GREATER
-				m_dataSource?.Pool ??
-#endif
+			var pool = m_dataSource?.Pool ??
 				ConnectionPool.GetPool(m_connectionString, LoggingConfiguration, createIfNotFound: true);
 			m_connectionSettings ??= pool?.ConnectionSettings ?? new ConnectionSettings(new MySqlConnectionStringBuilder(m_connectionString));
 
@@ -1118,9 +1113,7 @@ private ConnectionSettings GetConnectionSettings() =>
 	private static readonly object s_lock = new();
 	private static readonly Dictionary<System.Transactions.Transaction, List<EnlistedTransactionBase>> s_transactionConnections = new();
 
-#if NET7_0_OR_GREATER
 	private readonly MySqlDataSource? m_dataSource;
-#endif
 	private readonly ILogger m_logger;
 	private string m_connectionString;
 	private ConnectionSettings? m_connectionSettings;
diff --git a/src/MySqlConnector/MySqlConnectorFactory.cs b/src/MySqlConnector/MySqlConnectorFactory.cs
index 6d91b47df..02a358d10 100644
--- a/src/MySqlConnector/MySqlConnectorFactory.cs
+++ b/src/MySqlConnector/MySqlConnectorFactory.cs
@@ -85,15 +85,17 @@ public sealed class MySqlConnectorFactory : DbProviderFactory
 #else
 	public bool CanCreateBatch => true;
 #endif
-#pragma warning restore CA1822 // Mark members as static
 
-#if NET7_0_OR_GREATER
 	/// <summary>
 	/// Creates a new <see cref="MySqlDataSource"/> object.
 	/// </summary>
 	/// <param name="connectionString">The connection string.</param>
-	public override DbDataSource CreateDataSource(string connectionString) => new MySqlDataSource(connectionString);
+	public
+#if NET7_0_OR_GREATER
+	override
 #endif
+	DbDataSource CreateDataSource(string connectionString) => new MySqlDataSource(connectionString);
+#pragma warning restore CA1822 // Mark members as static
 
 	private MySqlConnectorFactory()
 	{
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index 84442ce0d..3e287ca19 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -1,4 +1,3 @@
-#if NET7_0_OR_GREATER
 using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
 using MySqlConnector.Logging;
@@ -114,4 +113,3 @@ private async ValueTask DisposeAsync(IOBehavior ioBehavior)
 	private readonly string m_connectionString;
 	private bool m_isDisposed;
 }
-#endif
diff --git a/src/MySqlConnector/Shims/DbDataSource.cs b/src/MySqlConnector/Shims/DbDataSource.cs
index 97f13acbd..91a55f833 100644
--- a/src/MySqlConnector/Shims/DbDataSource.cs
+++ b/src/MySqlConnector/Shims/DbDataSource.cs
@@ -41,10 +41,43 @@ public async ValueTask DisposeAsync()
 
 	protected abstract DbConnection CreateDbConnection();
 
-	// No need for actual implementations in this compat shim - its only implementation will be MySqlDataSource, which overrides these.
-	protected virtual DbConnection OpenDbConnection() => throw new NotSupportedException();
-	protected virtual ValueTask<DbConnection> OpenDbConnectionAsync(CancellationToken cancellationToken = default) =>
-		throw new NotSupportedException();
+	protected virtual DbConnection OpenDbConnection()
+	{
+		var connection = CreateDbConnection();
+
+		try
+		{
+			connection.Open();
+			return connection;
+		}
+		catch
+		{
+			connection.Dispose();
+			throw;
+		}
+	}
+
+	protected virtual async ValueTask<DbConnection> OpenDbConnectionAsync(CancellationToken cancellationToken = default)
+	{
+		var connection = CreateDbConnection();
+
+		try
+		{
+			await connection.OpenAsync(cancellationToken).ConfigureAwait(false);
+			return connection;
+		}
+		catch
+		{
+#if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+			await connection.DisposeAsync().ConfigureAwait(false);
+#else
+			connection.Dispose();
+#endif
+			throw;
+		}
+	}
+
+	// The shim doesn't support these methods; to use the full DbDataSource the client needs to be on .NET 7.0.
 	protected virtual DbCommand CreateDbCommand(string? commandText = null) => throw new NotSupportedException();
 
 #if NET6_0_OR_GREATER
diff --git a/tests/IntegrationTests/MySqlDataSourceTests.cs b/tests/IntegrationTests/MySqlDataSourceTests.cs
index c8c8bfd76..b8d006cfb 100644
--- a/tests/IntegrationTests/MySqlDataSourceTests.cs
+++ b/tests/IntegrationTests/MySqlDataSourceTests.cs
@@ -1,11 +1,4 @@
 #if !MYSQL_DATA
-#if NET7_0_OR_GREATER
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
 namespace IntegrationTests;
 
 public class MySqlDataSourceTests : IClassFixture<DatabaseFixture>
@@ -103,4 +96,3 @@ public void CreateFromDbFactory()
 	}
 }
 #endif
-#endif

From 50fd50672b99d29a18bdbf11532818ca88774d24 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 16:29:14 -0800
Subject: [PATCH 013/393] Make MySqlDataSourceBuilder available in all TFMs.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../CompatibilitySuppressions.xml             | 12 ++++++++++
 src/MySqlConnector/MySqlDataSourceBuilder.cs  | 22 +++++++++++++++++--
 src/MySqlConnector/Shims/DbDataSource.cs      |  2 +-
 .../IntegrationTests/MySqlDataSourceTests.cs  | 11 ++++++++++
 4 files changed, 44 insertions(+), 3 deletions(-)

diff --git a/src/MySqlConnector/CompatibilitySuppressions.xml b/src/MySqlConnector/CompatibilitySuppressions.xml
index e84eb5224..fd40d06ea 100644
--- a/src/MySqlConnector/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector/CompatibilitySuppressions.xml
@@ -1,5 +1,11 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:System.Data.Common.DbDataSource</Target>
+    <Left>lib/net6.0/MySqlConnector.dll</Left>
+    <Right>lib/net7.0/MySqlConnector.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0008</DiagnosticId>
     <Target>T:MySqlConnector.MySqlBatchCommandCollection</Target>
@@ -18,6 +24,12 @@
     <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
     <Right>lib/net461/MySqlConnector.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:MySqlConnector.MySqlDataSource</Target>
+    <Left>lib/netstandard2.1/MySqlConnector.dll</Left>
+    <Right>lib/netcoreapp3.1/MySqlConnector.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>
     <Target>.NETFramework,Version=v4.5</Target>
diff --git a/src/MySqlConnector/MySqlDataSourceBuilder.cs b/src/MySqlConnector/MySqlDataSourceBuilder.cs
index 456990327..73e3908e8 100644
--- a/src/MySqlConnector/MySqlDataSourceBuilder.cs
+++ b/src/MySqlConnector/MySqlDataSourceBuilder.cs
@@ -3,20 +3,36 @@
 
 namespace MySqlConnector;
 
-#if NET7_0_OR_GREATER
+/// <summary>
+/// <see cref="MySqlDataSourceBuilder"/> provides an API for configuring and creating a <see cref="MySqlDataSource"/>,
+/// from which <see cref="MySqlConnection"/> objects can be obtained.
+/// </summary>
 public sealed class MySqlDataSourceBuilder
 {
+	/// <summary>
+	/// Initializes a new <see cref="MySqlDataSourceBuilder"/> with the specified connection string.
+	/// </summary>
+	/// <param name="connectionString">The optional connection string to use.</param>
 	public MySqlDataSourceBuilder(string? connectionString = null)
 	{
 		ConnectionStringBuilder = new(connectionString ?? "");
 	}
 
+	/// <summary>
+	/// Sets the <see cref="ILoggerFactory"/> that will be used for logging.
+	/// </summary>
+	/// <param name="loggerFactory">The logger factory.</param>
+	/// <returns>This object, so that method calls can be chained.</returns>
 	public MySqlDataSourceBuilder UseLoggerFactory(ILoggerFactory? loggerFactory)
 	{
 		m_loggerFactory = loggerFactory;
 		return this;
 	}
 
+	/// <summary>
+	/// Builds a <see cref="MySqlDataSource"/> which is ready for use.
+	/// </summary>
+	/// <returns>A new <see cref="MySqlDataSource"/> with the settings configured through this <see cref="MySqlDataSourceBuilder"/>.</returns>
 	public MySqlDataSource Build()
 	{
 		var loggingConfiguration = m_loggerFactory is null ? MySqlConnectorLoggingConfiguration.NullConfiguration : new(m_loggerFactory);
@@ -24,8 +40,10 @@ public MySqlDataSource Build()
 			loggingConfiguration);
 	}
 
+	/// <summary>
+	/// A <see cref="MySqlConnectionStringBuilder"/> that can be used to configure the connection string on this <see cref="MySqlDataSourceBuilder"/>.
+	/// </summary>
 	public MySqlConnectionStringBuilder ConnectionStringBuilder { get; }
 
 	private ILoggerFactory? m_loggerFactory;
 }
-#endif
diff --git a/src/MySqlConnector/Shims/DbDataSource.cs b/src/MySqlConnector/Shims/DbDataSource.cs
index 91a55f833..5152dcb37 100644
--- a/src/MySqlConnector/Shims/DbDataSource.cs
+++ b/src/MySqlConnector/Shims/DbDataSource.cs
@@ -2,7 +2,7 @@
 namespace System.Data.Common;
 
 public abstract class DbDataSource
-#if NET5_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NETCOREAPP3_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 	: IDisposable, IAsyncDisposable
 #else
 	: IDisposable
diff --git a/tests/IntegrationTests/MySqlDataSourceTests.cs b/tests/IntegrationTests/MySqlDataSourceTests.cs
index b8d006cfb..60bf5f6b3 100644
--- a/tests/IntegrationTests/MySqlDataSourceTests.cs
+++ b/tests/IntegrationTests/MySqlDataSourceTests.cs
@@ -94,5 +94,16 @@ public void CreateFromDbFactory()
 		Assert.IsType<MySqlDataSource>(dbSource);
 		Assert.Equal(AppConfig.ConnectionString, dbSource.ConnectionString);
 	}
+
+	[Fact]
+	public void CreateFromDataSourceBuilder()
+	{
+		var connectionString = AppConfig.CreateConnectionStringBuilder().ConnectionString;
+		var builder = new MySqlDataSourceBuilder(connectionString);
+		using var dataSource = builder.Build();
+		Assert.Equal(connectionString, dataSource.ConnectionString);
+		using var connection = dataSource.OpenConnection();
+		Assert.Equal(ConnectionState.Open, connection.State);
+	}
 }
 #endif

From c538c71b863229aaaef4056a73c902e23a46399c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 17:13:13 -0800
Subject: [PATCH 014/393] Add MySqlDataSourceBuilder documentation.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../MySqlDataSource/OpenConnectionAsync.md    |  3 +--
 .../MySqlDataSourceBuilder/Build.md           | 24 +++++++++++++++++
 .../ConnectionStringBuilder.md                | 20 ++++++++++++++
 .../MySqlDataSourceBuilder.md                 | 23 ++++++++++++++++
 .../UseLoggerFactory.md                       | 27 +++++++++++++++++++
 .../MySqlDataSourceBuilderType.md             | 27 +++++++++++++++++++
 docs/content/api/MySqlConnectorAssembly.md    |  1 +
 docs/content/api/MySqlConnectorNamespace.md   |  1 +
 8 files changed, 124 insertions(+), 2 deletions(-)
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSourceBuilder/Build.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSourceBuilder/ConnectionStringBuilder.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSourceBuilder/MySqlDataSourceBuilder.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseLoggerFactory.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md

diff --git a/docs/content/api/MySqlConnector/MySqlDataSource/OpenConnectionAsync.md b/docs/content/api/MySqlConnector/MySqlDataSource/OpenConnectionAsync.md
index 8edadf2eb..9b20554ef 100644
--- a/docs/content/api/MySqlConnector/MySqlDataSource/OpenConnectionAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlDataSource/OpenConnectionAsync.md
@@ -7,8 +7,7 @@ title: MySqlDataSource.OpenConnectionAsync method
 Asynchronously returns a new, open [`MySqlConnection`](../../MySqlConnectionType/) to the database represented by this [`MySqlDataSource`](../../MySqlDataSourceType/).
 
 ```csharp
-public ValueTask<MySqlConnection> OpenConnectionAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+public ValueTask<MySqlConnection> OpenConnectionAsync(CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/Build.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/Build.md
new file mode 100644
index 000000000..e8352a4e2
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/Build.md
@@ -0,0 +1,24 @@
+---
+title: MySqlDataSourceBuilder.Build method
+---
+
+# MySqlDataSourceBuilder.Build method
+
+Builds a [`MySqlDataSource`](../../MySqlDataSourceType/) which is ready for use.
+
+```csharp
+public MySqlDataSource Build()
+```
+
+## Return Value
+
+A new [`MySqlDataSource`](../../MySqlDataSourceType/) with the settings configured through this [`MySqlDataSourceBuilder`](../../MySqlDataSourceBuilderType/).
+
+## See Also
+
+* class [MySqlDataSource](../../MySqlDataSourceType/)
+* class [MySqlDataSourceBuilder](../../MySqlDataSourceBuilderType/)
+* namespace [MySqlConnector](../../MySqlDataSourceBuilderType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/ConnectionStringBuilder.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/ConnectionStringBuilder.md
new file mode 100644
index 000000000..8884808c5
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/ConnectionStringBuilder.md
@@ -0,0 +1,20 @@
+---
+title: MySqlDataSourceBuilder.ConnectionStringBuilder property
+---
+
+# MySqlDataSourceBuilder.ConnectionStringBuilder property
+
+A [`MySqlConnectionStringBuilder`](../../MySqlConnectionStringBuilderType/) that can be used to configure the connection string on this [`MySqlDataSourceBuilder`](../../MySqlDataSourceBuilderType/).
+
+```csharp
+public MySqlConnectionStringBuilder ConnectionStringBuilder { get; }
+```
+
+## See Also
+
+* class [MySqlConnectionStringBuilder](../../MySqlConnectionStringBuilderType/)
+* class [MySqlDataSourceBuilder](../../MySqlDataSourceBuilderType/)
+* namespace [MySqlConnector](../../MySqlDataSourceBuilderType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/MySqlDataSourceBuilder.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/MySqlDataSourceBuilder.md
new file mode 100644
index 000000000..f3f30d761
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/MySqlDataSourceBuilder.md
@@ -0,0 +1,23 @@
+---
+title: MySqlDataSourceBuilder constructor
+---
+
+# MySqlDataSourceBuilder constructor
+
+Initializes a new [`MySqlDataSourceBuilder`](../../MySqlDataSourceBuilderType/) with the specified connection string.
+
+```csharp
+public MySqlDataSourceBuilder(string? connectionString = null)
+```
+
+| parameter | description |
+| --- | --- |
+| connectionString | The optional connection string to use. |
+
+## See Also
+
+* class [MySqlDataSourceBuilder](../../MySqlDataSourceBuilderType/)
+* namespace [MySqlConnector](../../MySqlDataSourceBuilderType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseLoggerFactory.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseLoggerFactory.md
new file mode 100644
index 000000000..a129f1ea6
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseLoggerFactory.md
@@ -0,0 +1,27 @@
+---
+title: MySqlDataSourceBuilder.UseLoggerFactory method
+---
+
+# MySqlDataSourceBuilder.UseLoggerFactory method
+
+Sets the ILoggerFactory that will be used for logging.
+
+```csharp
+public MySqlDataSourceBuilder UseLoggerFactory(ILoggerFactory? loggerFactory)
+```
+
+| parameter | description |
+| --- | --- |
+| loggerFactory | The logger factory. |
+
+## Return Value
+
+This object, so that method calls can be chained.
+
+## See Also
+
+* class [MySqlDataSourceBuilder](../../MySqlDataSourceBuilderType/)
+* namespace [MySqlConnector](../../MySqlDataSourceBuilderType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
new file mode 100644
index 000000000..bda9100dc
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
@@ -0,0 +1,27 @@
+---
+title: MySqlDataSourceBuilder
+---
+
+# MySqlDataSourceBuilder class
+
+[`MySqlDataSourceBuilder`](../MySqlDataSourceBuilderType/) provides an API for configuring and creating a [`MySqlDataSource`](../MySqlDataSourceType/), from which [`MySqlConnection`](../MySqlConnectionType/) objects can be obtained.
+
+```csharp
+public sealed class MySqlDataSourceBuilder
+```
+
+## Public Members
+
+| name | description |
+| --- | --- |
+| [MySqlDataSourceBuilder](../MySqlDataSourceBuilder/MySqlDataSourceBuilder/)(…) | Initializes a new [`MySqlDataSourceBuilder`](../MySqlDataSourceBuilderType/) with the specified connection string. |
+| [ConnectionStringBuilder](../MySqlDataSourceBuilder/ConnectionStringBuilder/) { get; } | A [`MySqlConnectionStringBuilder`](../MySqlConnectionStringBuilderType/) that can be used to configure the connection string on this [`MySqlDataSourceBuilder`](../MySqlDataSourceBuilderType/). |
+| [Build](../MySqlDataSourceBuilder/Build/)() | Builds a [`MySqlDataSource`](../MySqlDataSourceType/) which is ready for use. |
+| [UseLoggerFactory](../MySqlDataSourceBuilder/UseLoggerFactory/)(…) | Sets the ILoggerFactory that will be used for logging. |
+
+## See Also
+
+* namespace [MySqlConnector](../../MySqlConnectorNamespace/)
+* assembly [MySqlConnector](../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnectorAssembly.md b/docs/content/api/MySqlConnectorAssembly.md
index 035b4362d..7d739a64f 100644
--- a/docs/content/api/MySqlConnectorAssembly.md
+++ b/docs/content/api/MySqlConnectorAssembly.md
@@ -30,6 +30,7 @@ title: MySqlConnector
 | class [MySqlDataAdapter](../MySqlConnector/MySqlDataAdapterType/) |  |
 | class [MySqlDataReader](../MySqlConnector/MySqlDataReaderType/) |  |
 | class [MySqlDataSource](../MySqlConnector/MySqlDataSourceType/) | [`MySqlDataSource`](../MySqlConnector/MySqlDataSourceType/) implements a MySQL data source which can be used to obtain open connections, and against which commands can be executed directly. |
+| class [MySqlDataSourceBuilder](../MySqlConnector/MySqlDataSourceBuilderType/) | [`MySqlDataSourceBuilder`](../MySqlConnector/MySqlDataSourceBuilderType/) provides an API for configuring and creating a [`MySqlDataSource`](../MySqlConnector/MySqlDataSourceType/), from which [`MySqlConnection`](../MySqlConnector/MySqlConnectionType/) objects can be obtained. |
 | struct [MySqlDateTime](../MySqlConnector/MySqlDateTimeType/) | Represents a MySQL date/time value. This type can be used to store `DATETIME` values such as `0000-00-00` that can be stored in MySQL (when [`AllowZeroDateTime`](../MySqlConnector/MySqlConnectionStringBuilder/AllowZeroDateTime/) is true) but can't be stored in a DateTime value. |
 | enum [MySqlDateTimeKind](../MySqlConnector/MySqlDateTimeKindType/) | The DateTimeKind used when reading DateTime from the database. |
 | class [MySqlDbColumn](../MySqlConnector/MySqlDbColumnType/) |  |
diff --git a/docs/content/api/MySqlConnectorNamespace.md b/docs/content/api/MySqlConnectorNamespace.md
index 83238b378..740a3c32a 100644
--- a/docs/content/api/MySqlConnectorNamespace.md
+++ b/docs/content/api/MySqlConnectorNamespace.md
@@ -28,6 +28,7 @@ title: MySqlConnector
 | class [MySqlDataAdapter](../MySqlConnector/MySqlDataAdapterType/) |  |
 | class [MySqlDataReader](../MySqlConnector/MySqlDataReaderType/) |  |
 | class [MySqlDataSource](../MySqlConnector/MySqlDataSourceType/) | [`MySqlDataSource`](../MySqlConnector/MySqlDataSourceType/) implements a MySQL data source which can be used to obtain open connections, and against which commands can be executed directly. |
+| class [MySqlDataSourceBuilder](../MySqlConnector/MySqlDataSourceBuilderType/) | [`MySqlDataSourceBuilder`](../MySqlConnector/MySqlDataSourceBuilderType/) provides an API for configuring and creating a [`MySqlDataSource`](../MySqlConnector/MySqlDataSourceType/), from which [`MySqlConnection`](../MySqlConnector/MySqlConnectionType/) objects can be obtained. |
 | struct [MySqlDateTime](../MySqlConnector/MySqlDateTimeType/) | Represents a MySQL date/time value. This type can be used to store `DATETIME` values such as `0000-00-00` that can be stored in MySQL (when [`AllowZeroDateTime`](../MySqlConnector/MySqlConnectionStringBuilder/AllowZeroDateTime/) is true) but can't be stored in a DateTime value. |
 | enum [MySqlDateTimeKind](../MySqlConnector/MySqlDateTimeKindType/) | The DateTimeKind used when reading DateTime from the database. |
 | class [MySqlDbColumn](../MySqlConnector/MySqlDbColumnType/) |  |

From fddf03ee4110479cdac535c582bd49517a73b699 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 27 Dec 2022 17:30:04 -0800
Subject: [PATCH 015/393] Update for compatibility with Hugo v0.109.0.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 docs/config.toml                  | 1 +
 docs/layouts/partials/header.html | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/config.toml b/docs/config.toml
index eb34b24ce..4eccb5cc7 100644
--- a/docs/config.toml
+++ b/docs/config.toml
@@ -4,6 +4,7 @@ MetaDataFormat = "yaml"
 pluralizeListTitles = false
 pygmentsCodeFences = true
 pygmentsStyle = "friendly"
+disablePathToLower = true
 
 [blackfriday]
   plainIDAnchors = true
diff --git a/docs/layouts/partials/header.html b/docs/layouts/partials/header.html
index 520e2a03c..9226312df 100644
--- a/docs/layouts/partials/header.html
+++ b/docs/layouts/partials/header.html
@@ -36,7 +36,6 @@
     <meta name="theme-color" content="#ffffff">
     <!--end icons-->
 
-    {{ .Hugo.Generator }}
     {{ .Scratch.Add "title" "" }}{{ if isset .Site.Data.titles .Title }}{{ .Scratch.Set "title" (index .Site.Data.titles .Title).title }}{{ else if isset .Page.Params "customtitle" }}{{ .Scratch.Set "title" .Page.Params.customtitle }}{{ else }}{{ .Scratch.Set "title" .Title}}{{end}}
     <title>{{ .Scratch.Get "title" }} - MySqlConnector</title>
     <meta property="og:title" content="{{ .Scratch.Get "title" }} - MySqlConnector" />

From 46f477b1c0ba022ef99eee88bece3a9265365b10 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 28 Dec 2022 11:50:49 -0800
Subject: [PATCH 016/393] Add certificate callbacks to MySqlDataSource.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../UseClientCertificatesCallback.md          | 28 ++++++++++++++
 .../UseLoggerFactory.md                       |  2 +-
 .../UseRemoteCertificateValidationCallback.md | 32 ++++++++++++++++
 .../MySqlDataSourceBuilderType.md             |  2 +
 src/MySqlConnector/MySqlConnection.cs         |  4 --
 src/MySqlConnector/MySqlDataSource.cs         | 19 ++++++++--
 src/MySqlConnector/MySqlDataSourceBuilder.cs  | 37 ++++++++++++++++++-
 .../IntegrationTests/MySqlDataSourceTests.cs  | 21 +++++++++++
 tests/IntegrationTests/SslTests.cs            | 10 +----
 9 files changed, 136 insertions(+), 19 deletions(-)
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseClientCertificatesCallback.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseRemoteCertificateValidationCallback.md

diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseClientCertificatesCallback.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseClientCertificatesCallback.md
new file mode 100644
index 000000000..d0fd09905
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseClientCertificatesCallback.md
@@ -0,0 +1,28 @@
+---
+title: MySqlDataSourceBuilder.UseClientCertificatesCallback method
+---
+
+# MySqlDataSourceBuilder.UseClientCertificatesCallback method
+
+Sets the callback used to provide client certificates for connecting to a server.
+
+```csharp
+public MySqlDataSourceBuilder UseClientCertificatesCallback(
+    Func<X509CertificateCollection, ValueTask>? callback)
+```
+
+| parameter | description |
+| --- | --- |
+| callback | The callback that will provide client certificates. The X509CertificateCollection provided to the callback should be filled with the client certificate(s) needed to connect to the server. |
+
+## Return Value
+
+This builder, so that method calls can be chained.
+
+## See Also
+
+* class [MySqlDataSourceBuilder](../../MySqlDataSourceBuilderType/)
+* namespace [MySqlConnector](../../MySqlDataSourceBuilderType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseLoggerFactory.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseLoggerFactory.md
index a129f1ea6..019d7e9c9 100644
--- a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseLoggerFactory.md
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseLoggerFactory.md
@@ -16,7 +16,7 @@ public MySqlDataSourceBuilder UseLoggerFactory(ILoggerFactory? loggerFactory)
 
 ## Return Value
 
-This object, so that method calls can be chained.
+This builder, so that method calls can be chained.
 
 ## See Also
 
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseRemoteCertificateValidationCallback.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseRemoteCertificateValidationCallback.md
new file mode 100644
index 000000000..c82769a02
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseRemoteCertificateValidationCallback.md
@@ -0,0 +1,32 @@
+---
+title: MySqlDataSourceBuilder.UseRemoteCertificateValidationCallback method
+---
+
+# MySqlDataSourceBuilder.UseRemoteCertificateValidationCallback method
+
+Sets the callback used to verify that the server's certificate is valid.
+
+```csharp
+public MySqlDataSourceBuilder UseRemoteCertificateValidationCallback(
+    RemoteCertificateValidationCallback callback)
+```
+
+| parameter | description |
+| --- | --- |
+| callback | The callback used to verify that the server's certificate is valid. |
+
+## Return Value
+
+This builder, so that method calls can be chained.
+
+## Remarks
+
+[`SslMode`](../../MySqlConnectionStringBuilder/SslMode/) must be set to Preferred or Required in order for this delegate to be invoked. See the documentation for RemoteCertificateValidationCallback for more information on the values passed to this delegate.
+
+## See Also
+
+* class [MySqlDataSourceBuilder](../../MySqlDataSourceBuilderType/)
+* namespace [MySqlConnector](../../MySqlDataSourceBuilderType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
index bda9100dc..2d639f166 100644
--- a/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
@@ -17,7 +17,9 @@ public sealed class MySqlDataSourceBuilder
 | [MySqlDataSourceBuilder](../MySqlDataSourceBuilder/MySqlDataSourceBuilder/)(…) | Initializes a new [`MySqlDataSourceBuilder`](../MySqlDataSourceBuilderType/) with the specified connection string. |
 | [ConnectionStringBuilder](../MySqlDataSourceBuilder/ConnectionStringBuilder/) { get; } | A [`MySqlConnectionStringBuilder`](../MySqlConnectionStringBuilderType/) that can be used to configure the connection string on this [`MySqlDataSourceBuilder`](../MySqlDataSourceBuilderType/). |
 | [Build](../MySqlDataSourceBuilder/Build/)() | Builds a [`MySqlDataSource`](../MySqlDataSourceType/) which is ready for use. |
+| [UseClientCertificatesCallback](../MySqlDataSourceBuilder/UseClientCertificatesCallback/)(…) | Sets the callback used to provide client certificates for connecting to a server. |
 | [UseLoggerFactory](../MySqlDataSourceBuilder/UseLoggerFactory/)(…) | Sets the ILoggerFactory that will be used for logging. |
+| [UseRemoteCertificateValidationCallback](../MySqlDataSourceBuilder/UseRemoteCertificateValidationCallback/)(…) | Sets the callback used to verify that the server's certificate is valid. |
 
 ## See Also
 
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 3e6c6f631..1a793b24a 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -513,11 +513,7 @@ public override string ConnectionString
 	/// Gets or sets the delegate used to provide client certificates for connecting to a server.
 	/// </summary>
 	/// <remarks>The provided <see cref="X509CertificateCollection"/> should be filled with the client certificate(s) needed to connect to the server.</remarks>
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 	public Func<X509CertificateCollection, ValueTask>? ProvideClientCertificatesCallback { get; set; }
-#else
-	public Func<X509CertificateCollection, Task>? ProvideClientCertificatesCallback { get; set; }
-#endif
 
 	/// <summary>
 	/// Gets or sets the delegate used to generate a password for new database connections.
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index 3e287ca19..112b10fd7 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -1,3 +1,5 @@
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
 using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
 using MySqlConnector.Logging;
@@ -16,14 +18,19 @@ public sealed class MySqlDataSource : DbDataSource
 	/// <param name="connectionString">The connection string for the MySQL Server. This parameter is required.</param>
 	/// <exception cref="ArgumentNullException">Thrown if <paramref name="connectionString"/> is <c>null</c>.</exception>
 	public MySqlDataSource(string connectionString)
-		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration)
+		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null)
 	{
 	}
 
-	internal MySqlDataSource(string connectionString, MySqlConnectorLoggingConfiguration loggingConfiguration)
+	internal MySqlDataSource(string connectionString,
+		MySqlConnectorLoggingConfiguration loggingConfiguration,
+		Func<X509CertificateCollection, ValueTask>? clientCertificatesCallback,
+		RemoteCertificateValidationCallback? remoteCertificateValidationCallback)
 	{
 		m_connectionString = connectionString;
 		LoggingConfiguration = loggingConfiguration;
+		m_clientCertificatesCallback = clientCertificatesCallback;
+		m_remoteCertificateValidationCallback = remoteCertificateValidationCallback;
 		m_logger = loggingConfiguration.DataSourceLogger;
 
 		Pool = ConnectionPool.CreatePool(m_connectionString, LoggingConfiguration);
@@ -72,7 +79,11 @@ protected override DbConnection CreateDbConnection()
 	{
 		if (m_isDisposed)
 			throw new ObjectDisposedException(nameof(MySqlDataSource));
-		return new MySqlConnection(this);
+		return new MySqlConnection(this)
+		{
+			ProvideClientCertificatesCallback = m_clientCertificatesCallback,
+			RemoteCertificateValidationCallback = m_remoteCertificateValidationCallback,
+		};
 	}
 
 	protected override void Dispose(bool disposing)
@@ -111,5 +122,7 @@ private async ValueTask DisposeAsync(IOBehavior ioBehavior)
 	private readonly ILogger m_logger;
 	private readonly int m_id;
 	private readonly string m_connectionString;
+	private readonly Func<X509CertificateCollection, ValueTask>? m_clientCertificatesCallback;
+	private readonly RemoteCertificateValidationCallback? m_remoteCertificateValidationCallback;
 	private bool m_isDisposed;
 }
diff --git a/src/MySqlConnector/MySqlDataSourceBuilder.cs b/src/MySqlConnector/MySqlDataSourceBuilder.cs
index 73e3908e8..7f2e6b7a3 100644
--- a/src/MySqlConnector/MySqlDataSourceBuilder.cs
+++ b/src/MySqlConnector/MySqlDataSourceBuilder.cs
@@ -1,3 +1,5 @@
+using System.Net.Security;
+using System.Security.Cryptography.X509Certificates;
 using Microsoft.Extensions.Logging;
 using MySqlConnector.Logging;
 
@@ -22,13 +24,39 @@ public MySqlDataSourceBuilder(string? connectionString = null)
 	/// Sets the <see cref="ILoggerFactory"/> that will be used for logging.
 	/// </summary>
 	/// <param name="loggerFactory">The logger factory.</param>
-	/// <returns>This object, so that method calls can be chained.</returns>
+	/// <returns>This builder, so that method calls can be chained.</returns>
 	public MySqlDataSourceBuilder UseLoggerFactory(ILoggerFactory? loggerFactory)
 	{
 		m_loggerFactory = loggerFactory;
 		return this;
 	}
 
+	/// <summary>
+	/// Sets the callback used to provide client certificates for connecting to a server.
+	/// </summary>
+	/// <param name="callback">The callback that will provide client certificates. The <see cref="X509CertificateCollection"/>
+	/// provided to the callback should be filled with the client certificate(s) needed to connect to the server.</param>
+	/// <returns>This builder, so that method calls can be chained.</returns>
+	public MySqlDataSourceBuilder UseClientCertificatesCallback(Func<X509CertificateCollection, ValueTask>? callback)
+	{
+		m_clientCertificatesCallback = callback;
+		return this;
+	}
+
+	/// <summary>
+	/// Sets the callback used to verify that the server's certificate is valid.
+	/// </summary>
+	/// <param name="callback">The callback used to verify that the server's certificate is valid.</param>
+	/// <returns>This builder, so that method calls can be chained.</returns>
+	/// <remarks><see cref="MySqlConnectionStringBuilder.SslMode"/> must be set to <see cref="MySqlSslMode.Preferred"/>
+	/// or <see cref="MySqlSslMode.Required"/> in order for this delegate to be invoked. See the documentation for
+	/// <see cref="RemoteCertificateValidationCallback"/> for more information on the values passed to this delegate.</remarks>
+	public MySqlDataSourceBuilder UseRemoteCertificateValidationCallback(RemoteCertificateValidationCallback callback)
+	{
+		m_remoteCertificateValidationCallback = callback;
+		return this;
+	}
+
 	/// <summary>
 	/// Builds a <see cref="MySqlDataSource"/> which is ready for use.
 	/// </summary>
@@ -37,7 +65,10 @@ public MySqlDataSource Build()
 	{
 		var loggingConfiguration = m_loggerFactory is null ? MySqlConnectorLoggingConfiguration.NullConfiguration : new(m_loggerFactory);
 		return new(ConnectionStringBuilder.ConnectionString,
-			loggingConfiguration);
+			loggingConfiguration,
+			m_clientCertificatesCallback,
+			m_remoteCertificateValidationCallback
+			);
 	}
 
 	/// <summary>
@@ -46,4 +77,6 @@ public MySqlDataSource Build()
 	public MySqlConnectionStringBuilder ConnectionStringBuilder { get; }
 
 	private ILoggerFactory? m_loggerFactory;
+	private Func<X509CertificateCollection, ValueTask>? m_clientCertificatesCallback;
+	private RemoteCertificateValidationCallback? m_remoteCertificateValidationCallback;
 }
diff --git a/tests/IntegrationTests/MySqlDataSourceTests.cs b/tests/IntegrationTests/MySqlDataSourceTests.cs
index 60bf5f6b3..0c486e3c5 100644
--- a/tests/IntegrationTests/MySqlDataSourceTests.cs
+++ b/tests/IntegrationTests/MySqlDataSourceTests.cs
@@ -105,5 +105,26 @@ public void CreateFromDataSourceBuilder()
 		using var connection = dataSource.OpenConnection();
 		Assert.Equal(ConnectionState.Open, connection.State);
 	}
+
+	[SkippableTheory(ServerFeatures.KnownCertificateAuthority, ConfigSettings.RequiresSsl)]
+	[InlineData(MySqlSslMode.VerifyCA, false, false)]
+	[InlineData(MySqlSslMode.VerifyCA, true, false)]
+	[InlineData(MySqlSslMode.Required, true, true)]
+	public async Task ConnectSslRemoteCertificateValidationCallback(MySqlSslMode sslMode, bool clearCA, bool expectedSuccess)
+	{
+		var builder = new MySqlDataSourceBuilder(AppConfig.ConnectionString)
+			.UseRemoteCertificateValidationCallback((s, c, h, e) => true);
+		builder.ConnectionStringBuilder.CertificateFile = Path.Combine(AppConfig.CertsPath, "ssl-client.pfx");
+		builder.ConnectionStringBuilder.SslMode = sslMode;
+		builder.ConnectionStringBuilder.SslCa = clearCA ? "" : Path.Combine(AppConfig.CertsPath, "non-ca-client-cert.pem");
+
+		using var dataSource = builder.Build();
+		using var connection = dataSource.CreateConnection();
+		if (expectedSuccess)
+			await connection.OpenAsync();
+		else
+			await Assert.ThrowsAsync<MySqlException>(connection.OpenAsync);
+	}
+
 }
 #endif
diff --git a/tests/IntegrationTests/SslTests.cs b/tests/IntegrationTests/SslTests.cs
index df3042d71..94179aa7c 100644
--- a/tests/IntegrationTests/SslTests.cs
+++ b/tests/IntegrationTests/SslTests.cs
@@ -62,19 +62,11 @@ public async Task ConnectSslClientCertificateCallback(string certificateFile, st
 		var certificateFilePath = Path.Combine(AppConfig.CertsPath, certificateFile);
 
 		using var connection = new MySqlConnection(csb.ConnectionString);
-#if NETFRAMEWORK
 		connection.ProvideClientCertificatesCallback = x =>
 		{
 			x.Add(new X509Certificate2(certificateFilePath, certificateFilePassword));
-			return Task.CompletedTask;
+			return default;
 		};
-#else
-		connection.ProvideClientCertificatesCallback = async x =>
-		{
-			var certificateBytes = await File.ReadAllBytesAsync(certificateFilePath);
-			x.Add(new X509Certificate2(certificateBytes, certificateFilePassword));
-		};
-#endif
 
 		await connection.OpenAsync();
 		Assert.True(connection.SslIsEncrypted);

From 8a5d259a5fc85b88e0fdd5cc0b837cf1c30320f9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 29 Dec 2022 09:34:01 -0800
Subject: [PATCH 017/393] Fix formatting in documentation.

---
 docs/content/tutorials/migrating-from-connector-net.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index a9303bf0d..188cfedcd 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -236,7 +236,7 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * [#92734](https://bugs.mysql.com/bug.php?id=92734): `MySqlParameter.Clone` doesn't copy all property values
 * ~~[#92789](https://bugs.mysql.com/bug.php?id=92789): Illegal connection attributes written for non-ASCII values~~
 * ~~[#92912](https://bugs.mysql.com/bug.php?id=92912): `MySqlDbType.LongText` values encoded incorrectly with prepared statements~~
-* ~~[#92982](https://bugs.mysql.com/bug.php?id=92982), [#93399](https://bugs.mysql.com/bug.php?id=93399)~~: `FormatException` thrown when connecting to MySQL Server 8.0.13~~
+* ~~[#92982](https://bugs.mysql.com/bug.php?id=92982), [#93399](https://bugs.mysql.com/bug.php?id=93399): `FormatException` thrown when connecting to MySQL Server 8.0.13~~
 * [#93047](https://bugs.mysql.com/bug.php?id=93047): `MySqlDataAdapter` throws timeout exception when an error occurs
 * ~~[#93202](https://bugs.mysql.com/bug.php?id=93202): Connector runs `SHOW VARIABLES` when connection is made~~
 * [#93220](https://bugs.mysql.com/bug.php?id=93220): Can’t call FUNCTION when parameter name contains parentheses
@@ -293,7 +293,7 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * ~~[#106243](https://bugs.mysql.com/bug.php?id=106243): `CancellationToken` doesn't cancel `MySqlConnection.OpenAsync`~~
 * ~~[#106244](https://bugs.mysql.com/bug.php?id=106244): `MySqlDataReader.GetFieldValue<Stream>` throws `InvalidCastException`~~
 * [#106247](https://bugs.mysql.com/bug.php?id=106247): Can't use ~~`MySqlDbType.Enum` or~~ `MySqlDbType.Set` with prepared command
-* [#108756](https://bugs.mysql.com/bug.php?id=108756): Can't insert negative number using prepared statement with MySqlDbType.Int24]
+* [#108756](https://bugs.mysql.com/bug.php?id=108756): Can't insert negative number using prepared statement with `MySqlDbType.Int24`
 * [#108970](https://bugs.mysql.com/bug.php?id=108970): `MySqlConnectionStringBuilder.ContainsKey` method gives wrong result
 * [#109141](https://bugs.mysql.com/bug.php?id=109141): Insert of data into a table results in `System.ArgumentException`
 * [#109331](https://bugs.mysql.com/bug.php?id=109331): MySQL Connector/NET is incompatible with MariaDB 10.10 and later

From c8a48ef0eebb2b94d105594f1e8103df451996e2 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 29 Dec 2022 09:49:57 -0800
Subject: [PATCH 018/393] Add nullable annotations to API documentation.

Update to XmlDocMarkdown 2.8.0.
---
 .../MySqlConnector.Logging/IMySqlConnectorLogger/Log.md  | 4 ++--
 .../content/api/MySqlConnector.Logging/NoOpLogger/Log.md | 4 ++--
 .../api/MySqlConnector/MySqlAttribute/MySqlAttribute.md  | 2 +-
 docs/content/api/MySqlConnector/MySqlAttribute/Value.md  | 2 +-
 .../MySqlAttributeCollection/SetAttribute.md             | 2 +-
 docs/content/api/MySqlConnector/MySqlBatch/Connection.md | 2 +-
 .../api/MySqlConnector/MySqlBatch/DbConnection.md        | 2 +-
 .../api/MySqlConnector/MySqlBatch/DbTransaction.md       | 2 +-
 .../MySqlConnector/MySqlBatch/ExecuteNonQueryAsync.md    | 3 +--
 .../api/MySqlConnector/MySqlBatch/ExecuteReaderAsync.md  | 3 +--
 .../api/MySqlConnector/MySqlBatch/ExecuteScalar.md       | 2 +-
 .../api/MySqlConnector/MySqlBatch/ExecuteScalarAsync.md  | 3 +--
 docs/content/api/MySqlConnector/MySqlBatch/MySqlBatch.md | 2 +-
 .../api/MySqlConnector/MySqlBatch/PrepareAsync.md        | 2 +-
 .../content/api/MySqlConnector/MySqlBatch/Transaction.md | 2 +-
 .../MySqlBatchCommand/MySqlBatchCommand.md               | 2 +-
 .../MySqlConnector/MySqlBulkCopy/DestinationTableName.md | 2 +-
 .../api/MySqlConnector/MySqlBulkCopy/MySqlBulkCopy.md    | 2 +-
 .../api/MySqlConnector/MySqlBulkCopy/MySqlRowsCopied.md  | 2 +-
 .../MySqlConnector/MySqlBulkCopy/WriteToServerAsync.md   | 6 +++---
 .../MySqlBulkCopyColumnMapping/Expression.md             | 2 +-
 .../MySqlBulkCopyColumnMapping.md                        | 2 +-
 .../api/MySqlConnector/MySqlBulkLoader/CharacterSet.md   | 2 +-
 .../MySqlConnector/MySqlBulkLoader/FieldTerminator.md    | 2 +-
 .../api/MySqlConnector/MySqlBulkLoader/FileName.md       | 2 +-
 .../api/MySqlConnector/MySqlBulkLoader/LinePrefix.md     | 2 +-
 .../api/MySqlConnector/MySqlBulkLoader/LineTerminator.md | 2 +-
 .../api/MySqlConnector/MySqlBulkLoader/SourceStream.md   | 2 +-
 .../api/MySqlConnector/MySqlBulkLoader/TableName.md      | 2 +-
 .../api/MySqlConnector/MySqlCommand/Connection.md        | 2 +-
 .../api/MySqlConnector/MySqlCommand/DbConnection.md      | 2 +-
 .../api/MySqlConnector/MySqlCommand/DbTransaction.md     | 2 +-
 .../MySqlConnector/MySqlCommand/ExecuteReaderAsync.md    | 5 ++---
 .../api/MySqlConnector/MySqlCommand/ExecuteScalar.md     | 2 +-
 .../MySqlConnector/MySqlCommand/ExecuteScalarAsync.md    | 2 +-
 .../api/MySqlConnector/MySqlCommand/MySqlCommand.md      | 8 ++++----
 .../api/MySqlConnector/MySqlCommand/PrepareAsync.md      | 2 +-
 .../api/MySqlConnector/MySqlCommand/Transaction.md       | 2 +-
 .../MySqlConnector/MySqlCommandBuilder/DataAdapter.md    | 2 +-
 .../MySqlConnection/BeginTransactionAsync.md             | 6 +++---
 .../MySqlConnection/ChangeDatabaseAsync.md               | 2 +-
 .../MySqlConnector/MySqlConnection/ClearAllPoolsAsync.md | 3 +--
 .../api/MySqlConnector/MySqlConnection/ClearPoolAsync.md | 2 +-
 .../MySqlConnector/MySqlConnection/EnlistTransaction.md  | 2 +-
 .../api/MySqlConnector/MySqlConnection/GetSchema.md      | 2 +-
 .../api/MySqlConnector/MySqlConnection/GetSchemaAsync.md | 9 ++++-----
 .../api/MySqlConnector/MySqlConnection/InfoMessage.md    | 2 +-
 .../MySqlConnector/MySqlConnection/MySqlConnection.md    | 2 +-
 .../api/MySqlConnector/MySqlConnection/PingAsync.md      | 2 +-
 .../MySqlConnection/ProvideClientCertificatesCallback.md | 2 +-
 .../MySqlConnection/ProvidePasswordCallback.md           | 2 +-
 .../RemoteCertificateValidationCallback.md               | 2 +-
 .../MySqlConnection/ResetConnectionAsync.md              | 3 +--
 .../MySqlDataAdapter/CreateRowUpdatedEvent.md            | 2 +-
 .../MySqlDataAdapter/CreateRowUpdatingEvent.md           | 4 ++--
 .../api/MySqlConnector/MySqlDataAdapter/DeleteCommand.md | 2 +-
 .../api/MySqlConnector/MySqlDataAdapter/InsertCommand.md | 2 +-
 .../api/MySqlConnector/MySqlDataAdapter/RowUpdated.md    | 2 +-
 .../api/MySqlConnector/MySqlDataAdapter/RowUpdating.md   | 2 +-
 .../api/MySqlConnector/MySqlDataAdapter/SelectCommand.md | 2 +-
 .../api/MySqlConnector/MySqlDataAdapter/UpdateCommand.md | 2 +-
 .../api/MySqlConnector/MySqlDataReader/GetBytes.md       | 4 ++--
 .../api/MySqlConnector/MySqlDataReader/GetChars.md       | 2 +-
 .../MySqlDataReader/GetColumnSchemaAsync.md              | 2 +-
 .../api/MySqlConnector/MySqlDataReader/GetSchemaTable.md | 2 +-
 .../MySqlDataReader/GetSchemaTableAsync.md               | 3 +--
 docs/content/api/MySqlConnector/MySqlDateTime/Equals.md  | 2 +-
 .../api/MySqlConnector/MySqlException/SqlState.md        | 2 +-
 .../api/MySqlConnector/MySqlParameter/MySqlParameter.md  | 2 +-
 docs/content/api/MySqlConnector/MySqlParameter/Value.md  | 2 +-
 .../MySqlParameterCollection/AddWithValue.md             | 2 +-
 .../MySqlConnector/MySqlRowUpdatedEventArgs/Command.md   | 2 +-
 .../MySqlRowUpdatedEventArgs/MySqlRowUpdatedEventArgs.md | 2 +-
 .../MySqlConnector/MySqlRowUpdatingEventArgs/Command.md  | 2 +-
 .../MySqlRowUpdatingEventArgs.md                         | 2 +-
 .../api/MySqlConnector/MySqlTransaction/CommitAsync.md   | 2 +-
 .../api/MySqlConnector/MySqlTransaction/Connection.md    | 2 +-
 .../api/MySqlConnector/MySqlTransaction/DbConnection.md  | 2 +-
 .../api/MySqlConnector/MySqlTransaction/ReleaseAsync.md  | 2 +-
 .../api/MySqlConnector/MySqlTransaction/RollbackAsync.md | 4 ++--
 .../api/MySqlConnector/MySqlTransaction/SaveAsync.md     | 3 +--
 81 files changed, 97 insertions(+), 106 deletions(-)

diff --git a/docs/content/api/MySqlConnector.Logging/IMySqlConnectorLogger/Log.md b/docs/content/api/MySqlConnector.Logging/IMySqlConnectorLogger/Log.md
index 2660f6679..159d5d2a1 100644
--- a/docs/content/api/MySqlConnector.Logging/IMySqlConnectorLogger/Log.md
+++ b/docs/content/api/MySqlConnector.Logging/IMySqlConnectorLogger/Log.md
@@ -7,8 +7,8 @@ title: IMySqlConnectorLogger.Log method
 Writes a log message to the target.
 
 ```csharp
-public void Log(MySqlConnectorLogLevel level, string message, object[] args = null, 
-    Exception exception = null)
+public void Log(MySqlConnectorLogLevel level, string message, object?[]? args = null, 
+    Exception? exception = null)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector.Logging/NoOpLogger/Log.md b/docs/content/api/MySqlConnector.Logging/NoOpLogger/Log.md
index 92ee93e66..4ce6bb9ef 100644
--- a/docs/content/api/MySqlConnector.Logging/NoOpLogger/Log.md
+++ b/docs/content/api/MySqlConnector.Logging/NoOpLogger/Log.md
@@ -7,8 +7,8 @@ title: NoOpLogger.Log method
 Ignores the specified log message.
 
 ```csharp
-public void Log(MySqlConnectorLogLevel level, string message, object[] args = null, 
-    Exception exception = null)
+public void Log(MySqlConnectorLogLevel level, string message, object?[]? args = null, 
+    Exception? exception = null)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlAttribute/MySqlAttribute.md b/docs/content/api/MySqlConnector/MySqlAttribute/MySqlAttribute.md
index 917fc4ac8..ef2815a3b 100644
--- a/docs/content/api/MySqlConnector/MySqlAttribute/MySqlAttribute.md
+++ b/docs/content/api/MySqlConnector/MySqlAttribute/MySqlAttribute.md
@@ -23,7 +23,7 @@ public MySqlAttribute()
 Initializes a new [`MySqlAttribute`](../../MySqlAttributeType/) with the specified attribute name and value.
 
 ```csharp
-public MySqlAttribute(string attributeName, object value)
+public MySqlAttribute(string attributeName, object? value)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlAttribute/Value.md b/docs/content/api/MySqlConnector/MySqlAttribute/Value.md
index 3c733c04c..36ff07b24 100644
--- a/docs/content/api/MySqlConnector/MySqlAttribute/Value.md
+++ b/docs/content/api/MySqlConnector/MySqlAttribute/Value.md
@@ -7,7 +7,7 @@ title: MySqlAttribute.Value property
 Gets or sets the attribute value.
 
 ```csharp
-public object Value { get; set; }
+public object? Value { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlAttributeCollection/SetAttribute.md b/docs/content/api/MySqlConnector/MySqlAttributeCollection/SetAttribute.md
index b8ce434db..a63742c28 100644
--- a/docs/content/api/MySqlConnector/MySqlAttributeCollection/SetAttribute.md
+++ b/docs/content/api/MySqlConnector/MySqlAttributeCollection/SetAttribute.md
@@ -7,7 +7,7 @@ title: MySqlAttributeCollection.SetAttribute method
 Sets the attribute with the specified name to the given value, overwriting it if it already exists.
 
 ```csharp
-public void SetAttribute(string attributeName, object value)
+public void SetAttribute(string attributeName, object? value)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlBatch/Connection.md b/docs/content/api/MySqlConnector/MySqlBatch/Connection.md
index 103f0e90f..3cbe0547d 100644
--- a/docs/content/api/MySqlConnector/MySqlBatch/Connection.md
+++ b/docs/content/api/MySqlConnector/MySqlBatch/Connection.md
@@ -5,7 +5,7 @@ title: MySqlBatch.Connection property
 # MySqlBatch.Connection property
 
 ```csharp
-public MySqlConnection Connection { get; set; }
+public MySqlConnection? Connection { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBatch/DbConnection.md b/docs/content/api/MySqlConnector/MySqlBatch/DbConnection.md
index 045708f79..d3bd41404 100644
--- a/docs/content/api/MySqlConnector/MySqlBatch/DbConnection.md
+++ b/docs/content/api/MySqlConnector/MySqlBatch/DbConnection.md
@@ -5,7 +5,7 @@ title: MySqlBatch.DbConnection property
 # MySqlBatch.DbConnection property
 
 ```csharp
-protected override DbConnection DbConnection { get; set; }
+protected override DbConnection? DbConnection { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBatch/DbTransaction.md b/docs/content/api/MySqlConnector/MySqlBatch/DbTransaction.md
index 93fe5b4fc..923e20a4e 100644
--- a/docs/content/api/MySqlConnector/MySqlBatch/DbTransaction.md
+++ b/docs/content/api/MySqlConnector/MySqlBatch/DbTransaction.md
@@ -5,7 +5,7 @@ title: MySqlBatch.DbTransaction property
 # MySqlBatch.DbTransaction property
 
 ```csharp
-protected override DbTransaction DbTransaction { get; set; }
+protected override DbTransaction? DbTransaction { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBatch/ExecuteNonQueryAsync.md b/docs/content/api/MySqlConnector/MySqlBatch/ExecuteNonQueryAsync.md
index 4b7e741ef..0d1935d27 100644
--- a/docs/content/api/MySqlConnector/MySqlBatch/ExecuteNonQueryAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlBatch/ExecuteNonQueryAsync.md
@@ -5,8 +5,7 @@ title: MySqlBatch.ExecuteNonQueryAsync method
 # MySqlBatch.ExecuteNonQueryAsync method
 
 ```csharp
-public override Task<int> ExecuteNonQueryAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken = default)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBatch/ExecuteReaderAsync.md b/docs/content/api/MySqlConnector/MySqlBatch/ExecuteReaderAsync.md
index 4d6951ffd..761061ba1 100644
--- a/docs/content/api/MySqlConnector/MySqlBatch/ExecuteReaderAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlBatch/ExecuteReaderAsync.md
@@ -7,8 +7,7 @@ title: MySqlBatch.ExecuteReaderAsync method
 Executes all the commands in the batch, returning a [`MySqlDataReader`](../../MySqlDataReaderType/) that can iterate over the result sets. If multiple resultsets are returned, use [`NextResultAsync`](../../MySqlDataReader/NextResultAsync/) to access them.
 
 ```csharp
-public Task<MySqlDataReader> ExecuteReaderAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+public Task<MySqlDataReader> ExecuteReaderAsync(CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlBatch/ExecuteScalar.md b/docs/content/api/MySqlConnector/MySqlBatch/ExecuteScalar.md
index 4b01057e9..a0361049b 100644
--- a/docs/content/api/MySqlConnector/MySqlBatch/ExecuteScalar.md
+++ b/docs/content/api/MySqlConnector/MySqlBatch/ExecuteScalar.md
@@ -5,7 +5,7 @@ title: MySqlBatch.ExecuteScalar method
 # MySqlBatch.ExecuteScalar method
 
 ```csharp
-public override object ExecuteScalar()
+public override object? ExecuteScalar()
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBatch/ExecuteScalarAsync.md b/docs/content/api/MySqlConnector/MySqlBatch/ExecuteScalarAsync.md
index e39d8b2dc..368b3df70 100644
--- a/docs/content/api/MySqlConnector/MySqlBatch/ExecuteScalarAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlBatch/ExecuteScalarAsync.md
@@ -5,8 +5,7 @@ title: MySqlBatch.ExecuteScalarAsync method
 # MySqlBatch.ExecuteScalarAsync method
 
 ```csharp
-public override Task<object> ExecuteScalarAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+public override Task<object?> ExecuteScalarAsync(CancellationToken cancellationToken = default)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBatch/MySqlBatch.md b/docs/content/api/MySqlConnector/MySqlBatch/MySqlBatch.md
index 63339b2b6..a8c026cdb 100644
--- a/docs/content/api/MySqlConnector/MySqlBatch/MySqlBatch.md
+++ b/docs/content/api/MySqlConnector/MySqlBatch/MySqlBatch.md
@@ -23,7 +23,7 @@ public MySqlBatch()
 Initializes a new [`MySqlBatch`](../../MySqlBatchType/) object, setting the [`Connection`](../Connection/) and [`Transaction`](../Transaction/) if specified.
 
 ```csharp
-public MySqlBatch(MySqlConnection connection = null, MySqlTransaction transaction = null)
+public MySqlBatch(MySqlConnection? connection = null, MySqlTransaction? transaction = null)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlBatch/PrepareAsync.md b/docs/content/api/MySqlConnector/MySqlBatch/PrepareAsync.md
index f6192b653..e53c2c1f8 100644
--- a/docs/content/api/MySqlConnector/MySqlBatch/PrepareAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlBatch/PrepareAsync.md
@@ -5,7 +5,7 @@ title: MySqlBatch.PrepareAsync method
 # MySqlBatch.PrepareAsync method
 
 ```csharp
-public override Task PrepareAsync(CancellationToken cancellationToken = default(CancellationToken))
+public override Task PrepareAsync(CancellationToken cancellationToken = default)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBatch/Transaction.md b/docs/content/api/MySqlConnector/MySqlBatch/Transaction.md
index 81ef352af..4f24205d3 100644
--- a/docs/content/api/MySqlConnector/MySqlBatch/Transaction.md
+++ b/docs/content/api/MySqlConnector/MySqlBatch/Transaction.md
@@ -5,7 +5,7 @@ title: MySqlBatch.Transaction property
 # MySqlBatch.Transaction property
 
 ```csharp
-public MySqlTransaction Transaction { get; set; }
+public MySqlTransaction? Transaction { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBatchCommand/MySqlBatchCommand.md b/docs/content/api/MySqlConnector/MySqlBatchCommand/MySqlBatchCommand.md
index 7dd155319..8b012fdbe 100644
--- a/docs/content/api/MySqlConnector/MySqlBatchCommand/MySqlBatchCommand.md
+++ b/docs/content/api/MySqlConnector/MySqlBatchCommand/MySqlBatchCommand.md
@@ -21,7 +21,7 @@ public MySqlBatchCommand()
 # MySqlBatchCommand constructor (2 of 2)
 
 ```csharp
-public MySqlBatchCommand(string commandText)
+public MySqlBatchCommand(string? commandText)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBulkCopy/DestinationTableName.md b/docs/content/api/MySqlConnector/MySqlBulkCopy/DestinationTableName.md
index bdca57bb3..135e76ed1 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkCopy/DestinationTableName.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkCopy/DestinationTableName.md
@@ -7,7 +7,7 @@ title: MySqlBulkCopy.DestinationTableName property
 The name of the table to insert rows into.
 
 ```csharp
-public string DestinationTableName { get; set; }
+public string? DestinationTableName { get; set; }
 ```
 
 ## Remarks
diff --git a/docs/content/api/MySqlConnector/MySqlBulkCopy/MySqlBulkCopy.md b/docs/content/api/MySqlConnector/MySqlBulkCopy/MySqlBulkCopy.md
index cf4bf7248..1d0101b8a 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkCopy/MySqlBulkCopy.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkCopy/MySqlBulkCopy.md
@@ -7,7 +7,7 @@ title: MySqlBulkCopy constructor
 Initializes a [`MySqlBulkCopy`](../../MySqlBulkCopyType/) object with the specified connection, and optionally the active transaction.
 
 ```csharp
-public MySqlBulkCopy(MySqlConnection connection, MySqlTransaction transaction = null)
+public MySqlBulkCopy(MySqlConnection connection, MySqlTransaction? transaction = null)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlBulkCopy/MySqlRowsCopied.md b/docs/content/api/MySqlConnector/MySqlBulkCopy/MySqlRowsCopied.md
index e478807f4..66429cfec 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkCopy/MySqlRowsCopied.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkCopy/MySqlRowsCopied.md
@@ -7,7 +7,7 @@ title: MySqlBulkCopy.MySqlRowsCopied event
 This event is raised every time that the number of rows specified by the [`NotifyAfter`](../NotifyAfter/) property have been processed.
 
 ```csharp
-public event MySqlRowsCopiedEventHandler MySqlRowsCopied;
+public event MySqlRowsCopiedEventHandler? MySqlRowsCopied;
 ```
 
 ## Remarks
diff --git a/docs/content/api/MySqlConnector/MySqlBulkCopy/WriteToServerAsync.md b/docs/content/api/MySqlConnector/MySqlBulkCopy/WriteToServerAsync.md
index 3055fc5ad..0b5c4569f 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkCopy/WriteToServerAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkCopy/WriteToServerAsync.md
@@ -8,7 +8,7 @@ Asynchronously copies all rows in the supplied DataTable to the destination tabl
 
 ```csharp
 public ValueTask<MySqlBulkCopyResult> WriteToServerAsync(DataTable dataTable, 
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
@@ -35,7 +35,7 @@ Asynchronously copies all rows in the supplied IDataReader to the destination ta
 
 ```csharp
 public ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IDataReader dataReader, 
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
@@ -62,7 +62,7 @@ Asynchronously copies all rows in the supplied sequence of DataRow objects to th
 
 ```csharp
 public ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IEnumerable<DataRow> dataRows, 
-    int columnCount, CancellationToken cancellationToken = default(CancellationToken))
+    int columnCount, CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/Expression.md b/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/Expression.md
index 42be87106..5e17a1407 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/Expression.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/Expression.md
@@ -7,7 +7,7 @@ title: MySqlBulkCopyColumnMapping.Expression property
 An optional expression for setting a destination column. To use an expression, the [`DestinationColumn`](../DestinationColumn/) should be set to the name of a user-defined variable and this expression should set a column using that variable.
 
 ```csharp
-public string Expression { get; set; }
+public string? Expression { get; set; }
 ```
 
 ## Remarks
diff --git a/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping.md b/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping.md
index 5e394d20a..974817a66 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping.md
@@ -24,7 +24,7 @@ Initializes [`MySqlBulkCopyColumnMapping`](../../MySqlBulkCopyColumnMappingType/
 
 ```csharp
 public MySqlBulkCopyColumnMapping(int sourceOrdinal, string destinationColumn, 
-    string expression = null)
+    string? expression = null)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlBulkLoader/CharacterSet.md b/docs/content/api/MySqlConnector/MySqlBulkLoader/CharacterSet.md
index e914d8a7b..3b16471d0 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkLoader/CharacterSet.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkLoader/CharacterSet.md
@@ -7,7 +7,7 @@ title: MySqlBulkLoader.CharacterSet property
 (Optional) The character set of the source data. By default, the database's character set is used.
 
 ```csharp
-public string CharacterSet { get; set; }
+public string? CharacterSet { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBulkLoader/FieldTerminator.md b/docs/content/api/MySqlConnector/MySqlBulkLoader/FieldTerminator.md
index bbe29423d..33f540ecf 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkLoader/FieldTerminator.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkLoader/FieldTerminator.md
@@ -7,7 +7,7 @@ title: MySqlBulkLoader.FieldTerminator property
 (Optional) The string fields are terminated with.
 
 ```csharp
-public string FieldTerminator { get; set; }
+public string? FieldTerminator { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBulkLoader/FileName.md b/docs/content/api/MySqlConnector/MySqlBulkLoader/FileName.md
index d741de5c6..4c20a251a 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkLoader/FileName.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkLoader/FileName.md
@@ -7,7 +7,7 @@ title: MySqlBulkLoader.FileName property
 The name of the local (if [`Local`](../Local/) is `true`) or remote (otherwise) file to load. Either this or [`SourceStream`](../SourceStream/) must be set.
 
 ```csharp
-public string FileName { get; set; }
+public string? FileName { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBulkLoader/LinePrefix.md b/docs/content/api/MySqlConnector/MySqlBulkLoader/LinePrefix.md
index 88b4593ef..d924dd7a5 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkLoader/LinePrefix.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkLoader/LinePrefix.md
@@ -7,7 +7,7 @@ title: MySqlBulkLoader.LinePrefix property
 (Optional) A prefix in each line that should be skipped when loading.
 
 ```csharp
-public string LinePrefix { get; set; }
+public string? LinePrefix { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBulkLoader/LineTerminator.md b/docs/content/api/MySqlConnector/MySqlBulkLoader/LineTerminator.md
index 61d9aa1df..f387c13ef 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkLoader/LineTerminator.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkLoader/LineTerminator.md
@@ -7,7 +7,7 @@ title: MySqlBulkLoader.LineTerminator property
 (Optional) The string lines are terminated with.
 
 ```csharp
-public string LineTerminator { get; set; }
+public string? LineTerminator { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBulkLoader/SourceStream.md b/docs/content/api/MySqlConnector/MySqlBulkLoader/SourceStream.md
index e9c884c88..58b3efcf4 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkLoader/SourceStream.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkLoader/SourceStream.md
@@ -7,7 +7,7 @@ title: MySqlBulkLoader.SourceStream property
 A Stream containing the data to load. Either this or [`FileName`](../FileName/) must be set. The [`Local`](../Local/) property must be `true` if this is set.
 
 ```csharp
-public Stream SourceStream { get; set; }
+public Stream? SourceStream { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlBulkLoader/TableName.md b/docs/content/api/MySqlConnector/MySqlBulkLoader/TableName.md
index cafb0a522..2190e201a 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkLoader/TableName.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkLoader/TableName.md
@@ -7,7 +7,7 @@ title: MySqlBulkLoader.TableName property
 The name of the table to load into. If this is a reserved word or contains spaces, it must be quoted.
 
 ```csharp
-public string TableName { get; set; }
+public string? TableName { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlCommand/Connection.md b/docs/content/api/MySqlConnector/MySqlCommand/Connection.md
index 97fb24da5..d22ae3ef5 100644
--- a/docs/content/api/MySqlConnector/MySqlCommand/Connection.md
+++ b/docs/content/api/MySqlConnector/MySqlCommand/Connection.md
@@ -5,7 +5,7 @@ title: MySqlCommand.Connection property
 # MySqlCommand.Connection property
 
 ```csharp
-public MySqlConnection Connection { get; set; }
+public MySqlConnection? Connection { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlCommand/DbConnection.md b/docs/content/api/MySqlConnector/MySqlCommand/DbConnection.md
index ce67ba1a4..a244c7e50 100644
--- a/docs/content/api/MySqlConnector/MySqlCommand/DbConnection.md
+++ b/docs/content/api/MySqlConnector/MySqlCommand/DbConnection.md
@@ -5,7 +5,7 @@ title: MySqlCommand.DbConnection property
 # MySqlCommand.DbConnection property
 
 ```csharp
-protected override DbConnection DbConnection { get; set; }
+protected override DbConnection? DbConnection { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlCommand/DbTransaction.md b/docs/content/api/MySqlConnector/MySqlCommand/DbTransaction.md
index b7e71644f..4edb70974 100644
--- a/docs/content/api/MySqlConnector/MySqlCommand/DbTransaction.md
+++ b/docs/content/api/MySqlConnector/MySqlCommand/DbTransaction.md
@@ -5,7 +5,7 @@ title: MySqlCommand.DbTransaction property
 # MySqlCommand.DbTransaction property
 
 ```csharp
-protected override DbTransaction DbTransaction { get; set; }
+protected override DbTransaction? DbTransaction { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlCommand/ExecuteReaderAsync.md b/docs/content/api/MySqlConnector/MySqlCommand/ExecuteReaderAsync.md
index 11f888c5c..4e875b087 100644
--- a/docs/content/api/MySqlConnector/MySqlCommand/ExecuteReaderAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlCommand/ExecuteReaderAsync.md
@@ -5,8 +5,7 @@ title: MySqlCommand.ExecuteReaderAsync methods
 # MySqlCommand.ExecuteReaderAsync method (1 of 2)
 
 ```csharp
-public Task<MySqlDataReader> ExecuteReaderAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+public Task<MySqlDataReader> ExecuteReaderAsync(CancellationToken cancellationToken = default)
 ```
 
 ## See Also
@@ -22,7 +21,7 @@ public Task<MySqlDataReader> ExecuteReaderAsync(
 
 ```csharp
 public Task<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, 
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlCommand/ExecuteScalar.md b/docs/content/api/MySqlConnector/MySqlCommand/ExecuteScalar.md
index e8a03ae5e..02bd3e2fd 100644
--- a/docs/content/api/MySqlConnector/MySqlCommand/ExecuteScalar.md
+++ b/docs/content/api/MySqlConnector/MySqlCommand/ExecuteScalar.md
@@ -5,7 +5,7 @@ title: MySqlCommand.ExecuteScalar method
 # MySqlCommand.ExecuteScalar method
 
 ```csharp
-public override object ExecuteScalar()
+public override object? ExecuteScalar()
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlCommand/ExecuteScalarAsync.md b/docs/content/api/MySqlConnector/MySqlCommand/ExecuteScalarAsync.md
index 524921777..bb4ddf153 100644
--- a/docs/content/api/MySqlConnector/MySqlCommand/ExecuteScalarAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlCommand/ExecuteScalarAsync.md
@@ -5,7 +5,7 @@ title: MySqlCommand.ExecuteScalarAsync method
 # MySqlCommand.ExecuteScalarAsync method
 
 ```csharp
-public override Task<object> ExecuteScalarAsync(CancellationToken cancellationToken)
+public override Task<object?> ExecuteScalarAsync(CancellationToken cancellationToken)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlCommand/MySqlCommand.md b/docs/content/api/MySqlConnector/MySqlCommand/MySqlCommand.md
index 6a179e7bf..8227af70b 100644
--- a/docs/content/api/MySqlConnector/MySqlCommand/MySqlCommand.md
+++ b/docs/content/api/MySqlConnector/MySqlCommand/MySqlCommand.md
@@ -23,7 +23,7 @@ public MySqlCommand()
 Initializes a new instance of the [`MySqlCommand`](../../MySqlCommandType/) class, setting [`CommandText`](../CommandText/) to *commandText*.
 
 ```csharp
-public MySqlCommand(string commandText)
+public MySqlCommand(string? commandText)
 ```
 
 | parameter | description |
@@ -43,7 +43,7 @@ public MySqlCommand(string commandText)
 Initializes a new instance of the [`MySqlCommand`](../../MySqlCommandType/) class with the specified [`MySqlConnection`](../../MySqlConnectionType/) and [`MySqlTransaction`](../../MySqlTransactionType/).
 
 ```csharp
-public MySqlCommand(MySqlConnection connection, MySqlTransaction transaction)
+public MySqlCommand(MySqlConnection? connection, MySqlTransaction? transaction)
 ```
 
 | parameter | description |
@@ -66,7 +66,7 @@ public MySqlCommand(MySqlConnection connection, MySqlTransaction transaction)
 Initializes a new instance of the [`MySqlCommand`](../../MySqlCommandType/) class with the specified command text and [`MySqlConnection`](../../MySqlConnectionType/).
 
 ```csharp
-public MySqlCommand(string commandText, MySqlConnection connection)
+public MySqlCommand(string? commandText, MySqlConnection? connection)
 ```
 
 | parameter | description |
@@ -88,7 +88,7 @@ public MySqlCommand(string commandText, MySqlConnection connection)
 Initializes a new instance of the [`MySqlCommand`](../../MySqlCommandType/) class with the specified command text,[`MySqlConnection`](../../MySqlConnectionType/), and [`MySqlTransaction`](../../MySqlTransactionType/).
 
 ```csharp
-public MySqlCommand(string commandText, MySqlConnection connection, MySqlTransaction transaction)
+public MySqlCommand(string? commandText, MySqlConnection? connection, MySqlTransaction? transaction)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlCommand/PrepareAsync.md b/docs/content/api/MySqlConnector/MySqlCommand/PrepareAsync.md
index 68ca471a2..0d63851e5 100644
--- a/docs/content/api/MySqlConnector/MySqlCommand/PrepareAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlCommand/PrepareAsync.md
@@ -5,7 +5,7 @@ title: MySqlCommand.PrepareAsync method
 # MySqlCommand.PrepareAsync method
 
 ```csharp
-public override Task PrepareAsync(CancellationToken cancellationToken = default(CancellationToken))
+public override Task PrepareAsync(CancellationToken cancellationToken = default)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlCommand/Transaction.md b/docs/content/api/MySqlConnector/MySqlCommand/Transaction.md
index 813d74246..d77b5d59c 100644
--- a/docs/content/api/MySqlConnector/MySqlCommand/Transaction.md
+++ b/docs/content/api/MySqlConnector/MySqlCommand/Transaction.md
@@ -5,7 +5,7 @@ title: MySqlCommand.Transaction property
 # MySqlCommand.Transaction property
 
 ```csharp
-public MySqlTransaction Transaction { get; set; }
+public MySqlTransaction? Transaction { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlCommandBuilder/DataAdapter.md b/docs/content/api/MySqlConnector/MySqlCommandBuilder/DataAdapter.md
index 048031ae4..866776c7e 100644
--- a/docs/content/api/MySqlConnector/MySqlCommandBuilder/DataAdapter.md
+++ b/docs/content/api/MySqlConnector/MySqlCommandBuilder/DataAdapter.md
@@ -5,7 +5,7 @@ title: MySqlCommandBuilder.DataAdapter property
 # MySqlCommandBuilder.DataAdapter property
 
 ```csharp
-public MySqlDataAdapter DataAdapter { get; set; }
+public MySqlDataAdapter? DataAdapter { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/BeginTransactionAsync.md b/docs/content/api/MySqlConnector/MySqlConnection/BeginTransactionAsync.md
index 06dfb80e9..120a3b3fb 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/BeginTransactionAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/BeginTransactionAsync.md
@@ -8,7 +8,7 @@ Begins a database transaction asynchronously.
 
 ```csharp
 public ValueTask<MySqlTransaction> BeginTransactionAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
@@ -38,7 +38,7 @@ Begins a database transaction asynchronously.
 
 ```csharp
 public ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel isolationLevel, 
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
@@ -69,7 +69,7 @@ Begins a database transaction asynchronously.
 
 ```csharp
 public ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel isolationLevel, 
-    bool isReadOnly, CancellationToken cancellationToken = default(CancellationToken))
+    bool isReadOnly, CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/ChangeDatabaseAsync.md b/docs/content/api/MySqlConnector/MySqlConnection/ChangeDatabaseAsync.md
index 7ea38ab30..1c0820fa3 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/ChangeDatabaseAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/ChangeDatabaseAsync.md
@@ -6,7 +6,7 @@ title: MySqlConnection.ChangeDatabaseAsync method
 
 ```csharp
 public override Task ChangeDatabaseAsync(string databaseName, 
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/ClearAllPoolsAsync.md b/docs/content/api/MySqlConnector/MySqlConnection/ClearAllPoolsAsync.md
index 1ef70be08..8239a16d2 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/ClearAllPoolsAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/ClearAllPoolsAsync.md
@@ -7,8 +7,7 @@ title: MySqlConnection.ClearAllPoolsAsync method
 Asynchronously clears all connection pools.
 
 ```csharp
-public static Task ClearAllPoolsAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+public static Task ClearAllPoolsAsync(CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/ClearPoolAsync.md b/docs/content/api/MySqlConnector/MySqlConnection/ClearPoolAsync.md
index 49fdcb698..d981c83cb 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/ClearPoolAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/ClearPoolAsync.md
@@ -8,7 +8,7 @@ Asynchronously clears the connection pool that *connection* belongs to.
 
 ```csharp
 public static Task ClearPoolAsync(MySqlConnection connection, 
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/EnlistTransaction.md b/docs/content/api/MySqlConnector/MySqlConnection/EnlistTransaction.md
index cd452486d..68de061e9 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/EnlistTransaction.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/EnlistTransaction.md
@@ -5,7 +5,7 @@ title: MySqlConnection.EnlistTransaction method
 # MySqlConnection.EnlistTransaction method
 
 ```csharp
-public override void EnlistTransaction(Transaction transaction)
+public override void EnlistTransaction(Transaction? transaction)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/GetSchema.md b/docs/content/api/MySqlConnector/MySqlConnection/GetSchema.md
index e11ceff1a..42a6bf46e 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/GetSchema.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/GetSchema.md
@@ -51,7 +51,7 @@ A DataTable containing schema information.
 Returns schema information for the data source of this [`MySqlConnection`](../../MySqlConnectionType/).
 
 ```csharp
-public override DataTable GetSchema(string collectionName, string[] restrictionValues)
+public override DataTable GetSchema(string collectionName, string?[] restrictionValues)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/GetSchemaAsync.md b/docs/content/api/MySqlConnector/MySqlConnection/GetSchemaAsync.md
index 5686a8f30..e0d33cf3b 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/GetSchemaAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/GetSchemaAsync.md
@@ -7,8 +7,7 @@ title: MySqlConnection.GetSchemaAsync methods
 Asynchronously returns schema information for the data source of this [`MySqlConnection`](../../MySqlConnectionType/).
 
 ```csharp
-public override Task<DataTable> GetSchemaAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+public override Task<DataTable> GetSchemaAsync(CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
@@ -37,7 +36,7 @@ Asynchronously returns schema information for the data source of this [`MySqlCon
 
 ```csharp
 public override Task<DataTable> GetSchemaAsync(string collectionName, 
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
@@ -66,8 +65,8 @@ The proposed ADO.NET API that this is based on is not finalized; this API may ch
 Asynchronously returns schema information for the data source of this [`MySqlConnection`](../../MySqlConnectionType/).
 
 ```csharp
-public override Task<DataTable> GetSchemaAsync(string collectionName, string[] restrictionValues, 
-    CancellationToken cancellationToken = default(CancellationToken))
+public override Task<DataTable> GetSchemaAsync(string collectionName, string?[] restrictionValues, 
+    CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/InfoMessage.md b/docs/content/api/MySqlConnector/MySqlConnection/InfoMessage.md
index a564dc7f1..f91351abf 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/InfoMessage.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/InfoMessage.md
@@ -5,7 +5,7 @@ title: MySqlConnection.InfoMessage event
 # MySqlConnection.InfoMessage event
 
 ```csharp
-public event MySqlInfoMessageEventHandler InfoMessage;
+public event MySqlInfoMessageEventHandler? InfoMessage;
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/MySqlConnection.md b/docs/content/api/MySqlConnector/MySqlConnection/MySqlConnection.md
index 957ca9465..cb1cb1aa1 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/MySqlConnection.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/MySqlConnection.md
@@ -21,7 +21,7 @@ public MySqlConnection()
 # MySqlConnection constructor (2 of 2)
 
 ```csharp
-public MySqlConnection(string connectionString)
+public MySqlConnection(string? connectionString)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/PingAsync.md b/docs/content/api/MySqlConnector/MySqlConnection/PingAsync.md
index 2ab121b7f..4f052efd9 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/PingAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/PingAsync.md
@@ -5,7 +5,7 @@ title: MySqlConnection.PingAsync method
 # MySqlConnection.PingAsync method
 
 ```csharp
-public Task<bool> PingAsync(CancellationToken cancellationToken = default(CancellationToken))
+public Task<bool> PingAsync(CancellationToken cancellationToken = default)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/ProvideClientCertificatesCallback.md b/docs/content/api/MySqlConnector/MySqlConnection/ProvideClientCertificatesCallback.md
index ead598e30..e95403b5a 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/ProvideClientCertificatesCallback.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/ProvideClientCertificatesCallback.md
@@ -7,7 +7,7 @@ title: MySqlConnection.ProvideClientCertificatesCallback property
 Gets or sets the delegate used to provide client certificates for connecting to a server.
 
 ```csharp
-public Func<X509CertificateCollection, ValueTask> ProvideClientCertificatesCallback { get; set; }
+public Func<X509CertificateCollection, ValueTask>? ProvideClientCertificatesCallback { get; set; }
 ```
 
 ## Remarks
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/ProvidePasswordCallback.md b/docs/content/api/MySqlConnector/MySqlConnection/ProvidePasswordCallback.md
index 091767e55..78750de7a 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/ProvidePasswordCallback.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/ProvidePasswordCallback.md
@@ -7,7 +7,7 @@ title: MySqlConnection.ProvidePasswordCallback property
 Gets or sets the delegate used to generate a password for new database connections.
 
 ```csharp
-public Func<MySqlProvidePasswordContext, string> ProvidePasswordCallback { get; set; }
+public Func<MySqlProvidePasswordContext, string>? ProvidePasswordCallback { get; set; }
 ```
 
 ## Remarks
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/RemoteCertificateValidationCallback.md b/docs/content/api/MySqlConnector/MySqlConnection/RemoteCertificateValidationCallback.md
index 880ba2f04..08366c6bc 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/RemoteCertificateValidationCallback.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/RemoteCertificateValidationCallback.md
@@ -7,7 +7,7 @@ title: MySqlConnection.RemoteCertificateValidationCallback property
 Gets or sets the delegate used to verify that the server's certificate is valid.
 
 ```csharp
-public RemoteCertificateValidationCallback RemoteCertificateValidationCallback { get; set; }
+public RemoteCertificateValidationCallback? RemoteCertificateValidationCallback { get; set; }
 ```
 
 ## Remarks
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/ResetConnectionAsync.md b/docs/content/api/MySqlConnector/MySqlConnection/ResetConnectionAsync.md
index 2fb89a0ad..a78428752 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/ResetConnectionAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/ResetConnectionAsync.md
@@ -7,8 +7,7 @@ title: MySqlConnection.ResetConnectionAsync method
 Resets the session state of the current open connection; this clears temporary tables and user-defined variables.
 
 ```csharp
-public ValueTask ResetConnectionAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+public ValueTask ResetConnectionAsync(CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlDataAdapter/CreateRowUpdatedEvent.md b/docs/content/api/MySqlConnector/MySqlDataAdapter/CreateRowUpdatedEvent.md
index 906472015..9076ec09c 100644
--- a/docs/content/api/MySqlConnector/MySqlDataAdapter/CreateRowUpdatedEvent.md
+++ b/docs/content/api/MySqlConnector/MySqlDataAdapter/CreateRowUpdatedEvent.md
@@ -5,7 +5,7 @@ title: MySqlDataAdapter.CreateRowUpdatedEvent method
 # MySqlDataAdapter.CreateRowUpdatedEvent method
 
 ```csharp
-protected override RowUpdatedEventArgs CreateRowUpdatedEvent(DataRow dataRow, IDbCommand command, 
+protected override RowUpdatedEventArgs CreateRowUpdatedEvent(DataRow dataRow, IDbCommand? command, 
     StatementType statementType, DataTableMapping tableMapping)
 ```
 
diff --git a/docs/content/api/MySqlConnector/MySqlDataAdapter/CreateRowUpdatingEvent.md b/docs/content/api/MySqlConnector/MySqlDataAdapter/CreateRowUpdatingEvent.md
index 08fd9e814..e63ede119 100644
--- a/docs/content/api/MySqlConnector/MySqlDataAdapter/CreateRowUpdatingEvent.md
+++ b/docs/content/api/MySqlConnector/MySqlDataAdapter/CreateRowUpdatingEvent.md
@@ -5,8 +5,8 @@ title: MySqlDataAdapter.CreateRowUpdatingEvent method
 # MySqlDataAdapter.CreateRowUpdatingEvent method
 
 ```csharp
-protected override RowUpdatingEventArgs CreateRowUpdatingEvent(DataRow dataRow, IDbCommand command, 
-    StatementType statementType, DataTableMapping tableMapping)
+protected override RowUpdatingEventArgs CreateRowUpdatingEvent(DataRow dataRow, 
+    IDbCommand? command, StatementType statementType, DataTableMapping tableMapping)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlDataAdapter/DeleteCommand.md b/docs/content/api/MySqlConnector/MySqlDataAdapter/DeleteCommand.md
index c0a6b71ce..7f9bc22e4 100644
--- a/docs/content/api/MySqlConnector/MySqlDataAdapter/DeleteCommand.md
+++ b/docs/content/api/MySqlConnector/MySqlDataAdapter/DeleteCommand.md
@@ -5,7 +5,7 @@ title: MySqlDataAdapter.DeleteCommand property
 # MySqlDataAdapter.DeleteCommand property
 
 ```csharp
-public MySqlCommand DeleteCommand { get; set; }
+public MySqlCommand? DeleteCommand { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlDataAdapter/InsertCommand.md b/docs/content/api/MySqlConnector/MySqlDataAdapter/InsertCommand.md
index 93c209a25..21219b482 100644
--- a/docs/content/api/MySqlConnector/MySqlDataAdapter/InsertCommand.md
+++ b/docs/content/api/MySqlConnector/MySqlDataAdapter/InsertCommand.md
@@ -5,7 +5,7 @@ title: MySqlDataAdapter.InsertCommand property
 # MySqlDataAdapter.InsertCommand property
 
 ```csharp
-public MySqlCommand InsertCommand { get; set; }
+public MySqlCommand? InsertCommand { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlDataAdapter/RowUpdated.md b/docs/content/api/MySqlConnector/MySqlDataAdapter/RowUpdated.md
index 82ac73734..9d935180b 100644
--- a/docs/content/api/MySqlConnector/MySqlDataAdapter/RowUpdated.md
+++ b/docs/content/api/MySqlConnector/MySqlDataAdapter/RowUpdated.md
@@ -5,7 +5,7 @@ title: MySqlDataAdapter.RowUpdated event
 # MySqlDataAdapter.RowUpdated event
 
 ```csharp
-public event MySqlRowUpdatedEventHandler RowUpdated;
+public event MySqlRowUpdatedEventHandler? RowUpdated;
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlDataAdapter/RowUpdating.md b/docs/content/api/MySqlConnector/MySqlDataAdapter/RowUpdating.md
index ffba0ecca..bffe8ab2d 100644
--- a/docs/content/api/MySqlConnector/MySqlDataAdapter/RowUpdating.md
+++ b/docs/content/api/MySqlConnector/MySqlDataAdapter/RowUpdating.md
@@ -5,7 +5,7 @@ title: MySqlDataAdapter.RowUpdating event
 # MySqlDataAdapter.RowUpdating event
 
 ```csharp
-public event MySqlRowUpdatingEventHandler RowUpdating;
+public event MySqlRowUpdatingEventHandler? RowUpdating;
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlDataAdapter/SelectCommand.md b/docs/content/api/MySqlConnector/MySqlDataAdapter/SelectCommand.md
index 5235be275..83494c40a 100644
--- a/docs/content/api/MySqlConnector/MySqlDataAdapter/SelectCommand.md
+++ b/docs/content/api/MySqlConnector/MySqlDataAdapter/SelectCommand.md
@@ -5,7 +5,7 @@ title: MySqlDataAdapter.SelectCommand property
 # MySqlDataAdapter.SelectCommand property
 
 ```csharp
-public MySqlCommand SelectCommand { get; set; }
+public MySqlCommand? SelectCommand { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlDataAdapter/UpdateCommand.md b/docs/content/api/MySqlConnector/MySqlDataAdapter/UpdateCommand.md
index 7cd16a8f4..5be3729dd 100644
--- a/docs/content/api/MySqlConnector/MySqlDataAdapter/UpdateCommand.md
+++ b/docs/content/api/MySqlConnector/MySqlDataAdapter/UpdateCommand.md
@@ -5,7 +5,7 @@ title: MySqlDataAdapter.UpdateCommand property
 # MySqlDataAdapter.UpdateCommand property
 
 ```csharp
-public MySqlCommand UpdateCommand { get; set; }
+public MySqlCommand? UpdateCommand { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlDataReader/GetBytes.md b/docs/content/api/MySqlConnector/MySqlDataReader/GetBytes.md
index c9bd4bb4f..900426e76 100644
--- a/docs/content/api/MySqlConnector/MySqlDataReader/GetBytes.md
+++ b/docs/content/api/MySqlConnector/MySqlDataReader/GetBytes.md
@@ -5,7 +5,7 @@ title: MySqlDataReader.GetBytes methods
 # MySqlDataReader.GetBytes method (1 of 2)
 
 ```csharp
-public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int bufferOffset, 
+public override long GetBytes(int ordinal, long dataOffset, byte[]? buffer, int bufferOffset, 
     int length)
 ```
 
@@ -20,7 +20,7 @@ public override long GetBytes(int ordinal, long dataOffset, byte[] buffer, int b
 # MySqlDataReader.GetBytes method (2 of 2)
 
 ```csharp
-public long GetBytes(string name, long dataOffset, byte[] buffer, int bufferOffset, int length)
+public long GetBytes(string name, long dataOffset, byte[]? buffer, int bufferOffset, int length)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlDataReader/GetChars.md b/docs/content/api/MySqlConnector/MySqlDataReader/GetChars.md
index ac652e534..3bf9914f6 100644
--- a/docs/content/api/MySqlConnector/MySqlDataReader/GetChars.md
+++ b/docs/content/api/MySqlConnector/MySqlDataReader/GetChars.md
@@ -5,7 +5,7 @@ title: MySqlDataReader.GetChars method
 # MySqlDataReader.GetChars method
 
 ```csharp
-public override long GetChars(int ordinal, long dataOffset, char[] buffer, int bufferOffset, 
+public override long GetChars(int ordinal, long dataOffset, char[]? buffer, int bufferOffset, 
     int length)
 ```
 
diff --git a/docs/content/api/MySqlConnector/MySqlDataReader/GetColumnSchemaAsync.md b/docs/content/api/MySqlConnector/MySqlDataReader/GetColumnSchemaAsync.md
index bfb54bcde..7145365c4 100644
--- a/docs/content/api/MySqlConnector/MySqlDataReader/GetColumnSchemaAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlDataReader/GetColumnSchemaAsync.md
@@ -8,7 +8,7 @@ Returns metadata about the columns in the result set.
 
 ```csharp
 public override Task<ReadOnlyCollection<DbColumn>> GetColumnSchemaAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlDataReader/GetSchemaTable.md b/docs/content/api/MySqlConnector/MySqlDataReader/GetSchemaTable.md
index b5c825ecb..daffb65bb 100644
--- a/docs/content/api/MySqlConnector/MySqlDataReader/GetSchemaTable.md
+++ b/docs/content/api/MySqlConnector/MySqlDataReader/GetSchemaTable.md
@@ -7,7 +7,7 @@ title: MySqlDataReader.GetSchemaTable method
 Returns a DataTable that contains metadata about the columns in the result set.
 
 ```csharp
-public override DataTable GetSchemaTable()
+public override DataTable? GetSchemaTable()
 ```
 
 ## Return Value
diff --git a/docs/content/api/MySqlConnector/MySqlDataReader/GetSchemaTableAsync.md b/docs/content/api/MySqlConnector/MySqlDataReader/GetSchemaTableAsync.md
index 153de15a4..05a27e481 100644
--- a/docs/content/api/MySqlConnector/MySqlDataReader/GetSchemaTableAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlDataReader/GetSchemaTableAsync.md
@@ -7,8 +7,7 @@ title: MySqlDataReader.GetSchemaTableAsync method
 Returns a DataTable that contains metadata about the columns in the result set.
 
 ```csharp
-public override Task<DataTable> GetSchemaTableAsync(
-    CancellationToken cancellationToken = default(CancellationToken))
+public override Task<DataTable?> GetSchemaTableAsync(CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlDateTime/Equals.md b/docs/content/api/MySqlConnector/MySqlDateTime/Equals.md
index eb2360bbd..c106c21fe 100644
--- a/docs/content/api/MySqlConnector/MySqlDateTime/Equals.md
+++ b/docs/content/api/MySqlConnector/MySqlDateTime/Equals.md
@@ -7,7 +7,7 @@ title: MySqlDateTime.Equals method
 Returns `true` if this [`MySqlDateTime`](../../MySqlDateTimeType/) is equal to *obj*.
 
 ```csharp
-public override bool Equals(object obj)
+public override bool Equals(object? obj)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlException/SqlState.md b/docs/content/api/MySqlConnector/MySqlException/SqlState.md
index 0ce40043a..06c5dbc0b 100644
--- a/docs/content/api/MySqlConnector/MySqlException/SqlState.md
+++ b/docs/content/api/MySqlConnector/MySqlException/SqlState.md
@@ -7,7 +7,7 @@ title: MySqlException.SqlState property
 A `SQLSTATE` code identifying the kind of error.
 
 ```csharp
-public override string SqlState { get; }
+public override string? SqlState { get; }
 ```
 
 ## Remarks
diff --git a/docs/content/api/MySqlConnector/MySqlParameter/MySqlParameter.md b/docs/content/api/MySqlConnector/MySqlParameter/MySqlParameter.md
index 4d0b97c85..bdab728b6 100644
--- a/docs/content/api/MySqlConnector/MySqlParameter/MySqlParameter.md
+++ b/docs/content/api/MySqlConnector/MySqlParameter/MySqlParameter.md
@@ -36,7 +36,7 @@ public MySqlParameter(string name, MySqlDbType mySqlDbType)
 # MySqlParameter constructor (3 of 6)
 
 ```csharp
-public MySqlParameter(string name, object value)
+public MySqlParameter(string? name, object? value)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlParameter/Value.md b/docs/content/api/MySqlConnector/MySqlParameter/Value.md
index 9858c9890..71785123c 100644
--- a/docs/content/api/MySqlConnector/MySqlParameter/Value.md
+++ b/docs/content/api/MySqlConnector/MySqlParameter/Value.md
@@ -5,7 +5,7 @@ title: MySqlParameter.Value property
 # MySqlParameter.Value property
 
 ```csharp
-public override object Value { get; set; }
+public override object? Value { get; set; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlParameterCollection/AddWithValue.md b/docs/content/api/MySqlConnector/MySqlParameterCollection/AddWithValue.md
index a7caf9fe1..9b3b806ce 100644
--- a/docs/content/api/MySqlConnector/MySqlParameterCollection/AddWithValue.md
+++ b/docs/content/api/MySqlConnector/MySqlParameterCollection/AddWithValue.md
@@ -5,7 +5,7 @@ title: MySqlParameterCollection.AddWithValue method
 # MySqlParameterCollection.AddWithValue method
 
 ```csharp
-public MySqlParameter AddWithValue(string parameterName, object value)
+public MySqlParameter AddWithValue(string parameterName, object? value)
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlRowUpdatedEventArgs/Command.md b/docs/content/api/MySqlConnector/MySqlRowUpdatedEventArgs/Command.md
index d0907f3ca..3a57e4d1b 100644
--- a/docs/content/api/MySqlConnector/MySqlRowUpdatedEventArgs/Command.md
+++ b/docs/content/api/MySqlConnector/MySqlRowUpdatedEventArgs/Command.md
@@ -5,7 +5,7 @@ title: MySqlRowUpdatedEventArgs.Command property
 # MySqlRowUpdatedEventArgs.Command property
 
 ```csharp
-public MySqlCommand Command { get; }
+public MySqlCommand? Command { get; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlRowUpdatedEventArgs/MySqlRowUpdatedEventArgs.md b/docs/content/api/MySqlConnector/MySqlRowUpdatedEventArgs/MySqlRowUpdatedEventArgs.md
index 4ef67b3b0..8ff21dd06 100644
--- a/docs/content/api/MySqlConnector/MySqlRowUpdatedEventArgs/MySqlRowUpdatedEventArgs.md
+++ b/docs/content/api/MySqlConnector/MySqlRowUpdatedEventArgs/MySqlRowUpdatedEventArgs.md
@@ -5,7 +5,7 @@ title: MySqlRowUpdatedEventArgs constructor
 # MySqlRowUpdatedEventArgs constructor
 
 ```csharp
-public MySqlRowUpdatedEventArgs(DataRow row, IDbCommand command, StatementType statementType, 
+public MySqlRowUpdatedEventArgs(DataRow row, IDbCommand? command, StatementType statementType, 
     DataTableMapping tableMapping)
 ```
 
diff --git a/docs/content/api/MySqlConnector/MySqlRowUpdatingEventArgs/Command.md b/docs/content/api/MySqlConnector/MySqlRowUpdatingEventArgs/Command.md
index dceb0c842..41d506a55 100644
--- a/docs/content/api/MySqlConnector/MySqlRowUpdatingEventArgs/Command.md
+++ b/docs/content/api/MySqlConnector/MySqlRowUpdatingEventArgs/Command.md
@@ -5,7 +5,7 @@ title: MySqlRowUpdatingEventArgs.Command property
 # MySqlRowUpdatingEventArgs.Command property
 
 ```csharp
-public MySqlCommand Command { get; }
+public MySqlCommand? Command { get; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlRowUpdatingEventArgs/MySqlRowUpdatingEventArgs.md b/docs/content/api/MySqlConnector/MySqlRowUpdatingEventArgs/MySqlRowUpdatingEventArgs.md
index 8d1e1b384..31d49b4b0 100644
--- a/docs/content/api/MySqlConnector/MySqlRowUpdatingEventArgs/MySqlRowUpdatingEventArgs.md
+++ b/docs/content/api/MySqlConnector/MySqlRowUpdatingEventArgs/MySqlRowUpdatingEventArgs.md
@@ -5,7 +5,7 @@ title: MySqlRowUpdatingEventArgs constructor
 # MySqlRowUpdatingEventArgs constructor
 
 ```csharp
-public MySqlRowUpdatingEventArgs(DataRow row, IDbCommand command, StatementType statementType, 
+public MySqlRowUpdatingEventArgs(DataRow row, IDbCommand? command, StatementType statementType, 
     DataTableMapping tableMapping)
 ```
 
diff --git a/docs/content/api/MySqlConnector/MySqlTransaction/CommitAsync.md b/docs/content/api/MySqlConnector/MySqlTransaction/CommitAsync.md
index 0c738180a..0eb3b9519 100644
--- a/docs/content/api/MySqlConnector/MySqlTransaction/CommitAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlTransaction/CommitAsync.md
@@ -7,7 +7,7 @@ title: MySqlTransaction.CommitAsync method
 Asynchronously commits the database transaction.
 
 ```csharp
-public override Task CommitAsync(CancellationToken cancellationToken = default(CancellationToken))
+public override Task CommitAsync(CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlTransaction/Connection.md b/docs/content/api/MySqlConnector/MySqlTransaction/Connection.md
index d57deae03..795159c21 100644
--- a/docs/content/api/MySqlConnector/MySqlTransaction/Connection.md
+++ b/docs/content/api/MySqlConnector/MySqlTransaction/Connection.md
@@ -7,7 +7,7 @@ title: MySqlTransaction.Connection property
 Gets the [`MySqlConnection`](../../MySqlConnectionType/) that this transaction is associated with.
 
 ```csharp
-public MySqlConnection Connection { get; }
+public MySqlConnection? Connection { get; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlTransaction/DbConnection.md b/docs/content/api/MySqlConnector/MySqlTransaction/DbConnection.md
index c6578f58f..8a19d6126 100644
--- a/docs/content/api/MySqlConnector/MySqlTransaction/DbConnection.md
+++ b/docs/content/api/MySqlConnector/MySqlTransaction/DbConnection.md
@@ -7,7 +7,7 @@ title: MySqlTransaction.DbConnection property
 Gets the [`MySqlConnection`](../../MySqlConnectionType/) that this transaction is associated with.
 
 ```csharp
-protected override DbConnection DbConnection { get; }
+protected override DbConnection? DbConnection { get; }
 ```
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlTransaction/ReleaseAsync.md b/docs/content/api/MySqlConnector/MySqlTransaction/ReleaseAsync.md
index 3a0b070a9..7ed4c792c 100644
--- a/docs/content/api/MySqlConnector/MySqlTransaction/ReleaseAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlTransaction/ReleaseAsync.md
@@ -8,7 +8,7 @@ Asynchronously removes the named transaction savepoint with the specified *savep
 
 ```csharp
 public override Task ReleaseAsync(string savepointName, 
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlTransaction/RollbackAsync.md b/docs/content/api/MySqlConnector/MySqlTransaction/RollbackAsync.md
index 869816ab5..49f2784e8 100644
--- a/docs/content/api/MySqlConnector/MySqlTransaction/RollbackAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlTransaction/RollbackAsync.md
@@ -7,7 +7,7 @@ title: MySqlTransaction.RollbackAsync methods
 Asynchronously rolls back the database transaction.
 
 ```csharp
-public override Task RollbackAsync(CancellationToken cancellationToken = default(CancellationToken))
+public override Task RollbackAsync(CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
@@ -32,7 +32,7 @@ Asynchronously rolls back the current transaction to the savepoint with the spec
 
 ```csharp
 public override Task RollbackAsync(string savepointName, 
-    CancellationToken cancellationToken = default(CancellationToken))
+    CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |
diff --git a/docs/content/api/MySqlConnector/MySqlTransaction/SaveAsync.md b/docs/content/api/MySqlConnector/MySqlTransaction/SaveAsync.md
index e097192b2..255e23577 100644
--- a/docs/content/api/MySqlConnector/MySqlTransaction/SaveAsync.md
+++ b/docs/content/api/MySqlConnector/MySqlTransaction/SaveAsync.md
@@ -7,8 +7,7 @@ title: MySqlTransaction.SaveAsync method
 Asynchronously sets a named transaction savepoint with the specified *savepointName*. If the current transaction already has a savepoint with the same name, the old savepoint is deleted and a new one is set.
 
 ```csharp
-public override Task SaveAsync(string savepointName, 
-    CancellationToken cancellationToken = default(CancellationToken))
+public override Task SaveAsync(string savepointName, CancellationToken cancellationToken = default)
 ```
 
 | parameter | description |

From 9e2851de06536842e1208feb61dbbb7a7741c4af Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 29 Dec 2022 10:36:50 -0800
Subject: [PATCH 019/393] Reword log messages slightly.

---
 src/MySqlConnector/Logging/Log.cs | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index 1051db5bf..7fcddb510 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -258,7 +258,7 @@ internal static partial class Log
 	[LoggerMessage(EventIds.ProcedureHasRoutineCount, LogLevel.Trace, "Procedure for {Schema}.{Component} has {RoutineCount} routines and {ParameterCount} parameters")]
 	public static partial void ProcedureHasRoutineCount(ILogger logger, string schema, string component, int routineCount, int parameterCount);
 
-	[LoggerMessage(EventIds.CreatedNewSession, LogLevel.Trace, "Session {SessionId} created new session")]
+	[LoggerMessage(EventIds.CreatedNewSession, LogLevel.Trace, "Created new session {SessionId}")]
 	public static partial void CreatedNewSession(ILogger logger, string sessionId);
 
 	[LoggerMessage(EventIds.PingingServer, LogLevel.Trace, "Session {SessionId} pinging server")]
@@ -282,10 +282,10 @@ internal static partial class Log
 	[LoggerMessage(EventIds.QueryWasInterrupted, LogLevel.Information, "Session {SessionId} query was interrupted")]
 	public static partial void QueryWasInterrupted(ILogger logger, string sessionId);
 
-	[LoggerMessage(EventIds.PreparingCommandPayload, LogLevel.Trace, "Session {SessionId} preparing command payload for {CommandText}")]
+	[LoggerMessage(EventIds.PreparingCommandPayload, LogLevel.Trace, "Session {SessionId} preparing command payload for: {CommandText}")]
 	public static partial void PreparingCommandPayload(ILogger logger, string sessionId, string commandText);
 
-	[LoggerMessage(EventIds.PreparingCommandPayloadWithId, LogLevel.Trace, "Session {SessionId} preparing statement payload with ID {StatementId} for {CommandText}")]
+	[LoggerMessage(EventIds.PreparingCommandPayloadWithId, LogLevel.Trace, "Session {SessionId} preparing statement payload with ID {StatementId} for: {CommandText}")]
 	public static partial void PreparingCommandPayloadWithId(ILogger logger, string sessionId, int statementId, string commandText);
 
 	[LoggerMessage(EventIds.QueryAttributesNotSupported, LogLevel.Warning, "Session {SessionId} has query attributes but server doesn't support them; CommandText: {CommandText}")]
@@ -327,7 +327,7 @@ internal static partial class Log
 	[LoggerMessage(EventIds.FailedInSendReplyAsync, LogLevel.Debug, "Session {SessionId} failed in SendReplyAsync")]
 	public static partial void FailedInSendReplyAsync(ILogger logger, Exception exception, string sessionId);
 
-	[LoggerMessage(EventIds.CreatingNewConnectionPool, LogLevel.Information, "Pool {PoolId} creating new connection pool for {ConnectionString}")]
+	[LoggerMessage(EventIds.CreatingNewConnectionPool, LogLevel.Information, "Creating new connection pool {PoolId} for {ConnectionString}")]
 	public static partial void CreatingNewConnectionPool(ILogger logger, int poolId, string connectionString);
 
 	[LoggerMessage(EventIds.ScanningForLeakedSessions, LogLevel.Debug, "Pool {PoolId} is empty; scanning for any leaked sessions")]
@@ -342,11 +342,11 @@ internal static partial class Log
 	[LoggerMessage(EventIds.SessionIsUnusable, LogLevel.Information, "Pool {PoolId} session {SessionId} is unusable; destroying it")]
 	public static partial void SessionIsUnusable(ILogger logger, int poolId, string sessionId);
 
-	[LoggerMessage(EventIds.ReturningPooledSession, LogLevel.Trace, "Pool {PoolId} returning pooled session {SessionId} to caller; {LeasedSessionsCount} leased sessions")]
-	public static partial void ReturningPooledSession(ILogger logger, int poolId, string sessionId, int leasedSessionsCount);
+	[LoggerMessage(EventIds.ReturningPooledSession, LogLevel.Trace, "Pool {PoolId} returning pooled session {SessionId} to caller; {LeasedSessionCount} leased session(s)")]
+	public static partial void ReturningPooledSession(ILogger logger, int poolId, string sessionId, int leasedSessionCount);
 
-	[LoggerMessage(EventIds.ReturningNewSession, LogLevel.Trace, "Pool {PoolId} returning new session {SessionId} to caller; {LeasedSessionsCount} leased sessions")]
-	public static partial void ReturningNewSession(ILogger logger, int poolId, string sessionId, int leasedSessionsCount);
+	[LoggerMessage(EventIds.ReturningNewSession, LogLevel.Trace, "Pool {PoolId} returning new session {SessionId} to caller; {LeasedSessionCount} leased session(s)")]
+	public static partial void ReturningNewSession(ILogger logger, int poolId, string sessionId, int leasedSessionCount);
 
 	[LoggerMessage(EventIds.DisposingCreatedSessionDueToException, LogLevel.Debug, "Pool {PoolId} disposing created session {SessionId} due to exception: {ExceptionMessage}")]
 	public static partial void DisposingCreatedSessionDueToException(ILogger logger, Exception exception, int poolId, string sessionId, string exceptionMessage);

From 122f4e2d6f0d85de701357f4e93c37ec37c01e93 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 29 Dec 2022 10:39:49 -0800
Subject: [PATCH 020/393] Add MySqlConnector.DependencyInjection. Fixes #1271

---
 MySqlConnector.sln                            |  6 ++
 .../MySqlConnector.DependencyInjection.csproj | 28 +++++++
 ...SqlConnectorServiceCollectionExtensions.cs | 74 +++++++++++++++++++
 .../docs/README.md                            | 49 ++++++++++++
 4 files changed, 157 insertions(+)
 create mode 100644 src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
 create mode 100644 src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
 create mode 100644 src/MySqlConnector.DependencyInjection/docs/README.md

diff --git a/MySqlConnector.sln b/MySqlConnector.sln
index 9b4d24e70..0d56e5fa4 100644
--- a/MySqlConnector.sln
+++ b/MySqlConnector.sln
@@ -24,6 +24,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{D0EDA522
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SchemaCollectionGenerator", "tools\SchemaCollectionGenerator\SchemaCollectionGenerator.csproj", "{2C8FA08E-755D-454D-B98E-10A122673ABE}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.DependencyInjection", "src\MySqlConnector.DependencyInjection\MySqlConnector.DependencyInjection.csproj", "{D48B3619-7FE1-420C-A96C-B231B7EA73EA}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -70,6 +72,10 @@ Global
 		{2C8FA08E-755D-454D-B98E-10A122673ABE}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{2C8FA08E-755D-454D-B98E-10A122673ABE}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{2C8FA08E-755D-454D-B98E-10A122673ABE}.Release|Any CPU.Build.0 = Release|Any CPU
+		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
new file mode 100644
index 000000000..5e1488102
--- /dev/null
+++ b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
@@ -0,0 +1,28 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>netstandard2.0;net7.0</TargetFrameworks>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <EnablePackageValidation>false</EnablePackageValidation>
+    <Title>MySqlConnector Dependency Injection Helpers</Title>
+    <Description>Provides extension methods for integrating MySqlConnector with .NET dependency injection.</Description>
+    <Copyright>Copyright 2022 Bradley Grainger</Copyright>
+    <Authors>Bradley Grainger</Authors>
+    <PackageReadmeFile>README.md</PackageReadmeFile>
+    <PackageTags>mysqlconnector;dependency injection;iservicecollection</PackageTags>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\MySqlConnector\MySqlConnector.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <None Include="docs\README.md" Pack="true" PackagePath="\"/>
+  </ItemGroup>
+
+</Project>
diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs b/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
new file mode 100644
index 000000000..4359192cb
--- /dev/null
+++ b/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
@@ -0,0 +1,74 @@
+using System.Data.Common;
+using Microsoft.Extensions.DependencyInjection.Extensions;
+using Microsoft.Extensions.Logging;
+using MySqlConnector;
+
+namespace Microsoft.Extensions.DependencyInjection;
+
+/// <summary>
+/// Extension method for setting up MySqlConnector services in an <see cref="IServiceCollection" />.
+/// </summary>
+public static class MySqlConnectorServiceCollectionExtensions
+{
+	/// <summary>
+	/// Registers a <see cref="MySqlDataSource" /> and a <see cref="MySqlConnection" /> in the <see cref="IServiceCollection" />.
+	/// </summary>
+	/// <param name="serviceCollection">The <see cref="IServiceCollection" /> to add services to.</param>
+	/// <param name="connectionString">A MySQL connection string.</param>
+	/// <param name="connectionLifetime">The lifetime with which to register the <see cref="MySqlConnection" /> in the container. Defaults to <see cref="ServiceLifetime.Transient" />.</param>
+	/// <param name="dataSourceLifetime">The lifetime with which to register the <see cref="MySqlDataSource" /> service in the container. Defaults to <see cref="ServiceLifetime.Singleton" />.</param>
+	/// <returns>The same service collection so that multiple calls can be chained.</returns>
+	public static IServiceCollection AddMySqlDataSource(
+		this IServiceCollection serviceCollection,
+		string connectionString,
+		ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
+		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
+		DoAddMySqlDataSource(serviceCollection, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
+
+	/// <summary>
+	/// Registers a <see cref="MySqlDataSource" /> and a <see cref="MySqlConnection" /> in the <see cref="IServiceCollection" />.
+	/// </summary>
+	/// <param name="serviceCollection">The <see cref="IServiceCollection" /> to add services to.</param>
+	/// <param name="connectionString">A MySQL connection string.</param>
+	/// <param name="dataSourceBuilderAction">An action to configure the <see cref="MySqlDataSourceBuilder" /> for further customizations of the <see cref="MySqlDataSource" />.</param>
+	/// <param name="connectionLifetime">The lifetime with which to register the <see cref="MySqlConnection" /> in the container. Defaults to <see cref="ServiceLifetime.Transient" />.</param>
+	/// <param name="dataSourceLifetime">The lifetime with which to register the <see cref="MySqlDataSource" /> service in the container. Defaults to <see cref="ServiceLifetime.Singleton" />.</param>
+	/// <returns>The same service collection so that multiple calls can be chained.</returns>
+	public static IServiceCollection AddMySqlDataSource(
+		this IServiceCollection serviceCollection,
+		string connectionString,
+		Action<MySqlDataSourceBuilder> dataSourceBuilderAction,
+		ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
+		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
+		DoAddMySqlDataSource(serviceCollection, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
+
+	private static IServiceCollection DoAddMySqlDataSource(
+		this IServiceCollection serviceCollection,
+		string connectionString,
+		Action<MySqlDataSourceBuilder>? dataSourceBuilderAction,
+		ServiceLifetime connectionLifetime,
+		ServiceLifetime dataSourceLifetime)
+	{
+		serviceCollection.TryAdd(
+			new ServiceDescriptor(
+				typeof(MySqlDataSource),
+				x =>
+				{
+					var dataSourceBuilder = new MySqlDataSourceBuilder(connectionString)
+						.UseLoggerFactory(x.GetService<ILoggerFactory>());
+					dataSourceBuilderAction?.Invoke(dataSourceBuilder);
+					return dataSourceBuilder.Build();
+				},
+				dataSourceLifetime));
+
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(MySqlConnection), x => x.GetRequiredService<MySqlDataSource>().CreateConnection(), connectionLifetime));
+
+#if NET7_0_OR_GREATER
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbDataSource), x => x.GetRequiredService<MySqlDataSource>(), dataSourceLifetime));
+#endif
+
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbConnection), x => x.GetRequiredService<MySqlConnection>(), connectionLifetime));
+
+		return serviceCollection;
+	}
+}
diff --git a/src/MySqlConnector.DependencyInjection/docs/README.md b/src/MySqlConnector.DependencyInjection/docs/README.md
new file mode 100644
index 000000000..45b9ffbf9
--- /dev/null
+++ b/src/MySqlConnector.DependencyInjection/docs/README.md
@@ -0,0 +1,49 @@
+## About
+
+MySqlConnector.DependencyInjection helps set up MySqlConnector in applications that use dependency injection, most notably in ASP.NET.
+It allows easy configuration of your MySQL connections and registers the appropriate services in your DI container.
+
+## How to Use
+
+For example, if using the ASP.NET minimal web API, use the following to register MySqlConnector:
+
+```csharp
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddMySqlDataSource("Server=server;User ID=test;Password=test;Database=test");
+```
+
+This registers a transient `MySqlConnection` which can get injected into your controllers:
+
+```csharp
+app.MapGet("/", async (MySqlConnection connection) =>
+{
+    await connection.OpenAsync();
+    await using var command = connection.CreateCommand();
+	command.CommandText = "SELECT name FROM users LIMIT 1";
+    return "Hello World: " + await command.ExecuteScalarAsync();
+});
+```
+
+You can use `MySqlDataSource` directly if you need more than one connection:
+
+```csharp
+app.MapGet("/", async (MySqlDataSource dataSource) =>
+{
+    await using var connection1 = await dataSource.OpenConnectionAsync();
+    await using var connection2 = await dataSource.OpenConnectionAsync();
+    // use the two connections...
+});
+```
+
+## Advanced Usage
+
+The `AddMySqlDataSource` method also accepts a lambda parameter allowing you to configure aspects of MySqlConnector beyond the connection string.
+
+```csharp
+var builder = WebApplication.CreateBuilder(args);
+
+builder.Services.AddMySqlDataSource("Server=server;User ID=test;Password=test;Database=test",
+	x => x.UseRemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) => { /* custom logic */ })
+);
+```

From 80b0cd709f3df3e1bfac5d553926a12e25e47709 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 29 Dec 2022 11:03:48 -0800
Subject: [PATCH 021/393] Clone MySqlDataSource in MySqlConnection. Fixes #1267

---
 src/MySqlConnector/MySqlConnection.cs         | 22 +++++++------------
 .../IntegrationTests/MySqlDataSourceTests.cs  | 16 ++++++++++++++
 2 files changed, 24 insertions(+), 14 deletions(-)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 1a793b24a..f22046ac4 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -702,12 +702,7 @@ public async Task DisposeAsync()
 		}
 	}
 
-	public MySqlConnection Clone() => new(m_connectionString, LoggingConfiguration, m_hasBeenOpened)
-	{
-		ProvideClientCertificatesCallback = ProvideClientCertificatesCallback,
-		ProvidePasswordCallback = ProvidePasswordCallback,
-		RemoteCertificateValidationCallback = RemoteCertificateValidationCallback,
-	};
+	public MySqlConnection Clone() => new(this, m_connectionString, m_hasBeenOpened);
 
 	object ICloneable.Clone() => Clone();
 
@@ -727,12 +722,7 @@ public MySqlConnection CloneWith(string connectionString)
 		var shouldCopyPassword = newBuilder.Password.Length == 0 && (!newBuilder.PersistSecurityInfo || currentBuilder.PersistSecurityInfo);
 		if (shouldCopyPassword)
 			newBuilder.Password = currentBuilder.Password;
-		return new MySqlConnection(newBuilder.ConnectionString, LoggingConfiguration, m_hasBeenOpened && shouldCopyPassword && !currentBuilder.PersistSecurityInfo)
-		{
-			ProvideClientCertificatesCallback = ProvideClientCertificatesCallback,
-			ProvidePasswordCallback = ProvidePasswordCallback,
-			RemoteCertificateValidationCallback = RemoteCertificateValidationCallback,
-		};
+		return new MySqlConnection(this, newBuilder.ConnectionString, m_hasBeenOpened && shouldCopyPassword && !currentBuilder.PersistSecurityInfo);
 	}
 
 	internal ServerSession Session
@@ -981,10 +971,14 @@ internal void SetState(ConnectionState newState)
 		}
 	}
 
-	private MySqlConnection(string connectionString, MySqlConnectorLoggingConfiguration loggingConfiguration, bool hasBeenOpened)
-		: this(connectionString, loggingConfiguration)
+	private MySqlConnection(MySqlConnection other, string connectionString, bool hasBeenOpened)
+		: this(connectionString, other.LoggingConfiguration)
 	{
+		m_dataSource = other.m_dataSource;
 		m_hasBeenOpened = hasBeenOpened;
+		ProvideClientCertificatesCallback = other.ProvideClientCertificatesCallback;
+		ProvidePasswordCallback = other.ProvidePasswordCallback;
+		RemoteCertificateValidationCallback = other.RemoteCertificateValidationCallback;
 	}
 
 	private void VerifyNotDisposed()
diff --git a/tests/IntegrationTests/MySqlDataSourceTests.cs b/tests/IntegrationTests/MySqlDataSourceTests.cs
index 0c486e3c5..5dc3a0f7d 100644
--- a/tests/IntegrationTests/MySqlDataSourceTests.cs
+++ b/tests/IntegrationTests/MySqlDataSourceTests.cs
@@ -50,6 +50,22 @@ public void OpenConnectionReusesConnection()
 		}
 	}
 
+	[Fact]
+	public void CloneReusesDataSource()
+	{
+		using var dbSource = new MySqlDataSource(AppConfig.ConnectionString);
+
+		using var connection1 = dbSource.CreateConnection();
+		using var connection2 = connection1.Clone();
+
+		connection1.Open();
+		var serverThread = connection1.ServerThread;
+		connection1.Close();
+
+		connection2.Open();
+		Assert.Equal(serverThread, connection2.ServerThread);
+	}
+
 	[Fact]
 	public void MultipleDataSourcesHaveDifferentPools()
 	{

From e5f5421c8aab04c1db5ae105ccfbe80471244ab9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 31 Dec 2022 12:48:07 -0800
Subject: [PATCH 022/393] Add password rotation support. Fixes #1270

---
 src/MySqlConnector/Logging/EventIds.cs        |  5 +-
 src/MySqlConnector/Logging/Log.cs             |  7 +-
 src/MySqlConnector/MySqlDataSource.cs         | 95 ++++++++++++++++++-
 src/MySqlConnector/MySqlDataSourceBuilder.cs  | 25 ++++-
 .../IntegrationTests/MySqlDataSourceTests.cs  | 78 +++++++++++++++
 5 files changed, 204 insertions(+), 6 deletions(-)

diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index 3d9204e25..9851298f1 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -2,10 +2,13 @@ namespace MySqlConnector.Logging;
 
 internal static class EventIds
 {
-	// DataSource events, 1000-1999
+	// DataSource events, 1000-1099
 	public const int DataSourceCreatedWithPool = 1000;
 	public const int DataSourceCreatedWithoutPool = 1001;
 
+	// DataSource errors, 1100-1199
+	public const int PeriodicPasswordProviderFailed = 1100;
+
 	// Core Session events, 2000-2099
 	public const int CreatedNewSession = 2000;
 	public const int CreatedNonPooledSession = 2001;
diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index 7fcddb510..c5e871feb 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -7,12 +7,15 @@ namespace MySqlConnector.Logging;
 
 internal static partial class Log
 {
-	[LoggerMessage(EventIds.DataSourceCreatedWithPool, LogLevel.Information, "DataSource {DataSourceId} created with pool {PoolId}")]
+	[LoggerMessage(EventIds.DataSourceCreatedWithPool, LogLevel.Information, "Data source {DataSourceId} created with pool {PoolId}")]
 	public static partial void DataSourceCreatedWithPool(ILogger logger, int dataSourceId, int poolId);
 
-	[LoggerMessage(EventIds.DataSourceCreatedWithoutPool, LogLevel.Information, "DataSource {DataSourceId} created with no pool")]
+	[LoggerMessage(EventIds.DataSourceCreatedWithoutPool, LogLevel.Information, "Data source {DataSourceId} created with no pool")]
 	public static partial void DataSourceCreatedWithoutPool(ILogger logger, int dataSourceId);
 
+	[LoggerMessage(EventIds.PeriodicPasswordProviderFailed, LogLevel.Error, "Periodic password provider for data source {DataSourceId} failed: {ExceptionMessage}")]
+	public static partial void PeriodicPasswordProviderFailed(ILogger logger, Exception exception, int dataSourceId, string exceptionMessage);
+
 	[LoggerMessage(EventIds.CreatedNonPooledSession, LogLevel.Debug, "Created new non-pooled session {SessionId}")]
 	public static partial void CreatedNonPooledSession(ILogger logger, string sessionId);
 
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index 112b10fd7..c6a14d66f 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -18,14 +18,17 @@ public sealed class MySqlDataSource : DbDataSource
 	/// <param name="connectionString">The connection string for the MySQL Server. This parameter is required.</param>
 	/// <exception cref="ArgumentNullException">Thrown if <paramref name="connectionString"/> is <c>null</c>.</exception>
 	public MySqlDataSource(string connectionString)
-		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null)
+		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, default, default)
 	{
 	}
 
 	internal MySqlDataSource(string connectionString,
 		MySqlConnectorLoggingConfiguration loggingConfiguration,
 		Func<X509CertificateCollection, ValueTask>? clientCertificatesCallback,
-		RemoteCertificateValidationCallback? remoteCertificateValidationCallback)
+		RemoteCertificateValidationCallback? remoteCertificateValidationCallback,
+		Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? periodicPasswordProvider,
+		TimeSpan periodicPasswordProviderSuccessRefreshInterval,
+		TimeSpan periodicPasswordProviderFailureRefreshInterval)
 	{
 		m_connectionString = connectionString;
 		LoggingConfiguration = loggingConfiguration;
@@ -39,6 +42,25 @@ internal MySqlDataSource(string connectionString,
 			Log.DataSourceCreatedWithPool(m_logger, m_id, Pool.Id);
 		else
 			Log.DataSourceCreatedWithoutPool(m_logger, m_id);
+
+		if (periodicPasswordProvider is not null)
+		{
+			m_periodicPasswordProvider = periodicPasswordProvider;
+			m_periodicPasswordProviderSuccessRefreshInterval = periodicPasswordProviderSuccessRefreshInterval;
+			m_periodicPasswordProviderFailureRefreshInterval = periodicPasswordProviderFailureRefreshInterval;
+
+			m_passwordProviderTimerCancellationTokenSource = new();
+			var csb = new MySqlConnectionStringBuilder(m_connectionString);
+			m_providePasswordContext = new(csb.Server, (int) csb.Port, csb.UserID, csb.Database);
+
+			// create the timer but don't start it; the manual run below will will schedule the first refresh
+			// see https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md#timer-callbacks for code pattern
+			m_passwordProviderTimer = new Timer(_ => _ = RefreshPassword(), null, Timeout.InfiniteTimeSpan, Timeout.InfiniteTimeSpan);
+
+			// trigger the first refresh attempt right now, outside the timer; this allows us to capture the Task so it can be observed in ComplexGetPassword
+			m_initialPasswordRefreshTask = Task.Run(RefreshPassword);
+			m_providePasswordCallback = ProvidePasswordFromInitialRefreshTask;
+		}
 	}
 
 	/// <summary>
@@ -75,6 +97,29 @@ internal MySqlDataSource(string connectionString,
 	/// </summary>
 	public override string ConnectionString => m_connectionString;
 
+#pragma warning disable CA1044 // Properties should not be write only
+	/// <summary>
+	/// Sets the password that will be used by the next <see cref="MySqlConnection"/> created from this <see cref="MySqlDataSource"/>.
+	/// </summary>
+	/// <remarks>
+	/// <para>This can be used to update the password for database servers that periodically rotate authentication tokens, without
+	/// affecting connection pooling. The <see cref="MySqlConnectionStringBuilder.Password"/> property must not be specified in
+	/// order for this field to be used.</para>
+	/// <para>Consider using <see cref="MySqlDataSourceBuilder.UsePeriodicPasswordProvider"/> instead.</para>
+	/// </remarks>
+	public string Password
+	{
+		set
+		{
+			if (m_periodicPasswordProvider is not null)
+				throw new InvalidOperationException("Cannot set Password when this MySqlDataSource is configured with a PeriodicPasswordProvider.");
+
+			m_password = value ?? throw new ArgumentNullException(nameof(value));
+			m_providePasswordCallback = ProvidePasswordFromField;
+		}
+	}
+#pragma warning restore CA1044 // Properties should not be write only
+
 	protected override DbConnection CreateDbConnection()
 	{
 		if (m_isDisposed)
@@ -82,6 +127,7 @@ protected override DbConnection CreateDbConnection()
 		return new MySqlConnection(this)
 		{
 			ProvideClientCertificatesCallback = m_clientCertificatesCallback,
+			ProvidePasswordCallback = m_providePasswordCallback,
 			RemoteCertificateValidationCallback = m_remoteCertificateValidationCallback,
 		};
 	}
@@ -105,6 +151,11 @@ protected override void Dispose(bool disposing)
 
 	private async ValueTask DisposeAsync(IOBehavior ioBehavior)
 	{
+		if (m_passwordProviderTimerCancellationTokenSource is { } cts)
+		{
+			cts.Cancel();
+			cts.Dispose();
+		}
 		if (Pool is not null)
 		{
 			await Pool.ClearAsync(ioBehavior, default).ConfigureAwait(false);
@@ -113,10 +164,41 @@ private async ValueTask DisposeAsync(IOBehavior ioBehavior)
 		m_isDisposed = true;
 	}
 
+	private async Task RefreshPassword()
+	{
+		try
+		{
+			// set the password from the callback, then queue another refresh after the 'success' interval
+			m_password = await m_periodicPasswordProvider!(m_providePasswordContext!, m_passwordProviderTimerCancellationTokenSource!.Token).ConfigureAwait(false);
+			m_providePasswordCallback = ProvidePasswordFromField;
+			m_passwordProviderTimer!.Change(m_periodicPasswordProviderSuccessRefreshInterval, Timeout.InfiniteTimeSpan);
+		}
+		catch (Exception e)
+		{
+			// queue a refresh after the 'failure' interval
+			Log.PeriodicPasswordProviderFailed(m_logger, e, m_id, e.Message);
+			m_passwordProviderTimer!.Change(m_periodicPasswordProviderFailureRefreshInterval, Timeout.InfiniteTimeSpan);
+			throw new MySqlException("The periodic password provider failed", e);
+		}
+	}
+
 	internal ConnectionPool? Pool { get; }
 
 	internal MySqlConnectorLoggingConfiguration LoggingConfiguration { get; }
 
+	private string ProvidePasswordFromField(MySqlProvidePasswordContext context) => m_password!;
+
+	private string ProvidePasswordFromInitialRefreshTask(MySqlProvidePasswordContext context)
+	{
+		if (m_password is null)
+		{
+			// password hasn't been set up, so wait (synchronously) for the task to complete the first time
+			m_initialPasswordRefreshTask!.GetAwaiter().GetResult();
+			m_providePasswordCallback = ProvidePasswordFromField;
+		}
+		return m_password!;
+	}
+
 	private static int s_lastId;
 
 	private readonly ILogger m_logger;
@@ -124,5 +206,14 @@ private async ValueTask DisposeAsync(IOBehavior ioBehavior)
 	private readonly string m_connectionString;
 	private readonly Func<X509CertificateCollection, ValueTask>? m_clientCertificatesCallback;
 	private readonly RemoteCertificateValidationCallback? m_remoteCertificateValidationCallback;
+	private readonly Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? m_periodicPasswordProvider;
+	private readonly TimeSpan m_periodicPasswordProviderSuccessRefreshInterval;
+	private readonly TimeSpan m_periodicPasswordProviderFailureRefreshInterval;
+	private readonly MySqlProvidePasswordContext? m_providePasswordContext;
+	private readonly CancellationTokenSource? m_passwordProviderTimerCancellationTokenSource;
+	private readonly Timer? m_passwordProviderTimer;
+	private readonly Task? m_initialPasswordRefreshTask;
 	private bool m_isDisposed;
+	private string? m_password;
+	private Func<MySqlProvidePasswordContext, string>? m_providePasswordCallback;
 }
diff --git a/src/MySqlConnector/MySqlDataSourceBuilder.cs b/src/MySqlConnector/MySqlDataSourceBuilder.cs
index 7f2e6b7a3..628e6375d 100644
--- a/src/MySqlConnector/MySqlDataSourceBuilder.cs
+++ b/src/MySqlConnector/MySqlDataSourceBuilder.cs
@@ -43,6 +43,23 @@ public MySqlDataSourceBuilder UseClientCertificatesCallback(Func<X509Certificate
 		return this;
 	}
 
+	/// <summary>
+	/// Configures a periodic password provider, which is automatically called by the data source at some regular interval. This is the
+	/// recommended way to fetch a rotating access token.
+	/// </summary>
+	/// <param name="passwordProvider">A callback which returns the password to be used by any new MySQL connections that are made.</param>
+	/// <param name="successRefreshInterval">How long to cache the password before re-invoking the callback.</param>
+	/// <param name="failureRefreshInterval">How long to wait before re-invoking the callback on failure. This should
+	/// typically be much shorter than <paramref name="successRefreshInterval"/>.</param>
+	/// <returns>This builder, so that method calls can be chained.</returns>
+	public MySqlDataSourceBuilder UsePeriodicPasswordProvider(Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? passwordProvider, TimeSpan successRefreshInterval, TimeSpan failureRefreshInterval)
+	{
+		m_periodicPasswordProvider = passwordProvider;
+		m_periodicPasswordProviderSuccessRefreshInterval = successRefreshInterval;
+		m_periodicPasswordProviderFailureRefreshInterval = failureRefreshInterval;
+		return this;
+	}
+
 	/// <summary>
 	/// Sets the callback used to verify that the server's certificate is valid.
 	/// </summary>
@@ -67,7 +84,10 @@ public MySqlDataSource Build()
 		return new(ConnectionStringBuilder.ConnectionString,
 			loggingConfiguration,
 			m_clientCertificatesCallback,
-			m_remoteCertificateValidationCallback
+			m_remoteCertificateValidationCallback,
+			m_periodicPasswordProvider,
+			m_periodicPasswordProviderSuccessRefreshInterval,
+			m_periodicPasswordProviderFailureRefreshInterval
 			);
 	}
 
@@ -79,4 +99,7 @@ public MySqlDataSource Build()
 	private ILoggerFactory? m_loggerFactory;
 	private Func<X509CertificateCollection, ValueTask>? m_clientCertificatesCallback;
 	private RemoteCertificateValidationCallback? m_remoteCertificateValidationCallback;
+	private Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? m_periodicPasswordProvider;
+	private TimeSpan m_periodicPasswordProviderSuccessRefreshInterval;
+	private TimeSpan m_periodicPasswordProviderFailureRefreshInterval;
 }
diff --git a/tests/IntegrationTests/MySqlDataSourceTests.cs b/tests/IntegrationTests/MySqlDataSourceTests.cs
index 5dc3a0f7d..5e6804b5a 100644
--- a/tests/IntegrationTests/MySqlDataSourceTests.cs
+++ b/tests/IntegrationTests/MySqlDataSourceTests.cs
@@ -142,5 +142,83 @@ public async Task ConnectSslRemoteCertificateValidationCallback(MySqlSslMode ssl
 			await Assert.ThrowsAsync<MySqlException>(connection.OpenAsync);
 	}
 
+	[Fact]
+	public void PasswordPropertyIsUsed()
+	{
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		var password = csb.Password;
+		csb.Password = "";
+
+		using var dataSource = new MySqlDataSource(csb.ConnectionString)
+		{
+			Password = password,
+		};
+		using var connection = dataSource.OpenConnection();
+		Assert.Equal(ConnectionState.Open, connection.State);
+	}
+
+	[Fact]
+	public void PasswordProviderCallbackIsInvoked()
+	{
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		var password = csb.Password;
+		csb.Password = "";
+
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UsePeriodicPasswordProvider((_, _) => new ValueTask<string>(password), TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(1))
+			.Build();
+		using var connection = dataSource.OpenConnection();
+		Assert.Equal(ConnectionState.Open, connection.State);
+	}
+
+	[Fact]
+	public void PasswordProviderCallbackIsWaitedFor()
+	{
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		var password = csb.Password;
+		csb.Password = "";
+
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UsePeriodicPasswordProvider(async (_, _) =>
+			{
+				await Task.Delay(TimeSpan.FromSeconds(0.5));
+				return password;
+			}, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(1))
+			.Build();
+		using var connection = dataSource.OpenConnection();
+		Assert.Equal(ConnectionState.Open, connection.State);
+	}
+
+	[Fact]
+	public void PasswordProviderFailureIsRetried()
+	{
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		var password = csb.Password;
+		csb.Password = "";
+
+		using var barrier = new Barrier(2);
+
+		var count = 0;
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UsePeriodicPasswordProvider((_, _) =>
+			{
+				if (count++ == 0)
+					throw new ApplicationException("First-time failure");
+				barrier.SignalAndWait();
+				return new ValueTask<string>(password);
+			}, TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(0.5))
+			.Build();
+
+		// throws the first time
+		var exception = Assert.Throws<MySqlException>(dataSource.OpenConnection); // Failed to obtain password via ProvidePasswordCallback
+		Assert.IsType<MySqlException>(exception.InnerException); // The periodic password provider failed
+		Assert.IsType<ApplicationException>(exception.InnerException.InnerException); // First-time failure
+			
+		// succeeds after failure retry
+		barrier.SignalAndWait();
+		Thread.Sleep(10);
+		using var connection = dataSource.OpenConnection();
+		Assert.Equal(ConnectionState.Open, connection.State);
+	}
 }
 #endif

From 7568616cb1869d21ff40011db1d6b3696ea9ed10 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 31 Dec 2022 14:20:54 -0800
Subject: [PATCH 023/393] Remove netcoreapp3.1 TFM.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/CompatibilitySuppressions.xml | 8 +-------
 src/MySqlConnector/MySqlConnector.csproj         | 4 ++--
 src/MySqlConnector/MySqlParameter.cs             | 2 +-
 3 files changed, 4 insertions(+), 10 deletions(-)

diff --git a/src/MySqlConnector/CompatibilitySuppressions.xml b/src/MySqlConnector/CompatibilitySuppressions.xml
index fd40d06ea..b7bf516eb 100644
--- a/src/MySqlConnector/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector/CompatibilitySuppressions.xml
@@ -9,7 +9,7 @@
   <Suppression>
     <DiagnosticId>CP0008</DiagnosticId>
     <Target>T:MySqlConnector.MySqlBatchCommandCollection</Target>
-    <Left>lib/netcoreapp3.1/MySqlConnector.dll</Left>
+    <Left>lib/netstandard2.1/MySqlConnector.dll</Left>
     <Right>lib/net6.0/MySqlConnector.dll</Right>
   </Suppression>
   <Suppression>
@@ -24,12 +24,6 @@
     <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
     <Right>lib/net461/MySqlConnector.dll</Right>
   </Suppression>
-  <Suppression>
-    <DiagnosticId>CP0008</DiagnosticId>
-    <Target>T:MySqlConnector.MySqlDataSource</Target>
-    <Left>lib/netstandard2.1/MySqlConnector.dll</Left>
-    <Right>lib/netcoreapp3.1/MySqlConnector.dll</Right>
-  </Suppression>
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>
     <Target>.NETFramework,Version=v4.5</Target>
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 62722c977..2d1d37dd5 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net461;net471;netstandard2.0;netstandard2.1;netcoreapp3.1;net6.0;net7.0</TargetFrameworks>
+    <TargetFrameworks>net461;net471;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
     <Description>A truly async MySQL ADO.NET provider, supporting MySQL Server, MariaDB, Percona Server, Amazon Aurora, Azure Database for MySQL and more.</Description>
     <Copyright>Copyright 2016–2022 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
@@ -31,7 +31,7 @@
     <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'netstandard2.1' OR '$(TargetFramework)' == 'netcoreapp3.1' ">
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'netstandard2.1' ">
     <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="6.0.0" />
   </ItemGroup>
 
diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 70fa0608c..38bb706fb 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -447,7 +447,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		}
 		else if (Value is StringBuilder stringBuilder)
 		{
-#if NETCOREAPP3_1 || NET5_0_OR_GREATER
+#if NETCOREAPP3_1_OR_GREATER
 			writer.Write((byte) '\'');
 			foreach (var chunk in stringBuilder.GetChunks())
 				WriteString(writer, noBackslashEscapes, writeDelimiters: false, chunk.Span);

From 17fdeeaa07b33207634227b6e20e623114ffb536 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 31 Dec 2022 14:56:52 -0800
Subject: [PATCH 024/393] Remove .NET Core 3.1 integration tests.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .ci/build-steps.yml                           | 12 ------------
 .ci/integration-tests-steps.yml               |  7 -------
 .ci/test.ps1                                  |  2 +-
 appveyor.yml                                  |  3 +--
 azure-pipelines.yml                           | 19 +++++++------------
 tests/IntegrationTests/InsertTests.cs         |  5 -----
 .../IntegrationTests/IntegrationTests.csproj  |  2 +-
 tests/IntegrationTests/SslTests.cs            | 19 -------------------
 .../MySqlConnector.Tests.csproj               |  7 +------
 9 files changed, 11 insertions(+), 65 deletions(-)

diff --git a/.ci/build-steps.yml b/.ci/build-steps.yml
index b4eef39cc..95cfc5553 100644
--- a/.ci/build-steps.yml
+++ b/.ci/build-steps.yml
@@ -46,18 +46,6 @@ steps:
     artifactName: 'Conformance.Tests-7.0-$(Agent.OS)'
     targetPath: 'tests/Conformance.Tests/bin/Release/net7.0/publish'
 
-- task: DotNetCoreCLI@2
-  displayName: 'Publish IntegrationTests (3.1)'
-  inputs:
-    command: 'publish'
-    arguments: '-c Release -f netcoreapp3.1 --no-build tests/IntegrationTests/IntegrationTests.csproj'
-    publishWebProjects: false
-    zipAfterPublish: false
-- task: PublishPipelineArtifact@0
-  inputs:
-    artifactName: 'IntegrationTests-netcoreapp3.1-$(Agent.OS)'
-    targetPath: 'tests/IntegrationTests/bin/Release/netcoreapp3.1/publish'
-
 - task: DotNetCoreCLI@2
   displayName: 'Publish IntegrationTests (7.0)'
   inputs:
diff --git a/.ci/integration-tests-steps.yml b/.ci/integration-tests-steps.yml
index b22e0f007..4e89b919c 100644
--- a/.ci/integration-tests-steps.yml
+++ b/.ci/integration-tests-steps.yml
@@ -25,13 +25,6 @@ steps:
   parameters:
     image: ${{ parameters.image }}
     connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=conformance;ssl mode=none;DefaultCommandTimeout=3600'
-- template: 'integration-test-steps.yml'
-  parameters:
-    image: ${{ parameters.image }}
-    unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
-    connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600'
-    platform: 'netcoreapp3.1'
-    description: 'No SSL'
 - template: 'integration-test-steps.yml'
   parameters:
     image: ${{ parameters.image }}
diff --git a/.ci/test.ps1 b/.ci/test.ps1
index 75d4a0c90..7ebe26842 100644
--- a/.ci/test.ps1
+++ b/.ci/test.ps1
@@ -39,7 +39,7 @@ if ($LASTEXITCODE -ne 0){
 
 echo "Executing integration tests with Compression, No SSL"
 Copy-Item -Force ..\..\.ci\config\config.compression.json config.json
-dotnet test -c Release -f netcoreapp3.1
+dotnet test -c Release -f net6.0
 if ($LASTEXITCODE -ne 0){
     exit $LASTEXITCODE;
 }
diff --git a/appveyor.yml b/appveyor.yml
index db45cdd1c..5720ee2a8 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -9,8 +9,7 @@ cache:
   - '%USERPROFILE%\.nuget\packages -> **\*.csproj'
 install:
   - ps: Invoke-WebRequest -Uri "https://dot.net/v1/dotnet-install.ps1" -OutFile "install-dotnet.ps1"
-  - ps: .\install-dotnet.ps1 -Channel 3.1 -InstallDir "dotnetcli"
-  - ps: .\install-dotnet.ps1 -Channel 5.0 -InstallDir "dotnetcli"
+  - ps: .\install-dotnet.ps1 -Channel 6.0 -InstallDir "dotnetcli"
   - ps: .\install-dotnet.ps1 -Channel 7.0 -InstallDir "dotnetcli"
 build_script:
 - dotnet --info
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 1b7a8cb8b..842650b5d 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -113,9 +113,9 @@ jobs:
       targetType: inline
       script: "mysql -u$(Azure MySQL Root User) -p$(Azure MySQL Root Password) -h '$(Azure MySQL Host)' -e 'create schema mysqltest_$(Build.BuildId) collate utf8mb4_0900_ai_ci;'"
   - task: UseDotNet@2
-    displayName: 'Install .NET Core 6.0'
+    displayName: 'Install .NET Core 7.0'
     inputs:
-      version: 6.0.x
+      version: 7.0.x
       packageType: runtime
   - task: UseDotNet@2
     displayName: 'Install .NET Core'
@@ -131,11 +131,11 @@ jobs:
     inputs:
       command: 'restore'
   - task: DotNetCoreCLI@2
-    displayName: 'Integration tests (net6.0)'
+    displayName: 'Integration tests (net7.0)'
     inputs:
       command: 'test'
       projects: 'tests/IntegrationTests/IntegrationTests.csproj'
-      arguments: '-c Release -f net6.0 --no-restore'
+      arguments: '-c Release -f net7.0 --no-restore'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'Azure', 'net6.0', 'SSL') }}
     env:
       DATA__UNSUPPORTEDFEATURES: 'CachingSha2Password,Ed25519,GlobalLog,KnownCertificateAuthority,QueryAttributes,RsaEncryption,Sha256Password,StreamingResults,Timeout,Tls11,Tls13,UnixDomainSocket,ZeroDateTime'
@@ -189,11 +189,6 @@ jobs:
     vmimage: 'windows-2019'
   steps:
   - template: '.ci/install-mysql-windows.yml'
-  - task: UseDotNet@2
-    displayName: 'Install .NET Core 3.1'
-    inputs:
-      version: 3.1.x
-      packageType: runtime
   - task: UseDotNet@2
     displayName: 'Install .NET Core 6.0'
     inputs:
@@ -209,18 +204,18 @@ jobs:
     displayName: 'Remove target frameworks'
     inputs:
       targetType: 'inline'
-      script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''<TargetFrameworks>.*</TargetFrameworks>'', ''<TargetFrameworks>netcoreapp3.1;net6.0</TargetFrameworks>'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
+      script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''<TargetFrameworks>.*</TargetFrameworks>'', ''<TargetFrameworks>net6.0</TargetFrameworks>'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
   - task: DotNetCoreCLI@2
     displayName: 'Restore packages'
     inputs:
       command: 'restore'
   - task: DotNetCoreCLI@2
-    displayName: 'Integration tests (netcoreapp3.1/net6.0)'
+    displayName: 'Integration tests (net6.0)'
     inputs:
       command: 'test'
       projects: 'tests/IntegrationTests/IntegrationTests.csproj'
       arguments: '-c Release --no-restore'
-      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'netcoreapp3.1/net6.0', 'No SSL') }}
+      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net6.0', 'No SSL') }}
     env:
       DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
diff --git a/tests/IntegrationTests/InsertTests.cs b/tests/IntegrationTests/InsertTests.cs
index 15b4366fb..6bcfec5de 100644
--- a/tests/IntegrationTests/InsertTests.cs
+++ b/tests/IntegrationTests/InsertTests.cs
@@ -387,12 +387,7 @@ public void InsertStringBuilder(bool prepare)
 
 		var value = new StringBuilder("\aAB\\12'ab\\'\\'");
 		for (var i = 0; i < 100; i++)
-#if !NETCOREAPP3_1
 			value.Append("\U0001F600\uD800\'\U0001F601\uD800");
-#else
-			// the netcoreapp3.1 implementation is broken when handling an unpaired surrogate; probably https://github.com/dotnet/runtime/issues/33817
-			value.Append("\U0001F600\U0001F601");
-#endif
 
 		using var cmd = connection.CreateCommand();
 		cmd.CommandText = @"insert into insert_string_builder(str) values(@str);";
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index e188e28a1..f67b1deeb 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup Condition=" '$(Configuration)' != 'MySqlData' ">
-    <TargetFrameworks>net462;net472;netcoreapp3.1;net6.0;net7.0</TargetFrameworks>
+    <TargetFrameworks>net462;net472;net6.0;net7.0</TargetFrameworks>
     <CheckEolTargetFramework>false</CheckEolTargetFramework>
   </PropertyGroup>
 
diff --git a/tests/IntegrationTests/SslTests.cs b/tests/IntegrationTests/SslTests.cs
index 94179aa7c..e22c034fb 100644
--- a/tests/IntegrationTests/SslTests.cs
+++ b/tests/IntegrationTests/SslTests.cs
@@ -260,24 +260,5 @@ public async Task ForceTls11()
 	}
 #endif
 
-#if NETCOREAPP3_1
-	[SkippableFact(ConfigSettings.RequiresSsl)]
-	public async Task RequireTls13()
-	{
-		if (AppConfig.SupportedFeatures.HasFlag(ServerFeatures.Tls13))
-			return;
-
-		var csb = AppConfig.CreateConnectionStringBuilder();
-		csb.TlsVersion = "TLS 1.3";
-
-		using var connection = new MySqlConnection(csb.ConnectionString);
-#if !MYSQL_DATA
-		await Assert.ThrowsAsync<MySqlException>(async () => await connection.OpenAsync());
-#else
-		await Assert.ThrowsAsync<Win32Exception>(async () => await connection.OpenAsync());
-#endif
-	}
-#endif
-
 	readonly DatabaseFixture m_database;
 }
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 5a7766def..72388241f 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup Condition=" '$(Configuration)' != 'MySqlData' ">
-    <TargetFrameworks>netcoreapp3.1;net7.0</TargetFrameworks>
+    <TargetFrameworks>net7.0</TargetFrameworks>
   </PropertyGroup>
 
  <PropertyGroup Condition=" '$(Configuration)' == 'MySqlData' ">
@@ -36,9 +36,4 @@
     <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' ">
-    <Reference Include="System" />
-    <Reference Include="Microsoft.CSharp" />
-  </ItemGroup>
-
 </Project>

From 7f751e78685d2bda94dce78f55b7b894410c3f1c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 1 Jan 2023 09:40:35 -0800
Subject: [PATCH 025/393] Update Microsoft.Extensions.Configuration packages.

---
 tests/IntegrationTests/IntegrationTests.csproj | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index f67b1deeb..51d614b64 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -32,9 +32,9 @@
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
     <!--app packages-->
     <PackageReference Include="Dapper.StrongName" Version="2.0.123" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="6.0.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="6.0.1" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.1" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
     <Using Include="System" />
     <Using Include="System.Data" />
     <Using Include="System.Data.Common" />

From a97535d72e8fa5bc61739caeb7e111cf1bf726ac Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 1 Jan 2023 11:58:12 -0800
Subject: [PATCH 026/393] Target .NET Framework 4.6.2 instead of 4.6.1

.NET Framework 4.6.1 support ended on 26 April 2022.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../MySqlConnector.Authentication.Ed25519.csproj  |  2 +-
 .../MySqlConnector.Logging.NLog.csproj            |  2 +-
 .../MySqlConnector.Logging.Serilog.csproj         |  2 +-
 .../MySqlConnector.Logging.log4net.csproj         |  2 +-
 src/MySqlConnector/CompatibilitySuppressions.xml  | 15 ++++++++++++++-
 src/MySqlConnector/Core/ServerSession.cs          |  2 +-
 src/MySqlConnector/MySqlConnector.csproj          |  8 ++++----
 src/MySqlConnector/MySqlDataReader.cs             |  2 +-
 src/MySqlConnector/Shims/DbColumn.cs              |  2 +-
 .../Utilities/NullableAttributes.cs               |  2 +-
 src/MySqlConnector/Utilities/Utility.cs           |  6 +++---
 11 files changed, 29 insertions(+), 16 deletions(-)

diff --git a/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj b/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj
index 7ff459ad5..f8e8efc10 100644
--- a/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj
+++ b/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
+    <TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
     <Title>MySqlConnector Ed25519 Authentication Plugin</Title>
     <Description>Implements the client_ed25519 authentication plugin for MariaDB.</Description>
     <Copyright>Copyright 2019–2022 Bradley Grainger</Copyright>
diff --git a/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj b/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj
index ff359b51e..baeb849b0 100644
--- a/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj
+++ b/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
+    <TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
     <Title>MySqlConnector Logging Adapter for NLog</Title>
     <Description>Writes lightly-structured MySqlConnector logging output to NLog.</Description>
     <Copyright>Copyright 2018–2022 Bradley Grainger</Copyright>
diff --git a/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj b/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj
index 4129910f3..b828bb8c2 100644
--- a/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj
+++ b/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
+    <TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
     <Title>MySqlConnector Logging Adapter for Serilog</Title>
     <Description>Writes lightly-structured MySqlConnector logging output to Serilog.</Description>
     <Copyright>Copyright 2017–2022 Bradley Grainger</Copyright>
diff --git a/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj b/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj
index 31f39920f..f3874bef9 100644
--- a/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj
+++ b/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
+    <TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
     <Title>MySqlConnector Logging Adapter for log4net</Title>
     <Description>Writes MySqlConnector logging output to log4net with one line of code.</Description>
     <Copyright>Copyright 2017–2022 Bradley Grainger</Copyright>
diff --git a/src/MySqlConnector/CompatibilitySuppressions.xml b/src/MySqlConnector/CompatibilitySuppressions.xml
index b7bf516eb..1bb44ab01 100644
--- a/src/MySqlConnector/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector/CompatibilitySuppressions.xml
@@ -18,11 +18,24 @@
     <Left>lib/net461/MySqlConnector.dll</Left>
     <Right>lib/net471/MySqlConnector.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:System.Data.Common.DbColumn</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:System.Data.Common.DbColumn</Target>
+    <Left>lib/net462/MySqlConnector.dll</Left>
+    <Right>lib/net471/MySqlConnector.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0008</DiagnosticId>
     <Target>T:MySqlConnector.MySqlDataReader</Target>
     <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
-    <Right>lib/net461/MySqlConnector.dll</Right>
+    <Right>lib/net462/MySqlConnector.dll</Right>
   </Suppression>
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 9a6a92aa3..ea95530e6 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1597,7 +1597,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 				}
 				rsa.ImportParameters(rsaParameters);
 
-#if NET461 || NET471
+#if NET462 || NET471
 				var certificate = new X509Certificate2(sslCertificateFile, "", X509KeyStorageFlags.MachineKeySet)
 				{
 					PrivateKey = rsa,
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 2d1d37dd5..25a268d58 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net461;net471;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
+    <TargetFrameworks>net462;net471;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
     <Description>A truly async MySQL ADO.NET provider, supporting MySQL Server, MariaDB, Percona Server, Amazon Aurora, Azure Database for MySQL and more.</Description>
     <Copyright>Copyright 2016–2022 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
@@ -19,19 +19,19 @@
     <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.3" />
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' OR '$(TargetFramework)' == 'net471' ">
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' ">
     <Reference Include="System.Transactions" />
     <Using Remove="System.Net.Http" />
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'netstandard2.0' ">
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'netstandard2.0' ">
     <PackageReference Include="IndexRange" Version="1.0.2" PrivateAssets="All" />
     <PackageReference Include="System.Buffers" Version="4.5.1" />
     <PackageReference Include="System.Memory" Version="4.5.5" />
     <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'netstandard2.1' ">
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'netstandard2.1' ">
     <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="6.0.0" />
   </ItemGroup>
 
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index a196dbd5a..3dc1f9dd2 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -11,7 +11,7 @@ namespace MySqlConnector;
 
 #pragma warning disable CA1010 // Generic interface should also be implemented
 
-#if NET461
+#if NET462
 public sealed class MySqlDataReader : DbDataReader
 #else
 public sealed class MySqlDataReader : DbDataReader, IDbColumnSchemaGenerator
diff --git a/src/MySqlConnector/Shims/DbColumn.cs b/src/MySqlConnector/Shims/DbColumn.cs
index 7aa45e216..814d125bb 100644
--- a/src/MySqlConnector/Shims/DbColumn.cs
+++ b/src/MySqlConnector/Shims/DbColumn.cs
@@ -1,4 +1,4 @@
-#if NET461
+#if NET462
 #pragma warning disable CA1716 // Don't use reserved language keywords
 namespace System.Data.Common;
 
diff --git a/src/MySqlConnector/Utilities/NullableAttributes.cs b/src/MySqlConnector/Utilities/NullableAttributes.cs
index 833e9b0ec..39beefa27 100644
--- a/src/MySqlConnector/Utilities/NullableAttributes.cs
+++ b/src/MySqlConnector/Utilities/NullableAttributes.cs
@@ -1,7 +1,7 @@
 // Supports using nullable attributes on older frameworks.
 // Copied from https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
 
-#if NET461 || NET471 || NETSTANDARD2_0 || NETCOREAPP2_1
+#if NET462 || NET471 || NETSTANDARD2_0 || NETCOREAPP2_1
 namespace System.Diagnostics.CodeAnalysis;
 
 /// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index b15dd479b..6da68bcc7 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -518,7 +518,7 @@ public static void SwapBytes(byte[] bytes, int offset1, int offset2)
 		bytes[offset2] = swap;
 	}
 
-#if NET461
+#if NET462
 	public static bool IsWindows() => Environment.OSVersion.Platform == PlatformID.Win32NT;
 
 	public static void GetOSDetails(out string? os, out string osDescription, out string architecture)
@@ -557,13 +557,13 @@ public static void GetOSDetails(out string? os, out string osDescription, out st
 	}
 #endif
 
-#if NET461
+#if NET462
 	public static SslProtocols GetDefaultSslProtocols()
 	{
 		if (!s_defaultSslProtocols.HasValue)
 		{
 			// Prior to .NET Framework 4.7, SslProtocols.None is not a valid argument to SslStream.AuthenticateAsClientAsync.
-			// If the NET461 build is loaded by an application that targets .NET 4.7 (or later), or if app.config has set
+			// If the NET462 build is loaded by an application that targets .NET 4.7 (or later), or if app.config has set
 			// Switch.System.Net.DontEnableSystemDefaultTlsVersions to false, then SslProtocols.None will work; otherwise,
 			// if the application targets .NET 4.6.2 or earlier and hasn't changed the AppContext switch, then it will
 			// fail at runtime. We attempt to determine if it will fail by accessing the internal static

From 690c0685bd725ec002a72642ffe41e8994630a93 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 1 Jan 2023 12:10:02 -0800
Subject: [PATCH 027/393] Update System.Diagnostics.DiagnosticSource to 7.0.0.

Dropping netcoreapp3.1 and net461 allows us to use the latest version.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/MySqlConnector.csproj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 25a268d58..1c456bb79 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -32,7 +32,7 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'netstandard2.1' ">
-    <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="6.0.0" />
+    <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="7.0.0" />
   </ItemGroup>
 
   <ItemGroup>

From 14023fb2b398125e816fbe75af7066f490014e5e Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 1 Jan 2023 12:13:23 -0800
Subject: [PATCH 028/393] Update Microsoft.Extensions.Logging.Abstractions to
 7.0.0.

Dropping net461 allows the latest version to be used.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 ...MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj | 2 +-
 src/MySqlConnector/Logging/MySqlConnectorLogManager.cs        | 4 +++-
 src/MySqlConnector/MySqlConnector.csproj                      | 2 +-
 3 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
index 418c0971d..c0c4ea754 100644
--- a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
+++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
@@ -12,7 +12,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.3" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
index 226b2cb51..9c914425b 100644
--- a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
+++ b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
@@ -47,7 +47,9 @@ private sealed class MySqlConnectorLogger : ILogger
 		public MySqlConnectorLogger(IMySqlConnectorLogger logger) =>
 			m_logger = logger;
 
-		public IDisposable BeginScope<TState>(TState state) => throw new NotSupportedException();
+		public IDisposable BeginScope<TState>(TState state)
+			where TState : notnull
+			=> throw new NotSupportedException();
 
 		public bool IsEnabled(LogLevel logLevel) => m_logger.IsEnabled(ConvertLogLevel(logLevel));
 
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 1c456bb79..c80814b5d 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -16,7 +16,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.3" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
   </ItemGroup>
 
   <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' ">

From 2733cd258b8649d90346749967f08bf2d328f834 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 1 Jan 2023 13:34:50 -0800
Subject: [PATCH 029/393] Simplify condition.

See https://github.com/mysql-net/MySqlConnector/pull/1275#discussion_r1059794943.
---
 src/MySqlConnector/MySqlConnector.csproj | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index c80814b5d..6d07e262b 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -31,7 +31,7 @@
     <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == 'netstandard2.1' ">
+  <ItemGroup Condition=" '$(TargetFrameworkIdentifier)' != '.NETCoreApp' ">
     <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="7.0.0" />
   </ItemGroup>
 

From a9bddb651475bee20fa12cdb428e45c4b89b0f30 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 1 Jan 2023 15:47:13 -0800
Subject: [PATCH 030/393] Release 2.3.0 Beta 1.

---
 docs/content/overview/version-history.md | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 53e9a2bdb..6c859f8ed 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -10,6 +10,22 @@ weight: 30
 
 # Version History
 
+### 2.3.0 Beta 1
+
+* `MySqlDataSource` is now available for all TFMs, not just .NET 7.0: [#1269](https://github.com/mysql-net/MySqlConnector/pull/1269).
+  * This provides a single place to configure a MySQL connection and makes it easier to register `MySqlConnection` with dependency injection.
+  * Add `MySqlDataSourceBuilder` class to configure `MySqlDataSource` instances.
+* Microsoft.Extensions.Logging is now used as the core logging abstraction: [#1110](https://github.com/mysql-net/MySqlConnector/issues/1110).
+  * `MySqlConnectorLogManager.Provider` can still be used to add a logging destination, but it is now deprecated.
+  * Use `MySqlDataSourceBuilder.UseLoggerFactory` to configure logging.
+* Add new MySqlConnector.DependencyInjection package: [#1271](https://github.com/mysql-net/MySqlConnector/issues/1271).
+  * `MySqlDataSource` and `MySqlConnection` can be registered with dependency injection by using `builder.Services.AddMySqlDataSource(connectionString)`.
+  * This also configures logging automatically.
+* Drop support for .NET 4.6.1 and .NET Core 3.1: [#636](https://github.com/mysql-net/MySqlConnector/issues/636), [#1273](https://github.com/mysql-net/MySqlConnector/issues/1273).
+  * .NET 4.6.1 support ended on 26 April 2022, and .NET Core 3.1 on 13 December 2022.
+  * The minimum supported versions are now .NET Framework 4.6.2 and .NET 6.0, although other frameworks should be supported via `netstandard2.0`.
+* Fix bug that didn't copy `MySqlDataSource` in `MySqlConnection.Clone`: [#1267](https://github.com/mysql-net/MySqlConnector/issues/1267).
+
 ### 2.2.5
 
 * Add support for `DECIMAL UNSIGNED`, fixing error in `DeriveParameters`: [#1272](https://github.com/mysql-net/MySqlConnector/issues/1272).

From ba839f655c1bf0049bd409371e479a7298831ff8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 2 Jan 2023 09:34:10 -0800
Subject: [PATCH 031/393] Build the DI package with Appveyor.

---
 appveyor.yml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/appveyor.yml b/appveyor.yml
index 5720ee2a8..4248e6838 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -23,6 +23,7 @@ after_test:
 - cmd: |-
     dotnet pack src\MySqlConnector\MySqlConnector.csproj -c Release
     dotnet pack src\MySqlConnector.Authentication.Ed25519\MySqlConnector.Authentication.Ed25519.csproj -c Release
+    dotnet pack src\MySqlConnector.DependencyInjection\MySqlConnector.DependencyInjection.csproj -c Release
     dotnet pack src\MySqlConnector.Logging.log4net\MySqlConnector.Logging.log4net.csproj -c Release
     dotnet pack src\MySqlConnector.Logging.Microsoft.Extensions.Logging\MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj -c Release
     dotnet pack src\MySqlConnector.Logging.NLog\MySqlConnector.Logging.NLog.csproj -c Release

From 125cf06a667ddae9728473f2c7f72eb0a2733c16 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 2 Jan 2023 09:34:31 -0800
Subject: [PATCH 032/393] Add logging to the DI package README.

---
 src/MySqlConnector.DependencyInjection/docs/README.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/MySqlConnector.DependencyInjection/docs/README.md b/src/MySqlConnector.DependencyInjection/docs/README.md
index 45b9ffbf9..533064457 100644
--- a/src/MySqlConnector.DependencyInjection/docs/README.md
+++ b/src/MySqlConnector.DependencyInjection/docs/README.md
@@ -2,6 +2,7 @@
 
 MySqlConnector.DependencyInjection helps set up MySqlConnector in applications that use dependency injection, most notably in ASP.NET.
 It allows easy configuration of your MySQL connections and registers the appropriate services in your DI container.
+It also configures logging by integrating MySqlConnector with the `ILoggingFactory` registered with the service provider.
 
 ## How to Use
 

From a8164ae408da6ee4c79aa2458242c502ad9bb786 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 7 Jan 2023 17:30:54 -0800
Subject: [PATCH 033/393] Tweak wording on documentation site.

---
 docs/config.toml                  | 4 ++--
 docs/content/home.md              | 3 +--
 src/MySqlConnector/docs/README.md | 2 +-
 3 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/docs/config.toml b/docs/config.toml
index 4eccb5cc7..59db4a349 100644
--- a/docs/config.toml
+++ b/docs/config.toml
@@ -1,4 +1,4 @@
-title = "High-Performance Async MySQL Driver for .NET"
+title = "High-Performance .NET MySQL Driver"
 baseurl = "https://mysqlconnector.net/"
 MetaDataFormat = "yaml"
 pluralizeListTitles = false
@@ -15,7 +15,7 @@ disablePathToLower = true
       unsafe = true
 
 [params]
-  description = "Documentation for MySqlConnector: an Async MySQL Driver for .NET and .NET Core"
+  description = "Documentation for MySqlConnector: an Async MySQL Driver for .NET"
   author = "MySqlConnector Authors"
 
 [taxonomies]
diff --git a/docs/content/home.md b/docs/content/home.md
index 2f416c769..0151dcef8 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -18,8 +18,7 @@ menu:
 
 ## About
 
-MySqlConnector is an [ADO.NET](https://msdn.microsoft.com/en-us/library/e80y5yhx.aspx) data
-provider for [MySQL Server](https://www.mysql.com/), [MariaDB](https://mariadb.org/),
+MySqlConnector is a C# [ADO.NET](https://msdn.microsoft.com/en-us/library/e80y5yhx.aspx) driver for [MySQL Server](https://www.mysql.com/), [MariaDB](https://mariadb.org/),
 [Percona Server](https://www.percona.com/software/mysql-database/percona-server), [Amazon Aurora](https://aws.amazon.com/rds/aurora/),
 [Azure Database for MySQL](https://azure.microsoft.com/en-us/services/mysql/),
 [Google Cloud SQL for MySQL](https://cloud.google.com/sql/docs/mysql/) and more. It provides implementations of
diff --git a/src/MySqlConnector/docs/README.md b/src/MySqlConnector/docs/README.md
index 959bbcb90..6f11353c3 100644
--- a/src/MySqlConnector/docs/README.md
+++ b/src/MySqlConnector/docs/README.md
@@ -1,6 +1,6 @@
 ## About
 
-MySqlConnector is an [ADO.NET](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/) data provider for [MySQL](https://www.mysql.com/), [MariaDB](https://mariadb.org/), [Amazon Aurora](https://aws.amazon.com/rds/aurora/), [Azure Database for MySQL](https://azure.microsoft.com/en-us/services/mysql/) and other MySQL-compatible databases.
+MySqlConnector is a C# [ADO.NET](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/) driver for [MySQL](https://www.mysql.com/), [MariaDB](https://mariadb.org/), [Amazon Aurora](https://aws.amazon.com/rds/aurora/), [Azure Database for MySQL](https://azure.microsoft.com/en-us/services/mysql/) and other MySQL-compatible databases.
 
 More documentation is available at the [MySqlConnector website](https://mysqlconnector.net/).
 

From 6d5189dd70ad40630993990c1d5678de829eb3cc Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 6 Jan 2023 11:18:23 -0800
Subject: [PATCH 034/393] Recycle MySqlDataReader objects. Fixes #1277

---
 src/MySqlConnector/Core/CommandExecutor.cs | 10 ++-
 src/MySqlConnector/Core/ServerSession.cs   |  2 +
 src/MySqlConnector/MySqlDataReader.cs      | 96 +++++++++++++---------
 3 files changed, 64 insertions(+), 44 deletions(-)

diff --git a/src/MySqlConnector/Core/CommandExecutor.cs b/src/MySqlConnector/Core/CommandExecutor.cs
index 3c20a63f9..752329a4b 100644
--- a/src/MySqlConnector/Core/CommandExecutor.cs
+++ b/src/MySqlConnector/Core/CommandExecutor.cs
@@ -47,16 +47,18 @@ public static async Task<MySqlDataReader> ExecuteReaderAsync(IReadOnlyList<IMySq
 			cancellationToken.ThrowIfCancellationRequested();
 
 			using var payload = writer.ToPayloadData();
-			connection.Session.StartQuerying(command.CancellableCommand);
+			var session = connection.Session;
+			session.StartQuerying(command.CancellableCommand);
 			command.SetLastInsertedId(0);
 			try
 			{
-				await connection.Session.SendAsync(payload, ioBehavior, CancellationToken.None).ConfigureAwait(false);
-				return await MySqlDataReader.CreateAsync(commandListPosition, payloadCreator, cachedProcedures, command, behavior, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+				await session.SendAsync(payload, ioBehavior, CancellationToken.None).ConfigureAwait(false);
+				await session.DataReader.InitAsync(commandListPosition, payloadCreator, cachedProcedures, command, behavior, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+				return session.DataReader;
 			}
 			catch (MySqlException ex) when (ex.ErrorCode == MySqlErrorCode.QueryInterrupted && cancellationToken.IsCancellationRequested)
 			{
-				Log.QueryWasInterrupted(command.Logger, connection.Session.Id);
+				Log.QueryWasInterrupted(command.Logger, session.Id);
 				throw new OperationCanceledException(ex.Message, ex, cancellationToken);
 			}
 			catch (Exception ex) when (payload.Span.Length > 4_194_304 && (ex is SocketException or IOException or MySqlProtocolException))
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index ea95530e6..f9a7e5335 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -42,6 +42,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 		PoolGeneration = poolGeneration;
 		HostName = "";
 		m_activityTags = new ActivityTagsCollection();
+		DataReader = new();
 		Log.CreatedNewSession(m_logger, Id);
 	}
 
@@ -65,6 +66,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public bool SupportsSessionTrack => m_supportsSessionTrack;
 	public bool ProcAccessDenied { get; set; }
 	public ICollection<KeyValuePair<string, object?>> ActivityTags => m_activityTags;
+	public MySqlDataReader DataReader { get; }
 
 	public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? owningConnection)
 	{
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 3dc1f9dd2..2912b4c74 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -27,7 +27,7 @@ public override bool Read()
 	{
 		VerifyNotDisposed();
 		Command!.CancellableCommand.ResetCommandTimeout();
-		return m_resultSet!.Read();
+		return m_resultSet.Read();
 	}
 
 	public override async Task<bool> ReadAsync(CancellationToken cancellationToken)
@@ -35,11 +35,11 @@ public override async Task<bool> ReadAsync(CancellationToken cancellationToken)
 		VerifyNotDisposed();
 		Command!.CancellableCommand.ResetCommandTimeout();
 		using var registration = Command.CancellableCommand.RegisterCancel(cancellationToken);
-		return await m_resultSet!.ReadAsync(cancellationToken).ConfigureAwait(false);
+		return await m_resultSet.ReadAsync(cancellationToken).ConfigureAwait(false);
 	}
 
 	internal Task<bool> ReadAsync(IOBehavior ioBehavior, CancellationToken cancellationToken) =>
-		m_resultSet!.ReadAsync(ioBehavior, cancellationToken);
+		m_resultSet.ReadAsync(ioBehavior, cancellationToken);
 
 	public override async Task<bool> NextResultAsync(CancellationToken cancellationToken)
 	{
@@ -58,7 +58,7 @@ internal async Task<bool> NextResultAsync(IOBehavior ioBehavior, CancellationTok
 			{
 				while (true)
 				{
-					await m_resultSet!.ReadEntireAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+					await m_resultSet.ReadEntireAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 					await ScanResultSetAsync(ioBehavior, m_resultSet, cancellationToken).ConfigureAwait(false);
 					if (m_hasMoreResults && m_resultSet.ContainsCommandParameters)
 						await ReadOutParametersAsync(Command!, m_resultSet, ioBehavior, cancellationToken).ConfigureAwait(false);
@@ -74,7 +74,7 @@ internal async Task<bool> NextResultAsync(IOBehavior ioBehavior, CancellationTok
 						using (Command.CancellableCommand.RegisterCancel(cancellationToken))
 						{
 							var writer = new ByteBufferWriter();
-							if (!Command.Connection!.Session.IsCancelingQuery && m_payloadCreator.WriteQueryCommand(ref m_commandListPosition, m_cachedProcedures!, writer, false))
+							if (!Command.Connection!.Session.IsCancelingQuery && m_payloadCreator!.WriteQueryCommand(ref m_commandListPosition, m_cachedProcedures!, writer, false))
 							{
 								using var payload = writer.ToPayloadData();
 								await Command.Connection.Session.SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
@@ -99,7 +99,7 @@ internal async Task<bool> NextResultAsync(IOBehavior ioBehavior, CancellationTok
 		}
 		catch (MySqlException)
 		{
-			m_resultSet!.Reset();
+			m_resultSet.Reset();
 			m_hasMoreResults = false;
 			m_schemaTable = null;
 			throw;
@@ -108,7 +108,7 @@ internal async Task<bool> NextResultAsync(IOBehavior ioBehavior, CancellationTok
 
 	private void ActivateResultSet(CancellationToken cancellationToken)
 	{
-		if (m_resultSet!.ReadResultSetHeaderException is not null)
+		if (m_resultSet.ReadResultSetHeaderException is not null)
 		{
 			var mySqlException = m_resultSet.ReadResultSetHeaderException.SourceException as MySqlException;
 
@@ -350,8 +350,8 @@ public override long GetChars(int ordinal, long dataOffset, char[]? buffer, int
 	/// <returns>A <see cref="System.Collections.ObjectModel.ReadOnlyCollection{DbColumn}"/> containing metadata about the result set.</returns>
 	public ReadOnlyCollection<DbColumn> GetColumnSchema()
 	{
-		var columnDefinitions = m_resultSet?.ColumnDefinitions;
-		var hasNoSchema = columnDefinitions is null || m_resultSet!.ContainsCommandParameters;
+		var columnDefinitions = m_resultSet.ColumnDefinitions;
+		var hasNoSchema = columnDefinitions is null || m_resultSet.ContainsCommandParameters;
 		return hasNoSchema ? new List<DbColumn>().AsReadOnly() :
 			columnDefinitions!
 				.Select((c, n) => (DbColumn) new MySqlDbColumn(n, c, Connection!.AllowZeroDateTime, GetResultSet().ColumnTypes![n]))
@@ -451,30 +451,49 @@ protected override void Dispose(bool disposing)
 	public Task DisposeAsync() => DisposeAsync(Connection?.AsyncIOBehavior ?? IOBehavior.Asynchronous, CancellationToken.None);
 #endif
 
-	internal Activity? Activity { get; }
+	internal Activity? Activity { get; private set; }
 	internal IMySqlCommand? Command { get; private set; }
 	internal MySqlConnection? Connection => Command?.Connection;
 	internal ulong? RealRecordsAffected { get; set; }
 	internal ServerSession? Session => Command?.Connection!.Session;
 
-	internal static async Task<MySqlDataReader> CreateAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary<string, CachedProcedure?>? cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	internal async Task InitAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary<string, CachedProcedure?>? cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		var dataReader = new MySqlDataReader(commandListPosition, payloadCreator, cachedProcedures, command, behavior, activity);
-		command.Connection!.SetActiveReader(dataReader);
+		// reset fields from last use of this MySqlDataReader
+		if (m_hasMoreResults)
+			throw new InvalidOperationException("Expected m_hasMoreResults to be false");
+		if (m_resultSet.BufferState != ResultSetState.None || m_resultSet.State != ResultSetState.None)
+			throw new InvalidOperationException("Expected BufferState and State to be ResultSetState.None.");
+		if (m_resultSet.ColumnDefinitions is not null)
+			throw new InvalidOperationException("Expected ColumnDefinitions to be null");
+		m_closed = false;
+		m_hasWarnings = false;
+		m_initializationFailed = false;
+		RealRecordsAffected = null;
+
+		// initialize for new command
+		m_commandListPosition = commandListPosition;
+		m_payloadCreator = payloadCreator;
+		m_cachedProcedures = cachedProcedures;
+		Command = command;
+		m_behavior = behavior;
+		Activity = activity;
+
+		command.Connection!.SetActiveReader(this);
 
 		try
 		{
-			await dataReader.m_resultSet!.ReadResultSetHeaderAsync(ioBehavior).ConfigureAwait(false);
-			dataReader.ActivateResultSet(cancellationToken);
-			dataReader.m_hasMoreResults = true;
+			await m_resultSet.ReadResultSetHeaderAsync(ioBehavior).ConfigureAwait(false);
+			ActivateResultSet(cancellationToken);
+			m_hasMoreResults = true;
 
-			if (dataReader.m_resultSet.ContainsCommandParameters)
-				await ReadOutParametersAsync(dataReader.Command!, dataReader.m_resultSet, ioBehavior, cancellationToken).ConfigureAwait(false);
+			if (m_resultSet.ContainsCommandParameters)
+				await ReadOutParametersAsync(command, m_resultSet, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			// if the command list has multiple commands, keep reading until a result set is found
-			while (dataReader.m_resultSet.State == ResultSetState.NoMoreData && commandListPosition.CommandIndex < commandListPosition.Commands.Count)
+			while (m_resultSet.State == ResultSetState.NoMoreData && commandListPosition.CommandIndex < commandListPosition.Commands.Count)
 			{
-				await dataReader.NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+				await NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 			}
 		}
 		catch (Exception ex)
@@ -484,18 +503,16 @@ internal static async Task<MySqlDataReader> CreateAsync(CommandListPosition comm
 				activity.SetException(ex);
 				activity.Stop();
 			}
-			dataReader.m_creationFailed = true;
-			dataReader.Dispose();
+			m_initializationFailed = true;
+			Dispose();
 			throw;
 		}
-
-		return dataReader;
 	}
 
 	internal DataTable? BuildSchemaTable()
 	{
-		var columnDefinitions = m_resultSet?.ColumnDefinitions;
-		if (columnDefinitions is null || m_resultSet!.ContainsCommandParameters)
+		var columnDefinitions = m_resultSet.ColumnDefinitions;
+		if (columnDefinitions is null || m_resultSet.ContainsCommandParameters)
 			return null;
 
 		var schemaTable = new DataTable("SchemaTable") { Locale = CultureInfo.InvariantCulture };
@@ -583,15 +600,9 @@ internal static async Task<MySqlDataReader> CreateAsync(CommandListPosition comm
 		return schemaTable;
 	}
 
-	private MySqlDataReader(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary<string, CachedProcedure?>? cachedProcedures, IMySqlCommand command, CommandBehavior behavior, Activity? activity)
+	internal MySqlDataReader()
 	{
-		m_commandListPosition = commandListPosition;
-		m_payloadCreator = payloadCreator;
-		m_cachedProcedures = cachedProcedures;
-		Command = command;
-		m_behavior = behavior;
 		m_resultSet = new(this);
-		Activity = activity;
 	}
 
 #if NETCOREAPP3_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
@@ -619,7 +630,6 @@ internal async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancel
 					if (ex.ErrorCode != MySqlErrorCode.QueryInterrupted)
 						Log.IgnoringExceptionInDisposeAsync(Command.Logger, ex, Command.Connection.Session.Id, ex.Message, Command.CommandText!);
 				}
-				m_resultSet = null;
 			}
 
 			m_hasMoreResults = false;
@@ -628,13 +638,19 @@ internal async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancel
 			Command.CancellableCommand.SetTimeout(Constants.InfiniteTimeout);
 			connection.FinishQuerying(m_hasWarnings);
 
-			if (!m_creationFailed)
+			if (!m_initializationFailed)
 				Activity?.SetSuccess();
 			Activity?.Stop();
+			Activity = null;
 
 			if ((m_behavior & CommandBehavior.CloseConnection) != 0)
 				await connection.CloseAsync(ioBehavior).ConfigureAwait(false);
+
+			// clear fields (so that MySqlConnection can be GCed if the user doesn't hold a reference to it)
 			Command = null;
+			m_commandListPosition = default;
+			m_payloadCreator = null;
+			m_cachedProcedures = null;
 		}
 	}
 
@@ -683,14 +699,14 @@ private ResultSet GetResultSet()
 		return m_resultSet;
 	}
 
-	private readonly CommandBehavior m_behavior;
-	private readonly ICommandPayloadCreator m_payloadCreator;
-	private readonly IDictionary<string, CachedProcedure?>? m_cachedProcedures;
+	private readonly ResultSet m_resultSet;
+	private CommandBehavior m_behavior;
+	private ICommandPayloadCreator? m_payloadCreator;
+	private IDictionary<string, CachedProcedure?>? m_cachedProcedures;
 	private CommandListPosition m_commandListPosition;
 	private bool m_closed;
 	private bool m_hasWarnings;
 	private bool m_hasMoreResults;
-	private bool m_creationFailed;
-	private ResultSet? m_resultSet;
+	private bool m_initializationFailed;
 	private DataTable? m_schemaTable;
 }

From 09ac94912fc9e1c1fcaf11c1a718a918bb105e07 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 18 Jan 2023 09:26:30 -0800
Subject: [PATCH 035/393] Update MySql.Data to 8.0.32.

---
 azure-pipelines.yml                           |  4 +-
 .../tutorials/migrating-from-connector-net.md | 10 +++--
 tests/Benchmark/Benchmark.csproj              |  2 +-
 tests/IntegrationTests/BulkLoaderAsync.cs     | 36 +++++++++++----
 tests/IntegrationTests/BulkLoaderSync.cs      | 45 ++++++++++++-------
 tests/IntegrationTests/CommandTests.cs        |  4 +-
 tests/IntegrationTests/ConnectSync.cs         |  8 ++--
 tests/IntegrationTests/InsertTests.cs         |  4 +-
 .../IntegrationTests/IntegrationTests.csproj  |  7 +--
 tests/IntegrationTests/ParameterCollection.cs |  4 +-
 tests/IntegrationTests/ParameterTests.cs      | 14 +++---
 tests/IntegrationTests/QueryTests.cs          | 16 +++----
 tests/IntegrationTests/app.config             |  2 +-
 .../MySqlConnector.Tests.csproj               |  3 +-
 14 files changed, 100 insertions(+), 59 deletions(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 842650b5d..38de95847 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -41,9 +41,9 @@ jobs:
       contents: 'config.json'
       targetFolder: 'tests/IntegrationTests'
   - task: UseDotNet@2
-    displayName: 'Install .NET Core 6.0'
+    displayName: 'Install .NET Core 7.0'
     inputs:
-      version: '6.0.x'
+      version: '7.0.x'
   - task: UseDotNet@2
     displayName: 'Install .NET Core'
     inputs:
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 188cfedcd..af2d58e54 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -196,10 +196,11 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * [#72494](https://bugs.mysql.com/bug.php?id=72494), [#83330](https://bugs.mysql.com/bug.php?id=83330): EndOfStreamException inserting large blob with UseCompression=True
 * [#73610](https://bugs.mysql.com/bug.php?id=73610): Invalid password exception has wrong number
 * [#73788](https://bugs.mysql.com/bug.php?id=73788): Can’t use `DateTimeOffset`
+* ~~[#74392](https://bugs.mysql.com/bug.php?id=74392): Can't use `Stream` to bulk load data~~
 * ~~[#75604](https://bugs.mysql.com/bug.php?id=75604): Crash after 29.4 days of uptime~~
 * [#75917](https://bugs.mysql.com/bug.php?id=75917), [#76597](https://bugs.mysql.com/bug.php?id=76597), [#77691](https://bugs.mysql.com/bug.php?id=77691), [#78650](https://bugs.mysql.com/bug.php?id=78650), [#78919](https://bugs.mysql.com/bug.php?id=78919), [#80921](https://bugs.mysql.com/bug.php?id=80921), [#82136](https://bugs.mysql.com/bug.php?id=82136): “Reading from the stream has failed” when connecting to a server
 * [#77421](https://bugs.mysql.com/bug.php?id=77421): Connection is not reset when pulled from the connection pool
-* [#78426](https://bugs.mysql.com/bug.php?id=78426): Unknown database exception has wrong number
+* ~~[#78426](https://bugs.mysql.com/bug.php?id=78426): Unknown database exception has wrong number~~
 * ~~[#78760](https://bugs.mysql.com/bug.php?id=78760): Error when using tabs and newlines in SQL statements~~
 * ~~[#78917](https://bugs.mysql.com/bug.php?id=78917), [#79196](https://bugs.mysql.com/bug.php?id=79196), [#82292](https://bugs.mysql.com/bug.php?id=82292), [#89040](https://bugs.mysql.com/bug.php?id=89040): `TINYINT(1)` values start being returned as `sbyte` after `NULL`~~
 * ~~[#80030](https://bugs.mysql.com/bug.php?id=80030): Slow to connect with pooling disabled~~
@@ -233,7 +234,7 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * ~~[#91770](https://bugs.mysql.com/bug.php?id=91770): `TIME(n)` column loses microseconds with prepared command~~
 * [#92367](https://bugs.mysql.com/bug.php?id=92367): `MySqlDataReader.GetDateTime` and `GetValue` return inconsistent values
 * [#92465](https://bugs.mysql.com/bug.php?id=92465): “There is already an open DataReader” `MySqlException` thrown from `TransactionScope.Dispose`
-* [#92734](https://bugs.mysql.com/bug.php?id=92734): `MySqlParameter.Clone` doesn't copy all property values
+* ~~[#92734](https://bugs.mysql.com/bug.php?id=92734): `MySqlParameter.Clone` doesn't copy all property values~~
 * ~~[#92789](https://bugs.mysql.com/bug.php?id=92789): Illegal connection attributes written for non-ASCII values~~
 * ~~[#92912](https://bugs.mysql.com/bug.php?id=92912): `MySqlDbType.LongText` values encoded incorrectly with prepared statements~~
 * ~~[#92982](https://bugs.mysql.com/bug.php?id=92982), [#93399](https://bugs.mysql.com/bug.php?id=93399): `FormatException` thrown when connecting to MySQL Server 8.0.13~~
@@ -274,7 +275,7 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * ~~[#101253](https://bugs.mysql.com/bug.php?id=101253): Default value for `MySqlParameter.Value` changed from null to `0`~~
 * ~~[#101302](https://bugs.mysql.com/bug.php?id=101302): Stored Procedure `BOOL` parameter can only be mapped to `MySqlDbType.Byte`~~
 * ~~[#101485](https://bugs.mysql.com/bug.php?id=101485): Stored Procedure `JSON` parameter throws “Unhandled type encountered” `MySqlException`~~
-* [#101507](https://bugs.mysql.com/bug.php?id=101507): `MySqlCommand.Cancel` throws `NullReferenceException` for a closed connection
+* ~~[#101507](https://bugs.mysql.com/bug.php?id=101507): `MySqlCommand.Cancel` throws `NullReferenceException` for a closed connection~~
 * ~~[#101714](https://bugs.mysql.com/bug.php?id=101714): Extremely slow performance reading result sets~~
 * ~~[#102303](https://bugs.mysql.com/bug.php?id=102303): Preparing command with incorrect parameter type puts connection in bad state~~
 * ~~[#102593](https://bugs.mysql.com/bug.php?id=102593): Can't use `MemoryStream` as `MySqlParameter.Value`~~
@@ -299,3 +300,6 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * [#109331](https://bugs.mysql.com/bug.php?id=109331): MySQL Connector/NET is incompatible with MariaDB 10.10 and later
 * [#109390](https://bugs.mysql.com/bug.php?id=109390): Transaction lock held after connection timeout exception
 * [#109476](https://bugs.mysql.com/bug.php?id=109476): `TransactionScope.Dispose` throws "Connection must be valid and open to rollback"
+* [#109670](https://bugs.mysql.com/bug.php?id=109670): Many MSB3246 errors when referencing MySql.Data 8.0.32
+* [#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.
+* [#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.
diff --git a/tests/Benchmark/Benchmark.csproj b/tests/Benchmark/Benchmark.csproj
index 3ddca3f04..596970480 100644
--- a/tests/Benchmark/Benchmark.csproj
+++ b/tests/Benchmark/Benchmark.csproj
@@ -7,6 +7,6 @@
   <ItemGroup>
     <PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
     <PackageReference Include="MySqlConnector" Version="2.2.0-rc.1" />
-    <PackageReference Include="MySql.Data" Version="8.0.31" />
+    <PackageReference Include="MySql.Data" Version="8.0.32" />
   </ItemGroup>
 </Project>
diff --git a/tests/IntegrationTests/BulkLoaderAsync.cs b/tests/IntegrationTests/BulkLoaderAsync.cs
index 104e1f3df..04d4e7ec5 100644
--- a/tests/IntegrationTests/BulkLoaderAsync.cs
+++ b/tests/IntegrationTests/BulkLoaderAsync.cs
@@ -304,14 +304,15 @@ await Assert.ThrowsAsync<MySqlException>(async () =>
 #endif
 	}
 
-#if !MYSQL_DATA
 	[SkippableFact(ConfigSettings.LocalCsvFile)]
 	public async Task BulkLoadFileStreamInvalidOperation()
 	{
 		using var connection = new MySqlConnection(GetConnectionString());
 		var bl = new MySqlBulkLoader(connection);
 		using var fileStream = new FileStream(AppConfig.MySqlBulkLoaderLocalCsvFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
+#if !MYSQL_DATA
 		bl.SourceStream = fileStream;
+#endif
 		bl.TableName = m_testTable;
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
@@ -320,10 +321,11 @@ public async Task BulkLoadFileStreamInvalidOperation()
 		bl.FieldQuotationOptional = true;
 		bl.Expressions.Add("five = UNHEX(five)");
 		bl.Local = false;
-		await Assert.ThrowsAsync<System.InvalidOperationException>(async () =>
-		{
-			int rowCount = await bl.LoadAsync();
-		});
+#if !MYSQL_DATA
+		await Assert.ThrowsAsync<InvalidOperationException>(async () => { var rowCount = await bl.LoadAsync(); });
+#else
+		await Assert.ThrowsAsync<MySqlException>(async () => { var rowCount = await bl.LoadAsync(fileStream); });
+#endif
 	}
 
 	[SkippableFact(ConfigSettings.LocalCsvFile)]
@@ -333,7 +335,9 @@ public async Task BulkLoadLocalFileStream()
 		await connection.OpenAsync();
 		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
 		using var fileStream = new FileStream(AppConfig.MySqlBulkLoaderLocalCsvFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
+#if !MYSQL_DATA
 		bl.SourceStream = fileStream;
+#endif
 		bl.TableName = m_testTable;
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
@@ -342,7 +346,11 @@ public async Task BulkLoadLocalFileStream()
 		bl.FieldQuotationOptional = true;
 		bl.Expressions.Add("five = UNHEX(five)");
 		bl.Local = true;
+#if !MYSQL_DATA
 		int rowCount = await bl.LoadAsync();
+#else
+		int rowCount = await bl.LoadAsync(fileStream);
+#endif
 		Assert.Equal(20, rowCount);
 	}
 
@@ -353,7 +361,9 @@ public async Task BulkLoadMemoryStreamInvalidOperation()
 		await connection.OpenAsync();
 		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
 		using var memoryStream = new MemoryStream(m_memoryStreamBytes, false);
+#if !MYSQL_DATA
 		bl.SourceStream = memoryStream;
+#endif
 		bl.TableName = m_testTable;
 		bl.Columns.AddRange(new string[] { "one", "two", "three" });
 		bl.NumberOfLinesToSkip = 0;
@@ -361,10 +371,11 @@ public async Task BulkLoadMemoryStreamInvalidOperation()
 		bl.FieldQuotationCharacter = '"';
 		bl.FieldQuotationOptional = true;
 		bl.Local = false;
-		await Assert.ThrowsAsync<System.InvalidOperationException>(async () =>
-		{
-			int rowCount = await bl.LoadAsync();
-		});
+#if !MYSQL_DATA
+		await Assert.ThrowsAsync<InvalidOperationException>(async () => { var rowCount = await bl.LoadAsync(); });
+#else
+		await Assert.ThrowsAsync<MySqlException>(async () => { var rowCount = await bl.LoadAsync(memoryStream); });
+#endif
 	}
 
 	[Fact]
@@ -374,7 +385,9 @@ public async Task BulkLoadLocalMemoryStream()
 		await connection.OpenAsync();
 		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
 		using var memoryStream = new MemoryStream(m_memoryStreamBytes, false);
+#if !MYSQL_DATA
 		bl.SourceStream = memoryStream;
+#endif
 		bl.TableName = m_testTable;
 		bl.Columns.AddRange(new string[] { "one", "two", "three" });
 		bl.NumberOfLinesToSkip = 0;
@@ -382,10 +395,15 @@ public async Task BulkLoadLocalMemoryStream()
 		bl.FieldQuotationCharacter = '"';
 		bl.FieldQuotationOptional = true;
 		bl.Local = true;
+#if !MYSQL_DATA
 		int rowCount = await bl.LoadAsync();
+#else
+		int rowCount = await bl.LoadAsync(memoryStream);
+#endif
 		Assert.Equal(5, rowCount);
 	}
 
+#if !MYSQL_DATA
 	[Fact]
 	public async Task BulkCopyDataReader()
 	{
diff --git a/tests/IntegrationTests/BulkLoaderSync.cs b/tests/IntegrationTests/BulkLoaderSync.cs
index 4dc65ca38..c141eb688 100644
--- a/tests/IntegrationTests/BulkLoaderSync.cs
+++ b/tests/IntegrationTests/BulkLoaderSync.cs
@@ -345,15 +345,9 @@ public void BulkLoadMissingFileName()
 		bl.Expressions.Add("five = UNHEX(five)");
 		bl.Local = false;
 #if MYSQL_DATA
-		Assert.Throws<System.NullReferenceException>(() =>
-		{
-			int rowCount = bl.Load();
-		});
+		Assert.Throws<MySqlException>(() => { var rowCount = bl.Load(); });
 #else
-		Assert.Throws<System.InvalidOperationException>(() =>
-		{
-			int rowCount = bl.Load();
-		});
+		Assert.Throws<InvalidOperationException>(() => { var rowCount = bl.Load(); });
 #endif
 	}
 
@@ -384,7 +378,6 @@ public void BulkLoadMissingTableName()
 #endif
 	}
 
-#if !MYSQL_DATA
 	[SkippableFact(ConfigSettings.LocalCsvFile)]
 	public void BulkLoadFileStreamInvalidOperation()
 	{
@@ -392,7 +385,9 @@ public void BulkLoadFileStreamInvalidOperation()
 		connection.Open();
 		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
 		using var fileStream = new FileStream(AppConfig.MySqlBulkLoaderLocalCsvFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
+#if !MYSQL_DATA
 		bl.SourceStream = fileStream;
+#endif
 		bl.TableName = m_testTable;
 		bl.CharacterSet = "UTF8";
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
@@ -402,10 +397,11 @@ public void BulkLoadFileStreamInvalidOperation()
 		bl.FieldQuotationOptional = true;
 		bl.Expressions.Add("five = UNHEX(five)");
 		bl.Local = false;
-		Assert.Throws<System.InvalidOperationException>(() =>
-		{
-			int rowCount = bl.Load();
-		});
+#if !MYSQL_DATA
+		Assert.Throws<InvalidOperationException>(() => { int rowCount = bl.Load(); });
+#else
+		Assert.Throws<MySqlException>(() => { int rowCount = bl.Load(fileStream); });
+#endif
 	}
 
 	[SkippableFact(ConfigSettings.LocalCsvFile)]
@@ -415,7 +411,9 @@ public void BulkLoadLocalFileStream()
 		connection.Open();
 		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
 		using var fileStream = new FileStream(AppConfig.MySqlBulkLoaderLocalCsvFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, true);
+#if !MYSQL_DATA
 		bl.SourceStream = fileStream;
+#endif
 		bl.TableName = m_testTable;
 		bl.CharacterSet = "UTF8";
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
@@ -425,7 +423,11 @@ public void BulkLoadLocalFileStream()
 		bl.FieldQuotationOptional = true;
 		bl.Expressions.Add("five = UNHEX(five)");
 		bl.Local = true;
+#if !MYSQL_DATA
 		int rowCount = bl.Load();
+#else
+		int rowCount = bl.Load(fileStream);
+#endif
 		Assert.Equal(20, rowCount);
 	}
 
@@ -436,7 +438,9 @@ public void BulkLoadMemoryStreamInvalidOperation()
 		connection.Open();
 		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
 		using var memoryStream = new MemoryStream(m_memoryStreamBytes, false);
+#if !MYSQL_DATA
 		bl.SourceStream = memoryStream;
+#endif
 		bl.TableName = m_testTable;
 		bl.CharacterSet = "UTF8";
 		bl.Columns.AddRange(new string[] { "one", "two", "three" });
@@ -445,7 +449,11 @@ public void BulkLoadMemoryStreamInvalidOperation()
 		bl.FieldQuotationCharacter = '"';
 		bl.FieldQuotationOptional = true;
 		bl.Local = false;
-		Assert.Throws<System.InvalidOperationException>(() => bl.Load());
+#if !MYSQL_DATA
+		Assert.Throws<InvalidOperationException>(() => bl.Load());
+#else
+		Assert.Throws<MySqlException>(() => bl.Load(memoryStream));
+#endif
 	}
 
 	[Fact]
@@ -455,7 +463,9 @@ public void BulkLoadLocalMemoryStream()
 		connection.Open();
 		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
 		using var memoryStream = new MemoryStream(m_memoryStreamBytes, false);
+#if !MYSQL_DATA
 		bl.SourceStream = memoryStream;
+#endif
 		bl.TableName = m_testTable;
 		bl.CharacterSet = "UTF8";
 		bl.Columns.AddRange(new string[] { "one", "two", "three" });
@@ -464,10 +474,15 @@ public void BulkLoadLocalMemoryStream()
 		bl.FieldQuotationCharacter = '"';
 		bl.FieldQuotationOptional = true;
 		bl.Local = true;
+#if !MYSQL_DATA
 		int rowCount = bl.Load();
+#else
+		int rowCount = bl.Load(memoryStream);
+#endif
 		Assert.Equal(5, rowCount);
 	}
 
+#if !MYSQL_DATA
 	[Fact]
 	public void BulkCopyDataReader()
 	{
@@ -1213,7 +1228,7 @@ public void BulkCopyDataTableConflictOption(MySqlBulkLoaderConflictOption confli
 	}
 #endif
 
-	internal static string GetConnectionString() => AppConfig.ConnectionString;
+		internal static string GetConnectionString() => AppConfig.ConnectionString;
 
 	internal static string GetLocalConnectionString()
 	{
diff --git a/tests/IntegrationTests/CommandTests.cs b/tests/IntegrationTests/CommandTests.cs
index 1414042db..65d605e27 100644
--- a/tests/IntegrationTests/CommandTests.cs
+++ b/tests/IntegrationTests/CommandTests.cs
@@ -295,7 +295,7 @@ public void CancelEmptyCommandIsNoop()
 		cmd.Cancel();
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=101507")]
+	[Fact]
 	public void CancelCommandForClosedConnectionIsNoop()
 	{
 		using var connection = new MySqlConnection(AppConfig.ConnectionString);
@@ -305,7 +305,7 @@ public void CancelCommandForClosedConnectionIsNoop()
 		cmd.Cancel();
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=101507")]
+	[Fact]
 	public void CancelCommandForDisposedConnectionIsNoop()
 	{
 		using var connection = new MySqlConnection(AppConfig.ConnectionString);
diff --git a/tests/IntegrationTests/ConnectSync.cs b/tests/IntegrationTests/ConnectSync.cs
index 5b09ef908..bed47320a 100644
--- a/tests/IntegrationTests/ConnectSync.cs
+++ b/tests/IntegrationTests/ConnectSync.cs
@@ -60,10 +60,8 @@ public void ConnectBadDatabase()
 		csb.Database = "wrong_database";
 		using var connection = new MySqlConnection(csb.ConnectionString);
 		var ex = Assert.Throws<MySqlException>(connection.Open);
-#if !MYSQL_DATA // https://bugs.mysql.com/bug.php?id=78426
-		if (AppConfig.SupportedFeatures.HasFlag(ServerFeatures.ErrorCodes) || ex.ErrorCode != default)
-			Assert.Equal(MySqlErrorCode.UnknownDatabase, ex.ErrorCode);
-#endif
+		if (AppConfig.SupportedFeatures.HasFlag(ServerFeatures.ErrorCodes) || ex.Number != 0)
+			Assert.Equal((int) MySqlErrorCode.UnknownDatabase, ex.Number);
 		Assert.Equal(ConnectionState.Closed, connection.State);
 	}
 
@@ -74,7 +72,7 @@ public void ConnectBadPassword()
 		csb.Password = "wrong";
 		using var connection = new MySqlConnection(csb.ConnectionString);
 		var ex = Assert.Throws<MySqlException>(connection.Open);
-#if !MYSQL_DATA // https://bugs.mysql.com/bug.php?id=78426
+#if !MYSQL_DATA
 		if (AppConfig.SupportedFeatures.HasFlag(ServerFeatures.ErrorCodes) || ex.ErrorCode != default)
 			Assert.Equal(MySqlErrorCode.AccessDenied, ex.ErrorCode);
 #endif
diff --git a/tests/IntegrationTests/InsertTests.cs b/tests/IntegrationTests/InsertTests.cs
index 6bcfec5de..f90912c7f 100644
--- a/tests/IntegrationTests/InsertTests.cs
+++ b/tests/IntegrationTests/InsertTests.cs
@@ -113,7 +113,7 @@ public async Task LastInsertedIdBeforeSelect()
 		}
 	}
 
-	[Fact]
+	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=109683")]
 	public async Task LastInsertedIdTwoInserts()
 	{
 		await m_database.Connection.ExecuteAsync(@"drop table if exists insert_ai;
@@ -154,7 +154,7 @@ public async Task LastInsertedIdLockTables()
 		}
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=97061")]
+	[Fact]
 	public async Task LastInsertedIdInsertForeignKey()
 	{
 		await m_database.Connection.ExecuteAsync(@"drop table if exists TestTableWithForeignKey;
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index 51d614b64..9aef2ac8f 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -1,4 +1,4 @@
-<Project Sdk="Microsoft.NET.Sdk">
+﻿<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup Condition=" '$(Configuration)' != 'MySqlData' ">
     <TargetFrameworks>net462;net472;net6.0;net7.0</TargetFrameworks>
@@ -6,8 +6,9 @@
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <TargetFrameworks>net6.0</TargetFrameworks>
+    <TargetFrameworks>net7.0</TargetFrameworks>
     <DefineConstants>MYSQL_DATA</DefineConstants>
+    <NoWarn>$(NoWarn);MSB3246</NoWarn>
   </PropertyGroup>
 
   <PropertyGroup>
@@ -51,7 +52,7 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <PackageReference Include="MySql.Data" Version="8.0.31" />
+    <PackageReference Include="MySql.Data" Version="8.0.32" />
     <Using Include="MySql.Data.MySqlClient" />
   </ItemGroup>
 
diff --git a/tests/IntegrationTests/ParameterCollection.cs b/tests/IntegrationTests/ParameterCollection.cs
index 62227b5d7..9c5d36939 100644
--- a/tests/IntegrationTests/ParameterCollection.cs
+++ b/tests/IntegrationTests/ParameterCollection.cs
@@ -85,8 +85,8 @@ public void FindByNameIgnoringCase(string parameterName)
 	[Theory]
 	[InlineData("@test")]
 	[InlineData("@TEST")]
-#if !MYSQL_DATA
-	[InlineData("test")] // https://bugs.mysql.com/bug.php?id=93370
+	[InlineData("test")]
+#if !MYSQL_DATA // https://bugs.mysql.com/bug.php?id=93370
 	[InlineData("@'test'")]
 	[InlineData("@`TEST`")]
 #endif
diff --git a/tests/IntegrationTests/ParameterTests.cs b/tests/IntegrationTests/ParameterTests.cs
index 26b16f435..6c76dcf74 100644
--- a/tests/IntegrationTests/ParameterTests.cs
+++ b/tests/IntegrationTests/ParameterTests.cs
@@ -213,7 +213,11 @@ public void CloneDbType()
 	[Fact]
 	public void CloneMySqlDbType()
 	{
+#if MYSQL_DATA // https://bugs.mysql.com/bug.php?id=109682
+		var parameter = new MySqlParameter { MySqlDbType = MySqlDbType.Int64 };
+#else
 		var parameter = new MySqlParameter { MySqlDbType = MySqlDbType.MediumText };
+#endif
 		var clone = parameter.Clone();
 		Assert.Equal(parameter.MySqlDbType, clone.MySqlDbType);
 	}
@@ -226,7 +230,7 @@ public void CloneDirection()
 		Assert.Equal(parameter.Direction, clone.Direction);
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=92734")]
+	[Fact]
 	public void CloneIsNullable()
 	{
 		var parameter = new MySqlParameter { IsNullable = true };
@@ -234,7 +238,7 @@ public void CloneIsNullable()
 		Assert.Equal(parameter.IsNullable, clone.IsNullable);
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=92734")]
+	[Fact]
 	public void ClonePrecision()
 	{
 		var parameter = new MySqlParameter { Precision = 10 };
@@ -242,7 +246,7 @@ public void ClonePrecision()
 		Assert.Equal(parameter.Precision, clone.Precision);
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=92734")]
+	[Fact]
 	public void CloneScale()
 	{
 		var parameter = new MySqlParameter { Scale = 12 };
@@ -250,7 +254,7 @@ public void CloneScale()
 		Assert.Equal(parameter.Scale, clone.Scale);
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=92734")]
+	[Fact]
 	public void CloneSize()
 	{
 		var parameter = new MySqlParameter { Size = 8 };
@@ -266,7 +270,7 @@ public void CloneSourceColumn()
 		Assert.Equal(parameter.SourceColumn, clone.SourceColumn);
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=92734")]
+	[Fact]
 	public void CloneSourceColumnNullMapping()
 	{
 		var parameter = new MySqlParameter { SourceColumnNullMapping = true };
diff --git a/tests/IntegrationTests/QueryTests.cs b/tests/IntegrationTests/QueryTests.cs
index 022c524c2..ce43b8851 100644
--- a/tests/IntegrationTests/QueryTests.cs
+++ b/tests/IntegrationTests/QueryTests.cs
@@ -893,11 +893,15 @@ public void OutputParameter()
 		{
 			ParameterName = "@param",
 			Direction = ParameterDirection.Output,
+			MySqlDbType = MySqlDbType.Int32,
 		});
 
+#if MYSQL_DATA
+		cmd.ExecuteNonQuery();
+		Assert.Equal(1234, cmd.Parameters["@param"].Value);
+#else
 		Assert.Throws<MySqlException>(() => cmd.ExecuteNonQuery());
-
-		// Issue #231: Assert.Equal(1234, cmd.Parameters["@param"].Value);
+#endif
 	}
 
 	[Fact]
@@ -1246,8 +1250,10 @@ END AS value
 	[Theory]
 	[InlineData(false, false)]
 	[InlineData(false, true)]
+#if !MYSQL_DATA
 	[InlineData(true, false)]
 	[InlineData(true, true)]
+#endif
 	public void GetIntForTinyInt1(bool treatTinyAsBoolean, bool prepare)
 	{
 		var csb = AppConfig.CreateConnectionStringBuilder();
@@ -1270,19 +1276,13 @@ public void GetIntForTinyInt1(bool treatTinyAsBoolean, bool prepare)
 		for (int i = 0; i < expected.Length; i++)
 		{
 			Assert.True(reader.Read());
-#if !MYSQL_DATA
-			// https://bugs.mysql.com/bug.php?id=99091
 			Assert.Equal((sbyte) expected[i], reader.GetSByte(0));
 			if (treatTinyAsBoolean)
 				Assert.Equal((byte) expected[i], reader.GetByte(0));
-#endif
 			Assert.Equal((short) expected[i], reader.GetInt16(0));
 			Assert.Equal(expected[i], reader.GetInt32(0));
 			Assert.Equal((long) expected[i], reader.GetInt64(0));
-#if !MYSQL_DATA
-			// https://bugs.mysql.com/bug.php?id=99091
 			Assert.Equal(expected[i], reader.GetFieldValue<int>(0));
-#endif
 		}
 
 		Assert.False(reader.Read());
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index fc35e1bc3..21445d991 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -3,7 +3,7 @@
   <system.data>
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.31.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.32.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index ee7c0a043..faaaac4e0 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -7,6 +7,7 @@
  <PropertyGroup Condition=" '$(Configuration)' == 'MySqlData' ">
     <TargetFrameworks>net472</TargetFrameworks>
     <DefineConstants>MYSQL_DATA</DefineConstants>
+    <NoWarn>$(NoWarn);MSB3246</NoWarn>
   </PropertyGroup>
 
   <PropertyGroup>
@@ -36,7 +37,7 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <PackageReference Include="MySql.Data" Version="8.0.31" />
+    <PackageReference Include="MySql.Data" Version="8.0.32" />
     <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
   </ItemGroup>
 

From 05c06c9e1d2d11ddb7cb958a5eb9ad861c885968 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 21 Jan 2023 14:52:03 -0800
Subject: [PATCH 036/393] Update documentation site theme.

Just The Docs theme adapted from https://just-the-docs.github.io/just-the-docs/.
---
 docs/layouts/partials/footer.html |    2 -
 docs/layouts/partials/header.html |  121 +-
 docs/layouts/partials/menu.html   |   30 +-
 docs/static/css/site.css          | 7852 +++++++++++++++++++++++++++++
 docs/static/js/scripts.js         |    0
 docs/static/js/site.js            |  111 +
 6 files changed, 8002 insertions(+), 114 deletions(-)
 create mode 100644 docs/static/css/site.css
 delete mode 100644 docs/static/js/scripts.js
 create mode 100644 docs/static/js/site.js

diff --git a/docs/layouts/partials/footer.html b/docs/layouts/partials/footer.html
index c07ccc051..9b151ee89 100644
--- a/docs/layouts/partials/footer.html
+++ b/docs/layouts/partials/footer.html
@@ -1,5 +1,3 @@
-        </div>
-        {{ partial "menu.html" . }}
       </div>
     </div>
   </body>
diff --git a/docs/layouts/partials/header.html b/docs/layouts/partials/header.html
index 9226312df..b87ce80b3 100644
--- a/docs/layouts/partials/header.html
+++ b/docs/layouts/partials/header.html
@@ -3,6 +3,7 @@
   <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <script src="/js/site.js"></script>
     <!-- Global site tag (gtag.js) - Google Analytics -->
     <script async src="https://www.googletagmanager.com/gtag/js?id=UA-142489411-1"></script>
     <script>
@@ -12,8 +13,6 @@
 
       gtag('config', 'UA-142489411-1');
     </script>
-    <link rel="preconnect" href='https://fonts.gstatic.com' crossorigin>
-    <link rel="preload" href="https://fonts.googleapis.com/css?family=Open+Sans:400,600,700&display=block" as="style">
     <meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}">
     {{ with .Site.Params.author }}<meta name="author" content="{{ . }}">{{ end }}
     <!--icons generated by http://www.favicon-generator.org/-->
@@ -50,105 +49,27 @@
     <meta property="og:url" content="{{ .Page.Permalink }}" />
     <meta property="fb:app_id" content="422535981632483" />
 
-    <link href="https://netdna.bootstrapcdn.com/twitter-bootstrap/2.2.1/css/bootstrap-combined.min.css" rel="stylesheet">
-    <link href="https://fonts.googleapis.com/css?family=Open+Sans:400,600,700&display=block" rel="stylesheet">
-	<style type="text/css">
-/* derived from https://github.com/fsprojects/SQLProvider/blob/gh-pages/content/style.css */
-pre {
-	  word-wrap: inherit;
-}
-
-.masthead ul, .masthead li {
-	margin-bottom:0px;
-}
-.masthead .nav li {
-	margin-top: 15px;
-	font-size:110%;
-}
-.masthead h3 {
-	margin-bottom:5px;
-	font-size:170%;
-}
-
-/* Make table headings and td.title bold */
-td.title, thead {
-	font-weight:bold;
-}
-
-table {
-  width: 100%;
-  max-width: 100%;
-  margin-bottom: 21px;
-}
-
-table > thead > tr > th,
-table > tbody > tr > th,
-table > tfoot > tr > th,
-table > thead > tr > td,
-table > tbody > tr > td,
-table > tfoot > tr > td {
-  padding: 8px;
-  line-height: 1.42857143;
-  vertical-align: top;
-  border-top: 1px solid #dddddd;
-}
-table > thead > tr > th {
-  vertical-align: bottom;
-  border-bottom: 2px solid #dddddd;
-}
-table > caption + thead > tr:first-child > th,
-table > colgroup + thead > tr:first-child > th,
-table > thead:first-child > tr:first-child > th,
-table > caption + thead > tr:first-child > td,
-table > colgroup + thead > tr:first-child > td,
-table > thead:first-child > tr:first-child > td {
-  border-top: 0;
-}
-table > tbody + tbody {
-  border-top: 2px solid #ecf0f1;
-}
-table table {
-  background-color: #ffffff;
-}
-table > tbody > tr:nth-of-type(odd) {
-  background-color: #f9f9f9;
-}
-table > tbody > tr:hover {
-  background-color: #f5f5f5;
-}
-
-/* Format the right-side menu */
-#menu {
-	margin-top:50px;
-	font-size:11pt;
-	padding-left:20px;
-}
-
-#menu li {
-	line-height:25px;
-}
-
-/* Change font sizes for headings etc. */
-#main h1 { font-size: 26pt; margin:10px 0px 15px 0px; font-weight:400; }
-#main h2 { font-size: 20pt; margin:20px 0px 0px 0px; font-weight:400; }
-#main h3 { font-size: 14pt; margin:15px 0px 0px 0px; font-weight:600; }
-#main p  { font-size: 11pt; margin:5px 0px 15px 0px; }
-#main ul { font-size: 11pt; margin-top:10px; }
-#main li { font-size: 11pt; margin: 5px 0px 5px 0px; }
-#main strong { font-weight:700; }
-	</style>
+    <link href="/css/site.css" rel="stylesheet">
     <link rel="canonical" href="{{ .Page.Permalink }}" />
   </head>
 
-  <body style="font-family: 'Open Sans', serif;padding-top: 0;padding-bottom: 40px;">
+  <body>
+    <svg xmlns="http://www.w3.org/2000/svg" class="d-none">
+      <symbol id="svg-link" viewBox="0 0 24 24"><title>Link</title><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-link"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"></path><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"></path></svg></symbol>
+      <symbol id="svg-menu" viewBox="0 0 24 24"><title>Menu</title><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-menu"><line x1="3" y1="12" x2="21" y2="12"></line><line x1="3" y1="6" x2="21" y2="6"></line><line x1="3" y1="18" x2="21" y2="18"></line></svg></symbol>
+      <symbol id="svg-arrow-right" viewBox="0 0 24 24"><title>Expand</title><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-chevron-right"><polyline points="9 18 15 12 9 6"></polyline></svg></symbol>
+      <symbol id="svg-external-link" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-external-link"><title id="svg-external-link-title">(external link)</title><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></symbol>
+      <symbol id="svg-doc" viewBox="0 0 24 24"><title>Document</title><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-file"><path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z"></path><polyline points="13 2 13 9 20 9"></polyline></svg></symbol>
+      <symbol id="svg-search" viewBox="0 0 24 24"><title>Search</title><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg></symbol>
+      <symbol id="svg-copy" viewBox="0 0 16 16"><title>Copy</title><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16"><path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1h1a1 1 0 0 1 1 1V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3.5a1 1 0 0 1 1-1h1v-1z"></path><path d="M9.5 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3zm-3-1A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3z"></path></svg></symbol>
+      <symbol id="svg-copied" viewBox="0 0 16 16"><title>Copied</title><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard-check-fill" viewBox="0 0 16 16"><path d="M6.5 0A1.5 1.5 0 0 0 5 1.5v1A1.5 1.5 0 0 0 6.5 4h3A1.5 1.5 0 0 0 11 2.5v-1A1.5 1.5 0 0 0 9.5 0h-3Zm3 1a.5.5 0 0 1 .5.5v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5v-1a.5.5 0 0 1 .5-.5h3Z"></path><path d="M4 1.5H3a2 2 0 0 0-2 2V14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V3.5a2 2 0 0 0-2-2h-1v1A2.5 2.5 0 0 1 9.5 5h-3A2.5 2.5 0 0 1 4 2.5v-1Zm6.854 7.354-3 3a.5.5 0 0 1-.708 0l-1.5-1.5a.5.5 0 0 1 .708-.708L7.5 10.793l2.646-2.647a.5.5 0 0 1 .708.708Z"></path></svg></symbol>
+    </svg>
+
+    <div class="side-bar">
+      <div class="site-header"> <a href="/" class="site-title lh-tight"> MySqlConnector </a> <a href="#" id="menu-button" class="site-button"> <svg viewBox="0 0 24 24" class="icon"><use xlink:href="#svg-menu"></use></svg> </a></div>
+    {{ partial "menu.html" . }}
+    </div>
+
+    <div class="main" id="top">
+        <div id="main-content-wrap" class="main-content-wrap">
 
-    <div class="container">
-        <div class="masthead" style="overflow: hidden">
-            <ul class="nav nav-pills pull-right">
-            <li><a href="https://github.com/mysql-net/MySqlConnector/">GitHub</a></li>
-            </ul>
-            <h3 class="muted"><a href="/" style="text-decoration: none; color: #999999">MySqlConnector</a></h3>
-        </div>
-        <hr style="margin:0 0 20px 0;"/>
-        <div class="row">
-            <div class="span9" id="main">
diff --git a/docs/layouts/partials/menu.html b/docs/layouts/partials/menu.html
index 7f27f8da1..965c608bc 100644
--- a/docs/layouts/partials/menu.html
+++ b/docs/layouts/partials/menu.html
@@ -1,17 +1,23 @@
-<div class="span3">
-    <ul class="nav nav-list" id="menu">
+<nav aria-label="Main" id="site-nav" class="site-nav">
+  {{ $currentPage := . }}
+  <ul class="nav-list">
 
-        {{ range $i, $el := .Site.Menus.main }}
-        <li class="nav-header" style="font-size:12pt;color:#606060;margin-top:20px;">
-            {{ if .HasChildren }}{{ .Name }}{{ else }}<a href="{{ .URL }}">{{ .Name }}</a>{{ end }}
-        </li>
+    {{ range $i, $el := .Site.Menus.main }}
+      <li class="nav-list-item{{ if $currentPage.HasMenuCurrent "main" . }} active{{ end }}">
+        {{ if .HasChildren }}<a href="#" class="nav-list-expander" aria-label="toggle links in UI Components category"> <svg viewBox="0 0 24 24"><use xlink:href="#svg-arrow-right"></use></svg> </a>{{ range first 1 .Children }}<a class="nav-list-link" href="{{ .URL }}">{{ end }}{{ .Name }}</a><ul class="nav-list">{{ else }}<a href="{{ .URL }}" class="nav-list-link{{ if $currentPage.IsMenuCurrent "main" . }} active{{ end }}">{{ .Name }}</a>{{ end }}
 
         {{ range .Children }}
-        <li>
-            <a href="{{.URL}}">{{ .Name }}</a>
-        </li>
+          <li class="nav-list-item{{ if $currentPage.HasMenuCurrent "main" . }} active{{ end }}">
+            <a href="{{.URL}}" class="nav-list-link{{ if $currentPage.IsMenuCurrent "main" . }} active{{ end }}">{{ .Name }}</a>
+          </li>
         {{ end }}
 
-        {{ end }}
-    </ul>
-</div>
+        {{ if .HasChildren }}</ul>{{ end }}
+      </li>
+    {{ end }}
+    <li class="nav-list-item external">
+      <a href="https://github.com/mysql-net/MySqlConnector" class="nav-list-link external">GitHub
+      <svg viewBox="0 0 24 24" aria-labelledby="svg-external-link-title"><use xlink:href="#svg-external-link"></use></svg></a>
+    </li>
+  </ul>
+</nav>
diff --git a/docs/static/css/site.css b/docs/static/css/site.css
new file mode 100644
index 000000000..bbb17aa1d
--- /dev/null
+++ b/docs/static/css/site.css
@@ -0,0 +1,7852 @@
+@charset "UTF-8";
+.highlight .c {
+  color: #586e75;
+}
+
+.highlight .err {
+  color: #93a1a1;
+}
+
+.highlight .g {
+  color: #93a1a1;
+}
+
+.highlight .k {
+  color: #859900;
+}
+
+.highlight .l {
+  color: #93a1a1;
+}
+
+.highlight .n {
+  color: #93a1a1;
+}
+
+.highlight .o {
+  color: #859900;
+}
+
+.highlight .x {
+  color: #cb4b16;
+}
+
+.highlight .p {
+  color: #93a1a1;
+}
+
+.highlight .cm {
+  color: #586e75;
+}
+
+.highlight .cp {
+  color: #859900;
+}
+
+.highlight .c1 {
+  color: #586e75;
+}
+
+.highlight .cs {
+  color: #859900;
+}
+
+.highlight .gd {
+  color: #2aa198;
+}
+
+.highlight .ge {
+  font-style: italic;
+  color: #93a1a1;
+}
+
+.highlight .gr {
+  color: #dc322f;
+}
+
+.highlight .gh {
+  color: #cb4b16;
+}
+
+.highlight .gi {
+  color: #859900;
+}
+
+.highlight .go {
+  color: #93a1a1;
+}
+
+.highlight .gp {
+  color: #93a1a1;
+}
+
+.highlight .gs {
+  font-weight: bold;
+  color: #93a1a1;
+}
+
+.highlight .gu {
+  color: #cb4b16;
+}
+
+.highlight .gt {
+  color: #93a1a1;
+}
+
+.highlight .kc {
+  color: #cb4b16;
+}
+
+.highlight .kd {
+  color: #268bd2;
+}
+
+.highlight .kn {
+  color: #859900;
+}
+
+.highlight .kp {
+  color: #859900;
+}
+
+.highlight .kr {
+  color: #268bd2;
+}
+
+.highlight .kt {
+  color: #dc322f;
+}
+
+.highlight .ld {
+  color: #93a1a1;
+}
+
+.highlight .m {
+  color: #2aa198;
+}
+
+.highlight .s {
+  color: #2aa198;
+}
+
+.highlight .na {
+  color: #555;
+}
+
+.highlight .nb {
+  color: #b58900;
+}
+
+.highlight .nc {
+  color: #268bd2;
+}
+
+.highlight .no {
+  color: #cb4b16;
+}
+
+.highlight .nd {
+  color: #268bd2;
+}
+
+.highlight .ni {
+  color: #cb4b16;
+}
+
+.highlight .ne {
+  color: #cb4b16;
+}
+
+.highlight .nf {
+  color: #268bd2;
+}
+
+.highlight .nl {
+  color: #555;
+}
+
+.highlight .nn {
+  color: #93a1a1;
+}
+
+.highlight .nx {
+  color: #555;
+}
+
+.highlight .py {
+  color: #93a1a1;
+}
+
+.highlight .nt {
+  color: #268bd2;
+}
+
+.highlight .nv {
+  color: #268bd2;
+}
+
+.highlight .ow {
+  color: #859900;
+}
+
+.highlight .w {
+  color: #93a1a1;
+}
+
+.highlight .mf {
+  color: #2aa198;
+}
+
+.highlight .mh {
+  color: #2aa198;
+}
+
+.highlight .mi {
+  color: #2aa198;
+}
+
+.highlight .mo {
+  color: #2aa198;
+}
+
+.highlight .sb {
+  color: #586e75;
+}
+
+.highlight .sc {
+  color: #2aa198;
+}
+
+.highlight .sd {
+  color: #93a1a1;
+}
+
+.highlight .s2 {
+  color: #2aa198;
+}
+
+.highlight .se {
+  color: #cb4b16;
+}
+
+.highlight .sh {
+  color: #93a1a1;
+}
+
+.highlight .si {
+  color: #2aa198;
+}
+
+.highlight .sx {
+  color: #2aa198;
+}
+
+.highlight .sr {
+  color: #dc322f;
+}
+
+.highlight .s1 {
+  color: #2aa198;
+}
+
+.highlight .ss {
+  color: #2aa198;
+}
+
+.highlight .bp {
+  color: #268bd2;
+}
+
+.highlight .vc {
+  color: #268bd2;
+}
+
+.highlight .vg {
+  color: #268bd2;
+}
+
+.highlight .vi {
+  color: #268bd2;
+}
+
+.highlight .il {
+  color: #2aa198;
+}
+
+.highlight .c {
+  color: #586e75;
+}
+
+.highlight .err {
+  color: #93a1a1;
+}
+
+.highlight .g {
+  color: #93a1a1;
+}
+
+.highlight .k {
+  color: #859900;
+}
+
+.highlight .l {
+  color: #93a1a1;
+}
+
+.highlight .n {
+  color: #93a1a1;
+}
+
+.highlight .o {
+  color: #859900;
+}
+
+.highlight .x {
+  color: #cb4b16;
+}
+
+.highlight .p {
+  color: #93a1a1;
+}
+
+.highlight .cm {
+  color: #586e75;
+}
+
+.highlight .cp {
+  color: #859900;
+}
+
+.highlight .c1 {
+  color: #586e75;
+}
+
+.highlight .cs {
+  color: #859900;
+}
+
+.highlight .gd {
+  color: #2aa198;
+}
+
+.highlight .ge {
+  font-style: italic;
+  color: #93a1a1;
+}
+
+.highlight .gr {
+  color: #dc322f;
+}
+
+.highlight .gh {
+  color: #cb4b16;
+}
+
+.highlight .gi {
+  color: #859900;
+}
+
+.highlight .go {
+  color: #93a1a1;
+}
+
+.highlight .gp {
+  color: #93a1a1;
+}
+
+.highlight .gs {
+  font-weight: bold;
+  color: #93a1a1;
+}
+
+.highlight .gu {
+  color: #cb4b16;
+}
+
+.highlight .gt {
+  color: #93a1a1;
+}
+
+.highlight .kc {
+  color: #cb4b16;
+}
+
+.highlight .kd {
+  color: #268bd2;
+}
+
+.highlight .kn {
+  color: #859900;
+}
+
+.highlight .kp {
+  color: #859900;
+}
+
+.highlight .kr {
+  color: #268bd2;
+}
+
+.highlight .kt {
+  color: #dc322f;
+}
+
+.highlight .ld {
+  color: #93a1a1;
+}
+
+.highlight .m {
+  color: #2aa198;
+}
+
+.highlight .s {
+  color: #2aa198;
+}
+
+.highlight .na {
+  color: #555;
+}
+
+.highlight .nb {
+  color: #b58900;
+}
+
+.highlight .nc {
+  color: #268bd2;
+}
+
+.highlight .no {
+  color: #cb4b16;
+}
+
+.highlight .nd {
+  color: #268bd2;
+}
+
+.highlight .ni {
+  color: #cb4b16;
+}
+
+.highlight .ne {
+  color: #cb4b16;
+}
+
+.highlight .nf {
+  color: #268bd2;
+}
+
+.highlight .nl {
+  color: #555;
+}
+
+.highlight .nn {
+  color: #93a1a1;
+}
+
+.highlight .nx {
+  color: #555;
+}
+
+.highlight .py {
+  color: #93a1a1;
+}
+
+.highlight .nt {
+  color: #268bd2;
+}
+
+.highlight .nv {
+  color: #268bd2;
+}
+
+.highlight .ow {
+  color: #859900;
+}
+
+.highlight .w {
+  color: #93a1a1;
+}
+
+.highlight .mf {
+  color: #2aa198;
+}
+
+.highlight .mh {
+  color: #2aa198;
+}
+
+.highlight .mi {
+  color: #2aa198;
+}
+
+.highlight .mo {
+  color: #2aa198;
+}
+
+.highlight .sb {
+  color: #586e75;
+}
+
+.highlight .sc {
+  color: #2aa198;
+}
+
+.highlight .sd {
+  color: #93a1a1;
+}
+
+.highlight .s2 {
+  color: #2aa198;
+}
+
+.highlight .se {
+  color: #cb4b16;
+}
+
+.highlight .sh {
+  color: #93a1a1;
+}
+
+.highlight .si {
+  color: #2aa198;
+}
+
+.highlight .sx {
+  color: #2aa198;
+}
+
+.highlight .sr {
+  color: #dc322f;
+}
+
+.highlight .s1 {
+  color: #2aa198;
+}
+
+.highlight .ss {
+  color: #2aa198;
+}
+
+.highlight .bp {
+  color: #268bd2;
+}
+
+.highlight .vc {
+  color: #268bd2;
+}
+
+.highlight .vg {
+  color: #268bd2;
+}
+
+.highlight .vi {
+  color: #268bd2;
+}
+
+.highlight .il {
+  color: #2aa198;
+}
+
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+/* Document
+   ========================================================================== */
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
+ */
+html {
+  line-height: 1.15; /* 1 */
+  -webkit-text-size-adjust: 100%; /* 2 */
+}
+
+/* Sections
+   ========================================================================== */
+/**
+ * Remove the margin in all browsers.
+ */
+body {
+  margin: 0;
+}
+
+/**
+ * Render the `main` element consistently in IE.
+ */
+main {
+  display: block;
+}
+
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
+}
+
+/* Grouping content
+   ========================================================================== */
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+hr {
+  box-sizing: content-box; /* 1 */
+  height: 0; /* 1 */
+  overflow: visible; /* 2 */
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+pre {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/* Text-level semantics
+   ========================================================================== */
+/**
+ * Remove the gray background on active links in IE 10.
+ */
+a {
+  background-color: transparent;
+}
+
+/**
+ * 1. Remove the bottom border in Chrome 57-
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+abbr[title] {
+  border-bottom: none; /* 1 */
+  text-decoration: underline; /* 2 */
+  text-decoration: underline dotted; /* 2 */
+}
+
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+b,
+strong {
+  font-weight: bolder;
+}
+
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+code,
+kbd,
+samp {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
+}
+
+/**
+ * Add the correct font size in all browsers.
+ */
+small {
+  font-size: 80%;
+}
+
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+sub,
+sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
+}
+
+sub {
+  bottom: -0.25em;
+}
+
+sup {
+  top: -0.5em;
+}
+
+/* Embedded content
+   ========================================================================== */
+/**
+ * Remove the border on images inside links in IE 10.
+ */
+img {
+  border-style: none;
+}
+
+/* Forms
+   ========================================================================== */
+/**
+ * 1. Change the font styles in all browsers.
+ * 2. Remove the margin in Firefox and Safari.
+ */
+button,
+input,
+optgroup,
+select,
+textarea {
+  font-family: inherit; /* 1 */
+  font-size: 100%; /* 1 */
+  line-height: 1.15; /* 1 */
+  margin: 0; /* 2 */
+}
+
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+button,
+input {
+  /* 1 */
+  overflow: visible;
+}
+
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+button,
+select {
+  /* 1 */
+  text-transform: none;
+}
+
+/**
+ * Correct the inability to style clickable types in iOS and Safari.
+ */
+button,
+[type=button],
+[type=reset],
+[type=submit] {
+  -webkit-appearance: button;
+}
+
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+button::-moz-focus-inner,
+[type=button]::-moz-focus-inner,
+[type=reset]::-moz-focus-inner,
+[type=submit]::-moz-focus-inner {
+  border-style: none;
+  padding: 0;
+}
+
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+button:-moz-focusring,
+[type=button]:-moz-focusring,
+[type=reset]:-moz-focusring,
+[type=submit]:-moz-focusring {
+  outline: 1px dotted ButtonText;
+}
+
+/**
+ * Correct the padding in Firefox.
+ */
+fieldset {
+  padding: 0.35em 0.75em 0.625em;
+}
+
+/**
+ * 1. Correct the text wrapping in Edge and IE.
+ * 2. Correct the color inheritance from `fieldset` elements in IE.
+ * 3. Remove the padding so developers are not caught out when they zero out
+ *    `fieldset` elements in all browsers.
+ */
+legend {
+  box-sizing: border-box; /* 1 */
+  color: inherit; /* 2 */
+  display: table; /* 1 */
+  max-width: 100%; /* 1 */
+  padding: 0; /* 3 */
+  white-space: normal; /* 1 */
+}
+
+/**
+ * Add the correct vertical alignment in Chrome, Firefox, and Opera.
+ */
+progress {
+  vertical-align: baseline;
+}
+
+/**
+ * Remove the default vertical scrollbar in IE 10+.
+ */
+textarea {
+  overflow: auto;
+}
+
+/**
+ * 1. Add the correct box sizing in IE 10.
+ * 2. Remove the padding in IE 10.
+ */
+[type=checkbox],
+[type=radio] {
+  box-sizing: border-box; /* 1 */
+  padding: 0; /* 2 */
+}
+
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+[type=number]::-webkit-inner-spin-button,
+[type=number]::-webkit-outer-spin-button {
+  height: auto;
+}
+
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+[type=search] {
+  -webkit-appearance: textfield; /* 1 */
+  outline-offset: -2px; /* 2 */
+}
+
+/**
+ * Remove the inner padding in Chrome and Safari on macOS.
+ */
+[type=search]::-webkit-search-decoration {
+  -webkit-appearance: none;
+}
+
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+::-webkit-file-upload-button {
+  -webkit-appearance: button; /* 1 */
+  font: inherit; /* 2 */
+}
+
+/* Interactive
+   ========================================================================== */
+/*
+ * Add the correct display in Edge, IE 10+, and Firefox.
+ */
+details {
+  display: block;
+}
+
+/*
+ * Add the correct display in all browsers.
+ */
+summary {
+  display: list-item;
+}
+
+/* Misc
+   ========================================================================== */
+/**
+ * Add the correct display in IE 10+.
+ */
+template {
+  display: none;
+}
+
+/**
+ * Add the correct display in IE 10.
+ */
+[hidden] {
+  display: none;
+}
+
+* {
+  box-sizing: border-box;
+}
+
+::selection {
+  color: #fff;
+  background: #0969da;
+}
+
+html {
+  font-size: 14px !important;
+  scroll-behavior: smooth;
+}
+@media (min-width: 31.25rem) {
+  html {
+    font-size: 16px !important;
+  }
+}
+
+body {
+  font-family: system-ui, -apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, sans-serif;
+  font-size: inherit;
+  line-height: 1.4;
+  color: #5c5962;
+  background-color: #fff;
+  overflow-wrap: break-word;
+}
+
+ol,
+ul,
+dl,
+pre,
+address,
+blockquote,
+table,
+div,
+hr,
+form,
+fieldset,
+noscript .table-wrapper {
+  margin-top: 0;
+}
+
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+#toctitle {
+  margin-top: 0;
+  margin-bottom: 1em;
+  font-weight: 500;
+  line-height: 1.25;
+  color: #27262b;
+}
+
+p {
+  margin-top: 1em;
+  margin-bottom: 1em;
+}
+
+a {
+  color: #0969da;
+  text-decoration: none;
+}
+
+a:not([class]) {
+  text-decoration: underline;
+  text-decoration-color: #eeebee;
+  text-underline-offset: 2px;
+}
+a:not([class]):hover {
+  text-decoration-color: rgba(9, 105, 218, 0.45);
+}
+
+code {
+  font-family: "SFMono-Regular", menlo, consolas, monospace;
+  font-size: 0.75em;
+  line-height: 1.4;
+}
+
+figure,
+pre {
+  margin: 16px 0;
+}
+
+li {
+  margin: 0.25em 0;
+}
+
+img {
+  max-width: 100%;
+  height: auto;
+}
+
+hr {
+  height: 1px;
+  padding: 0;
+  margin: 2rem 0;
+  background-color: #eeebee;
+  border: 0;
+}
+
+blockquote {
+  margin: 10px 0;
+  margin-block-start: 0;
+  margin-inline-start: 0;
+  padding-left: 15px;
+  border-left: 3px solid #eeebee;
+}
+
+.side-bar {
+  z-index: 0;
+  display: flex;
+  flex-wrap: wrap;
+  background-color: #f5f6fa;
+}
+@media (min-width: 50rem) {
+  .side-bar {
+    flex-flow: column nowrap;
+    position: fixed;
+    width: 248px;
+    height: 100%;
+    border-right: 1px solid #eeebee;
+    align-items: flex-end;
+  }
+}
+@media (min-width: 66.5rem) {
+  .side-bar {
+    width: calc((100% - 1064px) / 2 + 264px);
+    min-width: 264px;
+  }
+}
+
+@media (min-width: 50rem) {
+  .main {
+    position: relative;
+    max-width: 800px;
+    margin-left: 248px;
+  }
+}
+@media (min-width: 66.5rem) {
+  .main {
+    margin-left: calc(
+      (100% - 1064px) / 2 + 264px
+    );
+  }
+}
+
+.main-content-wrap {
+  padding-right: 1rem;
+  padding-left: 1rem;
+  padding-top: 1rem;
+  padding-bottom: 1rem;
+}
+@media (min-width: 50rem) {
+  .main-content-wrap {
+    padding-right: 2rem;
+    padding-left: 2rem;
+  }
+}
+@media (min-width: 50rem) {
+  .main-content-wrap {
+    padding-top: 2rem;
+    padding-bottom: 2rem;
+  }
+}
+
+.main-header {
+  z-index: 0;
+  display: none;
+  background-color: #f5f6fa;
+}
+@media (min-width: 50rem) {
+  .main-header {
+    display: flex;
+    justify-content: space-between;
+    height: 60px;
+    background-color: #fff;
+    border-bottom: 1px solid #eeebee;
+  }
+}
+.main-header.nav-open {
+  display: block;
+}
+@media (min-width: 50rem) {
+  .main-header.nav-open {
+    display: flex;
+  }
+}
+
+.site-nav,
+.site-header,
+.site-footer {
+  width: 100%;
+}
+@media (min-width: 66.5rem) {
+  .site-nav,
+  .site-header,
+  .site-footer {
+    width: 264px;
+  }
+}
+
+.site-nav {
+  display: none;
+}
+.site-nav.nav-open {
+  display: block;
+}
+@media (min-width: 50rem) {
+  .site-nav {
+    display: block;
+    padding-top: 3rem;
+    padding-bottom: 1rem;
+    overflow-y: auto;
+    flex: 1 1 auto;
+  }
+}
+
+.site-header {
+  display: flex;
+  min-height: 60px;
+  align-items: center;
+}
+@media (min-width: 50rem) {
+  .site-header {
+    height: 60px;
+    max-height: 60px;
+    border-bottom: 1px solid #eeebee;
+  }
+}
+
+.site-title {
+  padding-right: 1rem;
+  padding-left: 1rem;
+  flex-grow: 1;
+  display: flex;
+  height: 100%;
+  align-items: center;
+  padding-top: 0.75rem;
+  padding-bottom: 0.75rem;
+  color: #27262b;
+  font-size: 18px !important;
+}
+@media (min-width: 50rem) {
+  .site-title {
+    padding-right: 2rem;
+    padding-left: 2rem;
+  }
+}
+@media (min-width: 31.25rem) {
+  .site-title {
+    font-size: 24px !important;
+    line-height: 1.25;
+  }
+}
+@media (min-width: 50rem) {
+  .site-title {
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+  }
+}
+
+.site-button {
+  display: flex;
+  height: 100%;
+  padding: 1rem;
+  align-items: center;
+}
+
+@media (min-width: 50rem) {
+  .site-header .site-button {
+    display: none;
+  }
+}
+.site-title:hover {
+  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%);
+}
+
+.site-button:hover {
+  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 100%);
+}
+
+body {
+  position: relative;
+  padding-bottom: 4rem;
+  overflow-y: scroll;
+}
+@media (min-width: 50rem) {
+  body {
+    position: static;
+    padding-bottom: 0;
+  }
+}
+
+.site-footer {
+  padding-right: 1rem;
+  padding-left: 1rem;
+  position: absolute;
+  bottom: 0;
+  left: 0;
+  padding-top: 1rem;
+  padding-bottom: 1rem;
+  color: #959396;
+  font-size: 11px !important;
+}
+@media (min-width: 50rem) {
+  .site-footer {
+    padding-right: 2rem;
+    padding-left: 2rem;
+  }
+}
+@media (min-width: 31.25rem) {
+  .site-footer {
+    font-size: 12px !important;
+  }
+}
+@media (min-width: 50rem) {
+  .site-footer {
+    position: static;
+    justify-self: end;
+  }
+}
+
+.icon {
+  width: 1.5rem;
+  height: 1.5rem;
+  color: #0969da;
+}
+
+.main-content {
+  line-height: 1.6;
+}
+.main-content ol,
+.main-content ul,
+.main-content dl,
+.main-content pre,
+.main-content address,
+.main-content blockquote,
+.main-content .table-wrapper {
+  margin-top: 0.5em;
+}
+.main-content a {
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+.main-content ul,
+.main-content ol {
+  padding-left: 1.5em;
+}
+.main-content li .highlight {
+  margin-top: 0.25rem;
+}
+.main-content ol {
+  list-style-type: none;
+  counter-reset: step-counter;
+}
+.main-content ol > li {
+  position: relative;
+}
+.main-content ol > li::before {
+  position: absolute;
+  top: 0.2em;
+  left: -1.6em;
+  color: #959396;
+  content: counter(step-counter);
+  counter-increment: step-counter;
+  font-size: 12px !important;
+}
+@media (min-width: 31.25rem) {
+  .main-content ol > li::before {
+    font-size: 14px !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .main-content ol > li::before {
+    top: 0.11em;
+  }
+}
+.main-content ol > li ol {
+  counter-reset: sub-counter;
+}
+.main-content ol > li ol > li::before {
+  content: counter(sub-counter, lower-alpha);
+  counter-increment: sub-counter;
+}
+.main-content ul {
+  list-style: none;
+}
+.main-content ul > li::before {
+  position: absolute;
+  margin-left: -1.4em;
+  color: #959396;
+  content: "•";
+}
+.main-content .task-list-item::before {
+  content: "";
+}
+.main-content .task-list-item-checkbox {
+  margin-right: 0.6em;
+  margin-left: -1.4em;
+}
+.main-content hr + * {
+  margin-top: 0;
+}
+.main-content h1:first-of-type {
+  margin-top: 0.5em;
+}
+.main-content dl {
+  display: grid;
+  grid-template: auto/10em 1fr;
+}
+.main-content dt,
+.main-content dd {
+  margin: 0.25em 0;
+}
+.main-content dt {
+  grid-column: 1;
+  font-weight: 500;
+  text-align: right;
+}
+.main-content dt::after {
+  content: ":";
+}
+.main-content dd {
+  grid-column: 2;
+  margin-bottom: 0;
+  margin-left: 1em;
+}
+.main-content dd blockquote:first-child,
+.main-content dd div:first-child,
+.main-content dd dl:first-child,
+.main-content dd dt:first-child,
+.main-content dd h1:first-child,
+.main-content dd h2:first-child,
+.main-content dd h3:first-child,
+.main-content dd h4:first-child,
+.main-content dd h5:first-child,
+.main-content dd h6:first-child,
+.main-content dd li:first-child,
+.main-content dd ol:first-child,
+.main-content dd p:first-child,
+.main-content dd pre:first-child,
+.main-content dd table:first-child,
+.main-content dd ul:first-child,
+.main-content dd .table-wrapper:first-child {
+  margin-top: 0;
+}
+.main-content dd dl:first-child dt:first-child,
+.main-content dd dl:first-child dd:nth-child(2),
+.main-content ol dl:first-child dt:first-child,
+.main-content ol dl:first-child dd:nth-child(2),
+.main-content ul dl:first-child dt:first-child,
+.main-content ul dl:first-child dd:nth-child(2) {
+  margin-top: 0;
+}
+.main-content .anchor-heading {
+  position: absolute;
+  right: -1rem;
+  width: 1.5rem;
+  height: 100%;
+  padding-right: 0.25rem;
+  padding-left: 0.25rem;
+  overflow: visible;
+}
+@media (min-width: 50rem) {
+  .main-content .anchor-heading {
+    right: auto;
+    left: -1.5rem;
+  }
+}
+.main-content .anchor-heading svg {
+  display: inline-block;
+  width: 100%;
+  height: 100%;
+  color: #0969da;
+  visibility: hidden;
+}
+.main-content .anchor-heading:hover svg,
+.main-content .anchor-heading:focus svg,
+.main-content h1:hover > .anchor-heading svg,
+.main-content h2:hover > .anchor-heading svg,
+.main-content h3:hover > .anchor-heading svg,
+.main-content h4:hover > .anchor-heading svg,
+.main-content h5:hover > .anchor-heading svg,
+.main-content h6:hover > .anchor-heading svg {
+  visibility: visible;
+}
+.main-content summary {
+  cursor: pointer;
+}
+.main-content h1,
+.main-content h2,
+.main-content h3,
+.main-content h4,
+.main-content h5,
+.main-content h6,
+.main-content #toctitle {
+  position: relative;
+  margin-top: 1.5em;
+  margin-bottom: 0.25em;
+}
+.main-content h1 + table,
+.main-content h1 + .table-wrapper,
+.main-content h1 + .code-example,
+.main-content h1 + .highlighter-rouge,
+.main-content h1 + .sectionbody .listingblock,
+.main-content h2 + table,
+.main-content h2 + .table-wrapper,
+.main-content h2 + .code-example,
+.main-content h2 + .highlighter-rouge,
+.main-content h2 + .sectionbody .listingblock,
+.main-content h3 + table,
+.main-content h3 + .table-wrapper,
+.main-content h3 + .code-example,
+.main-content h3 + .highlighter-rouge,
+.main-content h3 + .sectionbody .listingblock,
+.main-content h4 + table,
+.main-content h4 + .table-wrapper,
+.main-content h4 + .code-example,
+.main-content h4 + .highlighter-rouge,
+.main-content h4 + .sectionbody .listingblock,
+.main-content h5 + table,
+.main-content h5 + .table-wrapper,
+.main-content h5 + .code-example,
+.main-content h5 + .highlighter-rouge,
+.main-content h5 + .sectionbody .listingblock,
+.main-content h6 + table,
+.main-content h6 + .table-wrapper,
+.main-content h6 + .code-example,
+.main-content h6 + .highlighter-rouge,
+.main-content h6 + .sectionbody .listingblock,
+.main-content #toctitle + table,
+.main-content #toctitle + .table-wrapper,
+.main-content #toctitle + .code-example,
+.main-content #toctitle + .highlighter-rouge,
+.main-content #toctitle + .sectionbody .listingblock {
+  margin-top: 1em;
+}
+.main-content h1 + p:not(.label),
+.main-content h2 + p:not(.label),
+.main-content h3 + p:not(.label),
+.main-content h4 + p:not(.label),
+.main-content h5 + p:not(.label),
+.main-content h6 + p:not(.label),
+.main-content #toctitle + p:not(.label) {
+  margin-top: 0;
+}
+.main-content > h1:first-child,
+.main-content > h2:first-child,
+.main-content > h3:first-child,
+.main-content > h4:first-child,
+.main-content > h5:first-child,
+.main-content > h6:first-child,
+.main-content > .sect1:first-child > h2,
+.main-content > .sect2:first-child > h3,
+.main-content > .sect3:first-child > h4,
+.main-content > .sect4:first-child > h5,
+.main-content > .sect5:first-child > h6 {
+  margin-top: 0.5rem;
+}
+
+.nav-list {
+  padding: 0;
+  margin-top: 0;
+  margin-bottom: 0;
+  list-style: none;
+}
+.nav-list .nav-list-item {
+  font-size: 14px !important;
+  position: relative;
+  margin: 0;
+}
+@media (min-width: 31.25rem) {
+  .nav-list .nav-list-item {
+    font-size: 16px !important;
+  }
+}
+@media (min-width: 50rem) {
+  .nav-list .nav-list-item {
+    font-size: 12px !important;
+  }
+}
+@media (min-width: 50rem) and (min-width: 31.25rem) {
+  .nav-list .nav-list-item {
+    font-size: 14px !important;
+  }
+}
+.nav-list .nav-list-item .nav-list-link {
+  display: block;
+  min-height: 3rem;
+  padding-top: 0.25rem;
+  padding-bottom: 0.25rem;
+  line-height: 2.5rem;
+  padding-right: 3rem;
+  padding-left: 1rem;
+}
+@media (min-width: 50rem) {
+  .nav-list .nav-list-item .nav-list-link {
+    min-height: 2rem;
+    line-height: 1.5rem;
+    padding-right: 2rem;
+    padding-left: 2rem;
+  }
+}
+.nav-list .nav-list-item .nav-list-link.external > svg {
+  width: 1rem;
+  height: 1rem;
+  vertical-align: text-bottom;
+}
+.nav-list .nav-list-item .nav-list-link.active {
+  font-weight: 600;
+  text-decoration: none;
+}
+.nav-list .nav-list-item .nav-list-link:hover, .nav-list .nav-list-item .nav-list-link.active {
+  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%);
+}
+.nav-list .nav-list-item .nav-list-expander {
+  position: absolute;
+  right: 0;
+  width: 3rem;
+  height: 3rem;
+  padding: 0.75rem;
+  color: #0969da;
+}
+@media (min-width: 50rem) {
+  .nav-list .nav-list-item .nav-list-expander {
+    width: 2rem;
+    height: 2rem;
+    padding: 0.5rem;
+  }
+}
+.nav-list .nav-list-item .nav-list-expander:hover {
+  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 100%);
+}
+.nav-list .nav-list-item .nav-list-expander svg {
+  transform: rotate(90deg);
+}
+.nav-list .nav-list-item > .nav-list {
+  display: none;
+  padding-left: 0.75rem;
+  list-style: none;
+}
+.nav-list .nav-list-item > .nav-list .nav-list-item {
+  position: relative;
+}
+.nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-link {
+  color: #5c5962;
+}
+.nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-expander {
+  color: #5c5962;
+}
+.nav-list .nav-list-item.active > .nav-list-expander svg {
+  transform: rotate(-90deg);
+}
+.nav-list .nav-list-item.active > .nav-list {
+  display: block;
+}
+
+.nav-category {
+  padding: 0.5rem 1rem;
+  font-weight: 600;
+  text-align: start;
+  text-transform: uppercase;
+  border-bottom: 1px solid #eeebee;
+  font-size: 11px !important;
+}
+@media (min-width: 31.25rem) {
+  .nav-category {
+    font-size: 12px !important;
+  }
+}
+@media (min-width: 50rem) {
+  .nav-category {
+    padding: 0.5rem 2rem;
+    margin-top: 1rem;
+    text-align: start;
+  }
+  .nav-category:first-child {
+    margin-top: 0;
+  }
+}
+
+.nav-list.nav-category-list > .nav-list-item {
+  margin: 0;
+}
+.nav-list.nav-category-list > .nav-list-item > .nav-list {
+  padding: 0;
+}
+.nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-link {
+  color: #0969da;
+}
+.nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-expander {
+  color: #0969da;
+}
+
+.aux-nav {
+  height: 100%;
+  overflow-x: auto;
+  font-size: 11px !important;
+}
+@media (min-width: 31.25rem) {
+  .aux-nav {
+    font-size: 12px !important;
+  }
+}
+.aux-nav .aux-nav-list {
+  display: flex;
+  height: 100%;
+  padding: 0;
+  margin: 0;
+  list-style: none;
+}
+.aux-nav .aux-nav-list-item {
+  display: inline-block;
+  height: 100%;
+  padding: 0;
+  margin: 0;
+}
+@media (min-width: 50rem) {
+  .aux-nav {
+    padding-right: 1rem;
+  }
+}
+
+@media (min-width: 50rem) {
+  .breadcrumb-nav {
+    margin-top: -1rem;
+  }
+}
+
+.breadcrumb-nav-list {
+  padding-left: 0;
+  margin-bottom: 0.75rem;
+  list-style: none;
+}
+
+.breadcrumb-nav-list-item {
+  display: table-cell;
+  font-size: 11px !important;
+}
+@media (min-width: 31.25rem) {
+  .breadcrumb-nav-list-item {
+    font-size: 12px !important;
+  }
+}
+.breadcrumb-nav-list-item::before {
+  display: none;
+}
+.breadcrumb-nav-list-item::after {
+  display: inline-block;
+  margin-right: 0.5rem;
+  margin-left: 0.5rem;
+  color: #959396;
+  content: "/";
+}
+.breadcrumb-nav-list-item:last-child::after {
+  content: "";
+}
+
+h1,
+.text-alpha {
+  font-size: 32px !important;
+  line-height: 1.25;
+  font-weight: 300;
+}
+@media (min-width: 31.25rem) {
+  h1,
+  .text-alpha {
+    font-size: 36px !important;
+  }
+}
+
+h2,
+.text-beta,
+#toctitle {
+  font-size: 18px !important;
+}
+@media (min-width: 31.25rem) {
+  h2,
+  .text-beta,
+  #toctitle {
+    font-size: 24px !important;
+    line-height: 1.25;
+  }
+}
+
+h3,
+.text-gamma {
+  font-size: 16px !important;
+}
+@media (min-width: 31.25rem) {
+  h3,
+  .text-gamma {
+    font-size: 18px !important;
+  }
+}
+
+h4,
+.text-delta {
+  font-size: 11px !important;
+  font-weight: 400;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+}
+@media (min-width: 31.25rem) {
+  h4,
+  .text-delta {
+    font-size: 12px !important;
+  }
+}
+
+h4 code {
+  text-transform: none;
+}
+
+h5,
+.text-epsilon {
+  font-size: 12px !important;
+}
+@media (min-width: 31.25rem) {
+  h5,
+  .text-epsilon {
+    font-size: 14px !important;
+  }
+}
+
+h6,
+.text-zeta {
+  font-size: 11px !important;
+}
+@media (min-width: 31.25rem) {
+  h6,
+  .text-zeta {
+    font-size: 12px !important;
+  }
+}
+
+.text-small {
+  font-size: 11px !important;
+}
+@media (min-width: 31.25rem) {
+  .text-small {
+    font-size: 12px !important;
+  }
+}
+
+.text-mono {
+  font-family: "SFMono-Regular", menlo, consolas, monospace !important;
+}
+
+.text-left {
+  text-align: left !important;
+}
+
+.text-center {
+  text-align: center !important;
+}
+
+.text-right {
+  text-align: right !important;
+}
+
+.label,
+.label-blue {
+  display: inline-block;
+  padding: 0.16em 0.56em;
+  margin-right: 0.5rem;
+  margin-left: 0.5rem;
+  color: #fff;
+  text-transform: uppercase;
+  vertical-align: middle;
+  background-color: #2869e6;
+  font-size: 11px !important;
+  border-radius: 12px;
+}
+@media (min-width: 31.25rem) {
+  .label,
+  .label-blue {
+    font-size: 12px !important;
+  }
+}
+
+.label-green {
+  background-color: #009c7b;
+}
+
+.label-purple {
+  background-color: #5e41d0;
+}
+
+.label-red {
+  background-color: #e94c4c;
+}
+
+.label-yellow {
+  color: #44434d;
+  background-color: #f7d12e;
+}
+
+.btn {
+  display: inline-block;
+  box-sizing: border-box;
+  padding: 0.3em 1em;
+  margin: 0;
+  font-family: inherit;
+  font-size: inherit;
+  font-weight: 500;
+  line-height: 1.5;
+  color: #0969da;
+  text-decoration: none;
+  vertical-align: baseline;
+  cursor: pointer;
+  background-color: #f7f7f7;
+  border-width: 0;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  appearance: none;
+}
+.btn:focus {
+  text-decoration: none;
+  outline: none;
+  box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.25);
+}
+.btn:focus:hover, .btn.selected:focus {
+  box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.25);
+}
+.btn:hover, .btn.zeroclipboard-is-hover {
+  color: #6a4aec;
+}
+.btn:hover, .btn:active, .btn.zeroclipboard-is-hover, .btn.zeroclipboard-is-active {
+  text-decoration: none;
+  background-color: #f4f4f4;
+}
+.btn:active, .btn.selected, .btn.zeroclipboard-is-active {
+  background-color: #efefef;
+  background-image: none;
+  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
+}
+.btn.selected:hover {
+  background-color: #cfcfcf;
+}
+.btn:disabled, .btn:disabled:hover, .btn.disabled, .btn.disabled:hover {
+  color: rgba(102, 102, 102, 0.5);
+  cursor: default;
+  background-color: rgba(229, 229, 229, 0.5);
+  background-image: none;
+  box-shadow: none;
+}
+
+.btn-outline {
+  color: #0969da;
+  background: transparent;
+  box-shadow: inset 0 0 0 2px #e6e1e8;
+}
+.btn-outline:hover, .btn-outline:active, .btn-outline.zeroclipboard-is-hover, .btn-outline.zeroclipboard-is-active {
+  color: #6341eb;
+  text-decoration: none;
+  background-color: transparent;
+  box-shadow: inset 0 0 0 3px #e6e1e8;
+}
+.btn-outline:focus {
+  text-decoration: none;
+  outline: none;
+  box-shadow: inset 0 0 0 2px #5c5962, 0 0 0 3px rgba(0, 0, 255, 0.25);
+}
+.btn-outline:focus:hover, .btn-outline.selected:focus {
+  box-shadow: inset 0 0 0 2px #5c5962;
+}
+
+.btn-primary {
+  color: #fff;
+  background-color: #5739ce;
+  background-image: linear-gradient(#6f55d5, #5739ce);
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12);
+}
+.btn-primary:hover, .btn-primary.zeroclipboard-is-hover {
+  color: #fff;
+  background-color: #5132cb;
+  background-image: linear-gradient(#6549d2, #5132cb);
+}
+.btn-primary:active, .btn-primary.selected, .btn-primary.zeroclipboard-is-active {
+  background-color: #4f31c6;
+  background-image: none;
+  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
+}
+.btn-primary.selected:hover {
+  background-color: #472cb2;
+}
+
+.btn-purple {
+  color: #fff;
+  background-color: #5739ce;
+  background-image: linear-gradient(#6f55d5, #5739ce);
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12);
+}
+.btn-purple:hover, .btn-purple.zeroclipboard-is-hover {
+  color: #fff;
+  background-color: #5132cb;
+  background-image: linear-gradient(#6549d2, #5132cb);
+}
+.btn-purple:active, .btn-purple.selected, .btn-purple.zeroclipboard-is-active {
+  background-color: #4f31c6;
+  background-image: none;
+  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
+}
+.btn-purple.selected:hover {
+  background-color: #472cb2;
+}
+
+.btn-blue {
+  color: #fff;
+  background-color: #227efa;
+  background-image: linear-gradient(#4593fb, #227efa);
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12);
+}
+.btn-blue:hover, .btn-blue.zeroclipboard-is-hover {
+  color: #fff;
+  background-color: #1878fa;
+  background-image: linear-gradient(#368afa, #1878fa);
+}
+.btn-blue:active, .btn-blue.selected, .btn-blue.zeroclipboard-is-active {
+  background-color: #1375f9;
+  background-image: none;
+  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
+}
+.btn-blue.selected:hover {
+  background-color: #0669ed;
+}
+
+.btn-green {
+  color: #fff;
+  background-color: #10ac7d;
+  background-image: linear-gradient(#13cc95, #10ac7d);
+  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12);
+}
+.btn-green:hover, .btn-green.zeroclipboard-is-hover {
+  color: #fff;
+  background-color: #0fa276;
+  background-image: linear-gradient(#12be8b, #0fa276);
+}
+.btn-green:active, .btn-green.selected, .btn-green.zeroclipboard-is-active {
+  background-color: #0f9e73;
+  background-image: none;
+  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
+}
+.btn-green.selected:hover {
+  background-color: #0d8662;
+}
+
+.search {
+  position: relative;
+  z-index: 2;
+  flex-grow: 1;
+  height: 4rem;
+  padding: 0.5rem;
+  transition: padding linear 200ms;
+}
+@media (min-width: 50rem) {
+  .search {
+    position: relative !important;
+    width: auto !important;
+    height: 100% !important;
+    padding: 0;
+    transition: none;
+  }
+}
+
+.search-input-wrap {
+  position: relative;
+  z-index: 1;
+  height: 3rem;
+  overflow: hidden;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  transition: height linear 200ms;
+}
+@media (min-width: 50rem) {
+  .search-input-wrap {
+    position: absolute;
+    width: 100%;
+    max-width: 536px;
+    height: 100% !important;
+    border-radius: 0;
+    box-shadow: none;
+    transition: width ease 400ms;
+  }
+}
+
+.search-input {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  padding: 0.5rem 1rem 0.5rem 2.5rem;
+  font-size: 16px;
+  color: #5c5962;
+  background-color: #fff;
+  border-top: 0;
+  border-right: 0;
+  border-bottom: 0;
+  border-left: 0;
+  border-radius: 0;
+}
+@media (min-width: 50rem) {
+  .search-input {
+    padding: 0.5rem 1rem 0.5rem 3.5rem;
+    font-size: 14px;
+    background-color: #fff;
+    transition: padding-left linear 200ms;
+  }
+}
+.search-input:focus {
+  outline: 0;
+}
+.search-input:focus + .search-label .search-icon {
+  color: #0969da;
+}
+
+.search-label {
+  position: absolute;
+  display: flex;
+  height: 100%;
+  padding-left: 1rem;
+}
+@media (min-width: 50rem) {
+  .search-label {
+    padding-left: 2rem;
+    transition: padding-left linear 200ms;
+  }
+}
+.search-label .search-icon {
+  width: 1.2rem;
+  height: 1.2rem;
+  align-self: center;
+  color: #959396;
+}
+
+.search-results {
+  position: absolute;
+  left: 0;
+  display: none;
+  width: 100%;
+  max-height: calc(100% - 4rem);
+  overflow-y: auto;
+  background-color: #fff;
+  border-bottom-right-radius: 4px;
+  border-bottom-left-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+}
+@media (min-width: 50rem) {
+  .search-results {
+    top: 100%;
+    width: 536px;
+    max-height: calc(100vh - 200%) !important;
+  }
+}
+
+.search-results-list {
+  padding-left: 0;
+  margin-bottom: 0.25rem;
+  list-style: none;
+  font-size: 14px !important;
+}
+@media (min-width: 31.25rem) {
+  .search-results-list {
+    font-size: 16px !important;
+  }
+}
+@media (min-width: 50rem) {
+  .search-results-list {
+    font-size: 12px !important;
+  }
+}
+@media (min-width: 50rem) and (min-width: 31.25rem) {
+  .search-results-list {
+    font-size: 14px !important;
+  }
+}
+
+.search-results-list-item {
+  padding: 0;
+  margin: 0;
+}
+
+.search-result {
+  display: block;
+  padding: 0.25rem 0.75rem;
+}
+.search-result:hover, .search-result.active {
+  background-color: #ebedf5;
+}
+
+.search-result-title {
+  display: block;
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+}
+@media (min-width: 31.25rem) {
+  .search-result-title {
+    display: inline-block;
+    width: 40%;
+    padding-right: 0.5rem;
+    vertical-align: top;
+  }
+}
+
+.search-result-doc {
+  display: flex;
+  align-items: center;
+  word-wrap: break-word;
+}
+.search-result-doc.search-result-doc-parent {
+  opacity: 0.5;
+  font-size: 12px !important;
+}
+@media (min-width: 31.25rem) {
+  .search-result-doc.search-result-doc-parent {
+    font-size: 14px !important;
+  }
+}
+@media (min-width: 50rem) {
+  .search-result-doc.search-result-doc-parent {
+    font-size: 11px !important;
+  }
+}
+@media (min-width: 50rem) and (min-width: 31.25rem) {
+  .search-result-doc.search-result-doc-parent {
+    font-size: 12px !important;
+  }
+}
+.search-result-doc .search-result-icon {
+  width: 1rem;
+  height: 1rem;
+  margin-right: 0.5rem;
+  color: #0969da;
+  flex-shrink: 0;
+}
+.search-result-doc .search-result-doc-title {
+  overflow: auto;
+}
+
+.search-result-section {
+  margin-left: 1.5rem;
+  word-wrap: break-word;
+}
+
+.search-result-rel-url {
+  display: block;
+  margin-left: 1.5rem;
+  overflow: hidden;
+  color: #959396;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  font-size: 9px !important;
+}
+@media (min-width: 31.25rem) {
+  .search-result-rel-url {
+    font-size: 10px !important;
+  }
+}
+
+.search-result-previews {
+  display: block;
+  padding-top: 0.5rem;
+  padding-bottom: 0.5rem;
+  padding-left: 1rem;
+  margin-left: 0.5rem;
+  color: #959396;
+  word-wrap: break-word;
+  border-left: 1px solid;
+  border-left-color: #eeebee;
+  font-size: 11px !important;
+}
+@media (min-width: 31.25rem) {
+  .search-result-previews {
+    font-size: 12px !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .search-result-previews {
+    display: inline-block;
+    width: 60%;
+    padding-left: 0.5rem;
+    margin-left: 0;
+    vertical-align: top;
+  }
+}
+
+.search-result-preview + .search-result-preview {
+  margin-top: 0.25rem;
+}
+
+.search-result-highlight {
+  font-weight: bold;
+}
+
+.search-no-result {
+  padding: 0.5rem 0.75rem;
+  font-size: 12px !important;
+}
+@media (min-width: 31.25rem) {
+  .search-no-result {
+    font-size: 14px !important;
+  }
+}
+
+.search-button {
+  position: fixed;
+  right: 1rem;
+  bottom: 1rem;
+  display: flex;
+  width: 3.5rem;
+  height: 3.5rem;
+  background-color: #fff;
+  border: 1px solid rgba(114, 83, 237, 0.3);
+  border-radius: 1.75rem;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  align-items: center;
+  justify-content: center;
+}
+
+.search-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  z-index: 1;
+  width: 0;
+  height: 0;
+  background-color: rgba(0, 0, 0, 0.3);
+  opacity: 0;
+  transition: opacity ease 400ms, width 0s 400ms, height 0s 400ms;
+}
+
+.search-active .search {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  padding: 0;
+}
+.search-active .search-input-wrap {
+  height: 4rem;
+  border-radius: 0;
+}
+@media (min-width: 50rem) {
+  .search-active .search-input-wrap {
+    width: 536px;
+    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  }
+}
+.search-active .search-input {
+  background-color: #fff;
+}
+@media (min-width: 50rem) {
+  .search-active .search-input {
+    padding-left: 2.3rem;
+  }
+}
+@media (min-width: 50rem) {
+  .search-active .search-label {
+    padding-left: 0.6rem;
+  }
+}
+.search-active .search-results {
+  display: block;
+}
+.search-active .search-overlay {
+  width: 100%;
+  height: 100%;
+  opacity: 1;
+  transition: opacity ease 400ms, width 0s, height 0s;
+}
+@media (min-width: 50rem) {
+  .search-active .main {
+    position: fixed;
+    right: 0;
+    left: 0;
+  }
+}
+.search-active .main-header {
+  padding-top: 4rem;
+}
+@media (min-width: 50rem) {
+  .search-active .main-header {
+    padding-top: 0;
+  }
+}
+
+.table-wrapper {
+  display: block;
+  width: 100%;
+  max-width: 100%;
+  margin-bottom: 1.5rem;
+  overflow-x: auto;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+}
+
+table {
+  display: table;
+  min-width: 100%;
+  border-collapse: separate;
+}
+
+th,
+td {
+  font-size: 12px !important;
+  min-width: 120px;
+  padding: 0.5rem 0.75rem;
+  background-color: #fff;
+  border-bottom: 1px solid rgba(238, 235, 238, 0.5);
+  border-left: 1px solid #eeebee;
+}
+@media (min-width: 31.25rem) {
+  th,
+  td {
+    font-size: 14px !important;
+  }
+}
+th:first-of-type,
+td:first-of-type {
+  border-left: 0;
+}
+
+tbody tr:last-of-type th,
+tbody tr:last-of-type td {
+  border-bottom: 0;
+}
+tbody tr:last-of-type td {
+  padding-bottom: 0.75rem;
+}
+
+thead th {
+  border-bottom: 1px solid #eeebee;
+}
+
+:not(pre, figure) > code {
+  padding: 0.2em 0.15em;
+  font-weight: 400;
+  background-color: #f5f6fa;
+  border: 1px solid #eeebee;
+  border-radius: 4px;
+}
+
+a:visited code {
+  border-color: #eeebee;
+}
+
+div.highlight,
+div.listingblock > div.content,
+figure.highlight {
+  margin-top: 0;
+  margin-bottom: 0.75rem;
+  background-color: #f5f6fa;
+  border-radius: 4px;
+  box-shadow: none;
+  -webkit-overflow-scrolling: touch;
+  position: relative;
+  padding: 0;
+}
+div.highlight > button,
+div.listingblock > div.content > button,
+figure.highlight > button {
+  width: 0.75rem;
+  opacity: 0;
+  position: absolute;
+  top: 0;
+  right: 0;
+  border: 0.75rem solid #f5f6fa;
+  background-color: #f5f6fa;
+  color: #5c5962;
+  box-sizing: content-box;
+}
+div.highlight > button svg,
+div.listingblock > div.content > button svg,
+figure.highlight > button svg {
+  fill: #5c5962;
+}
+div.highlight > button:active,
+div.listingblock > div.content > button:active,
+figure.highlight > button:active {
+  text-decoration: none;
+  outline: none;
+  opacity: 1;
+}
+div.highlight > button:focus,
+div.listingblock > div.content > button:focus,
+figure.highlight > button:focus {
+  opacity: 1;
+}
+div.highlight:hover > button,
+div.listingblock > div.content:hover > button,
+figure.highlight:hover > button {
+  cursor: copy;
+  opacity: 1;
+}
+
+div.highlight div.highlight,
+div.listingblock div.highlight {
+  overflow-x: auto;
+  padding: 0.75rem;
+  margin: 0;
+  border: 0;
+}
+div.highlight pre.highlight,
+div.highlight code,
+div.listingblock pre.highlight,
+div.listingblock code {
+  padding: 0;
+  margin: 0;
+  border: 0;
+}
+
+figure.highlight pre,
+figure.highlight :not(pre) > code {
+  overflow-x: auto;
+  padding: 0.75rem;
+  margin: 0;
+  border: 0;
+}
+
+.highlight .table-wrapper {
+  padding: 0.75rem 0;
+  margin: 0;
+  border: 0;
+  box-shadow: none;
+}
+.highlight .table-wrapper td,
+.highlight .table-wrapper pre {
+  font-size: 11px !important;
+  min-width: 0;
+  padding: 0;
+  background-color: #f5f6fa;
+  border: 0;
+}
+@media (min-width: 31.25rem) {
+  .highlight .table-wrapper td,
+  .highlight .table-wrapper pre {
+    font-size: 12px !important;
+  }
+}
+.highlight .table-wrapper td.gl {
+  width: 1em;
+  padding-right: 0.75rem;
+  padding-left: 0.75rem;
+}
+.highlight .table-wrapper pre {
+  margin: 0;
+  line-height: 2;
+}
+
+.code-example,
+.listingblock > .title {
+  padding: 0.75rem;
+  margin-bottom: 0.75rem;
+  overflow: auto;
+  border: 1px solid #eeebee;
+  border-radius: 4px;
+}
+.code-example + .highlighter-rouge,
+.code-example + .sectionbody .listingblock,
+.code-example + .content,
+.code-example + figure.highlight,
+.listingblock > .title + .highlighter-rouge,
+.listingblock > .title + .sectionbody .listingblock,
+.listingblock > .title + .content,
+.listingblock > .title + figure.highlight {
+  position: relative;
+  margin-top: -1rem;
+  border-right: 1px solid #eeebee;
+  border-bottom: 1px solid #eeebee;
+  border-left: 1px solid #eeebee;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}
+
+code.language-mermaid {
+  padding: 0;
+  background-color: inherit;
+  border: 0;
+}
+
+.text-grey-dk-000 {
+  color: #959396 !important;
+}
+
+.text-grey-dk-100 {
+  color: #5c5962 !important;
+}
+
+.text-grey-dk-200 {
+  color: #44434d !important;
+}
+
+.text-grey-dk-250 {
+  color: #302d36 !important;
+}
+
+.text-grey-dk-300 {
+  color: #27262b !important;
+}
+
+.text-grey-lt-000 {
+  color: #f5f6fa !important;
+}
+
+.text-grey-lt-100 {
+  color: #eeebee !important;
+}
+
+.text-grey-lt-200 {
+  color: #ecebed !important;
+}
+
+.text-grey-lt-300 {
+  color: #e6e1e8 !important;
+}
+
+.text-blue-000 {
+  color: #2c84fa !important;
+}
+
+.text-blue-100 {
+  color: #2869e6 !important;
+}
+
+.text-blue-200 {
+  color: #264caf !important;
+}
+
+.text-blue-300 {
+  color: #183385 !important;
+}
+
+.text-green-000 {
+  color: #41d693 !important;
+}
+
+.text-green-100 {
+  color: #11b584 !important;
+}
+
+.text-green-200 {
+  color: #009c7b !important;
+}
+
+.text-green-300 {
+  color: #026e57 !important;
+}
+
+.text-purple-000 {
+  color: #7253ed !important;
+}
+
+.text-purple-100 {
+  color: #5e41d0 !important;
+}
+
+.text-purple-200 {
+  color: #4e26af !important;
+}
+
+.text-purple-300 {
+  color: #381885 !important;
+}
+
+.text-yellow-000 {
+  color: #ffeb82 !important;
+}
+
+.text-yellow-100 {
+  color: #fadf50 !important;
+}
+
+.text-yellow-200 {
+  color: #f7d12e !important;
+}
+
+.text-yellow-300 {
+  color: #e7af06 !important;
+}
+
+.text-red-000 {
+  color: #f77e7e !important;
+}
+
+.text-red-100 {
+  color: #f96e65 !important;
+}
+
+.text-red-200 {
+  color: #e94c4c !important;
+}
+
+.text-red-300 {
+  color: #dd2e2e !important;
+}
+
+.bg-grey-dk-000 {
+  background-color: #959396 !important;
+}
+
+.bg-grey-dk-100 {
+  background-color: #5c5962 !important;
+}
+
+.bg-grey-dk-200 {
+  background-color: #44434d !important;
+}
+
+.bg-grey-dk-250 {
+  background-color: #302d36 !important;
+}
+
+.bg-grey-dk-300 {
+  background-color: #27262b !important;
+}
+
+.bg-grey-lt-000 {
+  background-color: #f5f6fa !important;
+}
+
+.bg-grey-lt-100 {
+  background-color: #eeebee !important;
+}
+
+.bg-grey-lt-200 {
+  background-color: #ecebed !important;
+}
+
+.bg-grey-lt-300 {
+  background-color: #e6e1e8 !important;
+}
+
+.bg-blue-000 {
+  background-color: #2c84fa !important;
+}
+
+.bg-blue-100 {
+  background-color: #2869e6 !important;
+}
+
+.bg-blue-200 {
+  background-color: #264caf !important;
+}
+
+.bg-blue-300 {
+  background-color: #183385 !important;
+}
+
+.bg-green-000 {
+  background-color: #41d693 !important;
+}
+
+.bg-green-100 {
+  background-color: #11b584 !important;
+}
+
+.bg-green-200 {
+  background-color: #009c7b !important;
+}
+
+.bg-green-300 {
+  background-color: #026e57 !important;
+}
+
+.bg-purple-000 {
+  background-color: #7253ed !important;
+}
+
+.bg-purple-100 {
+  background-color: #5e41d0 !important;
+}
+
+.bg-purple-200 {
+  background-color: #4e26af !important;
+}
+
+.bg-purple-300 {
+  background-color: #381885 !important;
+}
+
+.bg-yellow-000 {
+  background-color: #ffeb82 !important;
+}
+
+.bg-yellow-100 {
+  background-color: #fadf50 !important;
+}
+
+.bg-yellow-200 {
+  background-color: #f7d12e !important;
+}
+
+.bg-yellow-300 {
+  background-color: #e7af06 !important;
+}
+
+.bg-red-000 {
+  background-color: #f77e7e !important;
+}
+
+.bg-red-100 {
+  background-color: #f96e65 !important;
+}
+
+.bg-red-200 {
+  background-color: #e94c4c !important;
+}
+
+.bg-red-300 {
+  background-color: #dd2e2e !important;
+}
+
+.d-block {
+  display: block !important;
+}
+
+.d-flex {
+  display: flex !important;
+}
+
+.d-inline {
+  display: inline !important;
+}
+
+.d-inline-block {
+  display: inline-block !important;
+}
+
+.d-none {
+  display: none !important;
+}
+
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
+  }
+  .d-xs-flex {
+    display: flex !important;
+  }
+  .d-xs-inline {
+    display: inline !important;
+  }
+  .d-xs-inline-block {
+    display: inline-block !important;
+  }
+  .d-xs-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
+  }
+  .d-sm-flex {
+    display: flex !important;
+  }
+  .d-sm-inline {
+    display: inline !important;
+  }
+  .d-sm-inline-block {
+    display: inline-block !important;
+  }
+  .d-sm-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
+  }
+  .d-md-flex {
+    display: flex !important;
+  }
+  .d-md-inline {
+    display: inline !important;
+  }
+  .d-md-inline-block {
+    display: inline-block !important;
+  }
+  .d-md-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
+  }
+  .d-lg-flex {
+    display: flex !important;
+  }
+  .d-lg-inline {
+    display: inline !important;
+  }
+  .d-lg-inline-block {
+    display: inline-block !important;
+  }
+  .d-lg-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
+  }
+  .d-xl-flex {
+    display: flex !important;
+  }
+  .d-xl-inline {
+    display: inline !important;
+  }
+  .d-xl-inline-block {
+    display: inline-block !important;
+  }
+  .d-xl-none {
+    display: none !important;
+  }
+}
+.float-left {
+  float: left !important;
+}
+
+.float-right {
+  float: right !important;
+}
+
+.flex-justify-start {
+  justify-content: flex-start !important;
+}
+
+.flex-justify-end {
+  justify-content: flex-end !important;
+}
+
+.flex-justify-between {
+  justify-content: space-between !important;
+}
+
+.flex-justify-around {
+  justify-content: space-around !important;
+}
+
+.v-align-baseline {
+  vertical-align: baseline !important;
+}
+
+.v-align-bottom {
+  vertical-align: bottom !important;
+}
+
+.v-align-middle {
+  vertical-align: middle !important;
+}
+
+.v-align-text-bottom {
+  vertical-align: text-bottom !important;
+}
+
+.v-align-text-top {
+  vertical-align: text-top !important;
+}
+
+.v-align-top {
+  vertical-align: top !important;
+}
+
+.fs-1 {
+  font-size: 9px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-1 {
+    font-size: 10px !important;
+  }
+}
+
+.fs-2 {
+  font-size: 11px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-2 {
+    font-size: 12px !important;
+  }
+}
+
+.fs-3 {
+  font-size: 12px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-3 {
+    font-size: 14px !important;
+  }
+}
+
+.fs-4 {
+  font-size: 14px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-4 {
+    font-size: 16px !important;
+  }
+}
+
+.fs-5 {
+  font-size: 16px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-5 {
+    font-size: 18px !important;
+  }
+}
+
+.fs-6 {
+  font-size: 18px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-6 {
+    font-size: 24px !important;
+    line-height: 1.25;
+  }
+}
+
+.fs-7 {
+  font-size: 24px !important;
+  line-height: 1.25;
+}
+@media (min-width: 31.25rem) {
+  .fs-7 {
+    font-size: 32px !important;
+  }
+}
+
+.fs-8 {
+  font-size: 32px !important;
+  line-height: 1.25;
+}
+@media (min-width: 31.25rem) {
+  .fs-8 {
+    font-size: 36px !important;
+  }
+}
+
+.fs-9 {
+  font-size: 36px !important;
+  line-height: 1.25;
+}
+@media (min-width: 31.25rem) {
+  .fs-9 {
+    font-size: 42px !important;
+  }
+}
+
+.fs-10 {
+  font-size: 42px !important;
+  line-height: 1.25;
+}
+@media (min-width: 31.25rem) {
+  .fs-10 {
+    font-size: 48px !important;
+  }
+}
+
+.fw-300 {
+  font-weight: 300 !important;
+}
+
+.fw-400 {
+  font-weight: 400 !important;
+}
+
+.fw-500 {
+  font-weight: 500 !important;
+}
+
+.fw-700 {
+  font-weight: 700 !important;
+}
+
+.lh-0 {
+  line-height: 0 !important;
+}
+
+.lh-default {
+  line-height: 1.4;
+}
+
+.lh-tight {
+  line-height: 1.25;
+}
+
+.ls-5 {
+  letter-spacing: 0.05em !important;
+}
+
+.ls-10 {
+  letter-spacing: 0.1em !important;
+}
+
+.ls-0 {
+  letter-spacing: 0 !important;
+}
+
+.text-uppercase {
+  text-transform: uppercase !important;
+}
+
+.list-style-none {
+  padding: 0 !important;
+  margin: 0 !important;
+  list-style: none !important;
+}
+.list-style-none li::before {
+  display: none !important;
+}
+
+.mx-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-0 {
+  margin: 0 !important;
+}
+
+.mt-0 {
+  margin-top: 0 !important;
+}
+
+.mr-0 {
+  margin-right: 0 !important;
+}
+
+.mb-0 {
+  margin-bottom: 0 !important;
+}
+
+.ml-0 {
+  margin-left: 0 !important;
+}
+
+.mx-0 {
+  margin-right: 0 !important;
+  margin-left: 0 !important;
+}
+
+.my-0 {
+  margin-top: 0 !important;
+  margin-bottom: 0 !important;
+}
+
+.mxn-0 {
+  margin-right: -0 !important;
+  margin-left: -0 !important;
+}
+
+.mx-0-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-1 {
+  margin: 0.25rem !important;
+}
+
+.mt-1 {
+  margin-top: 0.25rem !important;
+}
+
+.mr-1 {
+  margin-right: 0.25rem !important;
+}
+
+.mb-1 {
+  margin-bottom: 0.25rem !important;
+}
+
+.ml-1 {
+  margin-left: 0.25rem !important;
+}
+
+.mx-1 {
+  margin-right: 0.25rem !important;
+  margin-left: 0.25rem !important;
+}
+
+.my-1 {
+  margin-top: 0.25rem !important;
+  margin-bottom: 0.25rem !important;
+}
+
+.mxn-1 {
+  margin-right: -0.25rem !important;
+  margin-left: -0.25rem !important;
+}
+
+.mx-1-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-2 {
+  margin: 0.5rem !important;
+}
+
+.mt-2 {
+  margin-top: 0.5rem !important;
+}
+
+.mr-2 {
+  margin-right: 0.5rem !important;
+}
+
+.mb-2 {
+  margin-bottom: 0.5rem !important;
+}
+
+.ml-2 {
+  margin-left: 0.5rem !important;
+}
+
+.mx-2 {
+  margin-right: 0.5rem !important;
+  margin-left: 0.5rem !important;
+}
+
+.my-2 {
+  margin-top: 0.5rem !important;
+  margin-bottom: 0.5rem !important;
+}
+
+.mxn-2 {
+  margin-right: -0.5rem !important;
+  margin-left: -0.5rem !important;
+}
+
+.mx-2-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-3 {
+  margin: 0.75rem !important;
+}
+
+.mt-3 {
+  margin-top: 0.75rem !important;
+}
+
+.mr-3 {
+  margin-right: 0.75rem !important;
+}
+
+.mb-3 {
+  margin-bottom: 0.75rem !important;
+}
+
+.ml-3 {
+  margin-left: 0.75rem !important;
+}
+
+.mx-3 {
+  margin-right: 0.75rem !important;
+  margin-left: 0.75rem !important;
+}
+
+.my-3 {
+  margin-top: 0.75rem !important;
+  margin-bottom: 0.75rem !important;
+}
+
+.mxn-3 {
+  margin-right: -0.75rem !important;
+  margin-left: -0.75rem !important;
+}
+
+.mx-3-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-4 {
+  margin: 1rem !important;
+}
+
+.mt-4 {
+  margin-top: 1rem !important;
+}
+
+.mr-4 {
+  margin-right: 1rem !important;
+}
+
+.mb-4 {
+  margin-bottom: 1rem !important;
+}
+
+.ml-4 {
+  margin-left: 1rem !important;
+}
+
+.mx-4 {
+  margin-right: 1rem !important;
+  margin-left: 1rem !important;
+}
+
+.my-4 {
+  margin-top: 1rem !important;
+  margin-bottom: 1rem !important;
+}
+
+.mxn-4 {
+  margin-right: -1rem !important;
+  margin-left: -1rem !important;
+}
+
+.mx-4-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-5 {
+  margin: 1.5rem !important;
+}
+
+.mt-5 {
+  margin-top: 1.5rem !important;
+}
+
+.mr-5 {
+  margin-right: 1.5rem !important;
+}
+
+.mb-5 {
+  margin-bottom: 1.5rem !important;
+}
+
+.ml-5 {
+  margin-left: 1.5rem !important;
+}
+
+.mx-5 {
+  margin-right: 1.5rem !important;
+  margin-left: 1.5rem !important;
+}
+
+.my-5 {
+  margin-top: 1.5rem !important;
+  margin-bottom: 1.5rem !important;
+}
+
+.mxn-5 {
+  margin-right: -1.5rem !important;
+  margin-left: -1.5rem !important;
+}
+
+.mx-5-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-6 {
+  margin: 2rem !important;
+}
+
+.mt-6 {
+  margin-top: 2rem !important;
+}
+
+.mr-6 {
+  margin-right: 2rem !important;
+}
+
+.mb-6 {
+  margin-bottom: 2rem !important;
+}
+
+.ml-6 {
+  margin-left: 2rem !important;
+}
+
+.mx-6 {
+  margin-right: 2rem !important;
+  margin-left: 2rem !important;
+}
+
+.my-6 {
+  margin-top: 2rem !important;
+  margin-bottom: 2rem !important;
+}
+
+.mxn-6 {
+  margin-right: -2rem !important;
+  margin-left: -2rem !important;
+}
+
+.mx-6-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-7 {
+  margin: 2.5rem !important;
+}
+
+.mt-7 {
+  margin-top: 2.5rem !important;
+}
+
+.mr-7 {
+  margin-right: 2.5rem !important;
+}
+
+.mb-7 {
+  margin-bottom: 2.5rem !important;
+}
+
+.ml-7 {
+  margin-left: 2.5rem !important;
+}
+
+.mx-7 {
+  margin-right: 2.5rem !important;
+  margin-left: 2.5rem !important;
+}
+
+.my-7 {
+  margin-top: 2.5rem !important;
+  margin-bottom: 2.5rem !important;
+}
+
+.mxn-7 {
+  margin-right: -2.5rem !important;
+  margin-left: -2.5rem !important;
+}
+
+.mx-7-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-8 {
+  margin: 3rem !important;
+}
+
+.mt-8 {
+  margin-top: 3rem !important;
+}
+
+.mr-8 {
+  margin-right: 3rem !important;
+}
+
+.mb-8 {
+  margin-bottom: 3rem !important;
+}
+
+.ml-8 {
+  margin-left: 3rem !important;
+}
+
+.mx-8 {
+  margin-right: 3rem !important;
+  margin-left: 3rem !important;
+}
+
+.my-8 {
+  margin-top: 3rem !important;
+  margin-bottom: 3rem !important;
+}
+
+.mxn-8 {
+  margin-right: -3rem !important;
+  margin-left: -3rem !important;
+}
+
+.mx-8-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-9 {
+  margin: 3.5rem !important;
+}
+
+.mt-9 {
+  margin-top: 3.5rem !important;
+}
+
+.mr-9 {
+  margin-right: 3.5rem !important;
+}
+
+.mb-9 {
+  margin-bottom: 3.5rem !important;
+}
+
+.ml-9 {
+  margin-left: 3.5rem !important;
+}
+
+.mx-9 {
+  margin-right: 3.5rem !important;
+  margin-left: 3.5rem !important;
+}
+
+.my-9 {
+  margin-top: 3.5rem !important;
+  margin-bottom: 3.5rem !important;
+}
+
+.mxn-9 {
+  margin-right: -3.5rem !important;
+  margin-left: -3.5rem !important;
+}
+
+.mx-9-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+.m-10 {
+  margin: 4rem !important;
+}
+
+.mt-10 {
+  margin-top: 4rem !important;
+}
+
+.mr-10 {
+  margin-right: 4rem !important;
+}
+
+.mb-10 {
+  margin-bottom: 4rem !important;
+}
+
+.ml-10 {
+  margin-left: 4rem !important;
+}
+
+.mx-10 {
+  margin-right: 4rem !important;
+  margin-left: 4rem !important;
+}
+
+.my-10 {
+  margin-top: 4rem !important;
+  margin-bottom: 4rem !important;
+}
+
+.mxn-10 {
+  margin-right: -4rem !important;
+  margin-left: -4rem !important;
+}
+
+.mx-10-auto {
+  margin-right: auto !important;
+  margin-left: auto !important;
+}
+
+@media (min-width: 20rem) {
+  .m-xs-0 {
+    margin: 0 !important;
+  }
+  .mt-xs-0 {
+    margin-top: 0 !important;
+  }
+  .mr-xs-0 {
+    margin-right: 0 !important;
+  }
+  .mb-xs-0 {
+    margin-bottom: 0 !important;
+  }
+  .ml-xs-0 {
+    margin-left: 0 !important;
+  }
+  .mx-xs-0 {
+    margin-right: 0 !important;
+    margin-left: 0 !important;
+  }
+  .my-xs-0 {
+    margin-top: 0 !important;
+    margin-bottom: 0 !important;
+  }
+  .mxn-xs-0 {
+    margin-right: -0 !important;
+    margin-left: -0 !important;
+  }
+}
+@media (min-width: 20rem) {
+  .m-xs-1 {
+    margin: 0.25rem !important;
+  }
+  .mt-xs-1 {
+    margin-top: 0.25rem !important;
+  }
+  .mr-xs-1 {
+    margin-right: 0.25rem !important;
+  }
+  .mb-xs-1 {
+    margin-bottom: 0.25rem !important;
+  }
+  .ml-xs-1 {
+    margin-left: 0.25rem !important;
+  }
+  .mx-xs-1 {
+    margin-right: 0.25rem !important;
+    margin-left: 0.25rem !important;
+  }
+  .my-xs-1 {
+    margin-top: 0.25rem !important;
+    margin-bottom: 0.25rem !important;
+  }
+  .mxn-xs-1 {
+    margin-right: -0.25rem !important;
+    margin-left: -0.25rem !important;
+  }
+}
+@media (min-width: 20rem) {
+  .m-xs-2 {
+    margin: 0.5rem !important;
+  }
+  .mt-xs-2 {
+    margin-top: 0.5rem !important;
+  }
+  .mr-xs-2 {
+    margin-right: 0.5rem !important;
+  }
+  .mb-xs-2 {
+    margin-bottom: 0.5rem !important;
+  }
+  .ml-xs-2 {
+    margin-left: 0.5rem !important;
+  }
+  .mx-xs-2 {
+    margin-right: 0.5rem !important;
+    margin-left: 0.5rem !important;
+  }
+  .my-xs-2 {
+    margin-top: 0.5rem !important;
+    margin-bottom: 0.5rem !important;
+  }
+  .mxn-xs-2 {
+    margin-right: -0.5rem !important;
+    margin-left: -0.5rem !important;
+  }
+}
+@media (min-width: 20rem) {
+  .m-xs-3 {
+    margin: 0.75rem !important;
+  }
+  .mt-xs-3 {
+    margin-top: 0.75rem !important;
+  }
+  .mr-xs-3 {
+    margin-right: 0.75rem !important;
+  }
+  .mb-xs-3 {
+    margin-bottom: 0.75rem !important;
+  }
+  .ml-xs-3 {
+    margin-left: 0.75rem !important;
+  }
+  .mx-xs-3 {
+    margin-right: 0.75rem !important;
+    margin-left: 0.75rem !important;
+  }
+  .my-xs-3 {
+    margin-top: 0.75rem !important;
+    margin-bottom: 0.75rem !important;
+  }
+  .mxn-xs-3 {
+    margin-right: -0.75rem !important;
+    margin-left: -0.75rem !important;
+  }
+}
+@media (min-width: 20rem) {
+  .m-xs-4 {
+    margin: 1rem !important;
+  }
+  .mt-xs-4 {
+    margin-top: 1rem !important;
+  }
+  .mr-xs-4 {
+    margin-right: 1rem !important;
+  }
+  .mb-xs-4 {
+    margin-bottom: 1rem !important;
+  }
+  .ml-xs-4 {
+    margin-left: 1rem !important;
+  }
+  .mx-xs-4 {
+    margin-right: 1rem !important;
+    margin-left: 1rem !important;
+  }
+  .my-xs-4 {
+    margin-top: 1rem !important;
+    margin-bottom: 1rem !important;
+  }
+  .mxn-xs-4 {
+    margin-right: -1rem !important;
+    margin-left: -1rem !important;
+  }
+}
+@media (min-width: 20rem) {
+  .m-xs-5 {
+    margin: 1.5rem !important;
+  }
+  .mt-xs-5 {
+    margin-top: 1.5rem !important;
+  }
+  .mr-xs-5 {
+    margin-right: 1.5rem !important;
+  }
+  .mb-xs-5 {
+    margin-bottom: 1.5rem !important;
+  }
+  .ml-xs-5 {
+    margin-left: 1.5rem !important;
+  }
+  .mx-xs-5 {
+    margin-right: 1.5rem !important;
+    margin-left: 1.5rem !important;
+  }
+  .my-xs-5 {
+    margin-top: 1.5rem !important;
+    margin-bottom: 1.5rem !important;
+  }
+  .mxn-xs-5 {
+    margin-right: -1.5rem !important;
+    margin-left: -1.5rem !important;
+  }
+}
+@media (min-width: 20rem) {
+  .m-xs-6 {
+    margin: 2rem !important;
+  }
+  .mt-xs-6 {
+    margin-top: 2rem !important;
+  }
+  .mr-xs-6 {
+    margin-right: 2rem !important;
+  }
+  .mb-xs-6 {
+    margin-bottom: 2rem !important;
+  }
+  .ml-xs-6 {
+    margin-left: 2rem !important;
+  }
+  .mx-xs-6 {
+    margin-right: 2rem !important;
+    margin-left: 2rem !important;
+  }
+  .my-xs-6 {
+    margin-top: 2rem !important;
+    margin-bottom: 2rem !important;
+  }
+  .mxn-xs-6 {
+    margin-right: -2rem !important;
+    margin-left: -2rem !important;
+  }
+}
+@media (min-width: 20rem) {
+  .m-xs-7 {
+    margin: 2.5rem !important;
+  }
+  .mt-xs-7 {
+    margin-top: 2.5rem !important;
+  }
+  .mr-xs-7 {
+    margin-right: 2.5rem !important;
+  }
+  .mb-xs-7 {
+    margin-bottom: 2.5rem !important;
+  }
+  .ml-xs-7 {
+    margin-left: 2.5rem !important;
+  }
+  .mx-xs-7 {
+    margin-right: 2.5rem !important;
+    margin-left: 2.5rem !important;
+  }
+  .my-xs-7 {
+    margin-top: 2.5rem !important;
+    margin-bottom: 2.5rem !important;
+  }
+  .mxn-xs-7 {
+    margin-right: -2.5rem !important;
+    margin-left: -2.5rem !important;
+  }
+}
+@media (min-width: 20rem) {
+  .m-xs-8 {
+    margin: 3rem !important;
+  }
+  .mt-xs-8 {
+    margin-top: 3rem !important;
+  }
+  .mr-xs-8 {
+    margin-right: 3rem !important;
+  }
+  .mb-xs-8 {
+    margin-bottom: 3rem !important;
+  }
+  .ml-xs-8 {
+    margin-left: 3rem !important;
+  }
+  .mx-xs-8 {
+    margin-right: 3rem !important;
+    margin-left: 3rem !important;
+  }
+  .my-xs-8 {
+    margin-top: 3rem !important;
+    margin-bottom: 3rem !important;
+  }
+  .mxn-xs-8 {
+    margin-right: -3rem !important;
+    margin-left: -3rem !important;
+  }
+}
+@media (min-width: 20rem) {
+  .m-xs-9 {
+    margin: 3.5rem !important;
+  }
+  .mt-xs-9 {
+    margin-top: 3.5rem !important;
+  }
+  .mr-xs-9 {
+    margin-right: 3.5rem !important;
+  }
+  .mb-xs-9 {
+    margin-bottom: 3.5rem !important;
+  }
+  .ml-xs-9 {
+    margin-left: 3.5rem !important;
+  }
+  .mx-xs-9 {
+    margin-right: 3.5rem !important;
+    margin-left: 3.5rem !important;
+  }
+  .my-xs-9 {
+    margin-top: 3.5rem !important;
+    margin-bottom: 3.5rem !important;
+  }
+  .mxn-xs-9 {
+    margin-right: -3.5rem !important;
+    margin-left: -3.5rem !important;
+  }
+}
+@media (min-width: 20rem) {
+  .m-xs-10 {
+    margin: 4rem !important;
+  }
+  .mt-xs-10 {
+    margin-top: 4rem !important;
+  }
+  .mr-xs-10 {
+    margin-right: 4rem !important;
+  }
+  .mb-xs-10 {
+    margin-bottom: 4rem !important;
+  }
+  .ml-xs-10 {
+    margin-left: 4rem !important;
+  }
+  .mx-xs-10 {
+    margin-right: 4rem !important;
+    margin-left: 4rem !important;
+  }
+  .my-xs-10 {
+    margin-top: 4rem !important;
+    margin-bottom: 4rem !important;
+  }
+  .mxn-xs-10 {
+    margin-right: -4rem !important;
+    margin-left: -4rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-0 {
+    margin: 0 !important;
+  }
+  .mt-sm-0 {
+    margin-top: 0 !important;
+  }
+  .mr-sm-0 {
+    margin-right: 0 !important;
+  }
+  .mb-sm-0 {
+    margin-bottom: 0 !important;
+  }
+  .ml-sm-0 {
+    margin-left: 0 !important;
+  }
+  .mx-sm-0 {
+    margin-right: 0 !important;
+    margin-left: 0 !important;
+  }
+  .my-sm-0 {
+    margin-top: 0 !important;
+    margin-bottom: 0 !important;
+  }
+  .mxn-sm-0 {
+    margin-right: -0 !important;
+    margin-left: -0 !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-1 {
+    margin: 0.25rem !important;
+  }
+  .mt-sm-1 {
+    margin-top: 0.25rem !important;
+  }
+  .mr-sm-1 {
+    margin-right: 0.25rem !important;
+  }
+  .mb-sm-1 {
+    margin-bottom: 0.25rem !important;
+  }
+  .ml-sm-1 {
+    margin-left: 0.25rem !important;
+  }
+  .mx-sm-1 {
+    margin-right: 0.25rem !important;
+    margin-left: 0.25rem !important;
+  }
+  .my-sm-1 {
+    margin-top: 0.25rem !important;
+    margin-bottom: 0.25rem !important;
+  }
+  .mxn-sm-1 {
+    margin-right: -0.25rem !important;
+    margin-left: -0.25rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-2 {
+    margin: 0.5rem !important;
+  }
+  .mt-sm-2 {
+    margin-top: 0.5rem !important;
+  }
+  .mr-sm-2 {
+    margin-right: 0.5rem !important;
+  }
+  .mb-sm-2 {
+    margin-bottom: 0.5rem !important;
+  }
+  .ml-sm-2 {
+    margin-left: 0.5rem !important;
+  }
+  .mx-sm-2 {
+    margin-right: 0.5rem !important;
+    margin-left: 0.5rem !important;
+  }
+  .my-sm-2 {
+    margin-top: 0.5rem !important;
+    margin-bottom: 0.5rem !important;
+  }
+  .mxn-sm-2 {
+    margin-right: -0.5rem !important;
+    margin-left: -0.5rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-3 {
+    margin: 0.75rem !important;
+  }
+  .mt-sm-3 {
+    margin-top: 0.75rem !important;
+  }
+  .mr-sm-3 {
+    margin-right: 0.75rem !important;
+  }
+  .mb-sm-3 {
+    margin-bottom: 0.75rem !important;
+  }
+  .ml-sm-3 {
+    margin-left: 0.75rem !important;
+  }
+  .mx-sm-3 {
+    margin-right: 0.75rem !important;
+    margin-left: 0.75rem !important;
+  }
+  .my-sm-3 {
+    margin-top: 0.75rem !important;
+    margin-bottom: 0.75rem !important;
+  }
+  .mxn-sm-3 {
+    margin-right: -0.75rem !important;
+    margin-left: -0.75rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-4 {
+    margin: 1rem !important;
+  }
+  .mt-sm-4 {
+    margin-top: 1rem !important;
+  }
+  .mr-sm-4 {
+    margin-right: 1rem !important;
+  }
+  .mb-sm-4 {
+    margin-bottom: 1rem !important;
+  }
+  .ml-sm-4 {
+    margin-left: 1rem !important;
+  }
+  .mx-sm-4 {
+    margin-right: 1rem !important;
+    margin-left: 1rem !important;
+  }
+  .my-sm-4 {
+    margin-top: 1rem !important;
+    margin-bottom: 1rem !important;
+  }
+  .mxn-sm-4 {
+    margin-right: -1rem !important;
+    margin-left: -1rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-5 {
+    margin: 1.5rem !important;
+  }
+  .mt-sm-5 {
+    margin-top: 1.5rem !important;
+  }
+  .mr-sm-5 {
+    margin-right: 1.5rem !important;
+  }
+  .mb-sm-5 {
+    margin-bottom: 1.5rem !important;
+  }
+  .ml-sm-5 {
+    margin-left: 1.5rem !important;
+  }
+  .mx-sm-5 {
+    margin-right: 1.5rem !important;
+    margin-left: 1.5rem !important;
+  }
+  .my-sm-5 {
+    margin-top: 1.5rem !important;
+    margin-bottom: 1.5rem !important;
+  }
+  .mxn-sm-5 {
+    margin-right: -1.5rem !important;
+    margin-left: -1.5rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-6 {
+    margin: 2rem !important;
+  }
+  .mt-sm-6 {
+    margin-top: 2rem !important;
+  }
+  .mr-sm-6 {
+    margin-right: 2rem !important;
+  }
+  .mb-sm-6 {
+    margin-bottom: 2rem !important;
+  }
+  .ml-sm-6 {
+    margin-left: 2rem !important;
+  }
+  .mx-sm-6 {
+    margin-right: 2rem !important;
+    margin-left: 2rem !important;
+  }
+  .my-sm-6 {
+    margin-top: 2rem !important;
+    margin-bottom: 2rem !important;
+  }
+  .mxn-sm-6 {
+    margin-right: -2rem !important;
+    margin-left: -2rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-7 {
+    margin: 2.5rem !important;
+  }
+  .mt-sm-7 {
+    margin-top: 2.5rem !important;
+  }
+  .mr-sm-7 {
+    margin-right: 2.5rem !important;
+  }
+  .mb-sm-7 {
+    margin-bottom: 2.5rem !important;
+  }
+  .ml-sm-7 {
+    margin-left: 2.5rem !important;
+  }
+  .mx-sm-7 {
+    margin-right: 2.5rem !important;
+    margin-left: 2.5rem !important;
+  }
+  .my-sm-7 {
+    margin-top: 2.5rem !important;
+    margin-bottom: 2.5rem !important;
+  }
+  .mxn-sm-7 {
+    margin-right: -2.5rem !important;
+    margin-left: -2.5rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-8 {
+    margin: 3rem !important;
+  }
+  .mt-sm-8 {
+    margin-top: 3rem !important;
+  }
+  .mr-sm-8 {
+    margin-right: 3rem !important;
+  }
+  .mb-sm-8 {
+    margin-bottom: 3rem !important;
+  }
+  .ml-sm-8 {
+    margin-left: 3rem !important;
+  }
+  .mx-sm-8 {
+    margin-right: 3rem !important;
+    margin-left: 3rem !important;
+  }
+  .my-sm-8 {
+    margin-top: 3rem !important;
+    margin-bottom: 3rem !important;
+  }
+  .mxn-sm-8 {
+    margin-right: -3rem !important;
+    margin-left: -3rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-9 {
+    margin: 3.5rem !important;
+  }
+  .mt-sm-9 {
+    margin-top: 3.5rem !important;
+  }
+  .mr-sm-9 {
+    margin-right: 3.5rem !important;
+  }
+  .mb-sm-9 {
+    margin-bottom: 3.5rem !important;
+  }
+  .ml-sm-9 {
+    margin-left: 3.5rem !important;
+  }
+  .mx-sm-9 {
+    margin-right: 3.5rem !important;
+    margin-left: 3.5rem !important;
+  }
+  .my-sm-9 {
+    margin-top: 3.5rem !important;
+    margin-bottom: 3.5rem !important;
+  }
+  .mxn-sm-9 {
+    margin-right: -3.5rem !important;
+    margin-left: -3.5rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .m-sm-10 {
+    margin: 4rem !important;
+  }
+  .mt-sm-10 {
+    margin-top: 4rem !important;
+  }
+  .mr-sm-10 {
+    margin-right: 4rem !important;
+  }
+  .mb-sm-10 {
+    margin-bottom: 4rem !important;
+  }
+  .ml-sm-10 {
+    margin-left: 4rem !important;
+  }
+  .mx-sm-10 {
+    margin-right: 4rem !important;
+    margin-left: 4rem !important;
+  }
+  .my-sm-10 {
+    margin-top: 4rem !important;
+    margin-bottom: 4rem !important;
+  }
+  .mxn-sm-10 {
+    margin-right: -4rem !important;
+    margin-left: -4rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-0 {
+    margin: 0 !important;
+  }
+  .mt-md-0 {
+    margin-top: 0 !important;
+  }
+  .mr-md-0 {
+    margin-right: 0 !important;
+  }
+  .mb-md-0 {
+    margin-bottom: 0 !important;
+  }
+  .ml-md-0 {
+    margin-left: 0 !important;
+  }
+  .mx-md-0 {
+    margin-right: 0 !important;
+    margin-left: 0 !important;
+  }
+  .my-md-0 {
+    margin-top: 0 !important;
+    margin-bottom: 0 !important;
+  }
+  .mxn-md-0 {
+    margin-right: -0 !important;
+    margin-left: -0 !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-1 {
+    margin: 0.25rem !important;
+  }
+  .mt-md-1 {
+    margin-top: 0.25rem !important;
+  }
+  .mr-md-1 {
+    margin-right: 0.25rem !important;
+  }
+  .mb-md-1 {
+    margin-bottom: 0.25rem !important;
+  }
+  .ml-md-1 {
+    margin-left: 0.25rem !important;
+  }
+  .mx-md-1 {
+    margin-right: 0.25rem !important;
+    margin-left: 0.25rem !important;
+  }
+  .my-md-1 {
+    margin-top: 0.25rem !important;
+    margin-bottom: 0.25rem !important;
+  }
+  .mxn-md-1 {
+    margin-right: -0.25rem !important;
+    margin-left: -0.25rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-2 {
+    margin: 0.5rem !important;
+  }
+  .mt-md-2 {
+    margin-top: 0.5rem !important;
+  }
+  .mr-md-2 {
+    margin-right: 0.5rem !important;
+  }
+  .mb-md-2 {
+    margin-bottom: 0.5rem !important;
+  }
+  .ml-md-2 {
+    margin-left: 0.5rem !important;
+  }
+  .mx-md-2 {
+    margin-right: 0.5rem !important;
+    margin-left: 0.5rem !important;
+  }
+  .my-md-2 {
+    margin-top: 0.5rem !important;
+    margin-bottom: 0.5rem !important;
+  }
+  .mxn-md-2 {
+    margin-right: -0.5rem !important;
+    margin-left: -0.5rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-3 {
+    margin: 0.75rem !important;
+  }
+  .mt-md-3 {
+    margin-top: 0.75rem !important;
+  }
+  .mr-md-3 {
+    margin-right: 0.75rem !important;
+  }
+  .mb-md-3 {
+    margin-bottom: 0.75rem !important;
+  }
+  .ml-md-3 {
+    margin-left: 0.75rem !important;
+  }
+  .mx-md-3 {
+    margin-right: 0.75rem !important;
+    margin-left: 0.75rem !important;
+  }
+  .my-md-3 {
+    margin-top: 0.75rem !important;
+    margin-bottom: 0.75rem !important;
+  }
+  .mxn-md-3 {
+    margin-right: -0.75rem !important;
+    margin-left: -0.75rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-4 {
+    margin: 1rem !important;
+  }
+  .mt-md-4 {
+    margin-top: 1rem !important;
+  }
+  .mr-md-4 {
+    margin-right: 1rem !important;
+  }
+  .mb-md-4 {
+    margin-bottom: 1rem !important;
+  }
+  .ml-md-4 {
+    margin-left: 1rem !important;
+  }
+  .mx-md-4 {
+    margin-right: 1rem !important;
+    margin-left: 1rem !important;
+  }
+  .my-md-4 {
+    margin-top: 1rem !important;
+    margin-bottom: 1rem !important;
+  }
+  .mxn-md-4 {
+    margin-right: -1rem !important;
+    margin-left: -1rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-5 {
+    margin: 1.5rem !important;
+  }
+  .mt-md-5 {
+    margin-top: 1.5rem !important;
+  }
+  .mr-md-5 {
+    margin-right: 1.5rem !important;
+  }
+  .mb-md-5 {
+    margin-bottom: 1.5rem !important;
+  }
+  .ml-md-5 {
+    margin-left: 1.5rem !important;
+  }
+  .mx-md-5 {
+    margin-right: 1.5rem !important;
+    margin-left: 1.5rem !important;
+  }
+  .my-md-5 {
+    margin-top: 1.5rem !important;
+    margin-bottom: 1.5rem !important;
+  }
+  .mxn-md-5 {
+    margin-right: -1.5rem !important;
+    margin-left: -1.5rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-6 {
+    margin: 2rem !important;
+  }
+  .mt-md-6 {
+    margin-top: 2rem !important;
+  }
+  .mr-md-6 {
+    margin-right: 2rem !important;
+  }
+  .mb-md-6 {
+    margin-bottom: 2rem !important;
+  }
+  .ml-md-6 {
+    margin-left: 2rem !important;
+  }
+  .mx-md-6 {
+    margin-right: 2rem !important;
+    margin-left: 2rem !important;
+  }
+  .my-md-6 {
+    margin-top: 2rem !important;
+    margin-bottom: 2rem !important;
+  }
+  .mxn-md-6 {
+    margin-right: -2rem !important;
+    margin-left: -2rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-7 {
+    margin: 2.5rem !important;
+  }
+  .mt-md-7 {
+    margin-top: 2.5rem !important;
+  }
+  .mr-md-7 {
+    margin-right: 2.5rem !important;
+  }
+  .mb-md-7 {
+    margin-bottom: 2.5rem !important;
+  }
+  .ml-md-7 {
+    margin-left: 2.5rem !important;
+  }
+  .mx-md-7 {
+    margin-right: 2.5rem !important;
+    margin-left: 2.5rem !important;
+  }
+  .my-md-7 {
+    margin-top: 2.5rem !important;
+    margin-bottom: 2.5rem !important;
+  }
+  .mxn-md-7 {
+    margin-right: -2.5rem !important;
+    margin-left: -2.5rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-8 {
+    margin: 3rem !important;
+  }
+  .mt-md-8 {
+    margin-top: 3rem !important;
+  }
+  .mr-md-8 {
+    margin-right: 3rem !important;
+  }
+  .mb-md-8 {
+    margin-bottom: 3rem !important;
+  }
+  .ml-md-8 {
+    margin-left: 3rem !important;
+  }
+  .mx-md-8 {
+    margin-right: 3rem !important;
+    margin-left: 3rem !important;
+  }
+  .my-md-8 {
+    margin-top: 3rem !important;
+    margin-bottom: 3rem !important;
+  }
+  .mxn-md-8 {
+    margin-right: -3rem !important;
+    margin-left: -3rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-9 {
+    margin: 3.5rem !important;
+  }
+  .mt-md-9 {
+    margin-top: 3.5rem !important;
+  }
+  .mr-md-9 {
+    margin-right: 3.5rem !important;
+  }
+  .mb-md-9 {
+    margin-bottom: 3.5rem !important;
+  }
+  .ml-md-9 {
+    margin-left: 3.5rem !important;
+  }
+  .mx-md-9 {
+    margin-right: 3.5rem !important;
+    margin-left: 3.5rem !important;
+  }
+  .my-md-9 {
+    margin-top: 3.5rem !important;
+    margin-bottom: 3.5rem !important;
+  }
+  .mxn-md-9 {
+    margin-right: -3.5rem !important;
+    margin-left: -3.5rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .m-md-10 {
+    margin: 4rem !important;
+  }
+  .mt-md-10 {
+    margin-top: 4rem !important;
+  }
+  .mr-md-10 {
+    margin-right: 4rem !important;
+  }
+  .mb-md-10 {
+    margin-bottom: 4rem !important;
+  }
+  .ml-md-10 {
+    margin-left: 4rem !important;
+  }
+  .mx-md-10 {
+    margin-right: 4rem !important;
+    margin-left: 4rem !important;
+  }
+  .my-md-10 {
+    margin-top: 4rem !important;
+    margin-bottom: 4rem !important;
+  }
+  .mxn-md-10 {
+    margin-right: -4rem !important;
+    margin-left: -4rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-0 {
+    margin: 0 !important;
+  }
+  .mt-lg-0 {
+    margin-top: 0 !important;
+  }
+  .mr-lg-0 {
+    margin-right: 0 !important;
+  }
+  .mb-lg-0 {
+    margin-bottom: 0 !important;
+  }
+  .ml-lg-0 {
+    margin-left: 0 !important;
+  }
+  .mx-lg-0 {
+    margin-right: 0 !important;
+    margin-left: 0 !important;
+  }
+  .my-lg-0 {
+    margin-top: 0 !important;
+    margin-bottom: 0 !important;
+  }
+  .mxn-lg-0 {
+    margin-right: -0 !important;
+    margin-left: -0 !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-1 {
+    margin: 0.25rem !important;
+  }
+  .mt-lg-1 {
+    margin-top: 0.25rem !important;
+  }
+  .mr-lg-1 {
+    margin-right: 0.25rem !important;
+  }
+  .mb-lg-1 {
+    margin-bottom: 0.25rem !important;
+  }
+  .ml-lg-1 {
+    margin-left: 0.25rem !important;
+  }
+  .mx-lg-1 {
+    margin-right: 0.25rem !important;
+    margin-left: 0.25rem !important;
+  }
+  .my-lg-1 {
+    margin-top: 0.25rem !important;
+    margin-bottom: 0.25rem !important;
+  }
+  .mxn-lg-1 {
+    margin-right: -0.25rem !important;
+    margin-left: -0.25rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-2 {
+    margin: 0.5rem !important;
+  }
+  .mt-lg-2 {
+    margin-top: 0.5rem !important;
+  }
+  .mr-lg-2 {
+    margin-right: 0.5rem !important;
+  }
+  .mb-lg-2 {
+    margin-bottom: 0.5rem !important;
+  }
+  .ml-lg-2 {
+    margin-left: 0.5rem !important;
+  }
+  .mx-lg-2 {
+    margin-right: 0.5rem !important;
+    margin-left: 0.5rem !important;
+  }
+  .my-lg-2 {
+    margin-top: 0.5rem !important;
+    margin-bottom: 0.5rem !important;
+  }
+  .mxn-lg-2 {
+    margin-right: -0.5rem !important;
+    margin-left: -0.5rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-3 {
+    margin: 0.75rem !important;
+  }
+  .mt-lg-3 {
+    margin-top: 0.75rem !important;
+  }
+  .mr-lg-3 {
+    margin-right: 0.75rem !important;
+  }
+  .mb-lg-3 {
+    margin-bottom: 0.75rem !important;
+  }
+  .ml-lg-3 {
+    margin-left: 0.75rem !important;
+  }
+  .mx-lg-3 {
+    margin-right: 0.75rem !important;
+    margin-left: 0.75rem !important;
+  }
+  .my-lg-3 {
+    margin-top: 0.75rem !important;
+    margin-bottom: 0.75rem !important;
+  }
+  .mxn-lg-3 {
+    margin-right: -0.75rem !important;
+    margin-left: -0.75rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-4 {
+    margin: 1rem !important;
+  }
+  .mt-lg-4 {
+    margin-top: 1rem !important;
+  }
+  .mr-lg-4 {
+    margin-right: 1rem !important;
+  }
+  .mb-lg-4 {
+    margin-bottom: 1rem !important;
+  }
+  .ml-lg-4 {
+    margin-left: 1rem !important;
+  }
+  .mx-lg-4 {
+    margin-right: 1rem !important;
+    margin-left: 1rem !important;
+  }
+  .my-lg-4 {
+    margin-top: 1rem !important;
+    margin-bottom: 1rem !important;
+  }
+  .mxn-lg-4 {
+    margin-right: -1rem !important;
+    margin-left: -1rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-5 {
+    margin: 1.5rem !important;
+  }
+  .mt-lg-5 {
+    margin-top: 1.5rem !important;
+  }
+  .mr-lg-5 {
+    margin-right: 1.5rem !important;
+  }
+  .mb-lg-5 {
+    margin-bottom: 1.5rem !important;
+  }
+  .ml-lg-5 {
+    margin-left: 1.5rem !important;
+  }
+  .mx-lg-5 {
+    margin-right: 1.5rem !important;
+    margin-left: 1.5rem !important;
+  }
+  .my-lg-5 {
+    margin-top: 1.5rem !important;
+    margin-bottom: 1.5rem !important;
+  }
+  .mxn-lg-5 {
+    margin-right: -1.5rem !important;
+    margin-left: -1.5rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-6 {
+    margin: 2rem !important;
+  }
+  .mt-lg-6 {
+    margin-top: 2rem !important;
+  }
+  .mr-lg-6 {
+    margin-right: 2rem !important;
+  }
+  .mb-lg-6 {
+    margin-bottom: 2rem !important;
+  }
+  .ml-lg-6 {
+    margin-left: 2rem !important;
+  }
+  .mx-lg-6 {
+    margin-right: 2rem !important;
+    margin-left: 2rem !important;
+  }
+  .my-lg-6 {
+    margin-top: 2rem !important;
+    margin-bottom: 2rem !important;
+  }
+  .mxn-lg-6 {
+    margin-right: -2rem !important;
+    margin-left: -2rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-7 {
+    margin: 2.5rem !important;
+  }
+  .mt-lg-7 {
+    margin-top: 2.5rem !important;
+  }
+  .mr-lg-7 {
+    margin-right: 2.5rem !important;
+  }
+  .mb-lg-7 {
+    margin-bottom: 2.5rem !important;
+  }
+  .ml-lg-7 {
+    margin-left: 2.5rem !important;
+  }
+  .mx-lg-7 {
+    margin-right: 2.5rem !important;
+    margin-left: 2.5rem !important;
+  }
+  .my-lg-7 {
+    margin-top: 2.5rem !important;
+    margin-bottom: 2.5rem !important;
+  }
+  .mxn-lg-7 {
+    margin-right: -2.5rem !important;
+    margin-left: -2.5rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-8 {
+    margin: 3rem !important;
+  }
+  .mt-lg-8 {
+    margin-top: 3rem !important;
+  }
+  .mr-lg-8 {
+    margin-right: 3rem !important;
+  }
+  .mb-lg-8 {
+    margin-bottom: 3rem !important;
+  }
+  .ml-lg-8 {
+    margin-left: 3rem !important;
+  }
+  .mx-lg-8 {
+    margin-right: 3rem !important;
+    margin-left: 3rem !important;
+  }
+  .my-lg-8 {
+    margin-top: 3rem !important;
+    margin-bottom: 3rem !important;
+  }
+  .mxn-lg-8 {
+    margin-right: -3rem !important;
+    margin-left: -3rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-9 {
+    margin: 3.5rem !important;
+  }
+  .mt-lg-9 {
+    margin-top: 3.5rem !important;
+  }
+  .mr-lg-9 {
+    margin-right: 3.5rem !important;
+  }
+  .mb-lg-9 {
+    margin-bottom: 3.5rem !important;
+  }
+  .ml-lg-9 {
+    margin-left: 3.5rem !important;
+  }
+  .mx-lg-9 {
+    margin-right: 3.5rem !important;
+    margin-left: 3.5rem !important;
+  }
+  .my-lg-9 {
+    margin-top: 3.5rem !important;
+    margin-bottom: 3.5rem !important;
+  }
+  .mxn-lg-9 {
+    margin-right: -3.5rem !important;
+    margin-left: -3.5rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .m-lg-10 {
+    margin: 4rem !important;
+  }
+  .mt-lg-10 {
+    margin-top: 4rem !important;
+  }
+  .mr-lg-10 {
+    margin-right: 4rem !important;
+  }
+  .mb-lg-10 {
+    margin-bottom: 4rem !important;
+  }
+  .ml-lg-10 {
+    margin-left: 4rem !important;
+  }
+  .mx-lg-10 {
+    margin-right: 4rem !important;
+    margin-left: 4rem !important;
+  }
+  .my-lg-10 {
+    margin-top: 4rem !important;
+    margin-bottom: 4rem !important;
+  }
+  .mxn-lg-10 {
+    margin-right: -4rem !important;
+    margin-left: -4rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-0 {
+    margin: 0 !important;
+  }
+  .mt-xl-0 {
+    margin-top: 0 !important;
+  }
+  .mr-xl-0 {
+    margin-right: 0 !important;
+  }
+  .mb-xl-0 {
+    margin-bottom: 0 !important;
+  }
+  .ml-xl-0 {
+    margin-left: 0 !important;
+  }
+  .mx-xl-0 {
+    margin-right: 0 !important;
+    margin-left: 0 !important;
+  }
+  .my-xl-0 {
+    margin-top: 0 !important;
+    margin-bottom: 0 !important;
+  }
+  .mxn-xl-0 {
+    margin-right: -0 !important;
+    margin-left: -0 !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-1 {
+    margin: 0.25rem !important;
+  }
+  .mt-xl-1 {
+    margin-top: 0.25rem !important;
+  }
+  .mr-xl-1 {
+    margin-right: 0.25rem !important;
+  }
+  .mb-xl-1 {
+    margin-bottom: 0.25rem !important;
+  }
+  .ml-xl-1 {
+    margin-left: 0.25rem !important;
+  }
+  .mx-xl-1 {
+    margin-right: 0.25rem !important;
+    margin-left: 0.25rem !important;
+  }
+  .my-xl-1 {
+    margin-top: 0.25rem !important;
+    margin-bottom: 0.25rem !important;
+  }
+  .mxn-xl-1 {
+    margin-right: -0.25rem !important;
+    margin-left: -0.25rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-2 {
+    margin: 0.5rem !important;
+  }
+  .mt-xl-2 {
+    margin-top: 0.5rem !important;
+  }
+  .mr-xl-2 {
+    margin-right: 0.5rem !important;
+  }
+  .mb-xl-2 {
+    margin-bottom: 0.5rem !important;
+  }
+  .ml-xl-2 {
+    margin-left: 0.5rem !important;
+  }
+  .mx-xl-2 {
+    margin-right: 0.5rem !important;
+    margin-left: 0.5rem !important;
+  }
+  .my-xl-2 {
+    margin-top: 0.5rem !important;
+    margin-bottom: 0.5rem !important;
+  }
+  .mxn-xl-2 {
+    margin-right: -0.5rem !important;
+    margin-left: -0.5rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-3 {
+    margin: 0.75rem !important;
+  }
+  .mt-xl-3 {
+    margin-top: 0.75rem !important;
+  }
+  .mr-xl-3 {
+    margin-right: 0.75rem !important;
+  }
+  .mb-xl-3 {
+    margin-bottom: 0.75rem !important;
+  }
+  .ml-xl-3 {
+    margin-left: 0.75rem !important;
+  }
+  .mx-xl-3 {
+    margin-right: 0.75rem !important;
+    margin-left: 0.75rem !important;
+  }
+  .my-xl-3 {
+    margin-top: 0.75rem !important;
+    margin-bottom: 0.75rem !important;
+  }
+  .mxn-xl-3 {
+    margin-right: -0.75rem !important;
+    margin-left: -0.75rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-4 {
+    margin: 1rem !important;
+  }
+  .mt-xl-4 {
+    margin-top: 1rem !important;
+  }
+  .mr-xl-4 {
+    margin-right: 1rem !important;
+  }
+  .mb-xl-4 {
+    margin-bottom: 1rem !important;
+  }
+  .ml-xl-4 {
+    margin-left: 1rem !important;
+  }
+  .mx-xl-4 {
+    margin-right: 1rem !important;
+    margin-left: 1rem !important;
+  }
+  .my-xl-4 {
+    margin-top: 1rem !important;
+    margin-bottom: 1rem !important;
+  }
+  .mxn-xl-4 {
+    margin-right: -1rem !important;
+    margin-left: -1rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-5 {
+    margin: 1.5rem !important;
+  }
+  .mt-xl-5 {
+    margin-top: 1.5rem !important;
+  }
+  .mr-xl-5 {
+    margin-right: 1.5rem !important;
+  }
+  .mb-xl-5 {
+    margin-bottom: 1.5rem !important;
+  }
+  .ml-xl-5 {
+    margin-left: 1.5rem !important;
+  }
+  .mx-xl-5 {
+    margin-right: 1.5rem !important;
+    margin-left: 1.5rem !important;
+  }
+  .my-xl-5 {
+    margin-top: 1.5rem !important;
+    margin-bottom: 1.5rem !important;
+  }
+  .mxn-xl-5 {
+    margin-right: -1.5rem !important;
+    margin-left: -1.5rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-6 {
+    margin: 2rem !important;
+  }
+  .mt-xl-6 {
+    margin-top: 2rem !important;
+  }
+  .mr-xl-6 {
+    margin-right: 2rem !important;
+  }
+  .mb-xl-6 {
+    margin-bottom: 2rem !important;
+  }
+  .ml-xl-6 {
+    margin-left: 2rem !important;
+  }
+  .mx-xl-6 {
+    margin-right: 2rem !important;
+    margin-left: 2rem !important;
+  }
+  .my-xl-6 {
+    margin-top: 2rem !important;
+    margin-bottom: 2rem !important;
+  }
+  .mxn-xl-6 {
+    margin-right: -2rem !important;
+    margin-left: -2rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-7 {
+    margin: 2.5rem !important;
+  }
+  .mt-xl-7 {
+    margin-top: 2.5rem !important;
+  }
+  .mr-xl-7 {
+    margin-right: 2.5rem !important;
+  }
+  .mb-xl-7 {
+    margin-bottom: 2.5rem !important;
+  }
+  .ml-xl-7 {
+    margin-left: 2.5rem !important;
+  }
+  .mx-xl-7 {
+    margin-right: 2.5rem !important;
+    margin-left: 2.5rem !important;
+  }
+  .my-xl-7 {
+    margin-top: 2.5rem !important;
+    margin-bottom: 2.5rem !important;
+  }
+  .mxn-xl-7 {
+    margin-right: -2.5rem !important;
+    margin-left: -2.5rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-8 {
+    margin: 3rem !important;
+  }
+  .mt-xl-8 {
+    margin-top: 3rem !important;
+  }
+  .mr-xl-8 {
+    margin-right: 3rem !important;
+  }
+  .mb-xl-8 {
+    margin-bottom: 3rem !important;
+  }
+  .ml-xl-8 {
+    margin-left: 3rem !important;
+  }
+  .mx-xl-8 {
+    margin-right: 3rem !important;
+    margin-left: 3rem !important;
+  }
+  .my-xl-8 {
+    margin-top: 3rem !important;
+    margin-bottom: 3rem !important;
+  }
+  .mxn-xl-8 {
+    margin-right: -3rem !important;
+    margin-left: -3rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-9 {
+    margin: 3.5rem !important;
+  }
+  .mt-xl-9 {
+    margin-top: 3.5rem !important;
+  }
+  .mr-xl-9 {
+    margin-right: 3.5rem !important;
+  }
+  .mb-xl-9 {
+    margin-bottom: 3.5rem !important;
+  }
+  .ml-xl-9 {
+    margin-left: 3.5rem !important;
+  }
+  .mx-xl-9 {
+    margin-right: 3.5rem !important;
+    margin-left: 3.5rem !important;
+  }
+  .my-xl-9 {
+    margin-top: 3.5rem !important;
+    margin-bottom: 3.5rem !important;
+  }
+  .mxn-xl-9 {
+    margin-right: -3.5rem !important;
+    margin-left: -3.5rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .m-xl-10 {
+    margin: 4rem !important;
+  }
+  .mt-xl-10 {
+    margin-top: 4rem !important;
+  }
+  .mr-xl-10 {
+    margin-right: 4rem !important;
+  }
+  .mb-xl-10 {
+    margin-bottom: 4rem !important;
+  }
+  .ml-xl-10 {
+    margin-left: 4rem !important;
+  }
+  .mx-xl-10 {
+    margin-right: 4rem !important;
+    margin-left: 4rem !important;
+  }
+  .my-xl-10 {
+    margin-top: 4rem !important;
+    margin-bottom: 4rem !important;
+  }
+  .mxn-xl-10 {
+    margin-right: -4rem !important;
+    margin-left: -4rem !important;
+  }
+}
+.p-0 {
+  padding: 0 !important;
+}
+
+.pt-0 {
+  padding-top: 0 !important;
+}
+
+.pr-0 {
+  padding-right: 0 !important;
+}
+
+.pb-0 {
+  padding-bottom: 0 !important;
+}
+
+.pl-0 {
+  padding-left: 0 !important;
+}
+
+.px-0 {
+  padding-right: 0 !important;
+  padding-left: 0 !important;
+}
+
+.py-0 {
+  padding-top: 0 !important;
+  padding-bottom: 0 !important;
+}
+
+.p-1 {
+  padding: 0.25rem !important;
+}
+
+.pt-1 {
+  padding-top: 0.25rem !important;
+}
+
+.pr-1 {
+  padding-right: 0.25rem !important;
+}
+
+.pb-1 {
+  padding-bottom: 0.25rem !important;
+}
+
+.pl-1 {
+  padding-left: 0.25rem !important;
+}
+
+.px-1 {
+  padding-right: 0.25rem !important;
+  padding-left: 0.25rem !important;
+}
+
+.py-1 {
+  padding-top: 0.25rem !important;
+  padding-bottom: 0.25rem !important;
+}
+
+.p-2 {
+  padding: 0.5rem !important;
+}
+
+.pt-2 {
+  padding-top: 0.5rem !important;
+}
+
+.pr-2 {
+  padding-right: 0.5rem !important;
+}
+
+.pb-2 {
+  padding-bottom: 0.5rem !important;
+}
+
+.pl-2 {
+  padding-left: 0.5rem !important;
+}
+
+.px-2 {
+  padding-right: 0.5rem !important;
+  padding-left: 0.5rem !important;
+}
+
+.py-2 {
+  padding-top: 0.5rem !important;
+  padding-bottom: 0.5rem !important;
+}
+
+.p-3 {
+  padding: 0.75rem !important;
+}
+
+.pt-3 {
+  padding-top: 0.75rem !important;
+}
+
+.pr-3 {
+  padding-right: 0.75rem !important;
+}
+
+.pb-3 {
+  padding-bottom: 0.75rem !important;
+}
+
+.pl-3 {
+  padding-left: 0.75rem !important;
+}
+
+.px-3 {
+  padding-right: 0.75rem !important;
+  padding-left: 0.75rem !important;
+}
+
+.py-3 {
+  padding-top: 0.75rem !important;
+  padding-bottom: 0.75rem !important;
+}
+
+.p-4 {
+  padding: 1rem !important;
+}
+
+.pt-4 {
+  padding-top: 1rem !important;
+}
+
+.pr-4 {
+  padding-right: 1rem !important;
+}
+
+.pb-4 {
+  padding-bottom: 1rem !important;
+}
+
+.pl-4 {
+  padding-left: 1rem !important;
+}
+
+.px-4 {
+  padding-right: 1rem !important;
+  padding-left: 1rem !important;
+}
+
+.py-4 {
+  padding-top: 1rem !important;
+  padding-bottom: 1rem !important;
+}
+
+.p-5 {
+  padding: 1.5rem !important;
+}
+
+.pt-5 {
+  padding-top: 1.5rem !important;
+}
+
+.pr-5 {
+  padding-right: 1.5rem !important;
+}
+
+.pb-5 {
+  padding-bottom: 1.5rem !important;
+}
+
+.pl-5 {
+  padding-left: 1.5rem !important;
+}
+
+.px-5 {
+  padding-right: 1.5rem !important;
+  padding-left: 1.5rem !important;
+}
+
+.py-5 {
+  padding-top: 1.5rem !important;
+  padding-bottom: 1.5rem !important;
+}
+
+.p-6 {
+  padding: 2rem !important;
+}
+
+.pt-6 {
+  padding-top: 2rem !important;
+}
+
+.pr-6 {
+  padding-right: 2rem !important;
+}
+
+.pb-6 {
+  padding-bottom: 2rem !important;
+}
+
+.pl-6 {
+  padding-left: 2rem !important;
+}
+
+.px-6 {
+  padding-right: 2rem !important;
+  padding-left: 2rem !important;
+}
+
+.py-6 {
+  padding-top: 2rem !important;
+  padding-bottom: 2rem !important;
+}
+
+.p-7 {
+  padding: 2.5rem !important;
+}
+
+.pt-7 {
+  padding-top: 2.5rem !important;
+}
+
+.pr-7 {
+  padding-right: 2.5rem !important;
+}
+
+.pb-7 {
+  padding-bottom: 2.5rem !important;
+}
+
+.pl-7 {
+  padding-left: 2.5rem !important;
+}
+
+.px-7 {
+  padding-right: 2.5rem !important;
+  padding-left: 2.5rem !important;
+}
+
+.py-7 {
+  padding-top: 2.5rem !important;
+  padding-bottom: 2.5rem !important;
+}
+
+.p-8 {
+  padding: 3rem !important;
+}
+
+.pt-8 {
+  padding-top: 3rem !important;
+}
+
+.pr-8 {
+  padding-right: 3rem !important;
+}
+
+.pb-8 {
+  padding-bottom: 3rem !important;
+}
+
+.pl-8 {
+  padding-left: 3rem !important;
+}
+
+.px-8 {
+  padding-right: 3rem !important;
+  padding-left: 3rem !important;
+}
+
+.py-8 {
+  padding-top: 3rem !important;
+  padding-bottom: 3rem !important;
+}
+
+.p-9 {
+  padding: 3.5rem !important;
+}
+
+.pt-9 {
+  padding-top: 3.5rem !important;
+}
+
+.pr-9 {
+  padding-right: 3.5rem !important;
+}
+
+.pb-9 {
+  padding-bottom: 3.5rem !important;
+}
+
+.pl-9 {
+  padding-left: 3.5rem !important;
+}
+
+.px-9 {
+  padding-right: 3.5rem !important;
+  padding-left: 3.5rem !important;
+}
+
+.py-9 {
+  padding-top: 3.5rem !important;
+  padding-bottom: 3.5rem !important;
+}
+
+.p-10 {
+  padding: 4rem !important;
+}
+
+.pt-10 {
+  padding-top: 4rem !important;
+}
+
+.pr-10 {
+  padding-right: 4rem !important;
+}
+
+.pb-10 {
+  padding-bottom: 4rem !important;
+}
+
+.pl-10 {
+  padding-left: 4rem !important;
+}
+
+.px-10 {
+  padding-right: 4rem !important;
+  padding-left: 4rem !important;
+}
+
+.py-10 {
+  padding-top: 4rem !important;
+  padding-bottom: 4rem !important;
+}
+
+@media (min-width: 20rem) {
+  .p-xs-0 {
+    padding: 0 !important;
+  }
+  .pt-xs-0 {
+    padding-top: 0 !important;
+  }
+  .pr-xs-0 {
+    padding-right: 0 !important;
+  }
+  .pb-xs-0 {
+    padding-bottom: 0 !important;
+  }
+  .pl-xs-0 {
+    padding-left: 0 !important;
+  }
+  .px-xs-0 {
+    padding-right: 0 !important;
+    padding-left: 0 !important;
+  }
+  .py-xs-0 {
+    padding-top: 0 !important;
+    padding-bottom: 0 !important;
+  }
+  .p-xs-1 {
+    padding: 0.25rem !important;
+  }
+  .pt-xs-1 {
+    padding-top: 0.25rem !important;
+  }
+  .pr-xs-1 {
+    padding-right: 0.25rem !important;
+  }
+  .pb-xs-1 {
+    padding-bottom: 0.25rem !important;
+  }
+  .pl-xs-1 {
+    padding-left: 0.25rem !important;
+  }
+  .px-xs-1 {
+    padding-right: 0.25rem !important;
+    padding-left: 0.25rem !important;
+  }
+  .py-xs-1 {
+    padding-top: 0.25rem !important;
+    padding-bottom: 0.25rem !important;
+  }
+  .p-xs-2 {
+    padding: 0.5rem !important;
+  }
+  .pt-xs-2 {
+    padding-top: 0.5rem !important;
+  }
+  .pr-xs-2 {
+    padding-right: 0.5rem !important;
+  }
+  .pb-xs-2 {
+    padding-bottom: 0.5rem !important;
+  }
+  .pl-xs-2 {
+    padding-left: 0.5rem !important;
+  }
+  .px-xs-2 {
+    padding-right: 0.5rem !important;
+    padding-left: 0.5rem !important;
+  }
+  .py-xs-2 {
+    padding-top: 0.5rem !important;
+    padding-bottom: 0.5rem !important;
+  }
+  .p-xs-3 {
+    padding: 0.75rem !important;
+  }
+  .pt-xs-3 {
+    padding-top: 0.75rem !important;
+  }
+  .pr-xs-3 {
+    padding-right: 0.75rem !important;
+  }
+  .pb-xs-3 {
+    padding-bottom: 0.75rem !important;
+  }
+  .pl-xs-3 {
+    padding-left: 0.75rem !important;
+  }
+  .px-xs-3 {
+    padding-right: 0.75rem !important;
+    padding-left: 0.75rem !important;
+  }
+  .py-xs-3 {
+    padding-top: 0.75rem !important;
+    padding-bottom: 0.75rem !important;
+  }
+  .p-xs-4 {
+    padding: 1rem !important;
+  }
+  .pt-xs-4 {
+    padding-top: 1rem !important;
+  }
+  .pr-xs-4 {
+    padding-right: 1rem !important;
+  }
+  .pb-xs-4 {
+    padding-bottom: 1rem !important;
+  }
+  .pl-xs-4 {
+    padding-left: 1rem !important;
+  }
+  .px-xs-4 {
+    padding-right: 1rem !important;
+    padding-left: 1rem !important;
+  }
+  .py-xs-4 {
+    padding-top: 1rem !important;
+    padding-bottom: 1rem !important;
+  }
+  .p-xs-5 {
+    padding: 1.5rem !important;
+  }
+  .pt-xs-5 {
+    padding-top: 1.5rem !important;
+  }
+  .pr-xs-5 {
+    padding-right: 1.5rem !important;
+  }
+  .pb-xs-5 {
+    padding-bottom: 1.5rem !important;
+  }
+  .pl-xs-5 {
+    padding-left: 1.5rem !important;
+  }
+  .px-xs-5 {
+    padding-right: 1.5rem !important;
+    padding-left: 1.5rem !important;
+  }
+  .py-xs-5 {
+    padding-top: 1.5rem !important;
+    padding-bottom: 1.5rem !important;
+  }
+  .p-xs-6 {
+    padding: 2rem !important;
+  }
+  .pt-xs-6 {
+    padding-top: 2rem !important;
+  }
+  .pr-xs-6 {
+    padding-right: 2rem !important;
+  }
+  .pb-xs-6 {
+    padding-bottom: 2rem !important;
+  }
+  .pl-xs-6 {
+    padding-left: 2rem !important;
+  }
+  .px-xs-6 {
+    padding-right: 2rem !important;
+    padding-left: 2rem !important;
+  }
+  .py-xs-6 {
+    padding-top: 2rem !important;
+    padding-bottom: 2rem !important;
+  }
+  .p-xs-7 {
+    padding: 2.5rem !important;
+  }
+  .pt-xs-7 {
+    padding-top: 2.5rem !important;
+  }
+  .pr-xs-7 {
+    padding-right: 2.5rem !important;
+  }
+  .pb-xs-7 {
+    padding-bottom: 2.5rem !important;
+  }
+  .pl-xs-7 {
+    padding-left: 2.5rem !important;
+  }
+  .px-xs-7 {
+    padding-right: 2.5rem !important;
+    padding-left: 2.5rem !important;
+  }
+  .py-xs-7 {
+    padding-top: 2.5rem !important;
+    padding-bottom: 2.5rem !important;
+  }
+  .p-xs-8 {
+    padding: 3rem !important;
+  }
+  .pt-xs-8 {
+    padding-top: 3rem !important;
+  }
+  .pr-xs-8 {
+    padding-right: 3rem !important;
+  }
+  .pb-xs-8 {
+    padding-bottom: 3rem !important;
+  }
+  .pl-xs-8 {
+    padding-left: 3rem !important;
+  }
+  .px-xs-8 {
+    padding-right: 3rem !important;
+    padding-left: 3rem !important;
+  }
+  .py-xs-8 {
+    padding-top: 3rem !important;
+    padding-bottom: 3rem !important;
+  }
+  .p-xs-9 {
+    padding: 3.5rem !important;
+  }
+  .pt-xs-9 {
+    padding-top: 3.5rem !important;
+  }
+  .pr-xs-9 {
+    padding-right: 3.5rem !important;
+  }
+  .pb-xs-9 {
+    padding-bottom: 3.5rem !important;
+  }
+  .pl-xs-9 {
+    padding-left: 3.5rem !important;
+  }
+  .px-xs-9 {
+    padding-right: 3.5rem !important;
+    padding-left: 3.5rem !important;
+  }
+  .py-xs-9 {
+    padding-top: 3.5rem !important;
+    padding-bottom: 3.5rem !important;
+  }
+  .p-xs-10 {
+    padding: 4rem !important;
+  }
+  .pt-xs-10 {
+    padding-top: 4rem !important;
+  }
+  .pr-xs-10 {
+    padding-right: 4rem !important;
+  }
+  .pb-xs-10 {
+    padding-bottom: 4rem !important;
+  }
+  .pl-xs-10 {
+    padding-left: 4rem !important;
+  }
+  .px-xs-10 {
+    padding-right: 4rem !important;
+    padding-left: 4rem !important;
+  }
+  .py-xs-10 {
+    padding-top: 4rem !important;
+    padding-bottom: 4rem !important;
+  }
+}
+@media (min-width: 31.25rem) {
+  .p-sm-0 {
+    padding: 0 !important;
+  }
+  .pt-sm-0 {
+    padding-top: 0 !important;
+  }
+  .pr-sm-0 {
+    padding-right: 0 !important;
+  }
+  .pb-sm-0 {
+    padding-bottom: 0 !important;
+  }
+  .pl-sm-0 {
+    padding-left: 0 !important;
+  }
+  .px-sm-0 {
+    padding-right: 0 !important;
+    padding-left: 0 !important;
+  }
+  .py-sm-0 {
+    padding-top: 0 !important;
+    padding-bottom: 0 !important;
+  }
+  .p-sm-1 {
+    padding: 0.25rem !important;
+  }
+  .pt-sm-1 {
+    padding-top: 0.25rem !important;
+  }
+  .pr-sm-1 {
+    padding-right: 0.25rem !important;
+  }
+  .pb-sm-1 {
+    padding-bottom: 0.25rem !important;
+  }
+  .pl-sm-1 {
+    padding-left: 0.25rem !important;
+  }
+  .px-sm-1 {
+    padding-right: 0.25rem !important;
+    padding-left: 0.25rem !important;
+  }
+  .py-sm-1 {
+    padding-top: 0.25rem !important;
+    padding-bottom: 0.25rem !important;
+  }
+  .p-sm-2 {
+    padding: 0.5rem !important;
+  }
+  .pt-sm-2 {
+    padding-top: 0.5rem !important;
+  }
+  .pr-sm-2 {
+    padding-right: 0.5rem !important;
+  }
+  .pb-sm-2 {
+    padding-bottom: 0.5rem !important;
+  }
+  .pl-sm-2 {
+    padding-left: 0.5rem !important;
+  }
+  .px-sm-2 {
+    padding-right: 0.5rem !important;
+    padding-left: 0.5rem !important;
+  }
+  .py-sm-2 {
+    padding-top: 0.5rem !important;
+    padding-bottom: 0.5rem !important;
+  }
+  .p-sm-3 {
+    padding: 0.75rem !important;
+  }
+  .pt-sm-3 {
+    padding-top: 0.75rem !important;
+  }
+  .pr-sm-3 {
+    padding-right: 0.75rem !important;
+  }
+  .pb-sm-3 {
+    padding-bottom: 0.75rem !important;
+  }
+  .pl-sm-3 {
+    padding-left: 0.75rem !important;
+  }
+  .px-sm-3 {
+    padding-right: 0.75rem !important;
+    padding-left: 0.75rem !important;
+  }
+  .py-sm-3 {
+    padding-top: 0.75rem !important;
+    padding-bottom: 0.75rem !important;
+  }
+  .p-sm-4 {
+    padding: 1rem !important;
+  }
+  .pt-sm-4 {
+    padding-top: 1rem !important;
+  }
+  .pr-sm-4 {
+    padding-right: 1rem !important;
+  }
+  .pb-sm-4 {
+    padding-bottom: 1rem !important;
+  }
+  .pl-sm-4 {
+    padding-left: 1rem !important;
+  }
+  .px-sm-4 {
+    padding-right: 1rem !important;
+    padding-left: 1rem !important;
+  }
+  .py-sm-4 {
+    padding-top: 1rem !important;
+    padding-bottom: 1rem !important;
+  }
+  .p-sm-5 {
+    padding: 1.5rem !important;
+  }
+  .pt-sm-5 {
+    padding-top: 1.5rem !important;
+  }
+  .pr-sm-5 {
+    padding-right: 1.5rem !important;
+  }
+  .pb-sm-5 {
+    padding-bottom: 1.5rem !important;
+  }
+  .pl-sm-5 {
+    padding-left: 1.5rem !important;
+  }
+  .px-sm-5 {
+    padding-right: 1.5rem !important;
+    padding-left: 1.5rem !important;
+  }
+  .py-sm-5 {
+    padding-top: 1.5rem !important;
+    padding-bottom: 1.5rem !important;
+  }
+  .p-sm-6 {
+    padding: 2rem !important;
+  }
+  .pt-sm-6 {
+    padding-top: 2rem !important;
+  }
+  .pr-sm-6 {
+    padding-right: 2rem !important;
+  }
+  .pb-sm-6 {
+    padding-bottom: 2rem !important;
+  }
+  .pl-sm-6 {
+    padding-left: 2rem !important;
+  }
+  .px-sm-6 {
+    padding-right: 2rem !important;
+    padding-left: 2rem !important;
+  }
+  .py-sm-6 {
+    padding-top: 2rem !important;
+    padding-bottom: 2rem !important;
+  }
+  .p-sm-7 {
+    padding: 2.5rem !important;
+  }
+  .pt-sm-7 {
+    padding-top: 2.5rem !important;
+  }
+  .pr-sm-7 {
+    padding-right: 2.5rem !important;
+  }
+  .pb-sm-7 {
+    padding-bottom: 2.5rem !important;
+  }
+  .pl-sm-7 {
+    padding-left: 2.5rem !important;
+  }
+  .px-sm-7 {
+    padding-right: 2.5rem !important;
+    padding-left: 2.5rem !important;
+  }
+  .py-sm-7 {
+    padding-top: 2.5rem !important;
+    padding-bottom: 2.5rem !important;
+  }
+  .p-sm-8 {
+    padding: 3rem !important;
+  }
+  .pt-sm-8 {
+    padding-top: 3rem !important;
+  }
+  .pr-sm-8 {
+    padding-right: 3rem !important;
+  }
+  .pb-sm-8 {
+    padding-bottom: 3rem !important;
+  }
+  .pl-sm-8 {
+    padding-left: 3rem !important;
+  }
+  .px-sm-8 {
+    padding-right: 3rem !important;
+    padding-left: 3rem !important;
+  }
+  .py-sm-8 {
+    padding-top: 3rem !important;
+    padding-bottom: 3rem !important;
+  }
+  .p-sm-9 {
+    padding: 3.5rem !important;
+  }
+  .pt-sm-9 {
+    padding-top: 3.5rem !important;
+  }
+  .pr-sm-9 {
+    padding-right: 3.5rem !important;
+  }
+  .pb-sm-9 {
+    padding-bottom: 3.5rem !important;
+  }
+  .pl-sm-9 {
+    padding-left: 3.5rem !important;
+  }
+  .px-sm-9 {
+    padding-right: 3.5rem !important;
+    padding-left: 3.5rem !important;
+  }
+  .py-sm-9 {
+    padding-top: 3.5rem !important;
+    padding-bottom: 3.5rem !important;
+  }
+  .p-sm-10 {
+    padding: 4rem !important;
+  }
+  .pt-sm-10 {
+    padding-top: 4rem !important;
+  }
+  .pr-sm-10 {
+    padding-right: 4rem !important;
+  }
+  .pb-sm-10 {
+    padding-bottom: 4rem !important;
+  }
+  .pl-sm-10 {
+    padding-left: 4rem !important;
+  }
+  .px-sm-10 {
+    padding-right: 4rem !important;
+    padding-left: 4rem !important;
+  }
+  .py-sm-10 {
+    padding-top: 4rem !important;
+    padding-bottom: 4rem !important;
+  }
+}
+@media (min-width: 50rem) {
+  .p-md-0 {
+    padding: 0 !important;
+  }
+  .pt-md-0 {
+    padding-top: 0 !important;
+  }
+  .pr-md-0 {
+    padding-right: 0 !important;
+  }
+  .pb-md-0 {
+    padding-bottom: 0 !important;
+  }
+  .pl-md-0 {
+    padding-left: 0 !important;
+  }
+  .px-md-0 {
+    padding-right: 0 !important;
+    padding-left: 0 !important;
+  }
+  .py-md-0 {
+    padding-top: 0 !important;
+    padding-bottom: 0 !important;
+  }
+  .p-md-1 {
+    padding: 0.25rem !important;
+  }
+  .pt-md-1 {
+    padding-top: 0.25rem !important;
+  }
+  .pr-md-1 {
+    padding-right: 0.25rem !important;
+  }
+  .pb-md-1 {
+    padding-bottom: 0.25rem !important;
+  }
+  .pl-md-1 {
+    padding-left: 0.25rem !important;
+  }
+  .px-md-1 {
+    padding-right: 0.25rem !important;
+    padding-left: 0.25rem !important;
+  }
+  .py-md-1 {
+    padding-top: 0.25rem !important;
+    padding-bottom: 0.25rem !important;
+  }
+  .p-md-2 {
+    padding: 0.5rem !important;
+  }
+  .pt-md-2 {
+    padding-top: 0.5rem !important;
+  }
+  .pr-md-2 {
+    padding-right: 0.5rem !important;
+  }
+  .pb-md-2 {
+    padding-bottom: 0.5rem !important;
+  }
+  .pl-md-2 {
+    padding-left: 0.5rem !important;
+  }
+  .px-md-2 {
+    padding-right: 0.5rem !important;
+    padding-left: 0.5rem !important;
+  }
+  .py-md-2 {
+    padding-top: 0.5rem !important;
+    padding-bottom: 0.5rem !important;
+  }
+  .p-md-3 {
+    padding: 0.75rem !important;
+  }
+  .pt-md-3 {
+    padding-top: 0.75rem !important;
+  }
+  .pr-md-3 {
+    padding-right: 0.75rem !important;
+  }
+  .pb-md-3 {
+    padding-bottom: 0.75rem !important;
+  }
+  .pl-md-3 {
+    padding-left: 0.75rem !important;
+  }
+  .px-md-3 {
+    padding-right: 0.75rem !important;
+    padding-left: 0.75rem !important;
+  }
+  .py-md-3 {
+    padding-top: 0.75rem !important;
+    padding-bottom: 0.75rem !important;
+  }
+  .p-md-4 {
+    padding: 1rem !important;
+  }
+  .pt-md-4 {
+    padding-top: 1rem !important;
+  }
+  .pr-md-4 {
+    padding-right: 1rem !important;
+  }
+  .pb-md-4 {
+    padding-bottom: 1rem !important;
+  }
+  .pl-md-4 {
+    padding-left: 1rem !important;
+  }
+  .px-md-4 {
+    padding-right: 1rem !important;
+    padding-left: 1rem !important;
+  }
+  .py-md-4 {
+    padding-top: 1rem !important;
+    padding-bottom: 1rem !important;
+  }
+  .p-md-5 {
+    padding: 1.5rem !important;
+  }
+  .pt-md-5 {
+    padding-top: 1.5rem !important;
+  }
+  .pr-md-5 {
+    padding-right: 1.5rem !important;
+  }
+  .pb-md-5 {
+    padding-bottom: 1.5rem !important;
+  }
+  .pl-md-5 {
+    padding-left: 1.5rem !important;
+  }
+  .px-md-5 {
+    padding-right: 1.5rem !important;
+    padding-left: 1.5rem !important;
+  }
+  .py-md-5 {
+    padding-top: 1.5rem !important;
+    padding-bottom: 1.5rem !important;
+  }
+  .p-md-6 {
+    padding: 2rem !important;
+  }
+  .pt-md-6 {
+    padding-top: 2rem !important;
+  }
+  .pr-md-6 {
+    padding-right: 2rem !important;
+  }
+  .pb-md-6 {
+    padding-bottom: 2rem !important;
+  }
+  .pl-md-6 {
+    padding-left: 2rem !important;
+  }
+  .px-md-6 {
+    padding-right: 2rem !important;
+    padding-left: 2rem !important;
+  }
+  .py-md-6 {
+    padding-top: 2rem !important;
+    padding-bottom: 2rem !important;
+  }
+  .p-md-7 {
+    padding: 2.5rem !important;
+  }
+  .pt-md-7 {
+    padding-top: 2.5rem !important;
+  }
+  .pr-md-7 {
+    padding-right: 2.5rem !important;
+  }
+  .pb-md-7 {
+    padding-bottom: 2.5rem !important;
+  }
+  .pl-md-7 {
+    padding-left: 2.5rem !important;
+  }
+  .px-md-7 {
+    padding-right: 2.5rem !important;
+    padding-left: 2.5rem !important;
+  }
+  .py-md-7 {
+    padding-top: 2.5rem !important;
+    padding-bottom: 2.5rem !important;
+  }
+  .p-md-8 {
+    padding: 3rem !important;
+  }
+  .pt-md-8 {
+    padding-top: 3rem !important;
+  }
+  .pr-md-8 {
+    padding-right: 3rem !important;
+  }
+  .pb-md-8 {
+    padding-bottom: 3rem !important;
+  }
+  .pl-md-8 {
+    padding-left: 3rem !important;
+  }
+  .px-md-8 {
+    padding-right: 3rem !important;
+    padding-left: 3rem !important;
+  }
+  .py-md-8 {
+    padding-top: 3rem !important;
+    padding-bottom: 3rem !important;
+  }
+  .p-md-9 {
+    padding: 3.5rem !important;
+  }
+  .pt-md-9 {
+    padding-top: 3.5rem !important;
+  }
+  .pr-md-9 {
+    padding-right: 3.5rem !important;
+  }
+  .pb-md-9 {
+    padding-bottom: 3.5rem !important;
+  }
+  .pl-md-9 {
+    padding-left: 3.5rem !important;
+  }
+  .px-md-9 {
+    padding-right: 3.5rem !important;
+    padding-left: 3.5rem !important;
+  }
+  .py-md-9 {
+    padding-top: 3.5rem !important;
+    padding-bottom: 3.5rem !important;
+  }
+  .p-md-10 {
+    padding: 4rem !important;
+  }
+  .pt-md-10 {
+    padding-top: 4rem !important;
+  }
+  .pr-md-10 {
+    padding-right: 4rem !important;
+  }
+  .pb-md-10 {
+    padding-bottom: 4rem !important;
+  }
+  .pl-md-10 {
+    padding-left: 4rem !important;
+  }
+  .px-md-10 {
+    padding-right: 4rem !important;
+    padding-left: 4rem !important;
+  }
+  .py-md-10 {
+    padding-top: 4rem !important;
+    padding-bottom: 4rem !important;
+  }
+}
+@media (min-width: 66.5rem) {
+  .p-lg-0 {
+    padding: 0 !important;
+  }
+  .pt-lg-0 {
+    padding-top: 0 !important;
+  }
+  .pr-lg-0 {
+    padding-right: 0 !important;
+  }
+  .pb-lg-0 {
+    padding-bottom: 0 !important;
+  }
+  .pl-lg-0 {
+    padding-left: 0 !important;
+  }
+  .px-lg-0 {
+    padding-right: 0 !important;
+    padding-left: 0 !important;
+  }
+  .py-lg-0 {
+    padding-top: 0 !important;
+    padding-bottom: 0 !important;
+  }
+  .p-lg-1 {
+    padding: 0.25rem !important;
+  }
+  .pt-lg-1 {
+    padding-top: 0.25rem !important;
+  }
+  .pr-lg-1 {
+    padding-right: 0.25rem !important;
+  }
+  .pb-lg-1 {
+    padding-bottom: 0.25rem !important;
+  }
+  .pl-lg-1 {
+    padding-left: 0.25rem !important;
+  }
+  .px-lg-1 {
+    padding-right: 0.25rem !important;
+    padding-left: 0.25rem !important;
+  }
+  .py-lg-1 {
+    padding-top: 0.25rem !important;
+    padding-bottom: 0.25rem !important;
+  }
+  .p-lg-2 {
+    padding: 0.5rem !important;
+  }
+  .pt-lg-2 {
+    padding-top: 0.5rem !important;
+  }
+  .pr-lg-2 {
+    padding-right: 0.5rem !important;
+  }
+  .pb-lg-2 {
+    padding-bottom: 0.5rem !important;
+  }
+  .pl-lg-2 {
+    padding-left: 0.5rem !important;
+  }
+  .px-lg-2 {
+    padding-right: 0.5rem !important;
+    padding-left: 0.5rem !important;
+  }
+  .py-lg-2 {
+    padding-top: 0.5rem !important;
+    padding-bottom: 0.5rem !important;
+  }
+  .p-lg-3 {
+    padding: 0.75rem !important;
+  }
+  .pt-lg-3 {
+    padding-top: 0.75rem !important;
+  }
+  .pr-lg-3 {
+    padding-right: 0.75rem !important;
+  }
+  .pb-lg-3 {
+    padding-bottom: 0.75rem !important;
+  }
+  .pl-lg-3 {
+    padding-left: 0.75rem !important;
+  }
+  .px-lg-3 {
+    padding-right: 0.75rem !important;
+    padding-left: 0.75rem !important;
+  }
+  .py-lg-3 {
+    padding-top: 0.75rem !important;
+    padding-bottom: 0.75rem !important;
+  }
+  .p-lg-4 {
+    padding: 1rem !important;
+  }
+  .pt-lg-4 {
+    padding-top: 1rem !important;
+  }
+  .pr-lg-4 {
+    padding-right: 1rem !important;
+  }
+  .pb-lg-4 {
+    padding-bottom: 1rem !important;
+  }
+  .pl-lg-4 {
+    padding-left: 1rem !important;
+  }
+  .px-lg-4 {
+    padding-right: 1rem !important;
+    padding-left: 1rem !important;
+  }
+  .py-lg-4 {
+    padding-top: 1rem !important;
+    padding-bottom: 1rem !important;
+  }
+  .p-lg-5 {
+    padding: 1.5rem !important;
+  }
+  .pt-lg-5 {
+    padding-top: 1.5rem !important;
+  }
+  .pr-lg-5 {
+    padding-right: 1.5rem !important;
+  }
+  .pb-lg-5 {
+    padding-bottom: 1.5rem !important;
+  }
+  .pl-lg-5 {
+    padding-left: 1.5rem !important;
+  }
+  .px-lg-5 {
+    padding-right: 1.5rem !important;
+    padding-left: 1.5rem !important;
+  }
+  .py-lg-5 {
+    padding-top: 1.5rem !important;
+    padding-bottom: 1.5rem !important;
+  }
+  .p-lg-6 {
+    padding: 2rem !important;
+  }
+  .pt-lg-6 {
+    padding-top: 2rem !important;
+  }
+  .pr-lg-6 {
+    padding-right: 2rem !important;
+  }
+  .pb-lg-6 {
+    padding-bottom: 2rem !important;
+  }
+  .pl-lg-6 {
+    padding-left: 2rem !important;
+  }
+  .px-lg-6 {
+    padding-right: 2rem !important;
+    padding-left: 2rem !important;
+  }
+  .py-lg-6 {
+    padding-top: 2rem !important;
+    padding-bottom: 2rem !important;
+  }
+  .p-lg-7 {
+    padding: 2.5rem !important;
+  }
+  .pt-lg-7 {
+    padding-top: 2.5rem !important;
+  }
+  .pr-lg-7 {
+    padding-right: 2.5rem !important;
+  }
+  .pb-lg-7 {
+    padding-bottom: 2.5rem !important;
+  }
+  .pl-lg-7 {
+    padding-left: 2.5rem !important;
+  }
+  .px-lg-7 {
+    padding-right: 2.5rem !important;
+    padding-left: 2.5rem !important;
+  }
+  .py-lg-7 {
+    padding-top: 2.5rem !important;
+    padding-bottom: 2.5rem !important;
+  }
+  .p-lg-8 {
+    padding: 3rem !important;
+  }
+  .pt-lg-8 {
+    padding-top: 3rem !important;
+  }
+  .pr-lg-8 {
+    padding-right: 3rem !important;
+  }
+  .pb-lg-8 {
+    padding-bottom: 3rem !important;
+  }
+  .pl-lg-8 {
+    padding-left: 3rem !important;
+  }
+  .px-lg-8 {
+    padding-right: 3rem !important;
+    padding-left: 3rem !important;
+  }
+  .py-lg-8 {
+    padding-top: 3rem !important;
+    padding-bottom: 3rem !important;
+  }
+  .p-lg-9 {
+    padding: 3.5rem !important;
+  }
+  .pt-lg-9 {
+    padding-top: 3.5rem !important;
+  }
+  .pr-lg-9 {
+    padding-right: 3.5rem !important;
+  }
+  .pb-lg-9 {
+    padding-bottom: 3.5rem !important;
+  }
+  .pl-lg-9 {
+    padding-left: 3.5rem !important;
+  }
+  .px-lg-9 {
+    padding-right: 3.5rem !important;
+    padding-left: 3.5rem !important;
+  }
+  .py-lg-9 {
+    padding-top: 3.5rem !important;
+    padding-bottom: 3.5rem !important;
+  }
+  .p-lg-10 {
+    padding: 4rem !important;
+  }
+  .pt-lg-10 {
+    padding-top: 4rem !important;
+  }
+  .pr-lg-10 {
+    padding-right: 4rem !important;
+  }
+  .pb-lg-10 {
+    padding-bottom: 4rem !important;
+  }
+  .pl-lg-10 {
+    padding-left: 4rem !important;
+  }
+  .px-lg-10 {
+    padding-right: 4rem !important;
+    padding-left: 4rem !important;
+  }
+  .py-lg-10 {
+    padding-top: 4rem !important;
+    padding-bottom: 4rem !important;
+  }
+}
+@media (min-width: 87.5rem) {
+  .p-xl-0 {
+    padding: 0 !important;
+  }
+  .pt-xl-0 {
+    padding-top: 0 !important;
+  }
+  .pr-xl-0 {
+    padding-right: 0 !important;
+  }
+  .pb-xl-0 {
+    padding-bottom: 0 !important;
+  }
+  .pl-xl-0 {
+    padding-left: 0 !important;
+  }
+  .px-xl-0 {
+    padding-right: 0 !important;
+    padding-left: 0 !important;
+  }
+  .py-xl-0 {
+    padding-top: 0 !important;
+    padding-bottom: 0 !important;
+  }
+  .p-xl-1 {
+    padding: 0.25rem !important;
+  }
+  .pt-xl-1 {
+    padding-top: 0.25rem !important;
+  }
+  .pr-xl-1 {
+    padding-right: 0.25rem !important;
+  }
+  .pb-xl-1 {
+    padding-bottom: 0.25rem !important;
+  }
+  .pl-xl-1 {
+    padding-left: 0.25rem !important;
+  }
+  .px-xl-1 {
+    padding-right: 0.25rem !important;
+    padding-left: 0.25rem !important;
+  }
+  .py-xl-1 {
+    padding-top: 0.25rem !important;
+    padding-bottom: 0.25rem !important;
+  }
+  .p-xl-2 {
+    padding: 0.5rem !important;
+  }
+  .pt-xl-2 {
+    padding-top: 0.5rem !important;
+  }
+  .pr-xl-2 {
+    padding-right: 0.5rem !important;
+  }
+  .pb-xl-2 {
+    padding-bottom: 0.5rem !important;
+  }
+  .pl-xl-2 {
+    padding-left: 0.5rem !important;
+  }
+  .px-xl-2 {
+    padding-right: 0.5rem !important;
+    padding-left: 0.5rem !important;
+  }
+  .py-xl-2 {
+    padding-top: 0.5rem !important;
+    padding-bottom: 0.5rem !important;
+  }
+  .p-xl-3 {
+    padding: 0.75rem !important;
+  }
+  .pt-xl-3 {
+    padding-top: 0.75rem !important;
+  }
+  .pr-xl-3 {
+    padding-right: 0.75rem !important;
+  }
+  .pb-xl-3 {
+    padding-bottom: 0.75rem !important;
+  }
+  .pl-xl-3 {
+    padding-left: 0.75rem !important;
+  }
+  .px-xl-3 {
+    padding-right: 0.75rem !important;
+    padding-left: 0.75rem !important;
+  }
+  .py-xl-3 {
+    padding-top: 0.75rem !important;
+    padding-bottom: 0.75rem !important;
+  }
+  .p-xl-4 {
+    padding: 1rem !important;
+  }
+  .pt-xl-4 {
+    padding-top: 1rem !important;
+  }
+  .pr-xl-4 {
+    padding-right: 1rem !important;
+  }
+  .pb-xl-4 {
+    padding-bottom: 1rem !important;
+  }
+  .pl-xl-4 {
+    padding-left: 1rem !important;
+  }
+  .px-xl-4 {
+    padding-right: 1rem !important;
+    padding-left: 1rem !important;
+  }
+  .py-xl-4 {
+    padding-top: 1rem !important;
+    padding-bottom: 1rem !important;
+  }
+  .p-xl-5 {
+    padding: 1.5rem !important;
+  }
+  .pt-xl-5 {
+    padding-top: 1.5rem !important;
+  }
+  .pr-xl-5 {
+    padding-right: 1.5rem !important;
+  }
+  .pb-xl-5 {
+    padding-bottom: 1.5rem !important;
+  }
+  .pl-xl-5 {
+    padding-left: 1.5rem !important;
+  }
+  .px-xl-5 {
+    padding-right: 1.5rem !important;
+    padding-left: 1.5rem !important;
+  }
+  .py-xl-5 {
+    padding-top: 1.5rem !important;
+    padding-bottom: 1.5rem !important;
+  }
+  .p-xl-6 {
+    padding: 2rem !important;
+  }
+  .pt-xl-6 {
+    padding-top: 2rem !important;
+  }
+  .pr-xl-6 {
+    padding-right: 2rem !important;
+  }
+  .pb-xl-6 {
+    padding-bottom: 2rem !important;
+  }
+  .pl-xl-6 {
+    padding-left: 2rem !important;
+  }
+  .px-xl-6 {
+    padding-right: 2rem !important;
+    padding-left: 2rem !important;
+  }
+  .py-xl-6 {
+    padding-top: 2rem !important;
+    padding-bottom: 2rem !important;
+  }
+  .p-xl-7 {
+    padding: 2.5rem !important;
+  }
+  .pt-xl-7 {
+    padding-top: 2.5rem !important;
+  }
+  .pr-xl-7 {
+    padding-right: 2.5rem !important;
+  }
+  .pb-xl-7 {
+    padding-bottom: 2.5rem !important;
+  }
+  .pl-xl-7 {
+    padding-left: 2.5rem !important;
+  }
+  .px-xl-7 {
+    padding-right: 2.5rem !important;
+    padding-left: 2.5rem !important;
+  }
+  .py-xl-7 {
+    padding-top: 2.5rem !important;
+    padding-bottom: 2.5rem !important;
+  }
+  .p-xl-8 {
+    padding: 3rem !important;
+  }
+  .pt-xl-8 {
+    padding-top: 3rem !important;
+  }
+  .pr-xl-8 {
+    padding-right: 3rem !important;
+  }
+  .pb-xl-8 {
+    padding-bottom: 3rem !important;
+  }
+  .pl-xl-8 {
+    padding-left: 3rem !important;
+  }
+  .px-xl-8 {
+    padding-right: 3rem !important;
+    padding-left: 3rem !important;
+  }
+  .py-xl-8 {
+    padding-top: 3rem !important;
+    padding-bottom: 3rem !important;
+  }
+  .p-xl-9 {
+    padding: 3.5rem !important;
+  }
+  .pt-xl-9 {
+    padding-top: 3.5rem !important;
+  }
+  .pr-xl-9 {
+    padding-right: 3.5rem !important;
+  }
+  .pb-xl-9 {
+    padding-bottom: 3.5rem !important;
+  }
+  .pl-xl-9 {
+    padding-left: 3.5rem !important;
+  }
+  .px-xl-9 {
+    padding-right: 3.5rem !important;
+    padding-left: 3.5rem !important;
+  }
+  .py-xl-9 {
+    padding-top: 3.5rem !important;
+    padding-bottom: 3.5rem !important;
+  }
+  .p-xl-10 {
+    padding: 4rem !important;
+  }
+  .pt-xl-10 {
+    padding-top: 4rem !important;
+  }
+  .pr-xl-10 {
+    padding-right: 4rem !important;
+  }
+  .pb-xl-10 {
+    padding-bottom: 4rem !important;
+  }
+  .pl-xl-10 {
+    padding-left: 4rem !important;
+  }
+  .px-xl-10 {
+    padding-right: 4rem !important;
+    padding-left: 4rem !important;
+  }
+  .py-xl-10 {
+    padding-top: 4rem !important;
+    padding-bottom: 4rem !important;
+  }
+}
+@media print {
+  .site-footer,
+  .site-button,
+  #edit-this-page,
+  #back-to-top,
+  .site-nav,
+  .main-header {
+    display: none !important;
+  }
+  .side-bar {
+    width: 100%;
+    height: auto;
+    border-right: 0 !important;
+  }
+  .site-header {
+    border-bottom: 1px solid #eeebee;
+  }
+  .site-title {
+    font-size: 16px !important;
+    font-weight: 700 !important;
+  }
+  .text-small {
+    font-size: 8pt !important;
+  }
+  pre.highlight {
+    border: 1px solid #eeebee;
+  }
+  .main {
+    max-width: none;
+    margin-left: 0;
+  }
+}
+a.skip-to-main {
+  left: -999px;
+  position: absolute;
+  top: auto;
+  width: 1px;
+  height: 1px;
+  overflow: hidden;
+  z-index: -999;
+}
+
+a.skip-to-main:focus,
+a.skip-to-main:active {
+  color: #0969da;
+  background-color: #fff;
+  left: auto;
+  top: auto;
+  width: 30%;
+  height: auto;
+  overflow: auto;
+  margin: 10px 35%;
+  padding: 5px;
+  border-radius: 15px;
+  border: 4px solid #5e41d0;
+  text-align: center;
+  font-size: 1.2em;
+  z-index: 999;
+}
+
+div.opaque {
+  background-color: #fff;
+}
+
+p.highlight, blockquote.highlight {
+  background: rgba(255, 235, 130, 0.2);
+  border-left: 4px solid #e7af06;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  padding: 0.8rem;
+}
+p.highlight > .highlight-title, blockquote.highlight > .highlight-title {
+  color: #e7af06;
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+
+p.highlight-title, blockquote.highlight-title {
+  background: rgba(255, 235, 130, 0.2);
+  border-left: 4px solid #e7af06;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  padding: 0.8rem;
+}
+p.highlight-title > p:first-child, blockquote.highlight-title > p:first-child {
+  margin-top: 0;
+  margin-bottom: 0;
+  color: #e7af06;
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+
+blockquote.highlight {
+  margin-left: 0;
+  margin-right: 0;
+}
+blockquote.highlight > p:first-child {
+  margin-top: 0;
+}
+blockquote.highlight > p:last-child {
+  margin-bottom: 0;
+}
+
+blockquote.highlight-title {
+  margin-left: 0;
+  margin-right: 0;
+}
+blockquote.highlight-title > p:nth-child(2) {
+  margin-top: 0;
+}
+blockquote.highlight-title > p:last-child {
+  margin-bottom: 0;
+}
+
+p.important, blockquote.important {
+  background: rgba(44, 132, 250, 0.2);
+  border-left: 4px solid #183385;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  padding: 0.8rem;
+}
+p.important::before, blockquote.important::before {
+  color: #183385;
+  content: "Important";
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+p.important > .important-title, blockquote.important > .important-title {
+  color: #183385;
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+
+p.important-title, blockquote.important-title {
+  background: rgba(44, 132, 250, 0.2);
+  border-left: 4px solid #183385;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  padding: 0.8rem;
+}
+p.important-title > p:first-child, blockquote.important-title > p:first-child {
+  margin-top: 0;
+  margin-bottom: 0;
+  color: #183385;
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+
+blockquote.important {
+  margin-left: 0;
+  margin-right: 0;
+}
+blockquote.important > p:first-child {
+  margin-top: 0;
+}
+blockquote.important > p:last-child {
+  margin-bottom: 0;
+}
+
+blockquote.important-title {
+  margin-left: 0;
+  margin-right: 0;
+}
+blockquote.important-title > p:nth-child(2) {
+  margin-top: 0;
+}
+blockquote.important-title > p:last-child {
+  margin-bottom: 0;
+}
+
+p.new, blockquote.new {
+  background: rgba(65, 214, 147, 0.2);
+  border-left: 4px solid #026e57;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  padding: 0.8rem;
+}
+p.new::before, blockquote.new::before {
+  color: #026e57;
+  content: "New";
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+p.new > .new-title, blockquote.new > .new-title {
+  color: #026e57;
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+
+p.new-title, blockquote.new-title {
+  background: rgba(65, 214, 147, 0.2);
+  border-left: 4px solid #026e57;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  padding: 0.8rem;
+}
+p.new-title > p:first-child, blockquote.new-title > p:first-child {
+  margin-top: 0;
+  margin-bottom: 0;
+  color: #026e57;
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+
+blockquote.new {
+  margin-left: 0;
+  margin-right: 0;
+}
+blockquote.new > p:first-child {
+  margin-top: 0;
+}
+blockquote.new > p:last-child {
+  margin-bottom: 0;
+}
+
+blockquote.new-title {
+  margin-left: 0;
+  margin-right: 0;
+}
+blockquote.new-title > p:nth-child(2) {
+  margin-top: 0;
+}
+blockquote.new-title > p:last-child {
+  margin-bottom: 0;
+}
+
+p.note, blockquote.note {
+  background: rgba(114, 83, 237, 0.2);
+  border-left: 4px solid #381885;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  padding: 0.8rem;
+}
+p.note::before, blockquote.note::before {
+  color: #381885;
+  content: "Note";
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+p.note > .note-title, blockquote.note > .note-title {
+  color: #381885;
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+
+p.note-title, blockquote.note-title {
+  background: rgba(114, 83, 237, 0.2);
+  border-left: 4px solid #381885;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  padding: 0.8rem;
+}
+p.note-title > p:first-child, blockquote.note-title > p:first-child {
+  margin-top: 0;
+  margin-bottom: 0;
+  color: #381885;
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+
+blockquote.note {
+  margin-left: 0;
+  margin-right: 0;
+}
+blockquote.note > p:first-child {
+  margin-top: 0;
+}
+blockquote.note > p:last-child {
+  margin-bottom: 0;
+}
+
+blockquote.note-title {
+  margin-left: 0;
+  margin-right: 0;
+}
+blockquote.note-title > p:nth-child(2) {
+  margin-top: 0;
+}
+blockquote.note-title > p:last-child {
+  margin-bottom: 0;
+}
+
+p.warning, blockquote.warning {
+  background: rgba(247, 126, 126, 0.2);
+  border-left: 4px solid #dd2e2e;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  padding: 0.8rem;
+}
+p.warning::before, blockquote.warning::before {
+  color: #dd2e2e;
+  content: "Warning";
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+p.warning > .warning-title, blockquote.warning > .warning-title {
+  color: #dd2e2e;
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+
+p.warning-title, blockquote.warning-title {
+  background: rgba(247, 126, 126, 0.2);
+  border-left: 4px solid #dd2e2e;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
+  padding: 0.8rem;
+}
+p.warning-title > p:first-child, blockquote.warning-title > p:first-child {
+  margin-top: 0;
+  margin-bottom: 0;
+  color: #dd2e2e;
+  display: block;
+  font-weight: bold;
+  text-transform: uppercase;
+  font-size: 0.75em;
+  padding-bottom: 0.125rem;
+}
+
+blockquote.warning {
+  margin-left: 0;
+  margin-right: 0;
+}
+blockquote.warning > p:first-child {
+  margin-top: 0;
+}
+blockquote.warning > p:last-child {
+  margin-bottom: 0;
+}
+
+blockquote.warning-title {
+  margin-left: 0;
+  margin-right: 0;
+}
+blockquote.warning-title > p:nth-child(2) {
+  margin-top: 0;
+}
+blockquote.warning-title > p:last-child {
+  margin-bottom: 0;
+}
diff --git a/docs/static/js/scripts.js b/docs/static/js/scripts.js
deleted file mode 100644
index e69de29bb..000000000
diff --git a/docs/static/js/site.js b/docs/static/js/site.js
new file mode 100644
index 000000000..15f60c152
--- /dev/null
+++ b/docs/static/js/site.js
@@ -0,0 +1,111 @@
+(function (jtd, undefined) {
+
+// Event handling
+
+jtd.addEvent = function(el, type, handler) {
+  if (el.attachEvent) el.attachEvent('on'+type, handler); else el.addEventListener(type, handler);
+}
+jtd.removeEvent = function(el, type, handler) {
+  if (el.detachEvent) el.detachEvent('on'+type, handler); else el.removeEventListener(type, handler);
+}
+jtd.onReady = function(ready) {
+  // in case the document is already rendered
+  if (document.readyState!='loading') ready();
+  // modern browsers
+  else if (document.addEventListener) document.addEventListener('DOMContentLoaded', ready);
+  // IE <= 8
+  else document.attachEvent('onreadystatechange', function(){
+      if (document.readyState=='complete') ready();
+  });
+}
+
+// Show/hide mobile menu
+
+function initNav() {
+  jtd.addEvent(document, 'click', function(e){
+    var target = e.target;
+    while (target && !(target.classList && target.classList.contains('nav-list-expander'))) {
+      target = target.parentNode;
+    }
+    if (target) {
+      e.preventDefault();
+      target.parentNode.classList.toggle('active');
+    }
+  });
+
+  const siteNav = document.getElementById('site-nav');
+  const mainHeader = document.getElementById('main-header');
+  const menuButton = document.getElementById('menu-button');
+
+  jtd.addEvent(menuButton, 'click', function(e){
+    e.preventDefault();
+
+    if (menuButton.classList.toggle('nav-open')) {
+      siteNav.classList.add('nav-open');
+      mainHeader.classList.add('nav-open');
+    } else {
+      siteNav.classList.remove('nav-open');
+      mainHeader.classList.remove('nav-open');
+    }
+  });
+}
+
+// Scroll site-nav to ensure the link to the current page is visible
+
+function scrollNav() {
+  const href = document.location.pathname;
+  const siteNav = document.getElementById('site-nav');
+  const targetLink = siteNav.querySelector('a[href="' + href + '"], a[href="' + href + '/"]');
+  if(targetLink){
+    const rect = targetLink.getBoundingClientRect();
+    siteNav.scrollBy(0, rect.top - 3*rect.height);
+  }
+}
+
+// Document ready
+
+jtd.onReady(function(){
+  initNav();
+  scrollNav();
+});
+
+// Copy button on code
+
+jtd.onReady(function(){
+
+  var codeBlocks = document.querySelectorAll('div.highlight, div.listingblock, figure.highlight');
+
+  // note: the SVG svg-copied and svg-copy is only loaded as a Jekyll include if site.enable_copy_code_button is true; see _includes/icons/icons.html
+  var svgCopied =  '<svg viewBox="0 0 24 24" class="copy-icon"><use xlink:href="#svg-copied"></use></svg>';
+  var svgCopy =  '<svg viewBox="0 0 24 24" class="copy-icon"><use xlink:href="#svg-copy"></use></svg>';
+
+  codeBlocks.forEach(codeBlock => {
+    var copyButton = document.createElement('button');
+    var timeout = null;
+    copyButton.type = 'button';
+    copyButton.ariaLabel = 'Copy code to clipboard';
+    copyButton.innerHTML = svgCopy;
+    codeBlock.append(copyButton);
+
+    copyButton.addEventListener('click', function () {
+      if(timeout === null) {
+        var code = codeBlock.querySelector('pre:not(.lineno)').innerText;
+        window.navigator.clipboard.writeText(code);
+
+        copyButton.innerHTML = svgCopied;
+
+        var timeoutSetting = 4000;
+
+        timeout = setTimeout(function () {
+          copyButton.innerHTML = svgCopy;
+          timeout = null;
+        }, timeoutSetting);
+      }
+    });
+  });
+
+});
+
+})(window.jtd = window.jtd || {});
+
+

From 9ea84df9f2720bdaed4d041f11d6bf2fdb49d9de Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 21 Jan 2023 15:11:17 -0800
Subject: [PATCH 037/393] Update documentation to .NET 7.

---
 docs/content/home.md                       | 31 +++++++-------
 docs/content/overview/installing.md        |  8 ++--
 docs/content/overview/logging.md           |  4 +-
 docs/content/tutorials/basic-api.md        | 33 +++++++-------
 docs/content/tutorials/best-practices.md   | 50 ++++++++++------------
 docs/content/tutorials/connect-to-mysql.md | 23 +++++++---
 6 files changed, 79 insertions(+), 70 deletions(-)

diff --git a/docs/content/home.md b/docs/content/home.md
index 0151dcef8..b663d8a49 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2019-06-20
+lastmod: 2023-01-21
 date: 2016-10-16
 title: Home
 weight: 10
@@ -8,22 +8,17 @@ menu:
     url: "/"
 ---
 
-# MySqlConnector: High Performance MySQL Library for .NET
-
-* **Asynchronous:** True asynchronous I/O for maximum throughput
-* **Fast:** Optimized for speed and low memory usage
-* **Reliable:** Fixes [dozens of open bugs](/tutorials/migrating-from-connector-net/#fixed-bugs) in Oracle’s Connector/NET; passes all [ADO.NET Specification Tests](https://mysql-net.github.io/AdoNetResults/)
-* **Leading Edge:** First MySQL library to support .NET Core; uses the latest .NET features
-* **Open:** MIT license; development [happens on GitHub](https://github.com/mysql-net/MySqlConnector) with publicly visible roadmaps, issues, and PRs
+# MySqlConnector: High Performance .NET MySQL Driver
 
 ## About
 
-MySqlConnector is a C# [ADO.NET](https://msdn.microsoft.com/en-us/library/e80y5yhx.aspx) driver for [MySQL Server](https://www.mysql.com/), [MariaDB](https://mariadb.org/),
-[Percona Server](https://www.percona.com/software/mysql-database/percona-server), [Amazon Aurora](https://aws.amazon.com/rds/aurora/),
-[Azure Database for MySQL](https://azure.microsoft.com/en-us/services/mysql/),
-[Google Cloud SQL for MySQL](https://cloud.google.com/sql/docs/mysql/) and more. It provides implementations of
+MySqlConnector is a C# [ADO.NET](https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/) driver for [MySQL Server](https://www.mysql.com/), [MariaDB](https://mariadb.org/),
+[Amazon Aurora](https://aws.amazon.com/rds/aurora/),
+[Azure Database for MySQL](https://azure.microsoft.com/en-us/products/mysql/),
+[Google Cloud SQL for MySQL](https://cloud.google.com/sql/docs/mysql/),
+[Percona Server](https://www.percona.com/software/mysql-database/percona-server) and more. It provides implementations of
 `DbConnection`, `DbCommand`, `DbDataReader`, `DbTransaction` &mdash; the classes
-needed to query and update databases from managed code.
+needed to query and update databases from C# code.
 
 ## Getting Started
 
@@ -67,10 +62,16 @@ MySqlConnector outperforms Connector/NET (MySql.Data) on benchmarks:
 
 (Client: MySqlConnector 2.2.0, Ubuntu 20.04, .NET 7.0; Server: Azure Database for MySQL 8.0.28, TLS 1.2)
 
-## Why use MySqlConnector over Oracle’s Connector/NET?
+## Why use MySqlConnector over Oracle’s MySQL Connector/NET?
 
 MySqlConnector is a clean-room reimplementation of the [MySQL Protocol](https://dev.mysql.com/doc/internals/en/client-server-protocol.html)
-and is not based on [Oracle’s Connector/NET](https://github.com/mysql/mysql-connector-net).
+and is not based on [Oracle’s MySQL Connector/NET](https://github.com/mysql/mysql-connector-net).
+
+* **Asynchronous:** True asynchronous I/O for maximum throughput
+* **Fast:** Optimized for speed and low memory usage
+* **Reliable:** Fixes [dozens of open bugs](/tutorials/migrating-from-connector-net/#fixed-bugs) in Oracle’s Connector/NET; passes all [ADO.NET Specification Tests](https://mysql-net.github.io/AdoNetResults/)
+* **Leading Edge:** First MySQL library to support .NET Core; uses the latest .NET features
+* **Open:** MIT license; development [happens on GitHub](https://github.com/mysql-net/MySqlConnector) with publicly visible roadmaps, issues, and PRs
 
 <table class="table table-striped table-hover" style="max-width: 650px">
   <thead>
diff --git a/docs/content/overview/installing.md b/docs/content/overview/installing.md
index b6f8e51d8..b01ba35e4 100644
--- a/docs/content/overview/installing.md
+++ b/docs/content/overview/installing.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2020-01-07
+lastmod: 2023-01-21
 date: 2016-10-16
 menu:
   main:
@@ -16,7 +16,7 @@ Note that if you are using the `MySql.Data` NuGet package, it must be uninstalle
 
 ### Automatically
 
-If using the new project system, run: `dotnet add package MySqlConnector`
+At the command line, run: `dotnet add package MySqlConnector`
 
 Or, in Visual Studio, use the _NuGet Package Manager_ to browse for and install `MySqlConnector`.
 
@@ -30,11 +30,11 @@ Or, in Visual Studio, use the _NuGet Package Manager_ to browse for and install
     <AssemblyTitle>My Application</AssemblyTitle>
     <Description>A great application</Description>
     <OutputType>Exe</OutputType>
-    <TargetFramework>netcoreapp3.1</TargetFramework>
+    <TargetFramework>net7.0</TargetFramework>
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="MySqlConnector" Version="0.61.0" />
+    <PackageReference Include="MySqlConnector" Version="2.2.5" />
   </ItemGroup>
 
 </Project>
diff --git a/docs/content/overview/logging.md b/docs/content/overview/logging.md
index 3c113ae58..500f36f24 100644
--- a/docs/content/overview/logging.md
+++ b/docs/content/overview/logging.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2018-12-14
+lastmod: 2023-01-21
 date: 2018-01-20
 menu:
   main:
@@ -17,7 +17,7 @@ the existing logging framework, and install it by setting `MySqlConnector.Loggin
 
 The `MySqlConnectorLogManager.Provider` property may only be set once, and must be set before any other MySqlConnector library methods are called.
 
-Debug-level logging is useful for diagnosing problems with MySqlConnector itself; it is recommend that applications limit MySqlConnector
+Debug-level logging is useful for diagnosing problems with MySqlConnector itself; it is recommended that applications limit MySqlConnector
 logging to Info or higher.
 
 ### Existing Logging Providers
diff --git a/docs/content/tutorials/basic-api.md b/docs/content/tutorials/basic-api.md
index ebbda970e..2948fa999 100644
--- a/docs/content/tutorials/basic-api.md
+++ b/docs/content/tutorials/basic-api.md
@@ -1,4 +1,5 @@
 ---
+lastmod: 2023-01-21
 date: 2018-06-05
 menu:
   main:
@@ -16,25 +17,23 @@ Here’s a basic code snippet to get you started.
 ```csharp
 var connString = "Server=myserver;User ID=mylogin;Password=mypass;Database=mydatabase";
 
-using (var conn = new MySqlConnection(connString))
+await using var connection = new MySqlConnection(connString);
+await connection.OpenAsync();
+
+// Insert some data
+using (var cmd = new MySqlCommand())
 {
-    await conn.OpenAsync();
-
-    // Insert some data
-    using (var cmd = new MySqlCommand())
-    {
-        cmd.Connection = conn;
-        cmd.CommandText = "INSERT INTO data (some_field) VALUES (@p)";
-        cmd.Parameters.AddWithValue("p", "Hello world");
-        await cmd.ExecuteNonQueryAsync();
-    }
-
-    // Retrieve all rows
-    using (var cmd = new MySqlCommand("SELECT some_field FROM data", conn))
-    using (var reader = await cmd.ExecuteReaderAsync())
-        while (await reader.ReadAsync())
-            Console.WriteLine(reader.GetString(0));
+    cmd.Connection = connection;
+    cmd.CommandText = "INSERT INTO data (some_field) VALUES (@p)";
+    cmd.Parameters.AddWithValue("p", "Hello world");
+    await cmd.ExecuteNonQueryAsync();
 }
+
+// Retrieve all rows
+using var command = new MySqlCommand("SELECT some_field FROM data", connection);
+using var reader = await cmd.ExecuteReaderAsync();
+while (await reader.ReadAsync())
+    Console.WriteLine(reader.GetString(0));
 ```
 
 You can find more info about the ADO.NET API in the [MSDN documentation](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/ado-net-overview) or in many tutorials on the Internet.
diff --git a/docs/content/tutorials/best-practices.md b/docs/content/tutorials/best-practices.md
index 50eb5b0a5..696b16df0 100644
--- a/docs/content/tutorials/best-practices.md
+++ b/docs/content/tutorials/best-practices.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2021-09-21
+lastmod: 2023-01-21
 date: 2016-10-16
 menu:
   main:
@@ -67,7 +67,7 @@ should be familiar with [Async/Await - Best Practices in Asynchronous Programmin
   </tr>
   <tr>
     <td>
-      <span class="text-danger">*</span><a href="api/mysql-connection">MySqlConnection</a>.BeginTransactionAsync
+      BeginTransactionAsync
     </td>
     <td>BeginTransaction</td>
   </tr>
@@ -102,21 +102,18 @@ should be familiar with [Async/Await - Best Practices in Asynchronous Programmin
       <a href="https://docs.microsoft.com/en-us/dotnet/core/api/system.data.common.dbtransaction">DbTransaction</a>
     </td>
     <td>
-      <span class="text-danger">*</span><a href="api/mysql-transaction">MySqlTransaction</a>.CommitAsync
+      CommitAsync
     </td>
     <td>Commit</td>
   </tr>
   <tr>
     <td>
-      <span class="text-danger">*</span><a href="api/mysql-transaction">MySqlTransaction</a>.RollbackAsync
+      RollbackAsync
     </td>
     <td>Rollback</td>
   </tr>
 </table>
 
-<span class="text-danger">*</span>Async Transaction methods are not part of ADO.NET, they are provided by
-MySqlConnector to allow database code to remain fully asynchronous.
-
 ### Exceptions: DbDataReader.GetFieldValueAsync and IsDBNullAsync
 
 Once `DbDataReader.ReadAsync` (or `DbDataReader.Read`) has returned `true`, the full contents of the current
@@ -139,33 +136,32 @@ Example assumes a [configured AppDb](/overview/configuration) object in the `MyS
 using System.Threading.Tasks;
 using System.Collections.Generic;
 
-namespace MySqlConnector.Examples
+namespace MySqlConnector.Examples;
+
+public class Program
 {
-    public class Program
+    public static async Task Main(string[] args)
     {
-        public static async Task Main(string[] args)
+        var tasks = new List<Task>();
+        for (var i=0; i<100; i++)
         {
-            var tasks = new List<Task>();
-            for (var i=0; i<100; i++)
-            {
-                tasks.Add(Controllers.SleepOne());
-            }
-            // these 100 queries should all complete in around
-            // 1 second if "Max Pool Size=100" (the default)
-            await Task.WhenAll(tasks);
+            tasks.Add(Controllers.SleepOne());
         }
+        // these 100 queries should all complete in around
+        // 1 second if "Max Pool Size=100" (the default)
+        await Task.WhenAll(tasks);
     }
+}
 
-    public class Controllers
+public class Controllers
+{
+    public static async Task SleepOne()
     {
-        public static async Task SleepOne()
-        {
-            using var db = new AppDb();
-            await db.Connection.OpenAsync();
-            using var cmd = db.Connection.CreateCommand();
-            cmd.CommandText = @"SELECT SLEEP(1)";
-            await cmd.ExecuteNonQueryAsync();
-        }
+        using var db = new AppDb();
+        await db.Connection.OpenAsync();
+        using var cmd = db.Connection.CreateCommand();
+        cmd.CommandText = @"SELECT SLEEP(1)";
+        await cmd.ExecuteNonQueryAsync();
     }
 }
 ```
diff --git a/docs/content/tutorials/connect-to-mysql.md b/docs/content/tutorials/connect-to-mysql.md
index ee0bec476..1fdc42816 100644
--- a/docs/content/tutorials/connect-to-mysql.md
+++ b/docs/content/tutorials/connect-to-mysql.md
@@ -1,4 +1,5 @@
 ---
+lastmod: 2023-01-21
 date: 2019-11-18
 menu:
   main:
@@ -39,16 +40,27 @@ If you are using ASP.NET Core, your connection string will usually be stored in
 
 ```json
 {
-    ....
-    "ConnectionStrings": {
-        "Default": "Server=YOURSERVER;User ID=YOURUSERID;Password=YOURPASSWORD;Database=YOURDATABASE"
-    }
+  ....
+  "ConnectionStrings": {
+    "Default": "Server=YOURSERVER;User ID=YOURUSERID;Password=YOURPASSWORD;Database=YOURDATABASE"
+  }
 }
 ```
 
 ## 3. Configure Service (ASP.NET Core)
 
-If using ASP.NET Core, you will want to register a database connection in `Startup.cs`:
+### .NET 6.0
+
+If using ASP.NET Core, you will want to register a database connection in `Program.cs`:
+
+```csharp
+builder.services.AddTransient<MySqlConnection>(_ =>
+    new MySqlConnection(builder.Configuration.GetConnectionString["Default"]));
+```
+
+### .NET 5.0 and earlier
+
+For .NET 5.0 and earlier, register a database connection in `Startup.cs`:
 
 ```csharp
 public void ConfigureServices(IServiceCollection services)
@@ -58,6 +70,7 @@ public void ConfigureServices(IServiceCollection services)
 }
 ```
 
+
 ## 4. Open and Use the Connection
 
 In ASP.NET Core, the `MySqlConnection` object will be dependency-injected into your `Controller` class. For

From afdba7f3e6a768cb46f018fdcb4363f7a24bc277 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 21 Jan 2023 15:26:54 -0800
Subject: [PATCH 038/393] Minify CSS and JS.

---
 docs/{static => assets}/css/site.css | 0
 docs/{static => assets}/js/site.js   | 0
 docs/layouts/partials/header.html    | 6 ++++--
 3 files changed, 4 insertions(+), 2 deletions(-)
 rename docs/{static => assets}/css/site.css (100%)
 rename docs/{static => assets}/js/site.js (100%)

diff --git a/docs/static/css/site.css b/docs/assets/css/site.css
similarity index 100%
rename from docs/static/css/site.css
rename to docs/assets/css/site.css
diff --git a/docs/static/js/site.js b/docs/assets/js/site.js
similarity index 100%
rename from docs/static/js/site.js
rename to docs/assets/js/site.js
diff --git a/docs/layouts/partials/header.html b/docs/layouts/partials/header.html
index b87ce80b3..5723cda88 100644
--- a/docs/layouts/partials/header.html
+++ b/docs/layouts/partials/header.html
@@ -3,7 +3,8 @@
   <head>
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
-    <script src="/js/site.js"></script>
+    {{ $js := resources.Get "js/site.js" | resources.Minify | resources.Fingerprint }}
+    <script src="{{ $js.Permalink }}"></script>
     <!-- Global site tag (gtag.js) - Google Analytics -->
     <script async src="https://www.googletagmanager.com/gtag/js?id=UA-142489411-1"></script>
     <script>
@@ -49,7 +50,8 @@
     <meta property="og:url" content="{{ .Page.Permalink }}" />
     <meta property="fb:app_id" content="422535981632483" />
 
-    <link href="/css/site.css" rel="stylesheet">
+    {{ $style := resources.Get "css/site.css" | resources.Minify | resources.Fingerprint }}
+    <link rel="stylesheet" href="{{ $style.Permalink }}">
     <link rel="canonical" href="{{ .Page.Permalink }}" />
   </head>
 

From 46cba2439034849fc75257d21d0880dbf1eddd06 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 21 Jan 2023 15:27:20 -0800
Subject: [PATCH 039/393] Delete a lot of unused styles.

---
 docs/assets/css/site.css | 8808 +++++++++-----------------------------
 1 file changed, 2026 insertions(+), 6782 deletions(-)

diff --git a/docs/assets/css/site.css b/docs/assets/css/site.css
index bbb17aa1d..aeda04d79 100644
--- a/docs/assets/css/site.css
+++ b/docs/assets/css/site.css
@@ -1,7476 +1,2720 @@
 @charset "UTF-8";
-.highlight .c {
-  color: #586e75;
-}
-
-.highlight .err {
-  color: #93a1a1;
+/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
+/* Document
+   ========================================================================== */
+/**
+ * 1. Correct the line height in all browsers.
+ * 2. Prevent adjustments of font size after orientation changes in iOS.
+ */
+html {
+  line-height: 1.15; /* 1 */
+  -webkit-text-size-adjust: 100%; /* 2 */
 }
 
-.highlight .g {
-  color: #93a1a1;
+/* Sections
+   ========================================================================== */
+/**
+ * Remove the margin in all browsers.
+ */
+body {
+  margin: 0;
 }
 
-.highlight .k {
-  color: #859900;
+/**
+ * Render the `main` element consistently in IE.
+ */
+main {
+  display: block;
 }
 
-.highlight .l {
-  color: #93a1a1;
+/**
+ * Correct the font size and margin on `h1` elements within `section` and
+ * `article` contexts in Chrome, Firefox, and Safari.
+ */
+h1 {
+  font-size: 2em;
+  margin: 0.67em 0;
 }
 
-.highlight .n {
-  color: #93a1a1;
+/* Grouping content
+   ========================================================================== */
+/**
+ * 1. Add the correct box sizing in Firefox.
+ * 2. Show the overflow in Edge and IE.
+ */
+hr {
+  box-sizing: content-box; /* 1 */
+  height: 0; /* 1 */
+  overflow: visible; /* 2 */
 }
 
-.highlight .o {
-  color: #859900;
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+pre {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
 }
 
-.highlight .x {
-  color: #cb4b16;
+/* Text-level semantics
+   ========================================================================== */
+/**
+ * Remove the gray background on active links in IE 10.
+ */
+a {
+  background-color: transparent;
 }
 
-.highlight .p {
-  color: #93a1a1;
+/**
+ * 1. Remove the bottom border in Chrome 57-
+ * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
+ */
+abbr[title] {
+  border-bottom: none; /* 1 */
+  text-decoration: underline; /* 2 */
+  text-decoration: underline dotted; /* 2 */
 }
 
-.highlight .cm {
-  color: #586e75;
+/**
+ * Add the correct font weight in Chrome, Edge, and Safari.
+ */
+b,
+strong {
+  font-weight: bolder;
 }
 
-.highlight .cp {
-  color: #859900;
+/**
+ * 1. Correct the inheritance and scaling of font size in all browsers.
+ * 2. Correct the odd `em` font sizing in all browsers.
+ */
+code,
+kbd,
+samp {
+  font-family: monospace, monospace; /* 1 */
+  font-size: 1em; /* 2 */
 }
 
-.highlight .c1 {
-  color: #586e75;
+/**
+ * Add the correct font size in all browsers.
+ */
+small {
+  font-size: 80%;
 }
 
-.highlight .cs {
-  color: #859900;
+/**
+ * Prevent `sub` and `sup` elements from affecting the line height in
+ * all browsers.
+ */
+sub,
+sup {
+  font-size: 75%;
+  line-height: 0;
+  position: relative;
+  vertical-align: baseline;
 }
 
-.highlight .gd {
-  color: #2aa198;
+sub {
+  bottom: -0.25em;
 }
 
-.highlight .ge {
-  font-style: italic;
-  color: #93a1a1;
+sup {
+  top: -0.5em;
 }
 
-.highlight .gr {
-  color: #dc322f;
+/* Embedded content
+   ========================================================================== */
+/**
+ * Remove the border on images inside links in IE 10.
+ */
+img {
+  border-style: none;
 }
 
-.highlight .gh {
-  color: #cb4b16;
+/* Forms
+   ========================================================================== */
+/**
+ * 1. Change the font styles in all browsers.
+ * 2. Remove the margin in Firefox and Safari.
+ */
+button,
+input,
+optgroup,
+select,
+textarea {
+  font-family: inherit; /* 1 */
+  font-size: 100%; /* 1 */
+  line-height: 1.15; /* 1 */
+  margin: 0; /* 2 */
 }
 
-.highlight .gi {
-  color: #859900;
+/**
+ * Show the overflow in IE.
+ * 1. Show the overflow in Edge.
+ */
+button,
+input {
+  /* 1 */
+  overflow: visible;
 }
 
-.highlight .go {
-  color: #93a1a1;
+/**
+ * Remove the inheritance of text transform in Edge, Firefox, and IE.
+ * 1. Remove the inheritance of text transform in Firefox.
+ */
+button,
+select {
+  /* 1 */
+  text-transform: none;
 }
 
-.highlight .gp {
-  color: #93a1a1;
+/**
+ * Correct the inability to style clickable types in iOS and Safari.
+ */
+button,
+[type=button],
+[type=reset],
+[type=submit] {
+  -webkit-appearance: button;
 }
 
-.highlight .gs {
-  font-weight: bold;
-  color: #93a1a1;
+/**
+ * Remove the inner border and padding in Firefox.
+ */
+button::-moz-focus-inner,
+[type=button]::-moz-focus-inner,
+[type=reset]::-moz-focus-inner,
+[type=submit]::-moz-focus-inner {
+  border-style: none;
+  padding: 0;
 }
 
-.highlight .gu {
-  color: #cb4b16;
+/**
+ * Restore the focus styles unset by the previous rule.
+ */
+button:-moz-focusring,
+[type=button]:-moz-focusring,
+[type=reset]:-moz-focusring,
+[type=submit]:-moz-focusring {
+  outline: 1px dotted ButtonText;
 }
 
-.highlight .gt {
-  color: #93a1a1;
+/**
+ * Remove the default vertical scrollbar in IE 10+.
+ */
+textarea {
+  overflow: auto;
 }
 
-.highlight .kc {
-  color: #cb4b16;
+/**
+ * 1. Add the correct box sizing in IE 10.
+ * 2. Remove the padding in IE 10.
+ */
+[type=checkbox],
+[type=radio] {
+  box-sizing: border-box; /* 1 */
+  padding: 0; /* 2 */
 }
 
-.highlight .kd {
-  color: #268bd2;
+/**
+ * Correct the cursor style of increment and decrement buttons in Chrome.
+ */
+[type=number]::-webkit-inner-spin-button,
+[type=number]::-webkit-outer-spin-button {
+  height: auto;
 }
 
-.highlight .kn {
-  color: #859900;
+/**
+ * 1. Correct the odd appearance in Chrome and Safari.
+ * 2. Correct the outline style in Safari.
+ */
+[type=search] {
+  -webkit-appearance: textfield; /* 1 */
+  outline-offset: -2px; /* 2 */
 }
 
-.highlight .kp {
-  color: #859900;
+/**
+ * Remove the inner padding in Chrome and Safari on macOS.
+ */
+[type=search]::-webkit-search-decoration {
+  -webkit-appearance: none;
 }
 
-.highlight .kr {
-  color: #268bd2;
+/**
+ * 1. Correct the inability to style clickable types in iOS and Safari.
+ * 2. Change font properties to `inherit` in Safari.
+ */
+::-webkit-file-upload-button {
+  -webkit-appearance: button; /* 1 */
+  font: inherit; /* 2 */
 }
 
-.highlight .kt {
-  color: #dc322f;
+/* Misc
+   ========================================================================== */
+/**
+ * Add the correct display in IE 10+.
+ */
+template {
+  display: none;
 }
 
-.highlight .ld {
-  color: #93a1a1;
+/**
+ * Add the correct display in IE 10.
+ */
+[hidden] {
+  display: none;
 }
 
-.highlight .m {
-  color: #2aa198;
+* {
+  box-sizing: border-box;
 }
 
-.highlight .s {
-  color: #2aa198;
+::selection {
+  color: #fff;
+  background: #0969da;
 }
 
-.highlight .na {
-  color: #555;
+html {
+  font-size: 14px !important;
+  scroll-behavior: smooth;
 }
-
-.highlight .nb {
-  color: #b58900;
+@media (min-width: 31.25rem) {
+  html {
+    font-size: 16px !important;
+  }
 }
 
-.highlight .nc {
-  color: #268bd2;
+body {
+  font-family: system-ui, -apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, sans-serif;
+  font-size: inherit;
+  line-height: 1.4;
+  color: #5c5962;
+  background-color: #fff;
+  overflow-wrap: break-word;
 }
 
-.highlight .no {
-  color: #cb4b16;
+ol,
+ul,
+dl,
+pre,
+address,
+blockquote,
+table,
+div,
+hr,
+form,
+fieldset,
+noscript .table-wrapper {
+  margin-top: 0;
 }
 
-.highlight .nd {
-  color: #268bd2;
+h1,
+h2,
+h3,
+h4,
+h5,
+h6,
+#toctitle {
+  margin-top: 0;
+  margin-bottom: 1em;
+  font-weight: 500;
+  line-height: 1.25;
+  color: #27262b;
 }
 
-.highlight .ni {
-  color: #cb4b16;
-}
-
-.highlight .ne {
-  color: #cb4b16;
+p {
+  margin-top: 1em;
+  margin-bottom: 1em;
 }
 
-.highlight .nf {
-  color: #268bd2;
+a {
+  color: #0969da;
+  text-decoration: none;
 }
 
-.highlight .nl {
-  color: #555;
+a:not([class]) {
+  text-decoration: underline;
+  text-decoration-color: #eeebee;
+  text-underline-offset: 2px;
 }
-
-.highlight .nn {
-  color: #93a1a1;
+a:not([class]):hover {
+  text-decoration-color: rgba(9, 105, 218, 0.45);
 }
 
-.highlight .nx {
-  color: #555;
+code {
+  font-family: "SFMono-Regular", menlo, consolas, monospace;
+  font-size: 0.75em;
+  line-height: 1.4;
 }
 
-.highlight .py {
-  color: #93a1a1;
+figure,
+pre {
+  margin: 16px 0;
 }
 
-.highlight .nt {
-  color: #268bd2;
+li {
+  margin: 0.25em 0;
 }
 
-.highlight .nv {
-  color: #268bd2;
+img {
+  max-width: 100%;
+  height: auto;
 }
 
-.highlight .ow {
-  color: #859900;
+hr {
+  height: 1px;
+  padding: 0;
+  margin: 2rem 0;
+  background-color: #eeebee;
+  border: 0;
 }
 
-.highlight .w {
-  color: #93a1a1;
+blockquote {
+  margin: 10px 0;
+  margin-block-start: 0;
+  margin-inline-start: 0;
+  padding-left: 15px;
+  border-left: 3px solid #eeebee;
 }
 
-.highlight .mf {
-  color: #2aa198;
+.side-bar {
+  z-index: 0;
+  display: flex;
+  flex-wrap: wrap;
+  background-color: #f5f6fa;
 }
-
-.highlight .mh {
-  color: #2aa198;
+@media (min-width: 50rem) {
+  .side-bar {
+    flex-flow: column nowrap;
+    position: fixed;
+    width: 248px;
+    height: 100%;
+    border-right: 1px solid #eeebee;
+    align-items: flex-end;
+  }
 }
-
-.highlight .mi {
-  color: #2aa198;
+@media (min-width: 66.5rem) {
+  .side-bar {
+    width: calc((100% - 1064px) / 2 + 264px);
+    min-width: 264px;
+  }
 }
 
-.highlight .mo {
-  color: #2aa198;
+@media (min-width: 50rem) {
+  .main {
+    position: relative;
+    max-width: 800px;
+    margin-left: 248px;
+  }
 }
-
-.highlight .sb {
-  color: #586e75;
+@media (min-width: 66.5rem) {
+  .main {
+    margin-left: calc(
+      (100% - 1064px) / 2 + 264px
+    );
+  }
 }
 
-.highlight .sc {
-  color: #2aa198;
+.main-content-wrap {
+  padding-right: 1rem;
+  padding-left: 1rem;
+  padding-top: 1rem;
+  padding-bottom: 1rem;
 }
-
-.highlight .sd {
-  color: #93a1a1;
+@media (min-width: 50rem) {
+  .main-content-wrap {
+    padding-right: 2rem;
+    padding-left: 2rem;
+  }
 }
-
-.highlight .s2 {
-  color: #2aa198;
+@media (min-width: 50rem) {
+  .main-content-wrap {
+    padding-top: 2rem;
+    padding-bottom: 2rem;
+  }
 }
 
-.highlight .se {
-  color: #cb4b16;
+.main-header {
+  z-index: 0;
+  display: none;
+  background-color: #f5f6fa;
 }
-
-.highlight .sh {
-  color: #93a1a1;
+@media (min-width: 50rem) {
+  .main-header {
+    display: flex;
+    justify-content: space-between;
+    height: 60px;
+    background-color: #fff;
+    border-bottom: 1px solid #eeebee;
+  }
 }
-
-.highlight .si {
-  color: #2aa198;
+.main-header.nav-open {
+  display: block;
 }
-
-.highlight .sx {
-  color: #2aa198;
+@media (min-width: 50rem) {
+  .main-header.nav-open {
+    display: flex;
+  }
 }
 
-.highlight .sr {
-  color: #dc322f;
+.site-nav,
+.site-header,
+.site-footer {
+  width: 100%;
 }
-
-.highlight .s1 {
-  color: #2aa198;
+@media (min-width: 66.5rem) {
+  .site-nav,
+  .site-header,
+  .site-footer {
+    width: 264px;
+  }
 }
 
-.highlight .ss {
-  color: #2aa198;
+.site-nav {
+  display: none;
 }
-
-.highlight .bp {
-  color: #268bd2;
+.site-nav.nav-open {
+  display: block;
 }
-
-.highlight .vc {
-  color: #268bd2;
+@media (min-width: 50rem) {
+  .site-nav {
+    display: block;
+    padding-top: 3rem;
+    padding-bottom: 1rem;
+    overflow-y: auto;
+    flex: 1 1 auto;
+  }
 }
 
-.highlight .vg {
-  color: #268bd2;
+.site-header {
+  display: flex;
+  min-height: 60px;
+  align-items: center;
 }
-
-.highlight .vi {
-  color: #268bd2;
+@media (min-width: 50rem) {
+  .site-header {
+    height: 60px;
+    max-height: 60px;
+    border-bottom: 1px solid #eeebee;
+  }
 }
 
-.highlight .il {
-  color: #2aa198;
+.site-title {
+  padding-right: 1rem;
+  padding-left: 1rem;
+  flex-grow: 1;
+  display: flex;
+  height: 100%;
+  align-items: center;
+  padding-top: 0.75rem;
+  padding-bottom: 0.75rem;
+  color: #27262b;
+  font-size: 18px !important;
 }
-
-.highlight .c {
-  color: #586e75;
+@media (min-width: 50rem) {
+  .site-title {
+    padding-right: 2rem;
+    padding-left: 2rem;
+  }
 }
-
-.highlight .err {
-  color: #93a1a1;
+@media (min-width: 31.25rem) {
+  .site-title {
+    font-size: 24px !important;
+    line-height: 1.25;
+  }
 }
-
-.highlight .g {
-  color: #93a1a1;
+@media (min-width: 50rem) {
+  .site-title {
+    padding-top: 0.5rem;
+    padding-bottom: 0.5rem;
+  }
 }
 
-.highlight .k {
-  color: #859900;
+.site-button {
+  display: flex;
+  height: 100%;
+  padding: 1rem;
+  align-items: center;
 }
 
-.highlight .l {
-  color: #93a1a1;
+@media (min-width: 50rem) {
+  .site-header .site-button {
+    display: none;
+  }
 }
-
-.highlight .n {
-  color: #93a1a1;
+.site-title:hover {
+  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%);
 }
 
-.highlight .o {
-  color: #859900;
+.site-button:hover {
+  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 100%);
 }
 
-.highlight .x {
-  color: #cb4b16;
+body {
+  position: relative;
+  padding-bottom: 4rem;
+  overflow-y: scroll;
 }
-
-.highlight .p {
-  color: #93a1a1;
+@media (min-width: 50rem) {
+  body {
+    position: static;
+    padding-bottom: 0;
+  }
 }
 
-.highlight .cm {
-  color: #586e75;
+.icon {
+  width: 1.5rem;
+  height: 1.5rem;
+  color: #0969da;
 }
 
-.highlight .cp {
-  color: #859900;
+.main-content {
+  line-height: 1.6;
 }
-
-.highlight .c1 {
-  color: #586e75;
+.main-content ol,
+.main-content ul,
+.main-content dl,
+.main-content pre,
+.main-content address,
+.main-content blockquote,
+.main-content .table-wrapper {
+  margin-top: 0.5em;
 }
-
-.highlight .cs {
-  color: #859900;
+.main-content a {
+  overflow: hidden;
+  text-overflow: ellipsis;
 }
-
-.highlight .gd {
-  color: #2aa198;
+.main-content ul,
+.main-content ol {
+  padding-left: 1.5em;
 }
-
-.highlight .ge {
-  font-style: italic;
-  color: #93a1a1;
+.main-content li .highlight {
+  margin-top: 0.25rem;
 }
-
-.highlight .gr {
-  color: #dc322f;
+.main-content ol {
+  list-style-type: none;
+  counter-reset: step-counter;
 }
-
-.highlight .gh {
-  color: #cb4b16;
+.main-content ol > li {
+  position: relative;
 }
-
-.highlight .gi {
-  color: #859900;
+.main-content ol > li::before {
+  position: absolute;
+  top: 0.2em;
+  left: -1.6em;
+  color: #959396;
+  content: counter(step-counter);
+  counter-increment: step-counter;
+  font-size: 12px !important;
 }
-
-.highlight .go {
-  color: #93a1a1;
+@media (min-width: 31.25rem) {
+  .main-content ol > li::before {
+    font-size: 14px !important;
+  }
 }
-
-.highlight .gp {
-  color: #93a1a1;
+@media (min-width: 31.25rem) {
+  .main-content ol > li::before {
+    top: 0.11em;
+  }
 }
-
-.highlight .gs {
-  font-weight: bold;
-  color: #93a1a1;
+.main-content ol > li ol {
+  counter-reset: sub-counter;
 }
-
-.highlight .gu {
-  color: #cb4b16;
-}
-
-.highlight .gt {
-  color: #93a1a1;
+.main-content ol > li ol > li::before {
+  content: counter(sub-counter, lower-alpha);
+  counter-increment: sub-counter;
 }
-
-.highlight .kc {
-  color: #cb4b16;
+.main-content ul {
+  list-style: none;
 }
-
-.highlight .kd {
-  color: #268bd2;
+.main-content ul > li::before {
+  position: absolute;
+  margin-left: -1.4em;
+  color: #959396;
+  content: "•";
 }
-
-.highlight .kn {
-  color: #859900;
+.main-content .task-list-item::before {
+  content: "";
 }
-
-.highlight .kp {
-  color: #859900;
+.main-content .task-list-item-checkbox {
+  margin-right: 0.6em;
+  margin-left: -1.4em;
 }
-
-.highlight .kr {
-  color: #268bd2;
+.main-content hr + * {
+  margin-top: 0;
 }
-
-.highlight .kt {
-  color: #dc322f;
+.main-content h1:first-of-type {
+  margin-top: 0.5em;
 }
-
-.highlight .ld {
-  color: #93a1a1;
+.main-content dl {
+  display: grid;
+  grid-template: auto/10em 1fr;
 }
-
-.highlight .m {
-  color: #2aa198;
+.main-content dt,
+.main-content dd {
+  margin: 0.25em 0;
 }
-
-.highlight .s {
-  color: #2aa198;
+.main-content dt {
+  grid-column: 1;
+  font-weight: 500;
+  text-align: right;
 }
-
-.highlight .na {
-  color: #555;
+.main-content dt::after {
+  content: ":";
 }
-
-.highlight .nb {
-  color: #b58900;
+.main-content dd {
+  grid-column: 2;
+  margin-bottom: 0;
+  margin-left: 1em;
 }
-
-.highlight .nc {
-  color: #268bd2;
+.main-content dd blockquote:first-child,
+.main-content dd div:first-child,
+.main-content dd dl:first-child,
+.main-content dd dt:first-child,
+.main-content dd h1:first-child,
+.main-content dd h2:first-child,
+.main-content dd h3:first-child,
+.main-content dd h4:first-child,
+.main-content dd h5:first-child,
+.main-content dd h6:first-child,
+.main-content dd li:first-child,
+.main-content dd ol:first-child,
+.main-content dd p:first-child,
+.main-content dd pre:first-child,
+.main-content dd table:first-child,
+.main-content dd ul:first-child,
+.main-content dd .table-wrapper:first-child {
+  margin-top: 0;
 }
-
-.highlight .no {
-  color: #cb4b16;
+.main-content dd dl:first-child dt:first-child,
+.main-content dd dl:first-child dd:nth-child(2),
+.main-content ol dl:first-child dt:first-child,
+.main-content ol dl:first-child dd:nth-child(2),
+.main-content ul dl:first-child dt:first-child,
+.main-content ul dl:first-child dd:nth-child(2) {
+  margin-top: 0;
 }
-
-.highlight .nd {
-  color: #268bd2;
+.main-content .anchor-heading {
+  position: absolute;
+  right: -1rem;
+  width: 1.5rem;
+  height: 100%;
+  padding-right: 0.25rem;
+  padding-left: 0.25rem;
+  overflow: visible;
 }
-
-.highlight .ni {
-  color: #cb4b16;
+@media (min-width: 50rem) {
+  .main-content .anchor-heading {
+    right: auto;
+    left: -1.5rem;
+  }
 }
-
-.highlight .ne {
-  color: #cb4b16;
+.main-content .anchor-heading svg {
+  display: inline-block;
+  width: 100%;
+  height: 100%;
+  color: #0969da;
+  visibility: hidden;
 }
-
-.highlight .nf {
-  color: #268bd2;
+.main-content .anchor-heading:hover svg,
+.main-content .anchor-heading:focus svg,
+.main-content h1:hover > .anchor-heading svg,
+.main-content h2:hover > .anchor-heading svg,
+.main-content h3:hover > .anchor-heading svg,
+.main-content h4:hover > .anchor-heading svg,
+.main-content h5:hover > .anchor-heading svg,
+.main-content h6:hover > .anchor-heading svg {
+  visibility: visible;
 }
-
-.highlight .nl {
-  color: #555;
+.main-content summary {
+  cursor: pointer;
 }
-
-.highlight .nn {
-  color: #93a1a1;
+.main-content h1,
+.main-content h2,
+.main-content h3,
+.main-content h4,
+.main-content h5,
+.main-content h6,
+.main-content #toctitle {
+  position: relative;
+  margin-top: 1.5em;
+  margin-bottom: 0.25em;
 }
-
-.highlight .nx {
-  color: #555;
+.main-content h1 + table,
+.main-content h1 + .table-wrapper,
+.main-content h1 + .code-example,
+.main-content h1 + .highlighter-rouge,
+.main-content h1 + .sectionbody .listingblock,
+.main-content h2 + table,
+.main-content h2 + .table-wrapper,
+.main-content h2 + .code-example,
+.main-content h2 + .highlighter-rouge,
+.main-content h2 + .sectionbody .listingblock,
+.main-content h3 + table,
+.main-content h3 + .table-wrapper,
+.main-content h3 + .code-example,
+.main-content h3 + .highlighter-rouge,
+.main-content h3 + .sectionbody .listingblock,
+.main-content h4 + table,
+.main-content h4 + .table-wrapper,
+.main-content h4 + .code-example,
+.main-content h4 + .highlighter-rouge,
+.main-content h4 + .sectionbody .listingblock,
+.main-content h5 + table,
+.main-content h5 + .table-wrapper,
+.main-content h5 + .code-example,
+.main-content h5 + .highlighter-rouge,
+.main-content h5 + .sectionbody .listingblock,
+.main-content h6 + table,
+.main-content h6 + .table-wrapper,
+.main-content h6 + .code-example,
+.main-content h6 + .highlighter-rouge,
+.main-content h6 + .sectionbody .listingblock,
+.main-content #toctitle + table,
+.main-content #toctitle + .table-wrapper,
+.main-content #toctitle + .code-example,
+.main-content #toctitle + .highlighter-rouge,
+.main-content #toctitle + .sectionbody .listingblock {
+  margin-top: 1em;
 }
-
-.highlight .py {
-  color: #93a1a1;
+.main-content h1 + p:not(.label),
+.main-content h2 + p:not(.label),
+.main-content h3 + p:not(.label),
+.main-content h4 + p:not(.label),
+.main-content h5 + p:not(.label),
+.main-content h6 + p:not(.label),
+.main-content #toctitle + p:not(.label) {
+  margin-top: 0;
 }
-
-.highlight .nt {
-  color: #268bd2;
+.main-content > h1:first-child,
+.main-content > h2:first-child,
+.main-content > h3:first-child,
+.main-content > h4:first-child,
+.main-content > h5:first-child,
+.main-content > h6:first-child,
+.main-content > .sect1:first-child > h2,
+.main-content > .sect2:first-child > h3,
+.main-content > .sect3:first-child > h4,
+.main-content > .sect4:first-child > h5,
+.main-content > .sect5:first-child > h6 {
+  margin-top: 0.5rem;
 }
 
-.highlight .nv {
-  color: #268bd2;
+.nav-list {
+  padding: 0;
+  margin-top: 0;
+  margin-bottom: 0;
+  list-style: none;
 }
-
-.highlight .ow {
-  color: #859900;
+.nav-list .nav-list-item {
+  font-size: 14px !important;
+  position: relative;
+  margin: 0;
 }
-
-.highlight .w {
-  color: #93a1a1;
+@media (min-width: 31.25rem) {
+  .nav-list .nav-list-item {
+    font-size: 16px !important;
+  }
 }
-
-.highlight .mf {
-  color: #2aa198;
+@media (min-width: 50rem) {
+  .nav-list .nav-list-item {
+    font-size: 12px !important;
+  }
 }
-
-.highlight .mh {
-  color: #2aa198;
+@media (min-width: 50rem) and (min-width: 31.25rem) {
+  .nav-list .nav-list-item {
+    font-size: 14px !important;
+  }
 }
-
-.highlight .mi {
-  color: #2aa198;
+.nav-list .nav-list-item .nav-list-link {
+  display: block;
+  min-height: 3rem;
+  padding-top: 0.25rem;
+  padding-bottom: 0.25rem;
+  line-height: 2.5rem;
+  padding-right: 3rem;
+  padding-left: 1rem;
 }
-
-.highlight .mo {
-  color: #2aa198;
+@media (min-width: 50rem) {
+  .nav-list .nav-list-item .nav-list-link {
+    min-height: 2rem;
+    line-height: 1.5rem;
+    padding-right: 2rem;
+    padding-left: 2rem;
+  }
 }
-
-.highlight .sb {
-  color: #586e75;
+.nav-list .nav-list-item .nav-list-link.external > svg {
+  width: 1rem;
+  height: 1rem;
+  vertical-align: text-bottom;
 }
-
-.highlight .sc {
-  color: #2aa198;
+.nav-list .nav-list-item .nav-list-link.active {
+  font-weight: 600;
+  text-decoration: none;
 }
-
-.highlight .sd {
-  color: #93a1a1;
+.nav-list .nav-list-item .nav-list-link:hover, .nav-list .nav-list-item .nav-list-link.active {
+  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%);
 }
-
-.highlight .s2 {
-  color: #2aa198;
+.nav-list .nav-list-item .nav-list-expander {
+  position: absolute;
+  right: 0;
+  width: 3rem;
+  height: 3rem;
+  padding: 0.75rem;
+  color: #0969da;
 }
-
-.highlight .se {
-  color: #cb4b16;
+@media (min-width: 50rem) {
+  .nav-list .nav-list-item .nav-list-expander {
+    width: 2rem;
+    height: 2rem;
+    padding: 0.5rem;
+  }
 }
-
-.highlight .sh {
-  color: #93a1a1;
+.nav-list .nav-list-item .nav-list-expander:hover {
+  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 100%);
 }
-
-.highlight .si {
-  color: #2aa198;
+.nav-list .nav-list-item .nav-list-expander svg {
+  transform: rotate(90deg);
 }
-
-.highlight .sx {
-  color: #2aa198;
+.nav-list .nav-list-item > .nav-list {
+  display: none;
+  padding-left: 0.75rem;
+  list-style: none;
 }
-
-.highlight .sr {
-  color: #dc322f;
+.nav-list .nav-list-item > .nav-list .nav-list-item {
+  position: relative;
 }
-
-.highlight .s1 {
-  color: #2aa198;
+.nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-link {
+  color: #5c5962;
 }
-
-.highlight .ss {
-  color: #2aa198;
+.nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-expander {
+  color: #5c5962;
 }
-
-.highlight .bp {
-  color: #268bd2;
+.nav-list .nav-list-item.active > .nav-list-expander svg {
+  transform: rotate(-90deg);
 }
-
-.highlight .vc {
-  color: #268bd2;
+.nav-list .nav-list-item.active > .nav-list {
+  display: block;
 }
 
-.highlight .vg {
-  color: #268bd2;
+.nav-category {
+  padding: 0.5rem 1rem;
+  font-weight: 600;
+  text-align: start;
+  text-transform: uppercase;
+  border-bottom: 1px solid #eeebee;
+  font-size: 11px !important;
 }
-
-.highlight .vi {
-  color: #268bd2;
+@media (min-width: 31.25rem) {
+  .nav-category {
+    font-size: 12px !important;
+  }
 }
-
-.highlight .il {
-  color: #2aa198;
+@media (min-width: 50rem) {
+  .nav-category {
+    padding: 0.5rem 2rem;
+    margin-top: 1rem;
+    text-align: start;
+  }
+  .nav-category:first-child {
+    margin-top: 0;
+  }
 }
 
-/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */
-/* Document
-   ========================================================================== */
-/**
- * 1. Correct the line height in all browsers.
- * 2. Prevent adjustments of font size after orientation changes in iOS.
- */
-html {
-  line-height: 1.15; /* 1 */
-  -webkit-text-size-adjust: 100%; /* 2 */
+.nav-list.nav-category-list > .nav-list-item {
+  margin: 0;
+}
+.nav-list.nav-category-list > .nav-list-item > .nav-list {
+  padding: 0;
+}
+.nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-link {
+  color: #0969da;
+}
+.nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-expander {
+  color: #0969da;
 }
 
-/* Sections
-   ========================================================================== */
-/**
- * Remove the margin in all browsers.
- */
-body {
+.aux-nav {
+  height: 100%;
+  overflow-x: auto;
+  font-size: 11px !important;
+}
+@media (min-width: 31.25rem) {
+  .aux-nav {
+    font-size: 12px !important;
+  }
+}
+.aux-nav .aux-nav-list {
+  display: flex;
+  height: 100%;
+  padding: 0;
   margin: 0;
+  list-style: none;
 }
-
-/**
- * Render the `main` element consistently in IE.
- */
-main {
-  display: block;
+.aux-nav .aux-nav-list-item {
+  display: inline-block;
+  height: 100%;
+  padding: 0;
+  margin: 0;
 }
-
-/**
- * Correct the font size and margin on `h1` elements within `section` and
- * `article` contexts in Chrome, Firefox, and Safari.
- */
-h1 {
-  font-size: 2em;
-  margin: 0.67em 0;
+@media (min-width: 50rem) {
+  .aux-nav {
+    padding-right: 1rem;
+  }
 }
 
-/* Grouping content
-   ========================================================================== */
-/**
- * 1. Add the correct box sizing in Firefox.
- * 2. Show the overflow in Edge and IE.
- */
-hr {
-  box-sizing: content-box; /* 1 */
-  height: 0; /* 1 */
-  overflow: visible; /* 2 */
+@media (min-width: 50rem) {
+  .breadcrumb-nav {
+    margin-top: -1rem;
+  }
 }
 
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-pre {
-  font-family: monospace, monospace; /* 1 */
-  font-size: 1em; /* 2 */
+.breadcrumb-nav-list {
+  padding-left: 0;
+  margin-bottom: 0.75rem;
+  list-style: none;
 }
 
-/* Text-level semantics
-   ========================================================================== */
-/**
- * Remove the gray background on active links in IE 10.
- */
-a {
-  background-color: transparent;
+.breadcrumb-nav-list-item {
+  display: table-cell;
+  font-size: 11px !important;
 }
-
-/**
- * 1. Remove the bottom border in Chrome 57-
- * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari.
- */
-abbr[title] {
-  border-bottom: none; /* 1 */
-  text-decoration: underline; /* 2 */
-  text-decoration: underline dotted; /* 2 */
+@media (min-width: 31.25rem) {
+  .breadcrumb-nav-list-item {
+    font-size: 12px !important;
+  }
 }
-
-/**
- * Add the correct font weight in Chrome, Edge, and Safari.
- */
-b,
-strong {
-  font-weight: bolder;
+.breadcrumb-nav-list-item::before {
+  display: none;
 }
-
-/**
- * 1. Correct the inheritance and scaling of font size in all browsers.
- * 2. Correct the odd `em` font sizing in all browsers.
- */
-code,
-kbd,
-samp {
-  font-family: monospace, monospace; /* 1 */
-  font-size: 1em; /* 2 */
+.breadcrumb-nav-list-item::after {
+  display: inline-block;
+  margin-right: 0.5rem;
+  margin-left: 0.5rem;
+  color: #959396;
+  content: "/";
+}
+.breadcrumb-nav-list-item:last-child::after {
+  content: "";
 }
 
-/**
- * Add the correct font size in all browsers.
- */
-small {
-  font-size: 80%;
+h1,
+.text-alpha {
+  font-size: 32px !important;
+  line-height: 1.25;
+  font-weight: 300;
+}
+@media (min-width: 31.25rem) {
+  h1,
+  .text-alpha {
+    font-size: 36px !important;
+  }
 }
 
-/**
- * Prevent `sub` and `sup` elements from affecting the line height in
- * all browsers.
- */
-sub,
-sup {
-  font-size: 75%;
-  line-height: 0;
-  position: relative;
-  vertical-align: baseline;
+h2,
+.text-beta,
+#toctitle {
+  font-size: 18px !important;
+}
+@media (min-width: 31.25rem) {
+  h2,
+  .text-beta,
+  #toctitle {
+    font-size: 24px !important;
+    line-height: 1.25;
+  }
 }
 
-sub {
-  bottom: -0.25em;
+h3,
+.text-gamma {
+  font-size: 16px !important;
+}
+@media (min-width: 31.25rem) {
+  h3,
+  .text-gamma {
+    font-size: 18px !important;
+  }
 }
 
-sup {
-  top: -0.5em;
+h4,
+.text-delta {
+  font-size: 11px !important;
+  font-weight: 400;
+  text-transform: uppercase;
+  letter-spacing: 0.1em;
+}
+@media (min-width: 31.25rem) {
+  h4,
+  .text-delta {
+    font-size: 12px !important;
+  }
 }
 
-/* Embedded content
-   ========================================================================== */
-/**
- * Remove the border on images inside links in IE 10.
- */
-img {
-  border-style: none;
+h4 code {
+  text-transform: none;
 }
 
-/* Forms
-   ========================================================================== */
-/**
- * 1. Change the font styles in all browsers.
- * 2. Remove the margin in Firefox and Safari.
- */
-button,
-input,
-optgroup,
-select,
-textarea {
-  font-family: inherit; /* 1 */
-  font-size: 100%; /* 1 */
-  line-height: 1.15; /* 1 */
-  margin: 0; /* 2 */
-}
-
-/**
- * Show the overflow in IE.
- * 1. Show the overflow in Edge.
- */
-button,
-input {
-  /* 1 */
-  overflow: visible;
-}
-
-/**
- * Remove the inheritance of text transform in Edge, Firefox, and IE.
- * 1. Remove the inheritance of text transform in Firefox.
- */
-button,
-select {
-  /* 1 */
-  text-transform: none;
-}
-
-/**
- * Correct the inability to style clickable types in iOS and Safari.
- */
-button,
-[type=button],
-[type=reset],
-[type=submit] {
-  -webkit-appearance: button;
-}
-
-/**
- * Remove the inner border and padding in Firefox.
- */
-button::-moz-focus-inner,
-[type=button]::-moz-focus-inner,
-[type=reset]::-moz-focus-inner,
-[type=submit]::-moz-focus-inner {
-  border-style: none;
-  padding: 0;
-}
-
-/**
- * Restore the focus styles unset by the previous rule.
- */
-button:-moz-focusring,
-[type=button]:-moz-focusring,
-[type=reset]:-moz-focusring,
-[type=submit]:-moz-focusring {
-  outline: 1px dotted ButtonText;
-}
-
-/**
- * Correct the padding in Firefox.
- */
-fieldset {
-  padding: 0.35em 0.75em 0.625em;
-}
-
-/**
- * 1. Correct the text wrapping in Edge and IE.
- * 2. Correct the color inheritance from `fieldset` elements in IE.
- * 3. Remove the padding so developers are not caught out when they zero out
- *    `fieldset` elements in all browsers.
- */
-legend {
-  box-sizing: border-box; /* 1 */
-  color: inherit; /* 2 */
-  display: table; /* 1 */
-  max-width: 100%; /* 1 */
-  padding: 0; /* 3 */
-  white-space: normal; /* 1 */
-}
-
-/**
- * Add the correct vertical alignment in Chrome, Firefox, and Opera.
- */
-progress {
-  vertical-align: baseline;
-}
-
-/**
- * Remove the default vertical scrollbar in IE 10+.
- */
-textarea {
-  overflow: auto;
-}
-
-/**
- * 1. Add the correct box sizing in IE 10.
- * 2. Remove the padding in IE 10.
- */
-[type=checkbox],
-[type=radio] {
-  box-sizing: border-box; /* 1 */
-  padding: 0; /* 2 */
-}
-
-/**
- * Correct the cursor style of increment and decrement buttons in Chrome.
- */
-[type=number]::-webkit-inner-spin-button,
-[type=number]::-webkit-outer-spin-button {
-  height: auto;
-}
-
-/**
- * 1. Correct the odd appearance in Chrome and Safari.
- * 2. Correct the outline style in Safari.
- */
-[type=search] {
-  -webkit-appearance: textfield; /* 1 */
-  outline-offset: -2px; /* 2 */
-}
-
-/**
- * Remove the inner padding in Chrome and Safari on macOS.
- */
-[type=search]::-webkit-search-decoration {
-  -webkit-appearance: none;
-}
-
-/**
- * 1. Correct the inability to style clickable types in iOS and Safari.
- * 2. Change font properties to `inherit` in Safari.
- */
-::-webkit-file-upload-button {
-  -webkit-appearance: button; /* 1 */
-  font: inherit; /* 2 */
-}
-
-/* Interactive
-   ========================================================================== */
-/*
- * Add the correct display in Edge, IE 10+, and Firefox.
- */
-details {
-  display: block;
-}
-
-/*
- * Add the correct display in all browsers.
- */
-summary {
-  display: list-item;
-}
-
-/* Misc
-   ========================================================================== */
-/**
- * Add the correct display in IE 10+.
- */
-template {
-  display: none;
-}
-
-/**
- * Add the correct display in IE 10.
- */
-[hidden] {
-  display: none;
-}
-
-* {
-  box-sizing: border-box;
+h5,
+.text-epsilon {
+  font-size: 12px !important;
 }
-
-::selection {
-  color: #fff;
-  background: #0969da;
+@media (min-width: 31.25rem) {
+  h5,
+  .text-epsilon {
+    font-size: 14px !important;
+  }
 }
 
-html {
-  font-size: 14px !important;
-  scroll-behavior: smooth;
+h6,
+.text-zeta {
+  font-size: 11px !important;
 }
 @media (min-width: 31.25rem) {
-  html {
-    font-size: 16px !important;
+  h6,
+  .text-zeta {
+    font-size: 12px !important;
   }
 }
 
-body {
-  font-family: system-ui, -apple-system, blinkmacsystemfont, "Segoe UI", roboto, "Helvetica Neue", arial, sans-serif;
-  font-size: inherit;
-  line-height: 1.4;
-  color: #5c5962;
-  background-color: #fff;
-  overflow-wrap: break-word;
+.text-small {
+  font-size: 11px !important;
 }
-
-ol,
-ul,
-dl,
-pre,
-address,
-blockquote,
-table,
-div,
-hr,
-form,
-fieldset,
-noscript .table-wrapper {
-  margin-top: 0;
+@media (min-width: 31.25rem) {
+  .text-small {
+    font-size: 12px !important;
+  }
 }
 
-h1,
-h2,
-h3,
-h4,
-h5,
-h6,
-#toctitle {
-  margin-top: 0;
-  margin-bottom: 1em;
-  font-weight: 500;
-  line-height: 1.25;
-  color: #27262b;
+.text-mono {
+  font-family: "SFMono-Regular", menlo, consolas, monospace !important;
 }
 
-p {
-  margin-top: 1em;
-  margin-bottom: 1em;
+.text-left {
+  text-align: left !important;
 }
 
-a {
-  color: #0969da;
-  text-decoration: none;
+.text-center {
+  text-align: center !important;
 }
 
-a:not([class]) {
-  text-decoration: underline;
-  text-decoration-color: #eeebee;
-  text-underline-offset: 2px;
-}
-a:not([class]):hover {
-  text-decoration-color: rgba(9, 105, 218, 0.45);
+.text-right {
+  text-align: right !important;
 }
 
-code {
-  font-family: "SFMono-Regular", menlo, consolas, monospace;
-  font-size: 0.75em;
-  line-height: 1.4;
-}
-
-figure,
-pre {
-  margin: 16px 0;
-}
-
-li {
-  margin: 0.25em 0;
-}
-
-img {
-  max-width: 100%;
-  height: auto;
-}
-
-hr {
-  height: 1px;
-  padding: 0;
-  margin: 2rem 0;
-  background-color: #eeebee;
-  border: 0;
-}
-
-blockquote {
-  margin: 10px 0;
-  margin-block-start: 0;
-  margin-inline-start: 0;
-  padding-left: 15px;
-  border-left: 3px solid #eeebee;
-}
-
-.side-bar {
-  z-index: 0;
-  display: flex;
-  flex-wrap: wrap;
-  background-color: #f5f6fa;
-}
-@media (min-width: 50rem) {
-  .side-bar {
-    flex-flow: column nowrap;
-    position: fixed;
-    width: 248px;
-    height: 100%;
-    border-right: 1px solid #eeebee;
-    align-items: flex-end;
-  }
-}
-@media (min-width: 66.5rem) {
-  .side-bar {
-    width: calc((100% - 1064px) / 2 + 264px);
-    min-width: 264px;
-  }
-}
-
-@media (min-width: 50rem) {
-  .main {
-    position: relative;
-    max-width: 800px;
-    margin-left: 248px;
-  }
-}
-@media (min-width: 66.5rem) {
-  .main {
-    margin-left: calc(
-      (100% - 1064px) / 2 + 264px
-    );
-  }
-}
-
-.main-content-wrap {
-  padding-right: 1rem;
-  padding-left: 1rem;
-  padding-top: 1rem;
-  padding-bottom: 1rem;
-}
-@media (min-width: 50rem) {
-  .main-content-wrap {
-    padding-right: 2rem;
-    padding-left: 2rem;
-  }
-}
-@media (min-width: 50rem) {
-  .main-content-wrap {
-    padding-top: 2rem;
-    padding-bottom: 2rem;
-  }
-}
-
-.main-header {
-  z-index: 0;
-  display: none;
-  background-color: #f5f6fa;
-}
-@media (min-width: 50rem) {
-  .main-header {
-    display: flex;
-    justify-content: space-between;
-    height: 60px;
-    background-color: #fff;
-    border-bottom: 1px solid #eeebee;
-  }
-}
-.main-header.nav-open {
-  display: block;
-}
-@media (min-width: 50rem) {
-  .main-header.nav-open {
-    display: flex;
-  }
-}
-
-.site-nav,
-.site-header,
-.site-footer {
-  width: 100%;
-}
-@media (min-width: 66.5rem) {
-  .site-nav,
-  .site-header,
-  .site-footer {
-    width: 264px;
-  }
-}
-
-.site-nav {
-  display: none;
-}
-.site-nav.nav-open {
-  display: block;
-}
-@media (min-width: 50rem) {
-  .site-nav {
-    display: block;
-    padding-top: 3rem;
-    padding-bottom: 1rem;
-    overflow-y: auto;
-    flex: 1 1 auto;
-  }
-}
-
-.site-header {
-  display: flex;
-  min-height: 60px;
-  align-items: center;
-}
-@media (min-width: 50rem) {
-  .site-header {
-    height: 60px;
-    max-height: 60px;
-    border-bottom: 1px solid #eeebee;
-  }
-}
-
-.site-title {
-  padding-right: 1rem;
-  padding-left: 1rem;
-  flex-grow: 1;
-  display: flex;
-  height: 100%;
-  align-items: center;
-  padding-top: 0.75rem;
-  padding-bottom: 0.75rem;
-  color: #27262b;
-  font-size: 18px !important;
-}
-@media (min-width: 50rem) {
-  .site-title {
-    padding-right: 2rem;
-    padding-left: 2rem;
-  }
-}
-@media (min-width: 31.25rem) {
-  .site-title {
-    font-size: 24px !important;
-    line-height: 1.25;
-  }
-}
-@media (min-width: 50rem) {
-  .site-title {
-    padding-top: 0.5rem;
-    padding-bottom: 0.5rem;
-  }
-}
-
-.site-button {
-  display: flex;
-  height: 100%;
-  padding: 1rem;
-  align-items: center;
-}
-
-@media (min-width: 50rem) {
-  .site-header .site-button {
-    display: none;
-  }
-}
-.site-title:hover {
-  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%);
-}
-
-.site-button:hover {
-  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 100%);
-}
-
-body {
-  position: relative;
-  padding-bottom: 4rem;
-  overflow-y: scroll;
-}
-@media (min-width: 50rem) {
-  body {
-    position: static;
-    padding-bottom: 0;
-  }
-}
-
-.site-footer {
-  padding-right: 1rem;
-  padding-left: 1rem;
-  position: absolute;
-  bottom: 0;
-  left: 0;
-  padding-top: 1rem;
-  padding-bottom: 1rem;
-  color: #959396;
-  font-size: 11px !important;
-}
-@media (min-width: 50rem) {
-  .site-footer {
-    padding-right: 2rem;
-    padding-left: 2rem;
-  }
+.label,
+.label-blue {
+  display: inline-block;
+  padding: 0.16em 0.56em;
+  margin-right: 0.5rem;
+  margin-left: 0.5rem;
+  color: #fff;
+  text-transform: uppercase;
+  vertical-align: middle;
+  background-color: #2869e6;
+  font-size: 11px !important;
+  border-radius: 12px;
 }
 @media (min-width: 31.25rem) {
-  .site-footer {
-    font-size: 12px !important;
-  }
-}
-@media (min-width: 50rem) {
-  .site-footer {
-    position: static;
-    justify-self: end;
-  }
-}
-
-.icon {
-  width: 1.5rem;
-  height: 1.5rem;
-  color: #0969da;
-}
-
-.main-content {
-  line-height: 1.6;
-}
-.main-content ol,
-.main-content ul,
-.main-content dl,
-.main-content pre,
-.main-content address,
-.main-content blockquote,
-.main-content .table-wrapper {
-  margin-top: 0.5em;
-}
-.main-content a {
-  overflow: hidden;
-  text-overflow: ellipsis;
-}
-.main-content ul,
-.main-content ol {
-  padding-left: 1.5em;
-}
-.main-content li .highlight {
-  margin-top: 0.25rem;
-}
-.main-content ol {
-  list-style-type: none;
-  counter-reset: step-counter;
-}
-.main-content ol > li {
-  position: relative;
-}
-.main-content ol > li::before {
-  position: absolute;
-  top: 0.2em;
-  left: -1.6em;
-  color: #959396;
-  content: counter(step-counter);
-  counter-increment: step-counter;
-  font-size: 12px !important;
-}
-@media (min-width: 31.25rem) {
-  .main-content ol > li::before {
-    font-size: 14px !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .main-content ol > li::before {
-    top: 0.11em;
-  }
-}
-.main-content ol > li ol {
-  counter-reset: sub-counter;
-}
-.main-content ol > li ol > li::before {
-  content: counter(sub-counter, lower-alpha);
-  counter-increment: sub-counter;
-}
-.main-content ul {
-  list-style: none;
-}
-.main-content ul > li::before {
-  position: absolute;
-  margin-left: -1.4em;
-  color: #959396;
-  content: "•";
-}
-.main-content .task-list-item::before {
-  content: "";
-}
-.main-content .task-list-item-checkbox {
-  margin-right: 0.6em;
-  margin-left: -1.4em;
-}
-.main-content hr + * {
-  margin-top: 0;
-}
-.main-content h1:first-of-type {
-  margin-top: 0.5em;
-}
-.main-content dl {
-  display: grid;
-  grid-template: auto/10em 1fr;
-}
-.main-content dt,
-.main-content dd {
-  margin: 0.25em 0;
-}
-.main-content dt {
-  grid-column: 1;
-  font-weight: 500;
-  text-align: right;
-}
-.main-content dt::after {
-  content: ":";
-}
-.main-content dd {
-  grid-column: 2;
-  margin-bottom: 0;
-  margin-left: 1em;
-}
-.main-content dd blockquote:first-child,
-.main-content dd div:first-child,
-.main-content dd dl:first-child,
-.main-content dd dt:first-child,
-.main-content dd h1:first-child,
-.main-content dd h2:first-child,
-.main-content dd h3:first-child,
-.main-content dd h4:first-child,
-.main-content dd h5:first-child,
-.main-content dd h6:first-child,
-.main-content dd li:first-child,
-.main-content dd ol:first-child,
-.main-content dd p:first-child,
-.main-content dd pre:first-child,
-.main-content dd table:first-child,
-.main-content dd ul:first-child,
-.main-content dd .table-wrapper:first-child {
-  margin-top: 0;
-}
-.main-content dd dl:first-child dt:first-child,
-.main-content dd dl:first-child dd:nth-child(2),
-.main-content ol dl:first-child dt:first-child,
-.main-content ol dl:first-child dd:nth-child(2),
-.main-content ul dl:first-child dt:first-child,
-.main-content ul dl:first-child dd:nth-child(2) {
-  margin-top: 0;
-}
-.main-content .anchor-heading {
-  position: absolute;
-  right: -1rem;
-  width: 1.5rem;
-  height: 100%;
-  padding-right: 0.25rem;
-  padding-left: 0.25rem;
-  overflow: visible;
-}
-@media (min-width: 50rem) {
-  .main-content .anchor-heading {
-    right: auto;
-    left: -1.5rem;
-  }
-}
-.main-content .anchor-heading svg {
-  display: inline-block;
-  width: 100%;
-  height: 100%;
-  color: #0969da;
-  visibility: hidden;
-}
-.main-content .anchor-heading:hover svg,
-.main-content .anchor-heading:focus svg,
-.main-content h1:hover > .anchor-heading svg,
-.main-content h2:hover > .anchor-heading svg,
-.main-content h3:hover > .anchor-heading svg,
-.main-content h4:hover > .anchor-heading svg,
-.main-content h5:hover > .anchor-heading svg,
-.main-content h6:hover > .anchor-heading svg {
-  visibility: visible;
-}
-.main-content summary {
-  cursor: pointer;
-}
-.main-content h1,
-.main-content h2,
-.main-content h3,
-.main-content h4,
-.main-content h5,
-.main-content h6,
-.main-content #toctitle {
-  position: relative;
-  margin-top: 1.5em;
-  margin-bottom: 0.25em;
-}
-.main-content h1 + table,
-.main-content h1 + .table-wrapper,
-.main-content h1 + .code-example,
-.main-content h1 + .highlighter-rouge,
-.main-content h1 + .sectionbody .listingblock,
-.main-content h2 + table,
-.main-content h2 + .table-wrapper,
-.main-content h2 + .code-example,
-.main-content h2 + .highlighter-rouge,
-.main-content h2 + .sectionbody .listingblock,
-.main-content h3 + table,
-.main-content h3 + .table-wrapper,
-.main-content h3 + .code-example,
-.main-content h3 + .highlighter-rouge,
-.main-content h3 + .sectionbody .listingblock,
-.main-content h4 + table,
-.main-content h4 + .table-wrapper,
-.main-content h4 + .code-example,
-.main-content h4 + .highlighter-rouge,
-.main-content h4 + .sectionbody .listingblock,
-.main-content h5 + table,
-.main-content h5 + .table-wrapper,
-.main-content h5 + .code-example,
-.main-content h5 + .highlighter-rouge,
-.main-content h5 + .sectionbody .listingblock,
-.main-content h6 + table,
-.main-content h6 + .table-wrapper,
-.main-content h6 + .code-example,
-.main-content h6 + .highlighter-rouge,
-.main-content h6 + .sectionbody .listingblock,
-.main-content #toctitle + table,
-.main-content #toctitle + .table-wrapper,
-.main-content #toctitle + .code-example,
-.main-content #toctitle + .highlighter-rouge,
-.main-content #toctitle + .sectionbody .listingblock {
-  margin-top: 1em;
-}
-.main-content h1 + p:not(.label),
-.main-content h2 + p:not(.label),
-.main-content h3 + p:not(.label),
-.main-content h4 + p:not(.label),
-.main-content h5 + p:not(.label),
-.main-content h6 + p:not(.label),
-.main-content #toctitle + p:not(.label) {
-  margin-top: 0;
-}
-.main-content > h1:first-child,
-.main-content > h2:first-child,
-.main-content > h3:first-child,
-.main-content > h4:first-child,
-.main-content > h5:first-child,
-.main-content > h6:first-child,
-.main-content > .sect1:first-child > h2,
-.main-content > .sect2:first-child > h3,
-.main-content > .sect3:first-child > h4,
-.main-content > .sect4:first-child > h5,
-.main-content > .sect5:first-child > h6 {
-  margin-top: 0.5rem;
-}
-
-.nav-list {
-  padding: 0;
-  margin-top: 0;
-  margin-bottom: 0;
-  list-style: none;
-}
-.nav-list .nav-list-item {
-  font-size: 14px !important;
-  position: relative;
-  margin: 0;
-}
-@media (min-width: 31.25rem) {
-  .nav-list .nav-list-item {
-    font-size: 16px !important;
-  }
-}
-@media (min-width: 50rem) {
-  .nav-list .nav-list-item {
-    font-size: 12px !important;
-  }
-}
-@media (min-width: 50rem) and (min-width: 31.25rem) {
-  .nav-list .nav-list-item {
-    font-size: 14px !important;
-  }
-}
-.nav-list .nav-list-item .nav-list-link {
-  display: block;
-  min-height: 3rem;
-  padding-top: 0.25rem;
-  padding-bottom: 0.25rem;
-  line-height: 2.5rem;
-  padding-right: 3rem;
-  padding-left: 1rem;
-}
-@media (min-width: 50rem) {
-  .nav-list .nav-list-item .nav-list-link {
-    min-height: 2rem;
-    line-height: 1.5rem;
-    padding-right: 2rem;
-    padding-left: 2rem;
-  }
-}
-.nav-list .nav-list-item .nav-list-link.external > svg {
-  width: 1rem;
-  height: 1rem;
-  vertical-align: text-bottom;
-}
-.nav-list .nav-list-item .nav-list-link.active {
-  font-weight: 600;
-  text-decoration: none;
-}
-.nav-list .nav-list-item .nav-list-link:hover, .nav-list .nav-list-item .nav-list-link.active {
-  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 80%, rgba(235, 237, 245, 0) 100%);
-}
-.nav-list .nav-list-item .nav-list-expander {
-  position: absolute;
-  right: 0;
-  width: 3rem;
-  height: 3rem;
-  padding: 0.75rem;
-  color: #0969da;
-}
-@media (min-width: 50rem) {
-  .nav-list .nav-list-item .nav-list-expander {
-    width: 2rem;
-    height: 2rem;
-    padding: 0.5rem;
-  }
-}
-.nav-list .nav-list-item .nav-list-expander:hover {
-  background-image: linear-gradient(-90deg, #ebedf5 0%, rgba(235, 237, 245, 0.8) 100%);
-}
-.nav-list .nav-list-item .nav-list-expander svg {
-  transform: rotate(90deg);
-}
-.nav-list .nav-list-item > .nav-list {
-  display: none;
-  padding-left: 0.75rem;
-  list-style: none;
-}
-.nav-list .nav-list-item > .nav-list .nav-list-item {
-  position: relative;
-}
-.nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-link {
-  color: #5c5962;
-}
-.nav-list .nav-list-item > .nav-list .nav-list-item .nav-list-expander {
-  color: #5c5962;
-}
-.nav-list .nav-list-item.active > .nav-list-expander svg {
-  transform: rotate(-90deg);
-}
-.nav-list .nav-list-item.active > .nav-list {
-  display: block;
-}
-
-.nav-category {
-  padding: 0.5rem 1rem;
-  font-weight: 600;
-  text-align: start;
-  text-transform: uppercase;
-  border-bottom: 1px solid #eeebee;
-  font-size: 11px !important;
-}
-@media (min-width: 31.25rem) {
-  .nav-category {
-    font-size: 12px !important;
-  }
-}
-@media (min-width: 50rem) {
-  .nav-category {
-    padding: 0.5rem 2rem;
-    margin-top: 1rem;
-    text-align: start;
-  }
-  .nav-category:first-child {
-    margin-top: 0;
-  }
-}
-
-.nav-list.nav-category-list > .nav-list-item {
-  margin: 0;
-}
-.nav-list.nav-category-list > .nav-list-item > .nav-list {
-  padding: 0;
-}
-.nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-link {
-  color: #0969da;
-}
-.nav-list.nav-category-list > .nav-list-item > .nav-list > .nav-list-item > .nav-list-expander {
-  color: #0969da;
-}
-
-.aux-nav {
-  height: 100%;
-  overflow-x: auto;
-  font-size: 11px !important;
-}
-@media (min-width: 31.25rem) {
-  .aux-nav {
-    font-size: 12px !important;
-  }
-}
-.aux-nav .aux-nav-list {
-  display: flex;
-  height: 100%;
-  padding: 0;
-  margin: 0;
-  list-style: none;
-}
-.aux-nav .aux-nav-list-item {
-  display: inline-block;
-  height: 100%;
-  padding: 0;
-  margin: 0;
-}
-@media (min-width: 50rem) {
-  .aux-nav {
-    padding-right: 1rem;
-  }
-}
-
-@media (min-width: 50rem) {
-  .breadcrumb-nav {
-    margin-top: -1rem;
-  }
-}
-
-.breadcrumb-nav-list {
-  padding-left: 0;
-  margin-bottom: 0.75rem;
-  list-style: none;
-}
-
-.breadcrumb-nav-list-item {
-  display: table-cell;
-  font-size: 11px !important;
-}
-@media (min-width: 31.25rem) {
-  .breadcrumb-nav-list-item {
-    font-size: 12px !important;
-  }
-}
-.breadcrumb-nav-list-item::before {
-  display: none;
-}
-.breadcrumb-nav-list-item::after {
-  display: inline-block;
-  margin-right: 0.5rem;
-  margin-left: 0.5rem;
-  color: #959396;
-  content: "/";
-}
-.breadcrumb-nav-list-item:last-child::after {
-  content: "";
-}
-
-h1,
-.text-alpha {
-  font-size: 32px !important;
-  line-height: 1.25;
-  font-weight: 300;
-}
-@media (min-width: 31.25rem) {
-  h1,
-  .text-alpha {
-    font-size: 36px !important;
-  }
-}
-
-h2,
-.text-beta,
-#toctitle {
-  font-size: 18px !important;
-}
-@media (min-width: 31.25rem) {
-  h2,
-  .text-beta,
-  #toctitle {
-    font-size: 24px !important;
-    line-height: 1.25;
-  }
-}
-
-h3,
-.text-gamma {
-  font-size: 16px !important;
-}
-@media (min-width: 31.25rem) {
-  h3,
-  .text-gamma {
-    font-size: 18px !important;
-  }
-}
-
-h4,
-.text-delta {
-  font-size: 11px !important;
-  font-weight: 400;
-  text-transform: uppercase;
-  letter-spacing: 0.1em;
-}
-@media (min-width: 31.25rem) {
-  h4,
-  .text-delta {
-    font-size: 12px !important;
-  }
-}
-
-h4 code {
-  text-transform: none;
-}
-
-h5,
-.text-epsilon {
-  font-size: 12px !important;
-}
-@media (min-width: 31.25rem) {
-  h5,
-  .text-epsilon {
-    font-size: 14px !important;
-  }
-}
-
-h6,
-.text-zeta {
-  font-size: 11px !important;
-}
-@media (min-width: 31.25rem) {
-  h6,
-  .text-zeta {
-    font-size: 12px !important;
-  }
-}
-
-.text-small {
-  font-size: 11px !important;
-}
-@media (min-width: 31.25rem) {
-  .text-small {
-    font-size: 12px !important;
-  }
-}
-
-.text-mono {
-  font-family: "SFMono-Regular", menlo, consolas, monospace !important;
-}
-
-.text-left {
-  text-align: left !important;
-}
-
-.text-center {
-  text-align: center !important;
-}
-
-.text-right {
-  text-align: right !important;
-}
-
-.label,
-.label-blue {
-  display: inline-block;
-  padding: 0.16em 0.56em;
-  margin-right: 0.5rem;
-  margin-left: 0.5rem;
-  color: #fff;
-  text-transform: uppercase;
-  vertical-align: middle;
-  background-color: #2869e6;
-  font-size: 11px !important;
-  border-radius: 12px;
-}
-@media (min-width: 31.25rem) {
-  .label,
-  .label-blue {
-    font-size: 12px !important;
-  }
-}
-
-.label-green {
-  background-color: #009c7b;
-}
-
-.label-purple {
-  background-color: #5e41d0;
-}
-
-.label-red {
-  background-color: #e94c4c;
-}
-
-.label-yellow {
-  color: #44434d;
-  background-color: #f7d12e;
-}
-
-.btn {
-  display: inline-block;
-  box-sizing: border-box;
-  padding: 0.3em 1em;
-  margin: 0;
-  font-family: inherit;
-  font-size: inherit;
-  font-weight: 500;
-  line-height: 1.5;
-  color: #0969da;
-  text-decoration: none;
-  vertical-align: baseline;
-  cursor: pointer;
-  background-color: #f7f7f7;
-  border-width: 0;
-  border-radius: 4px;
-  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
-  appearance: none;
-}
-.btn:focus {
-  text-decoration: none;
-  outline: none;
-  box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.25);
-}
-.btn:focus:hover, .btn.selected:focus {
-  box-shadow: 0 0 0 3px rgba(0, 0, 255, 0.25);
-}
-.btn:hover, .btn.zeroclipboard-is-hover {
-  color: #6a4aec;
-}
-.btn:hover, .btn:active, .btn.zeroclipboard-is-hover, .btn.zeroclipboard-is-active {
-  text-decoration: none;
-  background-color: #f4f4f4;
-}
-.btn:active, .btn.selected, .btn.zeroclipboard-is-active {
-  background-color: #efefef;
-  background-image: none;
-  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
-}
-.btn.selected:hover {
-  background-color: #cfcfcf;
-}
-.btn:disabled, .btn:disabled:hover, .btn.disabled, .btn.disabled:hover {
-  color: rgba(102, 102, 102, 0.5);
-  cursor: default;
-  background-color: rgba(229, 229, 229, 0.5);
-  background-image: none;
-  box-shadow: none;
-}
-
-.btn-outline {
-  color: #0969da;
-  background: transparent;
-  box-shadow: inset 0 0 0 2px #e6e1e8;
-}
-.btn-outline:hover, .btn-outline:active, .btn-outline.zeroclipboard-is-hover, .btn-outline.zeroclipboard-is-active {
-  color: #6341eb;
-  text-decoration: none;
-  background-color: transparent;
-  box-shadow: inset 0 0 0 3px #e6e1e8;
-}
-.btn-outline:focus {
-  text-decoration: none;
-  outline: none;
-  box-shadow: inset 0 0 0 2px #5c5962, 0 0 0 3px rgba(0, 0, 255, 0.25);
-}
-.btn-outline:focus:hover, .btn-outline.selected:focus {
-  box-shadow: inset 0 0 0 2px #5c5962;
-}
-
-.btn-primary {
-  color: #fff;
-  background-color: #5739ce;
-  background-image: linear-gradient(#6f55d5, #5739ce);
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12);
-}
-.btn-primary:hover, .btn-primary.zeroclipboard-is-hover {
-  color: #fff;
-  background-color: #5132cb;
-  background-image: linear-gradient(#6549d2, #5132cb);
-}
-.btn-primary:active, .btn-primary.selected, .btn-primary.zeroclipboard-is-active {
-  background-color: #4f31c6;
-  background-image: none;
-  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
-}
-.btn-primary.selected:hover {
-  background-color: #472cb2;
-}
-
-.btn-purple {
-  color: #fff;
-  background-color: #5739ce;
-  background-image: linear-gradient(#6f55d5, #5739ce);
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12);
-}
-.btn-purple:hover, .btn-purple.zeroclipboard-is-hover {
-  color: #fff;
-  background-color: #5132cb;
-  background-image: linear-gradient(#6549d2, #5132cb);
-}
-.btn-purple:active, .btn-purple.selected, .btn-purple.zeroclipboard-is-active {
-  background-color: #4f31c6;
-  background-image: none;
-  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
-}
-.btn-purple.selected:hover {
-  background-color: #472cb2;
-}
-
-.btn-blue {
-  color: #fff;
-  background-color: #227efa;
-  background-image: linear-gradient(#4593fb, #227efa);
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12);
-}
-.btn-blue:hover, .btn-blue.zeroclipboard-is-hover {
-  color: #fff;
-  background-color: #1878fa;
-  background-image: linear-gradient(#368afa, #1878fa);
-}
-.btn-blue:active, .btn-blue.selected, .btn-blue.zeroclipboard-is-active {
-  background-color: #1375f9;
-  background-image: none;
-  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
-}
-.btn-blue.selected:hover {
-  background-color: #0669ed;
-}
-
-.btn-green {
-  color: #fff;
-  background-color: #10ac7d;
-  background-image: linear-gradient(#13cc95, #10ac7d);
-  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25), 0 4px 10px rgba(0, 0, 0, 0.12);
-}
-.btn-green:hover, .btn-green.zeroclipboard-is-hover {
-  color: #fff;
-  background-color: #0fa276;
-  background-image: linear-gradient(#12be8b, #0fa276);
-}
-.btn-green:active, .btn-green.selected, .btn-green.zeroclipboard-is-active {
-  background-color: #0f9e73;
-  background-image: none;
-  box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.15);
-}
-.btn-green.selected:hover {
-  background-color: #0d8662;
-}
-
-.search {
-  position: relative;
-  z-index: 2;
-  flex-grow: 1;
-  height: 4rem;
-  padding: 0.5rem;
-  transition: padding linear 200ms;
-}
-@media (min-width: 50rem) {
-  .search {
-    position: relative !important;
-    width: auto !important;
-    height: 100% !important;
-    padding: 0;
-    transition: none;
-  }
-}
-
-.search-input-wrap {
-  position: relative;
-  z-index: 1;
-  height: 3rem;
-  overflow: hidden;
-  border-radius: 4px;
-  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
-  transition: height linear 200ms;
-}
-@media (min-width: 50rem) {
-  .search-input-wrap {
-    position: absolute;
-    width: 100%;
-    max-width: 536px;
-    height: 100% !important;
-    border-radius: 0;
-    box-shadow: none;
-    transition: width ease 400ms;
-  }
-}
-
-.search-input {
-  position: absolute;
-  width: 100%;
-  height: 100%;
-  padding: 0.5rem 1rem 0.5rem 2.5rem;
-  font-size: 16px;
-  color: #5c5962;
-  background-color: #fff;
-  border-top: 0;
-  border-right: 0;
-  border-bottom: 0;
-  border-left: 0;
-  border-radius: 0;
-}
-@media (min-width: 50rem) {
-  .search-input {
-    padding: 0.5rem 1rem 0.5rem 3.5rem;
-    font-size: 14px;
-    background-color: #fff;
-    transition: padding-left linear 200ms;
-  }
-}
-.search-input:focus {
-  outline: 0;
-}
-.search-input:focus + .search-label .search-icon {
-  color: #0969da;
-}
-
-.search-label {
-  position: absolute;
-  display: flex;
-  height: 100%;
-  padding-left: 1rem;
-}
-@media (min-width: 50rem) {
-  .search-label {
-    padding-left: 2rem;
-    transition: padding-left linear 200ms;
-  }
-}
-.search-label .search-icon {
-  width: 1.2rem;
-  height: 1.2rem;
-  align-self: center;
-  color: #959396;
-}
-
-.search-results {
-  position: absolute;
-  left: 0;
-  display: none;
-  width: 100%;
-  max-height: calc(100% - 4rem);
-  overflow-y: auto;
-  background-color: #fff;
-  border-bottom-right-radius: 4px;
-  border-bottom-left-radius: 4px;
-  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
-}
-@media (min-width: 50rem) {
-  .search-results {
-    top: 100%;
-    width: 536px;
-    max-height: calc(100vh - 200%) !important;
-  }
-}
-
-.search-results-list {
-  padding-left: 0;
-  margin-bottom: 0.25rem;
-  list-style: none;
-  font-size: 14px !important;
-}
-@media (min-width: 31.25rem) {
-  .search-results-list {
-    font-size: 16px !important;
-  }
-}
-@media (min-width: 50rem) {
-  .search-results-list {
-    font-size: 12px !important;
-  }
-}
-@media (min-width: 50rem) and (min-width: 31.25rem) {
-  .search-results-list {
-    font-size: 14px !important;
-  }
-}
-
-.search-results-list-item {
-  padding: 0;
-  margin: 0;
-}
-
-.search-result {
-  display: block;
-  padding: 0.25rem 0.75rem;
-}
-.search-result:hover, .search-result.active {
-  background-color: #ebedf5;
-}
-
-.search-result-title {
-  display: block;
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
-}
-@media (min-width: 31.25rem) {
-  .search-result-title {
-    display: inline-block;
-    width: 40%;
-    padding-right: 0.5rem;
-    vertical-align: top;
-  }
-}
-
-.search-result-doc {
-  display: flex;
-  align-items: center;
-  word-wrap: break-word;
-}
-.search-result-doc.search-result-doc-parent {
-  opacity: 0.5;
-  font-size: 12px !important;
-}
-@media (min-width: 31.25rem) {
-  .search-result-doc.search-result-doc-parent {
-    font-size: 14px !important;
-  }
-}
-@media (min-width: 50rem) {
-  .search-result-doc.search-result-doc-parent {
-    font-size: 11px !important;
-  }
-}
-@media (min-width: 50rem) and (min-width: 31.25rem) {
-  .search-result-doc.search-result-doc-parent {
-    font-size: 12px !important;
-  }
-}
-.search-result-doc .search-result-icon {
-  width: 1rem;
-  height: 1rem;
-  margin-right: 0.5rem;
-  color: #0969da;
-  flex-shrink: 0;
-}
-.search-result-doc .search-result-doc-title {
-  overflow: auto;
-}
-
-.search-result-section {
-  margin-left: 1.5rem;
-  word-wrap: break-word;
-}
-
-.search-result-rel-url {
-  display: block;
-  margin-left: 1.5rem;
-  overflow: hidden;
-  color: #959396;
-  text-overflow: ellipsis;
-  white-space: nowrap;
-  font-size: 9px !important;
-}
-@media (min-width: 31.25rem) {
-  .search-result-rel-url {
-    font-size: 10px !important;
-  }
-}
-
-.search-result-previews {
-  display: block;
-  padding-top: 0.5rem;
-  padding-bottom: 0.5rem;
-  padding-left: 1rem;
-  margin-left: 0.5rem;
-  color: #959396;
-  word-wrap: break-word;
-  border-left: 1px solid;
-  border-left-color: #eeebee;
-  font-size: 11px !important;
-}
-@media (min-width: 31.25rem) {
-  .search-result-previews {
-    font-size: 12px !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .search-result-previews {
-    display: inline-block;
-    width: 60%;
-    padding-left: 0.5rem;
-    margin-left: 0;
-    vertical-align: top;
-  }
-}
-
-.search-result-preview + .search-result-preview {
-  margin-top: 0.25rem;
-}
-
-.search-result-highlight {
-  font-weight: bold;
-}
-
-.search-no-result {
-  padding: 0.5rem 0.75rem;
-  font-size: 12px !important;
-}
-@media (min-width: 31.25rem) {
-  .search-no-result {
-    font-size: 14px !important;
-  }
-}
-
-.search-button {
-  position: fixed;
-  right: 1rem;
-  bottom: 1rem;
-  display: flex;
-  width: 3.5rem;
-  height: 3.5rem;
-  background-color: #fff;
-  border: 1px solid rgba(114, 83, 237, 0.3);
-  border-radius: 1.75rem;
-  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
-  align-items: center;
-  justify-content: center;
-}
-
-.search-overlay {
-  position: fixed;
-  top: 0;
-  left: 0;
-  z-index: 1;
-  width: 0;
-  height: 0;
-  background-color: rgba(0, 0, 0, 0.3);
-  opacity: 0;
-  transition: opacity ease 400ms, width 0s 400ms, height 0s 400ms;
-}
-
-.search-active .search {
-  position: fixed;
-  top: 0;
-  left: 0;
-  width: 100%;
-  height: 100%;
-  padding: 0;
-}
-.search-active .search-input-wrap {
-  height: 4rem;
-  border-radius: 0;
-}
-@media (min-width: 50rem) {
-  .search-active .search-input-wrap {
-    width: 536px;
-    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
-  }
-}
-.search-active .search-input {
-  background-color: #fff;
-}
-@media (min-width: 50rem) {
-  .search-active .search-input {
-    padding-left: 2.3rem;
-  }
-}
-@media (min-width: 50rem) {
-  .search-active .search-label {
-    padding-left: 0.6rem;
-  }
-}
-.search-active .search-results {
-  display: block;
-}
-.search-active .search-overlay {
-  width: 100%;
-  height: 100%;
-  opacity: 1;
-  transition: opacity ease 400ms, width 0s, height 0s;
-}
-@media (min-width: 50rem) {
-  .search-active .main {
-    position: fixed;
-    right: 0;
-    left: 0;
-  }
-}
-.search-active .main-header {
-  padding-top: 4rem;
-}
-@media (min-width: 50rem) {
-  .search-active .main-header {
-    padding-top: 0;
-  }
-}
-
-.table-wrapper {
-  display: block;
-  width: 100%;
-  max-width: 100%;
-  margin-bottom: 1.5rem;
-  overflow-x: auto;
-  border-radius: 4px;
-  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
-}
-
-table {
-  display: table;
-  min-width: 100%;
-  border-collapse: separate;
-}
-
-th,
-td {
-  font-size: 12px !important;
-  min-width: 120px;
-  padding: 0.5rem 0.75rem;
-  background-color: #fff;
-  border-bottom: 1px solid rgba(238, 235, 238, 0.5);
-  border-left: 1px solid #eeebee;
-}
-@media (min-width: 31.25rem) {
-  th,
-  td {
-    font-size: 14px !important;
-  }
-}
-th:first-of-type,
-td:first-of-type {
-  border-left: 0;
-}
-
-tbody tr:last-of-type th,
-tbody tr:last-of-type td {
-  border-bottom: 0;
-}
-tbody tr:last-of-type td {
-  padding-bottom: 0.75rem;
-}
-
-thead th {
-  border-bottom: 1px solid #eeebee;
-}
-
-:not(pre, figure) > code {
-  padding: 0.2em 0.15em;
-  font-weight: 400;
-  background-color: #f5f6fa;
-  border: 1px solid #eeebee;
-  border-radius: 4px;
-}
-
-a:visited code {
-  border-color: #eeebee;
-}
-
-div.highlight,
-div.listingblock > div.content,
-figure.highlight {
-  margin-top: 0;
-  margin-bottom: 0.75rem;
-  background-color: #f5f6fa;
-  border-radius: 4px;
-  box-shadow: none;
-  -webkit-overflow-scrolling: touch;
-  position: relative;
-  padding: 0;
-}
-div.highlight > button,
-div.listingblock > div.content > button,
-figure.highlight > button {
-  width: 0.75rem;
-  opacity: 0;
-  position: absolute;
-  top: 0;
-  right: 0;
-  border: 0.75rem solid #f5f6fa;
-  background-color: #f5f6fa;
-  color: #5c5962;
-  box-sizing: content-box;
-}
-div.highlight > button svg,
-div.listingblock > div.content > button svg,
-figure.highlight > button svg {
-  fill: #5c5962;
-}
-div.highlight > button:active,
-div.listingblock > div.content > button:active,
-figure.highlight > button:active {
-  text-decoration: none;
-  outline: none;
-  opacity: 1;
-}
-div.highlight > button:focus,
-div.listingblock > div.content > button:focus,
-figure.highlight > button:focus {
-  opacity: 1;
-}
-div.highlight:hover > button,
-div.listingblock > div.content:hover > button,
-figure.highlight:hover > button {
-  cursor: copy;
-  opacity: 1;
-}
-
-div.highlight div.highlight,
-div.listingblock div.highlight {
-  overflow-x: auto;
-  padding: 0.75rem;
-  margin: 0;
-  border: 0;
-}
-div.highlight pre.highlight,
-div.highlight code,
-div.listingblock pre.highlight,
-div.listingblock code {
-  padding: 0;
-  margin: 0;
-  border: 0;
-}
-
-figure.highlight pre,
-figure.highlight :not(pre) > code {
-  overflow-x: auto;
-  padding: 0.75rem;
-  margin: 0;
-  border: 0;
-}
-
-.highlight .table-wrapper {
-  padding: 0.75rem 0;
-  margin: 0;
-  border: 0;
-  box-shadow: none;
-}
-.highlight .table-wrapper td,
-.highlight .table-wrapper pre {
-  font-size: 11px !important;
-  min-width: 0;
-  padding: 0;
-  background-color: #f5f6fa;
-  border: 0;
-}
-@media (min-width: 31.25rem) {
-  .highlight .table-wrapper td,
-  .highlight .table-wrapper pre {
-    font-size: 12px !important;
-  }
-}
-.highlight .table-wrapper td.gl {
-  width: 1em;
-  padding-right: 0.75rem;
-  padding-left: 0.75rem;
-}
-.highlight .table-wrapper pre {
-  margin: 0;
-  line-height: 2;
-}
-
-.code-example,
-.listingblock > .title {
-  padding: 0.75rem;
-  margin-bottom: 0.75rem;
-  overflow: auto;
-  border: 1px solid #eeebee;
-  border-radius: 4px;
-}
-.code-example + .highlighter-rouge,
-.code-example + .sectionbody .listingblock,
-.code-example + .content,
-.code-example + figure.highlight,
-.listingblock > .title + .highlighter-rouge,
-.listingblock > .title + .sectionbody .listingblock,
-.listingblock > .title + .content,
-.listingblock > .title + figure.highlight {
-  position: relative;
-  margin-top: -1rem;
-  border-right: 1px solid #eeebee;
-  border-bottom: 1px solid #eeebee;
-  border-left: 1px solid #eeebee;
-  border-top-left-radius: 0;
-  border-top-right-radius: 0;
-}
-
-code.language-mermaid {
-  padding: 0;
-  background-color: inherit;
-  border: 0;
-}
-
-.text-grey-dk-000 {
-  color: #959396 !important;
-}
-
-.text-grey-dk-100 {
-  color: #5c5962 !important;
-}
-
-.text-grey-dk-200 {
-  color: #44434d !important;
-}
-
-.text-grey-dk-250 {
-  color: #302d36 !important;
-}
-
-.text-grey-dk-300 {
-  color: #27262b !important;
-}
-
-.text-grey-lt-000 {
-  color: #f5f6fa !important;
-}
-
-.text-grey-lt-100 {
-  color: #eeebee !important;
-}
-
-.text-grey-lt-200 {
-  color: #ecebed !important;
-}
-
-.text-grey-lt-300 {
-  color: #e6e1e8 !important;
-}
-
-.text-blue-000 {
-  color: #2c84fa !important;
-}
-
-.text-blue-100 {
-  color: #2869e6 !important;
-}
-
-.text-blue-200 {
-  color: #264caf !important;
-}
-
-.text-blue-300 {
-  color: #183385 !important;
-}
-
-.text-green-000 {
-  color: #41d693 !important;
-}
-
-.text-green-100 {
-  color: #11b584 !important;
-}
-
-.text-green-200 {
-  color: #009c7b !important;
-}
-
-.text-green-300 {
-  color: #026e57 !important;
-}
-
-.text-purple-000 {
-  color: #7253ed !important;
-}
-
-.text-purple-100 {
-  color: #5e41d0 !important;
-}
-
-.text-purple-200 {
-  color: #4e26af !important;
-}
-
-.text-purple-300 {
-  color: #381885 !important;
-}
-
-.text-yellow-000 {
-  color: #ffeb82 !important;
-}
-
-.text-yellow-100 {
-  color: #fadf50 !important;
-}
-
-.text-yellow-200 {
-  color: #f7d12e !important;
-}
-
-.text-yellow-300 {
-  color: #e7af06 !important;
-}
-
-.text-red-000 {
-  color: #f77e7e !important;
-}
-
-.text-red-100 {
-  color: #f96e65 !important;
-}
-
-.text-red-200 {
-  color: #e94c4c !important;
-}
-
-.text-red-300 {
-  color: #dd2e2e !important;
-}
-
-.bg-grey-dk-000 {
-  background-color: #959396 !important;
-}
-
-.bg-grey-dk-100 {
-  background-color: #5c5962 !important;
-}
-
-.bg-grey-dk-200 {
-  background-color: #44434d !important;
-}
-
-.bg-grey-dk-250 {
-  background-color: #302d36 !important;
-}
-
-.bg-grey-dk-300 {
-  background-color: #27262b !important;
-}
-
-.bg-grey-lt-000 {
-  background-color: #f5f6fa !important;
-}
-
-.bg-grey-lt-100 {
-  background-color: #eeebee !important;
-}
-
-.bg-grey-lt-200 {
-  background-color: #ecebed !important;
-}
-
-.bg-grey-lt-300 {
-  background-color: #e6e1e8 !important;
-}
-
-.bg-blue-000 {
-  background-color: #2c84fa !important;
-}
-
-.bg-blue-100 {
-  background-color: #2869e6 !important;
-}
-
-.bg-blue-200 {
-  background-color: #264caf !important;
-}
-
-.bg-blue-300 {
-  background-color: #183385 !important;
-}
-
-.bg-green-000 {
-  background-color: #41d693 !important;
-}
-
-.bg-green-100 {
-  background-color: #11b584 !important;
-}
-
-.bg-green-200 {
-  background-color: #009c7b !important;
-}
-
-.bg-green-300 {
-  background-color: #026e57 !important;
-}
-
-.bg-purple-000 {
-  background-color: #7253ed !important;
-}
-
-.bg-purple-100 {
-  background-color: #5e41d0 !important;
-}
-
-.bg-purple-200 {
-  background-color: #4e26af !important;
-}
-
-.bg-purple-300 {
-  background-color: #381885 !important;
-}
-
-.bg-yellow-000 {
-  background-color: #ffeb82 !important;
-}
-
-.bg-yellow-100 {
-  background-color: #fadf50 !important;
-}
-
-.bg-yellow-200 {
-  background-color: #f7d12e !important;
-}
-
-.bg-yellow-300 {
-  background-color: #e7af06 !important;
-}
-
-.bg-red-000 {
-  background-color: #f77e7e !important;
-}
-
-.bg-red-100 {
-  background-color: #f96e65 !important;
-}
-
-.bg-red-200 {
-  background-color: #e94c4c !important;
-}
-
-.bg-red-300 {
-  background-color: #dd2e2e !important;
-}
-
-.d-block {
-  display: block !important;
-}
-
-.d-flex {
-  display: flex !important;
-}
-
-.d-inline {
-  display: inline !important;
-}
-
-.d-inline-block {
-  display: inline-block !important;
-}
-
-.d-none {
-  display: none !important;
-}
-
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 20rem) {
-  .d-xs-block {
-    display: block !important;
-  }
-  .d-xs-flex {
-    display: flex !important;
-  }
-  .d-xs-inline {
-    display: inline !important;
-  }
-  .d-xs-inline-block {
-    display: inline-block !important;
-  }
-  .d-xs-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .d-sm-block {
-    display: block !important;
-  }
-  .d-sm-flex {
-    display: flex !important;
-  }
-  .d-sm-inline {
-    display: inline !important;
-  }
-  .d-sm-inline-block {
-    display: inline-block !important;
-  }
-  .d-sm-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 50rem) {
-  .d-md-block {
-    display: block !important;
-  }
-  .d-md-flex {
-    display: flex !important;
-  }
-  .d-md-inline {
-    display: inline !important;
-  }
-  .d-md-inline-block {
-    display: inline-block !important;
-  }
-  .d-md-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .d-lg-block {
-    display: block !important;
-  }
-  .d-lg-flex {
-    display: flex !important;
-  }
-  .d-lg-inline {
-    display: inline !important;
-  }
-  .d-lg-inline-block {
-    display: inline-block !important;
-  }
-  .d-lg-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .d-xl-block {
-    display: block !important;
-  }
-  .d-xl-flex {
-    display: flex !important;
-  }
-  .d-xl-inline {
-    display: inline !important;
-  }
-  .d-xl-inline-block {
-    display: inline-block !important;
-  }
-  .d-xl-none {
-    display: none !important;
-  }
-}
-.float-left {
-  float: left !important;
-}
-
-.float-right {
-  float: right !important;
-}
-
-.flex-justify-start {
-  justify-content: flex-start !important;
-}
-
-.flex-justify-end {
-  justify-content: flex-end !important;
-}
-
-.flex-justify-between {
-  justify-content: space-between !important;
-}
-
-.flex-justify-around {
-  justify-content: space-around !important;
-}
-
-.v-align-baseline {
-  vertical-align: baseline !important;
-}
-
-.v-align-bottom {
-  vertical-align: bottom !important;
-}
-
-.v-align-middle {
-  vertical-align: middle !important;
-}
-
-.v-align-text-bottom {
-  vertical-align: text-bottom !important;
-}
-
-.v-align-text-top {
-  vertical-align: text-top !important;
-}
-
-.v-align-top {
-  vertical-align: top !important;
-}
-
-.fs-1 {
-  font-size: 9px !important;
-}
-@media (min-width: 31.25rem) {
-  .fs-1 {
-    font-size: 10px !important;
-  }
-}
-
-.fs-2 {
-  font-size: 11px !important;
-}
-@media (min-width: 31.25rem) {
-  .fs-2 {
-    font-size: 12px !important;
-  }
-}
-
-.fs-3 {
-  font-size: 12px !important;
-}
-@media (min-width: 31.25rem) {
-  .fs-3 {
-    font-size: 14px !important;
-  }
-}
-
-.fs-4 {
-  font-size: 14px !important;
-}
-@media (min-width: 31.25rem) {
-  .fs-4 {
-    font-size: 16px !important;
-  }
-}
-
-.fs-5 {
-  font-size: 16px !important;
-}
-@media (min-width: 31.25rem) {
-  .fs-5 {
-    font-size: 18px !important;
-  }
-}
-
-.fs-6 {
-  font-size: 18px !important;
-}
-@media (min-width: 31.25rem) {
-  .fs-6 {
-    font-size: 24px !important;
-    line-height: 1.25;
-  }
-}
-
-.fs-7 {
-  font-size: 24px !important;
-  line-height: 1.25;
-}
-@media (min-width: 31.25rem) {
-  .fs-7 {
-    font-size: 32px !important;
-  }
-}
-
-.fs-8 {
-  font-size: 32px !important;
-  line-height: 1.25;
-}
-@media (min-width: 31.25rem) {
-  .fs-8 {
-    font-size: 36px !important;
-  }
-}
-
-.fs-9 {
-  font-size: 36px !important;
-  line-height: 1.25;
-}
-@media (min-width: 31.25rem) {
-  .fs-9 {
-    font-size: 42px !important;
-  }
-}
-
-.fs-10 {
-  font-size: 42px !important;
-  line-height: 1.25;
-}
-@media (min-width: 31.25rem) {
-  .fs-10 {
-    font-size: 48px !important;
-  }
-}
-
-.fw-300 {
-  font-weight: 300 !important;
-}
-
-.fw-400 {
-  font-weight: 400 !important;
-}
-
-.fw-500 {
-  font-weight: 500 !important;
-}
-
-.fw-700 {
-  font-weight: 700 !important;
-}
-
-.lh-0 {
-  line-height: 0 !important;
-}
-
-.lh-default {
-  line-height: 1.4;
-}
-
-.lh-tight {
-  line-height: 1.25;
-}
-
-.ls-5 {
-  letter-spacing: 0.05em !important;
-}
-
-.ls-10 {
-  letter-spacing: 0.1em !important;
-}
-
-.ls-0 {
-  letter-spacing: 0 !important;
-}
-
-.text-uppercase {
-  text-transform: uppercase !important;
-}
-
-.list-style-none {
-  padding: 0 !important;
-  margin: 0 !important;
-  list-style: none !important;
-}
-.list-style-none li::before {
-  display: none !important;
-}
-
-.mx-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-0 {
-  margin: 0 !important;
-}
-
-.mt-0 {
-  margin-top: 0 !important;
-}
-
-.mr-0 {
-  margin-right: 0 !important;
-}
-
-.mb-0 {
-  margin-bottom: 0 !important;
-}
-
-.ml-0 {
-  margin-left: 0 !important;
-}
-
-.mx-0 {
-  margin-right: 0 !important;
-  margin-left: 0 !important;
-}
-
-.my-0 {
-  margin-top: 0 !important;
-  margin-bottom: 0 !important;
-}
-
-.mxn-0 {
-  margin-right: -0 !important;
-  margin-left: -0 !important;
-}
-
-.mx-0-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-1 {
-  margin: 0.25rem !important;
-}
-
-.mt-1 {
-  margin-top: 0.25rem !important;
-}
-
-.mr-1 {
-  margin-right: 0.25rem !important;
-}
-
-.mb-1 {
-  margin-bottom: 0.25rem !important;
-}
-
-.ml-1 {
-  margin-left: 0.25rem !important;
-}
-
-.mx-1 {
-  margin-right: 0.25rem !important;
-  margin-left: 0.25rem !important;
-}
-
-.my-1 {
-  margin-top: 0.25rem !important;
-  margin-bottom: 0.25rem !important;
-}
-
-.mxn-1 {
-  margin-right: -0.25rem !important;
-  margin-left: -0.25rem !important;
-}
-
-.mx-1-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-2 {
-  margin: 0.5rem !important;
-}
-
-.mt-2 {
-  margin-top: 0.5rem !important;
-}
-
-.mr-2 {
-  margin-right: 0.5rem !important;
-}
-
-.mb-2 {
-  margin-bottom: 0.5rem !important;
-}
-
-.ml-2 {
-  margin-left: 0.5rem !important;
-}
-
-.mx-2 {
-  margin-right: 0.5rem !important;
-  margin-left: 0.5rem !important;
-}
-
-.my-2 {
-  margin-top: 0.5rem !important;
-  margin-bottom: 0.5rem !important;
-}
-
-.mxn-2 {
-  margin-right: -0.5rem !important;
-  margin-left: -0.5rem !important;
-}
-
-.mx-2-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-3 {
-  margin: 0.75rem !important;
-}
-
-.mt-3 {
-  margin-top: 0.75rem !important;
-}
-
-.mr-3 {
-  margin-right: 0.75rem !important;
-}
-
-.mb-3 {
-  margin-bottom: 0.75rem !important;
-}
-
-.ml-3 {
-  margin-left: 0.75rem !important;
-}
-
-.mx-3 {
-  margin-right: 0.75rem !important;
-  margin-left: 0.75rem !important;
-}
-
-.my-3 {
-  margin-top: 0.75rem !important;
-  margin-bottom: 0.75rem !important;
-}
-
-.mxn-3 {
-  margin-right: -0.75rem !important;
-  margin-left: -0.75rem !important;
-}
-
-.mx-3-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-4 {
-  margin: 1rem !important;
-}
-
-.mt-4 {
-  margin-top: 1rem !important;
-}
-
-.mr-4 {
-  margin-right: 1rem !important;
-}
-
-.mb-4 {
-  margin-bottom: 1rem !important;
-}
-
-.ml-4 {
-  margin-left: 1rem !important;
-}
-
-.mx-4 {
-  margin-right: 1rem !important;
-  margin-left: 1rem !important;
-}
-
-.my-4 {
-  margin-top: 1rem !important;
-  margin-bottom: 1rem !important;
-}
-
-.mxn-4 {
-  margin-right: -1rem !important;
-  margin-left: -1rem !important;
-}
-
-.mx-4-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-5 {
-  margin: 1.5rem !important;
-}
-
-.mt-5 {
-  margin-top: 1.5rem !important;
-}
-
-.mr-5 {
-  margin-right: 1.5rem !important;
-}
-
-.mb-5 {
-  margin-bottom: 1.5rem !important;
-}
-
-.ml-5 {
-  margin-left: 1.5rem !important;
-}
-
-.mx-5 {
-  margin-right: 1.5rem !important;
-  margin-left: 1.5rem !important;
-}
-
-.my-5 {
-  margin-top: 1.5rem !important;
-  margin-bottom: 1.5rem !important;
-}
-
-.mxn-5 {
-  margin-right: -1.5rem !important;
-  margin-left: -1.5rem !important;
-}
-
-.mx-5-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-6 {
-  margin: 2rem !important;
-}
-
-.mt-6 {
-  margin-top: 2rem !important;
-}
-
-.mr-6 {
-  margin-right: 2rem !important;
-}
-
-.mb-6 {
-  margin-bottom: 2rem !important;
-}
-
-.ml-6 {
-  margin-left: 2rem !important;
-}
-
-.mx-6 {
-  margin-right: 2rem !important;
-  margin-left: 2rem !important;
-}
-
-.my-6 {
-  margin-top: 2rem !important;
-  margin-bottom: 2rem !important;
-}
-
-.mxn-6 {
-  margin-right: -2rem !important;
-  margin-left: -2rem !important;
-}
-
-.mx-6-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-7 {
-  margin: 2.5rem !important;
-}
-
-.mt-7 {
-  margin-top: 2.5rem !important;
-}
-
-.mr-7 {
-  margin-right: 2.5rem !important;
-}
-
-.mb-7 {
-  margin-bottom: 2.5rem !important;
-}
-
-.ml-7 {
-  margin-left: 2.5rem !important;
-}
-
-.mx-7 {
-  margin-right: 2.5rem !important;
-  margin-left: 2.5rem !important;
-}
-
-.my-7 {
-  margin-top: 2.5rem !important;
-  margin-bottom: 2.5rem !important;
-}
-
-.mxn-7 {
-  margin-right: -2.5rem !important;
-  margin-left: -2.5rem !important;
-}
-
-.mx-7-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-8 {
-  margin: 3rem !important;
-}
-
-.mt-8 {
-  margin-top: 3rem !important;
-}
-
-.mr-8 {
-  margin-right: 3rem !important;
-}
-
-.mb-8 {
-  margin-bottom: 3rem !important;
-}
-
-.ml-8 {
-  margin-left: 3rem !important;
-}
-
-.mx-8 {
-  margin-right: 3rem !important;
-  margin-left: 3rem !important;
-}
-
-.my-8 {
-  margin-top: 3rem !important;
-  margin-bottom: 3rem !important;
-}
-
-.mxn-8 {
-  margin-right: -3rem !important;
-  margin-left: -3rem !important;
-}
-
-.mx-8-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-9 {
-  margin: 3.5rem !important;
-}
-
-.mt-9 {
-  margin-top: 3.5rem !important;
-}
-
-.mr-9 {
-  margin-right: 3.5rem !important;
-}
-
-.mb-9 {
-  margin-bottom: 3.5rem !important;
-}
-
-.ml-9 {
-  margin-left: 3.5rem !important;
-}
-
-.mx-9 {
-  margin-right: 3.5rem !important;
-  margin-left: 3.5rem !important;
-}
-
-.my-9 {
-  margin-top: 3.5rem !important;
-  margin-bottom: 3.5rem !important;
-}
-
-.mxn-9 {
-  margin-right: -3.5rem !important;
-  margin-left: -3.5rem !important;
-}
-
-.mx-9-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-.m-10 {
-  margin: 4rem !important;
-}
-
-.mt-10 {
-  margin-top: 4rem !important;
-}
-
-.mr-10 {
-  margin-right: 4rem !important;
-}
-
-.mb-10 {
-  margin-bottom: 4rem !important;
-}
-
-.ml-10 {
-  margin-left: 4rem !important;
-}
-
-.mx-10 {
-  margin-right: 4rem !important;
-  margin-left: 4rem !important;
-}
-
-.my-10 {
-  margin-top: 4rem !important;
-  margin-bottom: 4rem !important;
-}
-
-.mxn-10 {
-  margin-right: -4rem !important;
-  margin-left: -4rem !important;
-}
-
-.mx-10-auto {
-  margin-right: auto !important;
-  margin-left: auto !important;
-}
-
-@media (min-width: 20rem) {
-  .m-xs-0 {
-    margin: 0 !important;
-  }
-  .mt-xs-0 {
-    margin-top: 0 !important;
-  }
-  .mr-xs-0 {
-    margin-right: 0 !important;
-  }
-  .mb-xs-0 {
-    margin-bottom: 0 !important;
-  }
-  .ml-xs-0 {
-    margin-left: 0 !important;
-  }
-  .mx-xs-0 {
-    margin-right: 0 !important;
-    margin-left: 0 !important;
-  }
-  .my-xs-0 {
-    margin-top: 0 !important;
-    margin-bottom: 0 !important;
-  }
-  .mxn-xs-0 {
-    margin-right: -0 !important;
-    margin-left: -0 !important;
-  }
-}
-@media (min-width: 20rem) {
-  .m-xs-1 {
-    margin: 0.25rem !important;
-  }
-  .mt-xs-1 {
-    margin-top: 0.25rem !important;
-  }
-  .mr-xs-1 {
-    margin-right: 0.25rem !important;
-  }
-  .mb-xs-1 {
-    margin-bottom: 0.25rem !important;
-  }
-  .ml-xs-1 {
-    margin-left: 0.25rem !important;
-  }
-  .mx-xs-1 {
-    margin-right: 0.25rem !important;
-    margin-left: 0.25rem !important;
-  }
-  .my-xs-1 {
-    margin-top: 0.25rem !important;
-    margin-bottom: 0.25rem !important;
-  }
-  .mxn-xs-1 {
-    margin-right: -0.25rem !important;
-    margin-left: -0.25rem !important;
-  }
-}
-@media (min-width: 20rem) {
-  .m-xs-2 {
-    margin: 0.5rem !important;
-  }
-  .mt-xs-2 {
-    margin-top: 0.5rem !important;
-  }
-  .mr-xs-2 {
-    margin-right: 0.5rem !important;
-  }
-  .mb-xs-2 {
-    margin-bottom: 0.5rem !important;
-  }
-  .ml-xs-2 {
-    margin-left: 0.5rem !important;
-  }
-  .mx-xs-2 {
-    margin-right: 0.5rem !important;
-    margin-left: 0.5rem !important;
-  }
-  .my-xs-2 {
-    margin-top: 0.5rem !important;
-    margin-bottom: 0.5rem !important;
-  }
-  .mxn-xs-2 {
-    margin-right: -0.5rem !important;
-    margin-left: -0.5rem !important;
-  }
-}
-@media (min-width: 20rem) {
-  .m-xs-3 {
-    margin: 0.75rem !important;
-  }
-  .mt-xs-3 {
-    margin-top: 0.75rem !important;
-  }
-  .mr-xs-3 {
-    margin-right: 0.75rem !important;
-  }
-  .mb-xs-3 {
-    margin-bottom: 0.75rem !important;
-  }
-  .ml-xs-3 {
-    margin-left: 0.75rem !important;
-  }
-  .mx-xs-3 {
-    margin-right: 0.75rem !important;
-    margin-left: 0.75rem !important;
-  }
-  .my-xs-3 {
-    margin-top: 0.75rem !important;
-    margin-bottom: 0.75rem !important;
-  }
-  .mxn-xs-3 {
-    margin-right: -0.75rem !important;
-    margin-left: -0.75rem !important;
-  }
-}
-@media (min-width: 20rem) {
-  .m-xs-4 {
-    margin: 1rem !important;
-  }
-  .mt-xs-4 {
-    margin-top: 1rem !important;
-  }
-  .mr-xs-4 {
-    margin-right: 1rem !important;
-  }
-  .mb-xs-4 {
-    margin-bottom: 1rem !important;
-  }
-  .ml-xs-4 {
-    margin-left: 1rem !important;
-  }
-  .mx-xs-4 {
-    margin-right: 1rem !important;
-    margin-left: 1rem !important;
-  }
-  .my-xs-4 {
-    margin-top: 1rem !important;
-    margin-bottom: 1rem !important;
-  }
-  .mxn-xs-4 {
-    margin-right: -1rem !important;
-    margin-left: -1rem !important;
-  }
-}
-@media (min-width: 20rem) {
-  .m-xs-5 {
-    margin: 1.5rem !important;
-  }
-  .mt-xs-5 {
-    margin-top: 1.5rem !important;
-  }
-  .mr-xs-5 {
-    margin-right: 1.5rem !important;
-  }
-  .mb-xs-5 {
-    margin-bottom: 1.5rem !important;
-  }
-  .ml-xs-5 {
-    margin-left: 1.5rem !important;
-  }
-  .mx-xs-5 {
-    margin-right: 1.5rem !important;
-    margin-left: 1.5rem !important;
-  }
-  .my-xs-5 {
-    margin-top: 1.5rem !important;
-    margin-bottom: 1.5rem !important;
-  }
-  .mxn-xs-5 {
-    margin-right: -1.5rem !important;
-    margin-left: -1.5rem !important;
-  }
-}
-@media (min-width: 20rem) {
-  .m-xs-6 {
-    margin: 2rem !important;
-  }
-  .mt-xs-6 {
-    margin-top: 2rem !important;
-  }
-  .mr-xs-6 {
-    margin-right: 2rem !important;
-  }
-  .mb-xs-6 {
-    margin-bottom: 2rem !important;
-  }
-  .ml-xs-6 {
-    margin-left: 2rem !important;
-  }
-  .mx-xs-6 {
-    margin-right: 2rem !important;
-    margin-left: 2rem !important;
-  }
-  .my-xs-6 {
-    margin-top: 2rem !important;
-    margin-bottom: 2rem !important;
-  }
-  .mxn-xs-6 {
-    margin-right: -2rem !important;
-    margin-left: -2rem !important;
-  }
-}
-@media (min-width: 20rem) {
-  .m-xs-7 {
-    margin: 2.5rem !important;
-  }
-  .mt-xs-7 {
-    margin-top: 2.5rem !important;
-  }
-  .mr-xs-7 {
-    margin-right: 2.5rem !important;
-  }
-  .mb-xs-7 {
-    margin-bottom: 2.5rem !important;
-  }
-  .ml-xs-7 {
-    margin-left: 2.5rem !important;
-  }
-  .mx-xs-7 {
-    margin-right: 2.5rem !important;
-    margin-left: 2.5rem !important;
-  }
-  .my-xs-7 {
-    margin-top: 2.5rem !important;
-    margin-bottom: 2.5rem !important;
-  }
-  .mxn-xs-7 {
-    margin-right: -2.5rem !important;
-    margin-left: -2.5rem !important;
-  }
-}
-@media (min-width: 20rem) {
-  .m-xs-8 {
-    margin: 3rem !important;
-  }
-  .mt-xs-8 {
-    margin-top: 3rem !important;
-  }
-  .mr-xs-8 {
-    margin-right: 3rem !important;
-  }
-  .mb-xs-8 {
-    margin-bottom: 3rem !important;
-  }
-  .ml-xs-8 {
-    margin-left: 3rem !important;
-  }
-  .mx-xs-8 {
-    margin-right: 3rem !important;
-    margin-left: 3rem !important;
-  }
-  .my-xs-8 {
-    margin-top: 3rem !important;
-    margin-bottom: 3rem !important;
-  }
-  .mxn-xs-8 {
-    margin-right: -3rem !important;
-    margin-left: -3rem !important;
-  }
-}
-@media (min-width: 20rem) {
-  .m-xs-9 {
-    margin: 3.5rem !important;
-  }
-  .mt-xs-9 {
-    margin-top: 3.5rem !important;
-  }
-  .mr-xs-9 {
-    margin-right: 3.5rem !important;
-  }
-  .mb-xs-9 {
-    margin-bottom: 3.5rem !important;
-  }
-  .ml-xs-9 {
-    margin-left: 3.5rem !important;
-  }
-  .mx-xs-9 {
-    margin-right: 3.5rem !important;
-    margin-left: 3.5rem !important;
-  }
-  .my-xs-9 {
-    margin-top: 3.5rem !important;
-    margin-bottom: 3.5rem !important;
-  }
-  .mxn-xs-9 {
-    margin-right: -3.5rem !important;
-    margin-left: -3.5rem !important;
-  }
-}
-@media (min-width: 20rem) {
-  .m-xs-10 {
-    margin: 4rem !important;
-  }
-  .mt-xs-10 {
-    margin-top: 4rem !important;
-  }
-  .mr-xs-10 {
-    margin-right: 4rem !important;
-  }
-  .mb-xs-10 {
-    margin-bottom: 4rem !important;
-  }
-  .ml-xs-10 {
-    margin-left: 4rem !important;
-  }
-  .mx-xs-10 {
-    margin-right: 4rem !important;
-    margin-left: 4rem !important;
-  }
-  .my-xs-10 {
-    margin-top: 4rem !important;
-    margin-bottom: 4rem !important;
-  }
-  .mxn-xs-10 {
-    margin-right: -4rem !important;
-    margin-left: -4rem !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-0 {
-    margin: 0 !important;
-  }
-  .mt-sm-0 {
-    margin-top: 0 !important;
-  }
-  .mr-sm-0 {
-    margin-right: 0 !important;
-  }
-  .mb-sm-0 {
-    margin-bottom: 0 !important;
-  }
-  .ml-sm-0 {
-    margin-left: 0 !important;
-  }
-  .mx-sm-0 {
-    margin-right: 0 !important;
-    margin-left: 0 !important;
-  }
-  .my-sm-0 {
-    margin-top: 0 !important;
-    margin-bottom: 0 !important;
-  }
-  .mxn-sm-0 {
-    margin-right: -0 !important;
-    margin-left: -0 !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-1 {
-    margin: 0.25rem !important;
-  }
-  .mt-sm-1 {
-    margin-top: 0.25rem !important;
-  }
-  .mr-sm-1 {
-    margin-right: 0.25rem !important;
-  }
-  .mb-sm-1 {
-    margin-bottom: 0.25rem !important;
-  }
-  .ml-sm-1 {
-    margin-left: 0.25rem !important;
-  }
-  .mx-sm-1 {
-    margin-right: 0.25rem !important;
-    margin-left: 0.25rem !important;
-  }
-  .my-sm-1 {
-    margin-top: 0.25rem !important;
-    margin-bottom: 0.25rem !important;
-  }
-  .mxn-sm-1 {
-    margin-right: -0.25rem !important;
-    margin-left: -0.25rem !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-2 {
-    margin: 0.5rem !important;
-  }
-  .mt-sm-2 {
-    margin-top: 0.5rem !important;
-  }
-  .mr-sm-2 {
-    margin-right: 0.5rem !important;
-  }
-  .mb-sm-2 {
-    margin-bottom: 0.5rem !important;
-  }
-  .ml-sm-2 {
-    margin-left: 0.5rem !important;
-  }
-  .mx-sm-2 {
-    margin-right: 0.5rem !important;
-    margin-left: 0.5rem !important;
-  }
-  .my-sm-2 {
-    margin-top: 0.5rem !important;
-    margin-bottom: 0.5rem !important;
-  }
-  .mxn-sm-2 {
-    margin-right: -0.5rem !important;
-    margin-left: -0.5rem !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-3 {
-    margin: 0.75rem !important;
-  }
-  .mt-sm-3 {
-    margin-top: 0.75rem !important;
-  }
-  .mr-sm-3 {
-    margin-right: 0.75rem !important;
-  }
-  .mb-sm-3 {
-    margin-bottom: 0.75rem !important;
-  }
-  .ml-sm-3 {
-    margin-left: 0.75rem !important;
-  }
-  .mx-sm-3 {
-    margin-right: 0.75rem !important;
-    margin-left: 0.75rem !important;
-  }
-  .my-sm-3 {
-    margin-top: 0.75rem !important;
-    margin-bottom: 0.75rem !important;
-  }
-  .mxn-sm-3 {
-    margin-right: -0.75rem !important;
-    margin-left: -0.75rem !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-4 {
-    margin: 1rem !important;
-  }
-  .mt-sm-4 {
-    margin-top: 1rem !important;
-  }
-  .mr-sm-4 {
-    margin-right: 1rem !important;
-  }
-  .mb-sm-4 {
-    margin-bottom: 1rem !important;
-  }
-  .ml-sm-4 {
-    margin-left: 1rem !important;
-  }
-  .mx-sm-4 {
-    margin-right: 1rem !important;
-    margin-left: 1rem !important;
-  }
-  .my-sm-4 {
-    margin-top: 1rem !important;
-    margin-bottom: 1rem !important;
-  }
-  .mxn-sm-4 {
-    margin-right: -1rem !important;
-    margin-left: -1rem !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-5 {
-    margin: 1.5rem !important;
-  }
-  .mt-sm-5 {
-    margin-top: 1.5rem !important;
-  }
-  .mr-sm-5 {
-    margin-right: 1.5rem !important;
-  }
-  .mb-sm-5 {
-    margin-bottom: 1.5rem !important;
-  }
-  .ml-sm-5 {
-    margin-left: 1.5rem !important;
-  }
-  .mx-sm-5 {
-    margin-right: 1.5rem !important;
-    margin-left: 1.5rem !important;
-  }
-  .my-sm-5 {
-    margin-top: 1.5rem !important;
-    margin-bottom: 1.5rem !important;
-  }
-  .mxn-sm-5 {
-    margin-right: -1.5rem !important;
-    margin-left: -1.5rem !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-6 {
-    margin: 2rem !important;
-  }
-  .mt-sm-6 {
-    margin-top: 2rem !important;
-  }
-  .mr-sm-6 {
-    margin-right: 2rem !important;
-  }
-  .mb-sm-6 {
-    margin-bottom: 2rem !important;
-  }
-  .ml-sm-6 {
-    margin-left: 2rem !important;
-  }
-  .mx-sm-6 {
-    margin-right: 2rem !important;
-    margin-left: 2rem !important;
-  }
-  .my-sm-6 {
-    margin-top: 2rem !important;
-    margin-bottom: 2rem !important;
-  }
-  .mxn-sm-6 {
-    margin-right: -2rem !important;
-    margin-left: -2rem !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-7 {
-    margin: 2.5rem !important;
-  }
-  .mt-sm-7 {
-    margin-top: 2.5rem !important;
-  }
-  .mr-sm-7 {
-    margin-right: 2.5rem !important;
-  }
-  .mb-sm-7 {
-    margin-bottom: 2.5rem !important;
-  }
-  .ml-sm-7 {
-    margin-left: 2.5rem !important;
-  }
-  .mx-sm-7 {
-    margin-right: 2.5rem !important;
-    margin-left: 2.5rem !important;
-  }
-  .my-sm-7 {
-    margin-top: 2.5rem !important;
-    margin-bottom: 2.5rem !important;
-  }
-  .mxn-sm-7 {
-    margin-right: -2.5rem !important;
-    margin-left: -2.5rem !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-8 {
-    margin: 3rem !important;
-  }
-  .mt-sm-8 {
-    margin-top: 3rem !important;
-  }
-  .mr-sm-8 {
-    margin-right: 3rem !important;
-  }
-  .mb-sm-8 {
-    margin-bottom: 3rem !important;
-  }
-  .ml-sm-8 {
-    margin-left: 3rem !important;
-  }
-  .mx-sm-8 {
-    margin-right: 3rem !important;
-    margin-left: 3rem !important;
-  }
-  .my-sm-8 {
-    margin-top: 3rem !important;
-    margin-bottom: 3rem !important;
-  }
-  .mxn-sm-8 {
-    margin-right: -3rem !important;
-    margin-left: -3rem !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-9 {
-    margin: 3.5rem !important;
-  }
-  .mt-sm-9 {
-    margin-top: 3.5rem !important;
-  }
-  .mr-sm-9 {
-    margin-right: 3.5rem !important;
-  }
-  .mb-sm-9 {
-    margin-bottom: 3.5rem !important;
-  }
-  .ml-sm-9 {
-    margin-left: 3.5rem !important;
-  }
-  .mx-sm-9 {
-    margin-right: 3.5rem !important;
-    margin-left: 3.5rem !important;
-  }
-  .my-sm-9 {
-    margin-top: 3.5rem !important;
-    margin-bottom: 3.5rem !important;
-  }
-  .mxn-sm-9 {
-    margin-right: -3.5rem !important;
-    margin-left: -3.5rem !important;
-  }
-}
-@media (min-width: 31.25rem) {
-  .m-sm-10 {
-    margin: 4rem !important;
-  }
-  .mt-sm-10 {
-    margin-top: 4rem !important;
-  }
-  .mr-sm-10 {
-    margin-right: 4rem !important;
-  }
-  .mb-sm-10 {
-    margin-bottom: 4rem !important;
-  }
-  .ml-sm-10 {
-    margin-left: 4rem !important;
-  }
-  .mx-sm-10 {
-    margin-right: 4rem !important;
-    margin-left: 4rem !important;
-  }
-  .my-sm-10 {
-    margin-top: 4rem !important;
-    margin-bottom: 4rem !important;
-  }
-  .mxn-sm-10 {
-    margin-right: -4rem !important;
-    margin-left: -4rem !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-0 {
-    margin: 0 !important;
-  }
-  .mt-md-0 {
-    margin-top: 0 !important;
-  }
-  .mr-md-0 {
-    margin-right: 0 !important;
-  }
-  .mb-md-0 {
-    margin-bottom: 0 !important;
-  }
-  .ml-md-0 {
-    margin-left: 0 !important;
-  }
-  .mx-md-0 {
-    margin-right: 0 !important;
-    margin-left: 0 !important;
-  }
-  .my-md-0 {
-    margin-top: 0 !important;
-    margin-bottom: 0 !important;
-  }
-  .mxn-md-0 {
-    margin-right: -0 !important;
-    margin-left: -0 !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-1 {
-    margin: 0.25rem !important;
-  }
-  .mt-md-1 {
-    margin-top: 0.25rem !important;
-  }
-  .mr-md-1 {
-    margin-right: 0.25rem !important;
-  }
-  .mb-md-1 {
-    margin-bottom: 0.25rem !important;
-  }
-  .ml-md-1 {
-    margin-left: 0.25rem !important;
-  }
-  .mx-md-1 {
-    margin-right: 0.25rem !important;
-    margin-left: 0.25rem !important;
-  }
-  .my-md-1 {
-    margin-top: 0.25rem !important;
-    margin-bottom: 0.25rem !important;
-  }
-  .mxn-md-1 {
-    margin-right: -0.25rem !important;
-    margin-left: -0.25rem !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-2 {
-    margin: 0.5rem !important;
-  }
-  .mt-md-2 {
-    margin-top: 0.5rem !important;
-  }
-  .mr-md-2 {
-    margin-right: 0.5rem !important;
-  }
-  .mb-md-2 {
-    margin-bottom: 0.5rem !important;
-  }
-  .ml-md-2 {
-    margin-left: 0.5rem !important;
-  }
-  .mx-md-2 {
-    margin-right: 0.5rem !important;
-    margin-left: 0.5rem !important;
-  }
-  .my-md-2 {
-    margin-top: 0.5rem !important;
-    margin-bottom: 0.5rem !important;
-  }
-  .mxn-md-2 {
-    margin-right: -0.5rem !important;
-    margin-left: -0.5rem !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-3 {
-    margin: 0.75rem !important;
-  }
-  .mt-md-3 {
-    margin-top: 0.75rem !important;
-  }
-  .mr-md-3 {
-    margin-right: 0.75rem !important;
-  }
-  .mb-md-3 {
-    margin-bottom: 0.75rem !important;
-  }
-  .ml-md-3 {
-    margin-left: 0.75rem !important;
-  }
-  .mx-md-3 {
-    margin-right: 0.75rem !important;
-    margin-left: 0.75rem !important;
-  }
-  .my-md-3 {
-    margin-top: 0.75rem !important;
-    margin-bottom: 0.75rem !important;
-  }
-  .mxn-md-3 {
-    margin-right: -0.75rem !important;
-    margin-left: -0.75rem !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-4 {
-    margin: 1rem !important;
-  }
-  .mt-md-4 {
-    margin-top: 1rem !important;
-  }
-  .mr-md-4 {
-    margin-right: 1rem !important;
-  }
-  .mb-md-4 {
-    margin-bottom: 1rem !important;
-  }
-  .ml-md-4 {
-    margin-left: 1rem !important;
-  }
-  .mx-md-4 {
-    margin-right: 1rem !important;
-    margin-left: 1rem !important;
-  }
-  .my-md-4 {
-    margin-top: 1rem !important;
-    margin-bottom: 1rem !important;
-  }
-  .mxn-md-4 {
-    margin-right: -1rem !important;
-    margin-left: -1rem !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-5 {
-    margin: 1.5rem !important;
-  }
-  .mt-md-5 {
-    margin-top: 1.5rem !important;
-  }
-  .mr-md-5 {
-    margin-right: 1.5rem !important;
-  }
-  .mb-md-5 {
-    margin-bottom: 1.5rem !important;
-  }
-  .ml-md-5 {
-    margin-left: 1.5rem !important;
-  }
-  .mx-md-5 {
-    margin-right: 1.5rem !important;
-    margin-left: 1.5rem !important;
-  }
-  .my-md-5 {
-    margin-top: 1.5rem !important;
-    margin-bottom: 1.5rem !important;
-  }
-  .mxn-md-5 {
-    margin-right: -1.5rem !important;
-    margin-left: -1.5rem !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-6 {
-    margin: 2rem !important;
-  }
-  .mt-md-6 {
-    margin-top: 2rem !important;
-  }
-  .mr-md-6 {
-    margin-right: 2rem !important;
-  }
-  .mb-md-6 {
-    margin-bottom: 2rem !important;
-  }
-  .ml-md-6 {
-    margin-left: 2rem !important;
-  }
-  .mx-md-6 {
-    margin-right: 2rem !important;
-    margin-left: 2rem !important;
-  }
-  .my-md-6 {
-    margin-top: 2rem !important;
-    margin-bottom: 2rem !important;
-  }
-  .mxn-md-6 {
-    margin-right: -2rem !important;
-    margin-left: -2rem !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-7 {
-    margin: 2.5rem !important;
-  }
-  .mt-md-7 {
-    margin-top: 2.5rem !important;
-  }
-  .mr-md-7 {
-    margin-right: 2.5rem !important;
-  }
-  .mb-md-7 {
-    margin-bottom: 2.5rem !important;
-  }
-  .ml-md-7 {
-    margin-left: 2.5rem !important;
-  }
-  .mx-md-7 {
-    margin-right: 2.5rem !important;
-    margin-left: 2.5rem !important;
-  }
-  .my-md-7 {
-    margin-top: 2.5rem !important;
-    margin-bottom: 2.5rem !important;
-  }
-  .mxn-md-7 {
-    margin-right: -2.5rem !important;
-    margin-left: -2.5rem !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-8 {
-    margin: 3rem !important;
-  }
-  .mt-md-8 {
-    margin-top: 3rem !important;
-  }
-  .mr-md-8 {
-    margin-right: 3rem !important;
-  }
-  .mb-md-8 {
-    margin-bottom: 3rem !important;
-  }
-  .ml-md-8 {
-    margin-left: 3rem !important;
-  }
-  .mx-md-8 {
-    margin-right: 3rem !important;
-    margin-left: 3rem !important;
-  }
-  .my-md-8 {
-    margin-top: 3rem !important;
-    margin-bottom: 3rem !important;
-  }
-  .mxn-md-8 {
-    margin-right: -3rem !important;
-    margin-left: -3rem !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-9 {
-    margin: 3.5rem !important;
-  }
-  .mt-md-9 {
-    margin-top: 3.5rem !important;
-  }
-  .mr-md-9 {
-    margin-right: 3.5rem !important;
-  }
-  .mb-md-9 {
-    margin-bottom: 3.5rem !important;
-  }
-  .ml-md-9 {
-    margin-left: 3.5rem !important;
-  }
-  .mx-md-9 {
-    margin-right: 3.5rem !important;
-    margin-left: 3.5rem !important;
-  }
-  .my-md-9 {
-    margin-top: 3.5rem !important;
-    margin-bottom: 3.5rem !important;
-  }
-  .mxn-md-9 {
-    margin-right: -3.5rem !important;
-    margin-left: -3.5rem !important;
-  }
-}
-@media (min-width: 50rem) {
-  .m-md-10 {
-    margin: 4rem !important;
-  }
-  .mt-md-10 {
-    margin-top: 4rem !important;
-  }
-  .mr-md-10 {
-    margin-right: 4rem !important;
-  }
-  .mb-md-10 {
-    margin-bottom: 4rem !important;
-  }
-  .ml-md-10 {
-    margin-left: 4rem !important;
-  }
-  .mx-md-10 {
-    margin-right: 4rem !important;
-    margin-left: 4rem !important;
-  }
-  .my-md-10 {
-    margin-top: 4rem !important;
-    margin-bottom: 4rem !important;
-  }
-  .mxn-md-10 {
-    margin-right: -4rem !important;
-    margin-left: -4rem !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-0 {
-    margin: 0 !important;
-  }
-  .mt-lg-0 {
-    margin-top: 0 !important;
-  }
-  .mr-lg-0 {
-    margin-right: 0 !important;
-  }
-  .mb-lg-0 {
-    margin-bottom: 0 !important;
-  }
-  .ml-lg-0 {
-    margin-left: 0 !important;
-  }
-  .mx-lg-0 {
-    margin-right: 0 !important;
-    margin-left: 0 !important;
-  }
-  .my-lg-0 {
-    margin-top: 0 !important;
-    margin-bottom: 0 !important;
-  }
-  .mxn-lg-0 {
-    margin-right: -0 !important;
-    margin-left: -0 !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-1 {
-    margin: 0.25rem !important;
-  }
-  .mt-lg-1 {
-    margin-top: 0.25rem !important;
-  }
-  .mr-lg-1 {
-    margin-right: 0.25rem !important;
-  }
-  .mb-lg-1 {
-    margin-bottom: 0.25rem !important;
-  }
-  .ml-lg-1 {
-    margin-left: 0.25rem !important;
-  }
-  .mx-lg-1 {
-    margin-right: 0.25rem !important;
-    margin-left: 0.25rem !important;
-  }
-  .my-lg-1 {
-    margin-top: 0.25rem !important;
-    margin-bottom: 0.25rem !important;
-  }
-  .mxn-lg-1 {
-    margin-right: -0.25rem !important;
-    margin-left: -0.25rem !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-2 {
-    margin: 0.5rem !important;
-  }
-  .mt-lg-2 {
-    margin-top: 0.5rem !important;
-  }
-  .mr-lg-2 {
-    margin-right: 0.5rem !important;
-  }
-  .mb-lg-2 {
-    margin-bottom: 0.5rem !important;
-  }
-  .ml-lg-2 {
-    margin-left: 0.5rem !important;
-  }
-  .mx-lg-2 {
-    margin-right: 0.5rem !important;
-    margin-left: 0.5rem !important;
-  }
-  .my-lg-2 {
-    margin-top: 0.5rem !important;
-    margin-bottom: 0.5rem !important;
-  }
-  .mxn-lg-2 {
-    margin-right: -0.5rem !important;
-    margin-left: -0.5rem !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-3 {
-    margin: 0.75rem !important;
-  }
-  .mt-lg-3 {
-    margin-top: 0.75rem !important;
-  }
-  .mr-lg-3 {
-    margin-right: 0.75rem !important;
-  }
-  .mb-lg-3 {
-    margin-bottom: 0.75rem !important;
-  }
-  .ml-lg-3 {
-    margin-left: 0.75rem !important;
-  }
-  .mx-lg-3 {
-    margin-right: 0.75rem !important;
-    margin-left: 0.75rem !important;
-  }
-  .my-lg-3 {
-    margin-top: 0.75rem !important;
-    margin-bottom: 0.75rem !important;
-  }
-  .mxn-lg-3 {
-    margin-right: -0.75rem !important;
-    margin-left: -0.75rem !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-4 {
-    margin: 1rem !important;
-  }
-  .mt-lg-4 {
-    margin-top: 1rem !important;
-  }
-  .mr-lg-4 {
-    margin-right: 1rem !important;
-  }
-  .mb-lg-4 {
-    margin-bottom: 1rem !important;
-  }
-  .ml-lg-4 {
-    margin-left: 1rem !important;
-  }
-  .mx-lg-4 {
-    margin-right: 1rem !important;
-    margin-left: 1rem !important;
-  }
-  .my-lg-4 {
-    margin-top: 1rem !important;
-    margin-bottom: 1rem !important;
-  }
-  .mxn-lg-4 {
-    margin-right: -1rem !important;
-    margin-left: -1rem !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-5 {
-    margin: 1.5rem !important;
-  }
-  .mt-lg-5 {
-    margin-top: 1.5rem !important;
-  }
-  .mr-lg-5 {
-    margin-right: 1.5rem !important;
-  }
-  .mb-lg-5 {
-    margin-bottom: 1.5rem !important;
-  }
-  .ml-lg-5 {
-    margin-left: 1.5rem !important;
-  }
-  .mx-lg-5 {
-    margin-right: 1.5rem !important;
-    margin-left: 1.5rem !important;
-  }
-  .my-lg-5 {
-    margin-top: 1.5rem !important;
-    margin-bottom: 1.5rem !important;
-  }
-  .mxn-lg-5 {
-    margin-right: -1.5rem !important;
-    margin-left: -1.5rem !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-6 {
-    margin: 2rem !important;
-  }
-  .mt-lg-6 {
-    margin-top: 2rem !important;
-  }
-  .mr-lg-6 {
-    margin-right: 2rem !important;
-  }
-  .mb-lg-6 {
-    margin-bottom: 2rem !important;
-  }
-  .ml-lg-6 {
-    margin-left: 2rem !important;
-  }
-  .mx-lg-6 {
-    margin-right: 2rem !important;
-    margin-left: 2rem !important;
-  }
-  .my-lg-6 {
-    margin-top: 2rem !important;
-    margin-bottom: 2rem !important;
-  }
-  .mxn-lg-6 {
-    margin-right: -2rem !important;
-    margin-left: -2rem !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-7 {
-    margin: 2.5rem !important;
-  }
-  .mt-lg-7 {
-    margin-top: 2.5rem !important;
-  }
-  .mr-lg-7 {
-    margin-right: 2.5rem !important;
-  }
-  .mb-lg-7 {
-    margin-bottom: 2.5rem !important;
-  }
-  .ml-lg-7 {
-    margin-left: 2.5rem !important;
-  }
-  .mx-lg-7 {
-    margin-right: 2.5rem !important;
-    margin-left: 2.5rem !important;
-  }
-  .my-lg-7 {
-    margin-top: 2.5rem !important;
-    margin-bottom: 2.5rem !important;
-  }
-  .mxn-lg-7 {
-    margin-right: -2.5rem !important;
-    margin-left: -2.5rem !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-8 {
-    margin: 3rem !important;
-  }
-  .mt-lg-8 {
-    margin-top: 3rem !important;
-  }
-  .mr-lg-8 {
-    margin-right: 3rem !important;
-  }
-  .mb-lg-8 {
-    margin-bottom: 3rem !important;
-  }
-  .ml-lg-8 {
-    margin-left: 3rem !important;
-  }
-  .mx-lg-8 {
-    margin-right: 3rem !important;
-    margin-left: 3rem !important;
-  }
-  .my-lg-8 {
-    margin-top: 3rem !important;
-    margin-bottom: 3rem !important;
-  }
-  .mxn-lg-8 {
-    margin-right: -3rem !important;
-    margin-left: -3rem !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-9 {
-    margin: 3.5rem !important;
-  }
-  .mt-lg-9 {
-    margin-top: 3.5rem !important;
-  }
-  .mr-lg-9 {
-    margin-right: 3.5rem !important;
-  }
-  .mb-lg-9 {
-    margin-bottom: 3.5rem !important;
-  }
-  .ml-lg-9 {
-    margin-left: 3.5rem !important;
-  }
-  .mx-lg-9 {
-    margin-right: 3.5rem !important;
-    margin-left: 3.5rem !important;
-  }
-  .my-lg-9 {
-    margin-top: 3.5rem !important;
-    margin-bottom: 3.5rem !important;
-  }
-  .mxn-lg-9 {
-    margin-right: -3.5rem !important;
-    margin-left: -3.5rem !important;
-  }
-}
-@media (min-width: 66.5rem) {
-  .m-lg-10 {
-    margin: 4rem !important;
-  }
-  .mt-lg-10 {
-    margin-top: 4rem !important;
-  }
-  .mr-lg-10 {
-    margin-right: 4rem !important;
-  }
-  .mb-lg-10 {
-    margin-bottom: 4rem !important;
-  }
-  .ml-lg-10 {
-    margin-left: 4rem !important;
-  }
-  .mx-lg-10 {
-    margin-right: 4rem !important;
-    margin-left: 4rem !important;
-  }
-  .my-lg-10 {
-    margin-top: 4rem !important;
-    margin-bottom: 4rem !important;
-  }
-  .mxn-lg-10 {
-    margin-right: -4rem !important;
-    margin-left: -4rem !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .m-xl-0 {
-    margin: 0 !important;
-  }
-  .mt-xl-0 {
-    margin-top: 0 !important;
-  }
-  .mr-xl-0 {
-    margin-right: 0 !important;
-  }
-  .mb-xl-0 {
-    margin-bottom: 0 !important;
-  }
-  .ml-xl-0 {
-    margin-left: 0 !important;
-  }
-  .mx-xl-0 {
-    margin-right: 0 !important;
-    margin-left: 0 !important;
-  }
-  .my-xl-0 {
-    margin-top: 0 !important;
-    margin-bottom: 0 !important;
-  }
-  .mxn-xl-0 {
-    margin-right: -0 !important;
-    margin-left: -0 !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .m-xl-1 {
-    margin: 0.25rem !important;
-  }
-  .mt-xl-1 {
-    margin-top: 0.25rem !important;
-  }
-  .mr-xl-1 {
-    margin-right: 0.25rem !important;
-  }
-  .mb-xl-1 {
-    margin-bottom: 0.25rem !important;
-  }
-  .ml-xl-1 {
-    margin-left: 0.25rem !important;
-  }
-  .mx-xl-1 {
-    margin-right: 0.25rem !important;
-    margin-left: 0.25rem !important;
-  }
-  .my-xl-1 {
-    margin-top: 0.25rem !important;
-    margin-bottom: 0.25rem !important;
-  }
-  .mxn-xl-1 {
-    margin-right: -0.25rem !important;
-    margin-left: -0.25rem !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .m-xl-2 {
-    margin: 0.5rem !important;
-  }
-  .mt-xl-2 {
-    margin-top: 0.5rem !important;
-  }
-  .mr-xl-2 {
-    margin-right: 0.5rem !important;
-  }
-  .mb-xl-2 {
-    margin-bottom: 0.5rem !important;
-  }
-  .ml-xl-2 {
-    margin-left: 0.5rem !important;
-  }
-  .mx-xl-2 {
-    margin-right: 0.5rem !important;
-    margin-left: 0.5rem !important;
-  }
-  .my-xl-2 {
-    margin-top: 0.5rem !important;
-    margin-bottom: 0.5rem !important;
-  }
-  .mxn-xl-2 {
-    margin-right: -0.5rem !important;
-    margin-left: -0.5rem !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .m-xl-3 {
-    margin: 0.75rem !important;
-  }
-  .mt-xl-3 {
-    margin-top: 0.75rem !important;
-  }
-  .mr-xl-3 {
-    margin-right: 0.75rem !important;
-  }
-  .mb-xl-3 {
-    margin-bottom: 0.75rem !important;
-  }
-  .ml-xl-3 {
-    margin-left: 0.75rem !important;
-  }
-  .mx-xl-3 {
-    margin-right: 0.75rem !important;
-    margin-left: 0.75rem !important;
-  }
-  .my-xl-3 {
-    margin-top: 0.75rem !important;
-    margin-bottom: 0.75rem !important;
-  }
-  .mxn-xl-3 {
-    margin-right: -0.75rem !important;
-    margin-left: -0.75rem !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .m-xl-4 {
-    margin: 1rem !important;
-  }
-  .mt-xl-4 {
-    margin-top: 1rem !important;
-  }
-  .mr-xl-4 {
-    margin-right: 1rem !important;
-  }
-  .mb-xl-4 {
-    margin-bottom: 1rem !important;
-  }
-  .ml-xl-4 {
-    margin-left: 1rem !important;
-  }
-  .mx-xl-4 {
-    margin-right: 1rem !important;
-    margin-left: 1rem !important;
-  }
-  .my-xl-4 {
-    margin-top: 1rem !important;
-    margin-bottom: 1rem !important;
-  }
-  .mxn-xl-4 {
-    margin-right: -1rem !important;
-    margin-left: -1rem !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .m-xl-5 {
-    margin: 1.5rem !important;
-  }
-  .mt-xl-5 {
-    margin-top: 1.5rem !important;
-  }
-  .mr-xl-5 {
-    margin-right: 1.5rem !important;
-  }
-  .mb-xl-5 {
-    margin-bottom: 1.5rem !important;
-  }
-  .ml-xl-5 {
-    margin-left: 1.5rem !important;
-  }
-  .mx-xl-5 {
-    margin-right: 1.5rem !important;
-    margin-left: 1.5rem !important;
-  }
-  .my-xl-5 {
-    margin-top: 1.5rem !important;
-    margin-bottom: 1.5rem !important;
-  }
-  .mxn-xl-5 {
-    margin-right: -1.5rem !important;
-    margin-left: -1.5rem !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .m-xl-6 {
-    margin: 2rem !important;
-  }
-  .mt-xl-6 {
-    margin-top: 2rem !important;
-  }
-  .mr-xl-6 {
-    margin-right: 2rem !important;
-  }
-  .mb-xl-6 {
-    margin-bottom: 2rem !important;
-  }
-  .ml-xl-6 {
-    margin-left: 2rem !important;
-  }
-  .mx-xl-6 {
-    margin-right: 2rem !important;
-    margin-left: 2rem !important;
-  }
-  .my-xl-6 {
-    margin-top: 2rem !important;
-    margin-bottom: 2rem !important;
-  }
-  .mxn-xl-6 {
-    margin-right: -2rem !important;
-    margin-left: -2rem !important;
-  }
-}
-@media (min-width: 87.5rem) {
-  .m-xl-7 {
-    margin: 2.5rem !important;
-  }
-  .mt-xl-7 {
-    margin-top: 2.5rem !important;
-  }
-  .mr-xl-7 {
-    margin-right: 2.5rem !important;
-  }
-  .mb-xl-7 {
-    margin-bottom: 2.5rem !important;
-  }
-  .ml-xl-7 {
-    margin-left: 2.5rem !important;
-  }
-  .mx-xl-7 {
-    margin-right: 2.5rem !important;
-    margin-left: 2.5rem !important;
-  }
-  .my-xl-7 {
-    margin-top: 2.5rem !important;
-    margin-bottom: 2.5rem !important;
-  }
-  .mxn-xl-7 {
-    margin-right: -2.5rem !important;
-    margin-left: -2.5rem !important;
+  .label,
+  .label-blue {
+    font-size: 12px !important;
   }
 }
-@media (min-width: 87.5rem) {
-  .m-xl-8 {
-    margin: 3rem !important;
-  }
-  .mt-xl-8 {
-    margin-top: 3rem !important;
-  }
-  .mr-xl-8 {
-    margin-right: 3rem !important;
-  }
-  .mb-xl-8 {
-    margin-bottom: 3rem !important;
-  }
-  .ml-xl-8 {
-    margin-left: 3rem !important;
-  }
-  .mx-xl-8 {
-    margin-right: 3rem !important;
-    margin-left: 3rem !important;
-  }
-  .my-xl-8 {
-    margin-top: 3rem !important;
-    margin-bottom: 3rem !important;
-  }
-  .mxn-xl-8 {
-    margin-right: -3rem !important;
-    margin-left: -3rem !important;
-  }
+
+.label-green {
+  background-color: #009c7b;
 }
-@media (min-width: 87.5rem) {
-  .m-xl-9 {
-    margin: 3.5rem !important;
-  }
-  .mt-xl-9 {
-    margin-top: 3.5rem !important;
-  }
-  .mr-xl-9 {
-    margin-right: 3.5rem !important;
-  }
-  .mb-xl-9 {
-    margin-bottom: 3.5rem !important;
-  }
-  .ml-xl-9 {
-    margin-left: 3.5rem !important;
-  }
-  .mx-xl-9 {
-    margin-right: 3.5rem !important;
-    margin-left: 3.5rem !important;
-  }
-  .my-xl-9 {
-    margin-top: 3.5rem !important;
-    margin-bottom: 3.5rem !important;
-  }
-  .mxn-xl-9 {
-    margin-right: -3.5rem !important;
-    margin-left: -3.5rem !important;
-  }
+
+.label-purple {
+  background-color: #5e41d0;
 }
-@media (min-width: 87.5rem) {
-  .m-xl-10 {
-    margin: 4rem !important;
-  }
-  .mt-xl-10 {
-    margin-top: 4rem !important;
-  }
-  .mr-xl-10 {
-    margin-right: 4rem !important;
-  }
-  .mb-xl-10 {
-    margin-bottom: 4rem !important;
-  }
-  .ml-xl-10 {
-    margin-left: 4rem !important;
-  }
-  .mx-xl-10 {
-    margin-right: 4rem !important;
-    margin-left: 4rem !important;
-  }
-  .my-xl-10 {
-    margin-top: 4rem !important;
-    margin-bottom: 4rem !important;
-  }
-  .mxn-xl-10 {
-    margin-right: -4rem !important;
-    margin-left: -4rem !important;
-  }
+
+.label-red {
+  background-color: #e94c4c;
 }
-.p-0 {
-  padding: 0 !important;
+
+.label-yellow {
+  color: #44434d;
+  background-color: #f7d12e;
 }
 
-.pt-0 {
-  padding-top: 0 !important;
+.table-wrapper {
+  display: block;
+  width: 100%;
+  max-width: 100%;
+  margin-bottom: 1.5rem;
+  overflow-x: auto;
+  border-radius: 4px;
+  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12), 0 3px 10px rgba(0, 0, 0, 0.08);
 }
 
-.pr-0 {
-  padding-right: 0 !important;
+table {
+  display: table;
+  min-width: 100%;
+  border-collapse: separate;
 }
 
-.pb-0 {
-  padding-bottom: 0 !important;
+th,
+td {
+  font-size: 12px !important;
+  min-width: 120px;
+  padding: 0.5rem 0.75rem;
+  background-color: #fff;
+  border-bottom: 1px solid rgba(238, 235, 238, 0.5);
+  border-left: 1px solid #eeebee;
+}
+@media (min-width: 31.25rem) {
+  th,
+  td {
+    font-size: 14px !important;
+  }
+}
+th:first-of-type,
+td:first-of-type {
+  border-left: 0;
 }
 
-.pl-0 {
-  padding-left: 0 !important;
+tbody tr:last-of-type th,
+tbody tr:last-of-type td {
+  border-bottom: 0;
+}
+tbody tr:last-of-type td {
+  padding-bottom: 0.75rem;
 }
 
-.px-0 {
-  padding-right: 0 !important;
-  padding-left: 0 !important;
+thead th {
+  border-bottom: 1px solid #eeebee;
 }
 
-.py-0 {
-  padding-top: 0 !important;
-  padding-bottom: 0 !important;
+:not(pre, figure) > code {
+  padding: 0.2em 0.15em;
+  font-weight: 400;
+  background-color: #f5f6fa;
+  border: 1px solid #eeebee;
+  border-radius: 4px;
 }
 
-.p-1 {
-  padding: 0.25rem !important;
+a:visited code {
+  border-color: #eeebee;
 }
 
-.pt-1 {
-  padding-top: 0.25rem !important;
+div.highlight,
+div.listingblock > div.content,
+figure.highlight {
+  margin-top: 0;
+  margin-bottom: 0.75rem;
+  background-color: #f5f6fa;
+  border-radius: 4px;
+  box-shadow: none;
+  -webkit-overflow-scrolling: touch;
+  position: relative;
+  padding: 0;
+}
+div.highlight > button,
+div.listingblock > div.content > button,
+figure.highlight > button {
+  width: 0.75rem;
+  opacity: 0;
+  position: absolute;
+  top: 0;
+  right: 0;
+  border: 0.75rem solid #f5f6fa;
+  background-color: #f5f6fa;
+  color: #5c5962;
+  box-sizing: content-box;
+}
+div.highlight > button svg,
+div.listingblock > div.content > button svg,
+figure.highlight > button svg {
+  fill: #5c5962;
+}
+div.highlight > button:active,
+div.listingblock > div.content > button:active,
+figure.highlight > button:active {
+  text-decoration: none;
+  outline: none;
+  opacity: 1;
+}
+div.highlight > button:focus,
+div.listingblock > div.content > button:focus,
+figure.highlight > button:focus {
+  opacity: 1;
+}
+div.highlight:hover > button,
+div.listingblock > div.content:hover > button,
+figure.highlight:hover > button {
+  cursor: copy;
+  opacity: 1;
 }
 
-.pr-1 {
-  padding-right: 0.25rem !important;
+div.highlight div.highlight,
+div.listingblock div.highlight {
+  overflow-x: auto;
+  padding: 0.75rem;
+  margin: 0;
+  border: 0;
+}
+div.highlight pre.highlight,
+div.highlight code,
+div.listingblock pre.highlight,
+div.listingblock code {
+  padding: 0;
+  margin: 0;
+  border: 0;
 }
 
-.pb-1 {
-  padding-bottom: 0.25rem !important;
+figure.highlight pre,
+figure.highlight :not(pre) > code {
+  overflow-x: auto;
+  padding: 0.75rem;
+  margin: 0;
+  border: 0;
 }
 
-.pl-1 {
-  padding-left: 0.25rem !important;
+.highlight .table-wrapper {
+  padding: 0.75rem 0;
+  margin: 0;
+  border: 0;
+  box-shadow: none;
+}
+.highlight .table-wrapper td,
+.highlight .table-wrapper pre {
+  font-size: 11px !important;
+  min-width: 0;
+  padding: 0;
+  background-color: #f5f6fa;
+  border: 0;
+}
+@media (min-width: 31.25rem) {
+  .highlight .table-wrapper td,
+  .highlight .table-wrapper pre {
+    font-size: 12px !important;
+  }
+}
+.highlight .table-wrapper td.gl {
+  width: 1em;
+  padding-right: 0.75rem;
+  padding-left: 0.75rem;
+}
+.highlight .table-wrapper pre {
+  margin: 0;
+  line-height: 2;
 }
 
-.px-1 {
-  padding-right: 0.25rem !important;
-  padding-left: 0.25rem !important;
+.code-example,
+.listingblock > .title {
+  padding: 0.75rem;
+  margin-bottom: 0.75rem;
+  overflow: auto;
+  border: 1px solid #eeebee;
+  border-radius: 4px;
+}
+.code-example + .highlighter-rouge,
+.code-example + .sectionbody .listingblock,
+.code-example + .content,
+.code-example + figure.highlight,
+.listingblock > .title + .highlighter-rouge,
+.listingblock > .title + .sectionbody .listingblock,
+.listingblock > .title + .content,
+.listingblock > .title + figure.highlight {
+  position: relative;
+  margin-top: -1rem;
+  border-right: 1px solid #eeebee;
+  border-bottom: 1px solid #eeebee;
+  border-left: 1px solid #eeebee;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
 }
 
-.py-1 {
-  padding-top: 0.25rem !important;
-  padding-bottom: 0.25rem !important;
+code.language-mermaid {
+  padding: 0;
+  background-color: inherit;
+  border: 0;
 }
 
-.p-2 {
-  padding: 0.5rem !important;
+.text-grey-dk-000 {
+  color: #959396 !important;
 }
 
-.pt-2 {
-  padding-top: 0.5rem !important;
+.text-grey-dk-100 {
+  color: #5c5962 !important;
 }
 
-.pr-2 {
-  padding-right: 0.5rem !important;
+.text-grey-dk-200 {
+  color: #44434d !important;
 }
 
-.pb-2 {
-  padding-bottom: 0.5rem !important;
+.text-grey-dk-250 {
+  color: #302d36 !important;
 }
 
-.pl-2 {
-  padding-left: 0.5rem !important;
+.text-grey-dk-300 {
+  color: #27262b !important;
 }
 
-.px-2 {
-  padding-right: 0.5rem !important;
-  padding-left: 0.5rem !important;
+.text-grey-lt-000 {
+  color: #f5f6fa !important;
 }
 
-.py-2 {
-  padding-top: 0.5rem !important;
-  padding-bottom: 0.5rem !important;
+.text-grey-lt-100 {
+  color: #eeebee !important;
 }
 
-.p-3 {
-  padding: 0.75rem !important;
+.text-grey-lt-200 {
+  color: #ecebed !important;
 }
 
-.pt-3 {
-  padding-top: 0.75rem !important;
+.text-grey-lt-300 {
+  color: #e6e1e8 !important;
 }
 
-.pr-3 {
-  padding-right: 0.75rem !important;
+.text-blue-000 {
+  color: #2c84fa !important;
 }
 
-.pb-3 {
-  padding-bottom: 0.75rem !important;
+.text-blue-100 {
+  color: #2869e6 !important;
 }
 
-.pl-3 {
-  padding-left: 0.75rem !important;
+.text-blue-200 {
+  color: #264caf !important;
 }
 
-.px-3 {
-  padding-right: 0.75rem !important;
-  padding-left: 0.75rem !important;
+.text-blue-300 {
+  color: #183385 !important;
 }
 
-.py-3 {
-  padding-top: 0.75rem !important;
-  padding-bottom: 0.75rem !important;
+.text-green-000 {
+  color: #41d693 !important;
 }
 
-.p-4 {
-  padding: 1rem !important;
+.text-green-100 {
+  color: #11b584 !important;
 }
 
-.pt-4 {
-  padding-top: 1rem !important;
+.text-green-200 {
+  color: #009c7b !important;
 }
 
-.pr-4 {
-  padding-right: 1rem !important;
+.text-green-300 {
+  color: #026e57 !important;
 }
 
-.pb-4 {
-  padding-bottom: 1rem !important;
+.text-purple-000 {
+  color: #7253ed !important;
 }
 
-.pl-4 {
-  padding-left: 1rem !important;
+.text-purple-100 {
+  color: #5e41d0 !important;
 }
 
-.px-4 {
-  padding-right: 1rem !important;
-  padding-left: 1rem !important;
+.text-purple-200 {
+  color: #4e26af !important;
 }
 
-.py-4 {
-  padding-top: 1rem !important;
-  padding-bottom: 1rem !important;
+.text-purple-300 {
+  color: #381885 !important;
 }
 
-.p-5 {
-  padding: 1.5rem !important;
+.text-yellow-000 {
+  color: #ffeb82 !important;
 }
 
-.pt-5 {
-  padding-top: 1.5rem !important;
+.text-yellow-100 {
+  color: #fadf50 !important;
 }
 
-.pr-5 {
-  padding-right: 1.5rem !important;
+.text-yellow-200 {
+  color: #f7d12e !important;
 }
 
-.pb-5 {
-  padding-bottom: 1.5rem !important;
+.text-yellow-300 {
+  color: #e7af06 !important;
 }
 
-.pl-5 {
-  padding-left: 1.5rem !important;
+.text-red-000 {
+  color: #f77e7e !important;
 }
 
-.px-5 {
-  padding-right: 1.5rem !important;
-  padding-left: 1.5rem !important;
+.text-red-100 {
+  color: #f96e65 !important;
 }
 
-.py-5 {
-  padding-top: 1.5rem !important;
-  padding-bottom: 1.5rem !important;
+.text-red-200 {
+  color: #e94c4c !important;
 }
 
-.p-6 {
-  padding: 2rem !important;
+.text-red-300 {
+  color: #dd2e2e !important;
 }
 
-.pt-6 {
-  padding-top: 2rem !important;
+.bg-grey-dk-000 {
+  background-color: #959396 !important;
 }
 
-.pr-6 {
-  padding-right: 2rem !important;
+.bg-grey-dk-100 {
+  background-color: #5c5962 !important;
 }
 
-.pb-6 {
-  padding-bottom: 2rem !important;
+.bg-grey-dk-200 {
+  background-color: #44434d !important;
 }
 
-.pl-6 {
-  padding-left: 2rem !important;
+.bg-grey-dk-250 {
+  background-color: #302d36 !important;
 }
 
-.px-6 {
-  padding-right: 2rem !important;
-  padding-left: 2rem !important;
+.bg-grey-dk-300 {
+  background-color: #27262b !important;
 }
 
-.py-6 {
-  padding-top: 2rem !important;
-  padding-bottom: 2rem !important;
+.bg-grey-lt-000 {
+  background-color: #f5f6fa !important;
 }
 
-.p-7 {
-  padding: 2.5rem !important;
+.bg-grey-lt-100 {
+  background-color: #eeebee !important;
 }
 
-.pt-7 {
-  padding-top: 2.5rem !important;
+.bg-grey-lt-200 {
+  background-color: #ecebed !important;
 }
 
-.pr-7 {
-  padding-right: 2.5rem !important;
+.bg-grey-lt-300 {
+  background-color: #e6e1e8 !important;
 }
 
-.pb-7 {
-  padding-bottom: 2.5rem !important;
+.bg-blue-000 {
+  background-color: #2c84fa !important;
 }
 
-.pl-7 {
-  padding-left: 2.5rem !important;
+.bg-blue-100 {
+  background-color: #2869e6 !important;
 }
 
-.px-7 {
-  padding-right: 2.5rem !important;
-  padding-left: 2.5rem !important;
+.bg-blue-200 {
+  background-color: #264caf !important;
 }
 
-.py-7 {
-  padding-top: 2.5rem !important;
-  padding-bottom: 2.5rem !important;
+.bg-blue-300 {
+  background-color: #183385 !important;
 }
 
-.p-8 {
-  padding: 3rem !important;
+.bg-green-000 {
+  background-color: #41d693 !important;
 }
 
-.pt-8 {
-  padding-top: 3rem !important;
+.bg-green-100 {
+  background-color: #11b584 !important;
 }
 
-.pr-8 {
-  padding-right: 3rem !important;
+.bg-green-200 {
+  background-color: #009c7b !important;
 }
 
-.pb-8 {
-  padding-bottom: 3rem !important;
+.bg-green-300 {
+  background-color: #026e57 !important;
 }
 
-.pl-8 {
-  padding-left: 3rem !important;
+.bg-purple-000 {
+  background-color: #7253ed !important;
 }
 
-.px-8 {
-  padding-right: 3rem !important;
-  padding-left: 3rem !important;
+.bg-purple-100 {
+  background-color: #5e41d0 !important;
 }
 
-.py-8 {
-  padding-top: 3rem !important;
-  padding-bottom: 3rem !important;
+.bg-purple-200 {
+  background-color: #4e26af !important;
 }
 
-.p-9 {
-  padding: 3.5rem !important;
+.bg-purple-300 {
+  background-color: #381885 !important;
 }
 
-.pt-9 {
-  padding-top: 3.5rem !important;
+.bg-yellow-000 {
+  background-color: #ffeb82 !important;
 }
 
-.pr-9 {
-  padding-right: 3.5rem !important;
+.bg-yellow-100 {
+  background-color: #fadf50 !important;
 }
 
-.pb-9 {
-  padding-bottom: 3.5rem !important;
+.bg-yellow-200 {
+  background-color: #f7d12e !important;
 }
 
-.pl-9 {
-  padding-left: 3.5rem !important;
+.bg-yellow-300 {
+  background-color: #e7af06 !important;
 }
 
-.px-9 {
-  padding-right: 3.5rem !important;
-  padding-left: 3.5rem !important;
+.bg-red-000 {
+  background-color: #f77e7e !important;
 }
 
-.py-9 {
-  padding-top: 3.5rem !important;
-  padding-bottom: 3.5rem !important;
+.bg-red-100 {
+  background-color: #f96e65 !important;
 }
 
-.p-10 {
-  padding: 4rem !important;
+.bg-red-200 {
+  background-color: #e94c4c !important;
 }
 
-.pt-10 {
-  padding-top: 4rem !important;
+.bg-red-300 {
+  background-color: #dd2e2e !important;
 }
 
-.pr-10 {
-  padding-right: 4rem !important;
+.d-block {
+  display: block !important;
 }
 
-.pb-10 {
-  padding-bottom: 4rem !important;
+.d-flex {
+  display: flex !important;
 }
 
-.pl-10 {
-  padding-left: 4rem !important;
+.d-inline {
+  display: inline !important;
 }
 
-.px-10 {
-  padding-right: 4rem !important;
-  padding-left: 4rem !important;
+.d-inline-block {
+  display: inline-block !important;
 }
 
-.py-10 {
-  padding-top: 4rem !important;
-  padding-bottom: 4rem !important;
+.d-none {
+  display: none !important;
 }
 
 @media (min-width: 20rem) {
-  .p-xs-0 {
-    padding: 0 !important;
-  }
-  .pt-xs-0 {
-    padding-top: 0 !important;
-  }
-  .pr-xs-0 {
-    padding-right: 0 !important;
-  }
-  .pb-xs-0 {
-    padding-bottom: 0 !important;
-  }
-  .pl-xs-0 {
-    padding-left: 0 !important;
-  }
-  .px-xs-0 {
-    padding-right: 0 !important;
-    padding-left: 0 !important;
-  }
-  .py-xs-0 {
-    padding-top: 0 !important;
-    padding-bottom: 0 !important;
-  }
-  .p-xs-1 {
-    padding: 0.25rem !important;
-  }
-  .pt-xs-1 {
-    padding-top: 0.25rem !important;
-  }
-  .pr-xs-1 {
-    padding-right: 0.25rem !important;
-  }
-  .pb-xs-1 {
-    padding-bottom: 0.25rem !important;
-  }
-  .pl-xs-1 {
-    padding-left: 0.25rem !important;
-  }
-  .px-xs-1 {
-    padding-right: 0.25rem !important;
-    padding-left: 0.25rem !important;
-  }
-  .py-xs-1 {
-    padding-top: 0.25rem !important;
-    padding-bottom: 0.25rem !important;
-  }
-  .p-xs-2 {
-    padding: 0.5rem !important;
-  }
-  .pt-xs-2 {
-    padding-top: 0.5rem !important;
-  }
-  .pr-xs-2 {
-    padding-right: 0.5rem !important;
-  }
-  .pb-xs-2 {
-    padding-bottom: 0.5rem !important;
-  }
-  .pl-xs-2 {
-    padding-left: 0.5rem !important;
-  }
-  .px-xs-2 {
-    padding-right: 0.5rem !important;
-    padding-left: 0.5rem !important;
-  }
-  .py-xs-2 {
-    padding-top: 0.5rem !important;
-    padding-bottom: 0.5rem !important;
-  }
-  .p-xs-3 {
-    padding: 0.75rem !important;
-  }
-  .pt-xs-3 {
-    padding-top: 0.75rem !important;
-  }
-  .pr-xs-3 {
-    padding-right: 0.75rem !important;
-  }
-  .pb-xs-3 {
-    padding-bottom: 0.75rem !important;
-  }
-  .pl-xs-3 {
-    padding-left: 0.75rem !important;
-  }
-  .px-xs-3 {
-    padding-right: 0.75rem !important;
-    padding-left: 0.75rem !important;
-  }
-  .py-xs-3 {
-    padding-top: 0.75rem !important;
-    padding-bottom: 0.75rem !important;
-  }
-  .p-xs-4 {
-    padding: 1rem !important;
-  }
-  .pt-xs-4 {
-    padding-top: 1rem !important;
-  }
-  .pr-xs-4 {
-    padding-right: 1rem !important;
-  }
-  .pb-xs-4 {
-    padding-bottom: 1rem !important;
-  }
-  .pl-xs-4 {
-    padding-left: 1rem !important;
-  }
-  .px-xs-4 {
-    padding-right: 1rem !important;
-    padding-left: 1rem !important;
-  }
-  .py-xs-4 {
-    padding-top: 1rem !important;
-    padding-bottom: 1rem !important;
-  }
-  .p-xs-5 {
-    padding: 1.5rem !important;
-  }
-  .pt-xs-5 {
-    padding-top: 1.5rem !important;
-  }
-  .pr-xs-5 {
-    padding-right: 1.5rem !important;
-  }
-  .pb-xs-5 {
-    padding-bottom: 1.5rem !important;
-  }
-  .pl-xs-5 {
-    padding-left: 1.5rem !important;
-  }
-  .px-xs-5 {
-    padding-right: 1.5rem !important;
-    padding-left: 1.5rem !important;
-  }
-  .py-xs-5 {
-    padding-top: 1.5rem !important;
-    padding-bottom: 1.5rem !important;
-  }
-  .p-xs-6 {
-    padding: 2rem !important;
-  }
-  .pt-xs-6 {
-    padding-top: 2rem !important;
-  }
-  .pr-xs-6 {
-    padding-right: 2rem !important;
-  }
-  .pb-xs-6 {
-    padding-bottom: 2rem !important;
-  }
-  .pl-xs-6 {
-    padding-left: 2rem !important;
-  }
-  .px-xs-6 {
-    padding-right: 2rem !important;
-    padding-left: 2rem !important;
-  }
-  .py-xs-6 {
-    padding-top: 2rem !important;
-    padding-bottom: 2rem !important;
-  }
-  .p-xs-7 {
-    padding: 2.5rem !important;
-  }
-  .pt-xs-7 {
-    padding-top: 2.5rem !important;
-  }
-  .pr-xs-7 {
-    padding-right: 2.5rem !important;
-  }
-  .pb-xs-7 {
-    padding-bottom: 2.5rem !important;
-  }
-  .pl-xs-7 {
-    padding-left: 2.5rem !important;
-  }
-  .px-xs-7 {
-    padding-right: 2.5rem !important;
-    padding-left: 2.5rem !important;
-  }
-  .py-xs-7 {
-    padding-top: 2.5rem !important;
-    padding-bottom: 2.5rem !important;
-  }
-  .p-xs-8 {
-    padding: 3rem !important;
-  }
-  .pt-xs-8 {
-    padding-top: 3rem !important;
-  }
-  .pr-xs-8 {
-    padding-right: 3rem !important;
-  }
-  .pb-xs-8 {
-    padding-bottom: 3rem !important;
-  }
-  .pl-xs-8 {
-    padding-left: 3rem !important;
-  }
-  .px-xs-8 {
-    padding-right: 3rem !important;
-    padding-left: 3rem !important;
-  }
-  .py-xs-8 {
-    padding-top: 3rem !important;
-    padding-bottom: 3rem !important;
-  }
-  .p-xs-9 {
-    padding: 3.5rem !important;
-  }
-  .pt-xs-9 {
-    padding-top: 3.5rem !important;
-  }
-  .pr-xs-9 {
-    padding-right: 3.5rem !important;
-  }
-  .pb-xs-9 {
-    padding-bottom: 3.5rem !important;
-  }
-  .pl-xs-9 {
-    padding-left: 3.5rem !important;
-  }
-  .px-xs-9 {
-    padding-right: 3.5rem !important;
-    padding-left: 3.5rem !important;
-  }
-  .py-xs-9 {
-    padding-top: 3.5rem !important;
-    padding-bottom: 3.5rem !important;
-  }
-  .p-xs-10 {
-    padding: 4rem !important;
-  }
-  .pt-xs-10 {
-    padding-top: 4rem !important;
-  }
-  .pr-xs-10 {
-    padding-right: 4rem !important;
+  .d-xs-block {
+    display: block !important;
   }
-  .pb-xs-10 {
-    padding-bottom: 4rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .pl-xs-10 {
-    padding-left: 4rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .px-xs-10 {
-    padding-right: 4rem !important;
-    padding-left: 4rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .py-xs-10 {
-    padding-top: 4rem !important;
-    padding-bottom: 4rem !important;
+  .d-xs-none {
+    display: none !important;
   }
 }
-@media (min-width: 31.25rem) {
-  .p-sm-0 {
-    padding: 0 !important;
-  }
-  .pt-sm-0 {
-    padding-top: 0 !important;
-  }
-  .pr-sm-0 {
-    padding-right: 0 !important;
-  }
-  .pb-sm-0 {
-    padding-bottom: 0 !important;
-  }
-  .pl-sm-0 {
-    padding-left: 0 !important;
-  }
-  .px-sm-0 {
-    padding-right: 0 !important;
-    padding-left: 0 !important;
-  }
-  .py-sm-0 {
-    padding-top: 0 !important;
-    padding-bottom: 0 !important;
-  }
-  .p-sm-1 {
-    padding: 0.25rem !important;
-  }
-  .pt-sm-1 {
-    padding-top: 0.25rem !important;
-  }
-  .pr-sm-1 {
-    padding-right: 0.25rem !important;
-  }
-  .pb-sm-1 {
-    padding-bottom: 0.25rem !important;
-  }
-  .pl-sm-1 {
-    padding-left: 0.25rem !important;
-  }
-  .px-sm-1 {
-    padding-right: 0.25rem !important;
-    padding-left: 0.25rem !important;
-  }
-  .py-sm-1 {
-    padding-top: 0.25rem !important;
-    padding-bottom: 0.25rem !important;
-  }
-  .p-sm-2 {
-    padding: 0.5rem !important;
-  }
-  .pt-sm-2 {
-    padding-top: 0.5rem !important;
-  }
-  .pr-sm-2 {
-    padding-right: 0.5rem !important;
-  }
-  .pb-sm-2 {
-    padding-bottom: 0.5rem !important;
-  }
-  .pl-sm-2 {
-    padding-left: 0.5rem !important;
-  }
-  .px-sm-2 {
-    padding-right: 0.5rem !important;
-    padding-left: 0.5rem !important;
-  }
-  .py-sm-2 {
-    padding-top: 0.5rem !important;
-    padding-bottom: 0.5rem !important;
-  }
-  .p-sm-3 {
-    padding: 0.75rem !important;
-  }
-  .pt-sm-3 {
-    padding-top: 0.75rem !important;
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
   }
-  .pr-sm-3 {
-    padding-right: 0.75rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .pb-sm-3 {
-    padding-bottom: 0.75rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .pl-sm-3 {
-    padding-left: 0.75rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .px-sm-3 {
-    padding-right: 0.75rem !important;
-    padding-left: 0.75rem !important;
+  .d-xs-none {
+    display: none !important;
   }
-  .py-sm-3 {
-    padding-top: 0.75rem !important;
-    padding-bottom: 0.75rem !important;
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
   }
-  .p-sm-4 {
-    padding: 1rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .pt-sm-4 {
-    padding-top: 1rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .pr-sm-4 {
-    padding-right: 1rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .pb-sm-4 {
-    padding-bottom: 1rem !important;
+  .d-xs-none {
+    display: none !important;
   }
-  .pl-sm-4 {
-    padding-left: 1rem !important;
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
   }
-  .px-sm-4 {
-    padding-right: 1rem !important;
-    padding-left: 1rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .py-sm-4 {
-    padding-top: 1rem !important;
-    padding-bottom: 1rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .p-sm-5 {
-    padding: 1.5rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .pt-sm-5 {
-    padding-top: 1.5rem !important;
+  .d-xs-none {
+    display: none !important;
   }
-  .pr-sm-5 {
-    padding-right: 1.5rem !important;
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
   }
-  .pb-sm-5 {
-    padding-bottom: 1.5rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .pl-sm-5 {
-    padding-left: 1.5rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .px-sm-5 {
-    padding-right: 1.5rem !important;
-    padding-left: 1.5rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .py-sm-5 {
-    padding-top: 1.5rem !important;
-    padding-bottom: 1.5rem !important;
+  .d-xs-none {
+    display: none !important;
   }
-  .p-sm-6 {
-    padding: 2rem !important;
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
   }
-  .pt-sm-6 {
-    padding-top: 2rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .pr-sm-6 {
-    padding-right: 2rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .pb-sm-6 {
-    padding-bottom: 2rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .pl-sm-6 {
-    padding-left: 2rem !important;
+  .d-xs-none {
+    display: none !important;
   }
-  .px-sm-6 {
-    padding-right: 2rem !important;
-    padding-left: 2rem !important;
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
   }
-  .py-sm-6 {
-    padding-top: 2rem !important;
-    padding-bottom: 2rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .p-sm-7 {
-    padding: 2.5rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .pt-sm-7 {
-    padding-top: 2.5rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .pr-sm-7 {
-    padding-right: 2.5rem !important;
+  .d-xs-none {
+    display: none !important;
   }
-  .pb-sm-7 {
-    padding-bottom: 2.5rem !important;
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
   }
-  .pl-sm-7 {
-    padding-left: 2.5rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .px-sm-7 {
-    padding-right: 2.5rem !important;
-    padding-left: 2.5rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .py-sm-7 {
-    padding-top: 2.5rem !important;
-    padding-bottom: 2.5rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .p-sm-8 {
-    padding: 3rem !important;
+  .d-xs-none {
+    display: none !important;
   }
-  .pt-sm-8 {
-    padding-top: 3rem !important;
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
   }
-  .pr-sm-8 {
-    padding-right: 3rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .pb-sm-8 {
-    padding-bottom: 3rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .pl-sm-8 {
-    padding-left: 3rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .px-sm-8 {
-    padding-right: 3rem !important;
-    padding-left: 3rem !important;
+  .d-xs-none {
+    display: none !important;
   }
-  .py-sm-8 {
-    padding-top: 3rem !important;
-    padding-bottom: 3rem !important;
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
   }
-  .p-sm-9 {
-    padding: 3.5rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .pt-sm-9 {
-    padding-top: 3.5rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .pr-sm-9 {
-    padding-right: 3.5rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .pb-sm-9 {
-    padding-bottom: 3.5rem !important;
+  .d-xs-none {
+    display: none !important;
   }
-  .pl-sm-9 {
-    padding-left: 3.5rem !important;
+}
+@media (min-width: 20rem) {
+  .d-xs-block {
+    display: block !important;
   }
-  .px-sm-9 {
-    padding-right: 3.5rem !important;
-    padding-left: 3.5rem !important;
+  .d-xs-flex {
+    display: flex !important;
   }
-  .py-sm-9 {
-    padding-top: 3.5rem !important;
-    padding-bottom: 3.5rem !important;
+  .d-xs-inline {
+    display: inline !important;
   }
-  .p-sm-10 {
-    padding: 4rem !important;
+  .d-xs-inline-block {
+    display: inline-block !important;
   }
-  .pt-sm-10 {
-    padding-top: 4rem !important;
+  .d-xs-none {
+    display: none !important;
   }
-  .pr-sm-10 {
-    padding-right: 4rem !important;
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .pb-sm-10 {
-    padding-bottom: 4rem !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .pl-sm-10 {
-    padding-left: 4rem !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .px-sm-10 {
-    padding-right: 4rem !important;
-    padding-left: 4rem !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .py-sm-10 {
-    padding-top: 4rem !important;
-    padding-bottom: 4rem !important;
+  .d-sm-none {
+    display: none !important;
   }
 }
-@media (min-width: 50rem) {
-  .p-md-0 {
-    padding: 0 !important;
-  }
-  .pt-md-0 {
-    padding-top: 0 !important;
-  }
-  .pr-md-0 {
-    padding-right: 0 !important;
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .pb-md-0 {
-    padding-bottom: 0 !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .pl-md-0 {
-    padding-left: 0 !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .px-md-0 {
-    padding-right: 0 !important;
-    padding-left: 0 !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .py-md-0 {
-    padding-top: 0 !important;
-    padding-bottom: 0 !important;
+  .d-sm-none {
+    display: none !important;
   }
-  .p-md-1 {
-    padding: 0.25rem !important;
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .pt-md-1 {
-    padding-top: 0.25rem !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .pr-md-1 {
-    padding-right: 0.25rem !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .pb-md-1 {
-    padding-bottom: 0.25rem !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .pl-md-1 {
-    padding-left: 0.25rem !important;
+  .d-sm-none {
+    display: none !important;
   }
-  .px-md-1 {
-    padding-right: 0.25rem !important;
-    padding-left: 0.25rem !important;
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .py-md-1 {
-    padding-top: 0.25rem !important;
-    padding-bottom: 0.25rem !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .p-md-2 {
-    padding: 0.5rem !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .pt-md-2 {
-    padding-top: 0.5rem !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .pr-md-2 {
-    padding-right: 0.5rem !important;
+  .d-sm-none {
+    display: none !important;
   }
-  .pb-md-2 {
-    padding-bottom: 0.5rem !important;
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .pl-md-2 {
-    padding-left: 0.5rem !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .px-md-2 {
-    padding-right: 0.5rem !important;
-    padding-left: 0.5rem !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .py-md-2 {
-    padding-top: 0.5rem !important;
-    padding-bottom: 0.5rem !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .p-md-3 {
-    padding: 0.75rem !important;
+  .d-sm-none {
+    display: none !important;
   }
-  .pt-md-3 {
-    padding-top: 0.75rem !important;
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .pr-md-3 {
-    padding-right: 0.75rem !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .pb-md-3 {
-    padding-bottom: 0.75rem !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .pl-md-3 {
-    padding-left: 0.75rem !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .px-md-3 {
-    padding-right: 0.75rem !important;
-    padding-left: 0.75rem !important;
+  .d-sm-none {
+    display: none !important;
   }
-  .py-md-3 {
-    padding-top: 0.75rem !important;
-    padding-bottom: 0.75rem !important;
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .p-md-4 {
-    padding: 1rem !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .pt-md-4 {
-    padding-top: 1rem !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .pr-md-4 {
-    padding-right: 1rem !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .pb-md-4 {
-    padding-bottom: 1rem !important;
+  .d-sm-none {
+    display: none !important;
   }
-  .pl-md-4 {
-    padding-left: 1rem !important;
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .px-md-4 {
-    padding-right: 1rem !important;
-    padding-left: 1rem !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .py-md-4 {
-    padding-top: 1rem !important;
-    padding-bottom: 1rem !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .p-md-5 {
-    padding: 1.5rem !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .pt-md-5 {
-    padding-top: 1.5rem !important;
+  .d-sm-none {
+    display: none !important;
   }
-  .pr-md-5 {
-    padding-right: 1.5rem !important;
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .pb-md-5 {
-    padding-bottom: 1.5rem !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .pl-md-5 {
-    padding-left: 1.5rem !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .px-md-5 {
-    padding-right: 1.5rem !important;
-    padding-left: 1.5rem !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .py-md-5 {
-    padding-top: 1.5rem !important;
-    padding-bottom: 1.5rem !important;
+  .d-sm-none {
+    display: none !important;
   }
-  .p-md-6 {
-    padding: 2rem !important;
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .pt-md-6 {
-    padding-top: 2rem !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .pr-md-6 {
-    padding-right: 2rem !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .pb-md-6 {
-    padding-bottom: 2rem !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .pl-md-6 {
-    padding-left: 2rem !important;
+  .d-sm-none {
+    display: none !important;
   }
-  .px-md-6 {
-    padding-right: 2rem !important;
-    padding-left: 2rem !important;
+}
+@media (min-width: 31.25rem) {
+  .d-sm-block {
+    display: block !important;
   }
-  .py-md-6 {
-    padding-top: 2rem !important;
-    padding-bottom: 2rem !important;
+  .d-sm-flex {
+    display: flex !important;
   }
-  .p-md-7 {
-    padding: 2.5rem !important;
+  .d-sm-inline {
+    display: inline !important;
   }
-  .pt-md-7 {
-    padding-top: 2.5rem !important;
+  .d-sm-inline-block {
+    display: inline-block !important;
   }
-  .pr-md-7 {
-    padding-right: 2.5rem !important;
+  .d-sm-none {
+    display: none !important;
   }
-  .pb-md-7 {
-    padding-bottom: 2.5rem !important;
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .pl-md-7 {
-    padding-left: 2.5rem !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .px-md-7 {
-    padding-right: 2.5rem !important;
-    padding-left: 2.5rem !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .py-md-7 {
-    padding-top: 2.5rem !important;
-    padding-bottom: 2.5rem !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .p-md-8 {
-    padding: 3rem !important;
+  .d-md-none {
+    display: none !important;
   }
-  .pt-md-8 {
-    padding-top: 3rem !important;
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .pr-md-8 {
-    padding-right: 3rem !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .pb-md-8 {
-    padding-bottom: 3rem !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .pl-md-8 {
-    padding-left: 3rem !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .px-md-8 {
-    padding-right: 3rem !important;
-    padding-left: 3rem !important;
+  .d-md-none {
+    display: none !important;
   }
-  .py-md-8 {
-    padding-top: 3rem !important;
-    padding-bottom: 3rem !important;
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .p-md-9 {
-    padding: 3.5rem !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .pt-md-9 {
-    padding-top: 3.5rem !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .pr-md-9 {
-    padding-right: 3.5rem !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .pb-md-9 {
-    padding-bottom: 3.5rem !important;
+  .d-md-none {
+    display: none !important;
   }
-  .pl-md-9 {
-    padding-left: 3.5rem !important;
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .px-md-9 {
-    padding-right: 3.5rem !important;
-    padding-left: 3.5rem !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .py-md-9 {
-    padding-top: 3.5rem !important;
-    padding-bottom: 3.5rem !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .p-md-10 {
-    padding: 4rem !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .pt-md-10 {
-    padding-top: 4rem !important;
+  .d-md-none {
+    display: none !important;
   }
-  .pr-md-10 {
-    padding-right: 4rem !important;
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .pb-md-10 {
-    padding-bottom: 4rem !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .pl-md-10 {
-    padding-left: 4rem !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .px-md-10 {
-    padding-right: 4rem !important;
-    padding-left: 4rem !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .py-md-10 {
-    padding-top: 4rem !important;
-    padding-bottom: 4rem !important;
+  .d-md-none {
+    display: none !important;
   }
 }
-@media (min-width: 66.5rem) {
-  .p-lg-0 {
-    padding: 0 !important;
-  }
-  .pt-lg-0 {
-    padding-top: 0 !important;
-  }
-  .pr-lg-0 {
-    padding-right: 0 !important;
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .pb-lg-0 {
-    padding-bottom: 0 !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .pl-lg-0 {
-    padding-left: 0 !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .px-lg-0 {
-    padding-right: 0 !important;
-    padding-left: 0 !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .py-lg-0 {
-    padding-top: 0 !important;
-    padding-bottom: 0 !important;
+  .d-md-none {
+    display: none !important;
   }
-  .p-lg-1 {
-    padding: 0.25rem !important;
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .pt-lg-1 {
-    padding-top: 0.25rem !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .pr-lg-1 {
-    padding-right: 0.25rem !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .pb-lg-1 {
-    padding-bottom: 0.25rem !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .pl-lg-1 {
-    padding-left: 0.25rem !important;
+  .d-md-none {
+    display: none !important;
   }
-  .px-lg-1 {
-    padding-right: 0.25rem !important;
-    padding-left: 0.25rem !important;
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .py-lg-1 {
-    padding-top: 0.25rem !important;
-    padding-bottom: 0.25rem !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .p-lg-2 {
-    padding: 0.5rem !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .pt-lg-2 {
-    padding-top: 0.5rem !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .pr-lg-2 {
-    padding-right: 0.5rem !important;
+  .d-md-none {
+    display: none !important;
   }
-  .pb-lg-2 {
-    padding-bottom: 0.5rem !important;
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .pl-lg-2 {
-    padding-left: 0.5rem !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .px-lg-2 {
-    padding-right: 0.5rem !important;
-    padding-left: 0.5rem !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .py-lg-2 {
-    padding-top: 0.5rem !important;
-    padding-bottom: 0.5rem !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .p-lg-3 {
-    padding: 0.75rem !important;
+  .d-md-none {
+    display: none !important;
   }
-  .pt-lg-3 {
-    padding-top: 0.75rem !important;
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .pr-lg-3 {
-    padding-right: 0.75rem !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .pb-lg-3 {
-    padding-bottom: 0.75rem !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .pl-lg-3 {
-    padding-left: 0.75rem !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .px-lg-3 {
-    padding-right: 0.75rem !important;
-    padding-left: 0.75rem !important;
+  .d-md-none {
+    display: none !important;
   }
-  .py-lg-3 {
-    padding-top: 0.75rem !important;
-    padding-bottom: 0.75rem !important;
+}
+@media (min-width: 50rem) {
+  .d-md-block {
+    display: block !important;
   }
-  .p-lg-4 {
-    padding: 1rem !important;
+  .d-md-flex {
+    display: flex !important;
   }
-  .pt-lg-4 {
-    padding-top: 1rem !important;
+  .d-md-inline {
+    display: inline !important;
   }
-  .pr-lg-4 {
-    padding-right: 1rem !important;
+  .d-md-inline-block {
+    display: inline-block !important;
   }
-  .pb-lg-4 {
-    padding-bottom: 1rem !important;
+  .d-md-none {
+    display: none !important;
   }
-  .pl-lg-4 {
-    padding-left: 1rem !important;
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .px-lg-4 {
-    padding-right: 1rem !important;
-    padding-left: 1rem !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .py-lg-4 {
-    padding-top: 1rem !important;
-    padding-bottom: 1rem !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .p-lg-5 {
-    padding: 1.5rem !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .pt-lg-5 {
-    padding-top: 1.5rem !important;
+  .d-lg-none {
+    display: none !important;
   }
-  .pr-lg-5 {
-    padding-right: 1.5rem !important;
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .pb-lg-5 {
-    padding-bottom: 1.5rem !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .pl-lg-5 {
-    padding-left: 1.5rem !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .px-lg-5 {
-    padding-right: 1.5rem !important;
-    padding-left: 1.5rem !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .py-lg-5 {
-    padding-top: 1.5rem !important;
-    padding-bottom: 1.5rem !important;
+  .d-lg-none {
+    display: none !important;
   }
-  .p-lg-6 {
-    padding: 2rem !important;
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .pt-lg-6 {
-    padding-top: 2rem !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .pr-lg-6 {
-    padding-right: 2rem !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .pb-lg-6 {
-    padding-bottom: 2rem !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .pl-lg-6 {
-    padding-left: 2rem !important;
+  .d-lg-none {
+    display: none !important;
   }
-  .px-lg-6 {
-    padding-right: 2rem !important;
-    padding-left: 2rem !important;
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .py-lg-6 {
-    padding-top: 2rem !important;
-    padding-bottom: 2rem !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .p-lg-7 {
-    padding: 2.5rem !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .pt-lg-7 {
-    padding-top: 2.5rem !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .pr-lg-7 {
-    padding-right: 2.5rem !important;
+  .d-lg-none {
+    display: none !important;
   }
-  .pb-lg-7 {
-    padding-bottom: 2.5rem !important;
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .pl-lg-7 {
-    padding-left: 2.5rem !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .px-lg-7 {
-    padding-right: 2.5rem !important;
-    padding-left: 2.5rem !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .py-lg-7 {
-    padding-top: 2.5rem !important;
-    padding-bottom: 2.5rem !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .p-lg-8 {
-    padding: 3rem !important;
+  .d-lg-none {
+    display: none !important;
   }
-  .pt-lg-8 {
-    padding-top: 3rem !important;
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .pr-lg-8 {
-    padding-right: 3rem !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .pb-lg-8 {
-    padding-bottom: 3rem !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .pl-lg-8 {
-    padding-left: 3rem !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .px-lg-8 {
-    padding-right: 3rem !important;
-    padding-left: 3rem !important;
+  .d-lg-none {
+    display: none !important;
   }
-  .py-lg-8 {
-    padding-top: 3rem !important;
-    padding-bottom: 3rem !important;
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .p-lg-9 {
-    padding: 3.5rem !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .pt-lg-9 {
-    padding-top: 3.5rem !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .pr-lg-9 {
-    padding-right: 3.5rem !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .pb-lg-9 {
-    padding-bottom: 3.5rem !important;
+  .d-lg-none {
+    display: none !important;
   }
-  .pl-lg-9 {
-    padding-left: 3.5rem !important;
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .px-lg-9 {
-    padding-right: 3.5rem !important;
-    padding-left: 3.5rem !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .py-lg-9 {
-    padding-top: 3.5rem !important;
-    padding-bottom: 3.5rem !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .p-lg-10 {
-    padding: 4rem !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .pt-lg-10 {
-    padding-top: 4rem !important;
+  .d-lg-none {
+    display: none !important;
   }
-  .pr-lg-10 {
-    padding-right: 4rem !important;
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .pb-lg-10 {
-    padding-bottom: 4rem !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .pl-lg-10 {
-    padding-left: 4rem !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .px-lg-10 {
-    padding-right: 4rem !important;
-    padding-left: 4rem !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .py-lg-10 {
-    padding-top: 4rem !important;
-    padding-bottom: 4rem !important;
+  .d-lg-none {
+    display: none !important;
   }
 }
-@media (min-width: 87.5rem) {
-  .p-xl-0 {
-    padding: 0 !important;
-  }
-  .pt-xl-0 {
-    padding-top: 0 !important;
-  }
-  .pr-xl-0 {
-    padding-right: 0 !important;
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .pb-xl-0 {
-    padding-bottom: 0 !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .pl-xl-0 {
-    padding-left: 0 !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .px-xl-0 {
-    padding-right: 0 !important;
-    padding-left: 0 !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .py-xl-0 {
-    padding-top: 0 !important;
-    padding-bottom: 0 !important;
+  .d-lg-none {
+    display: none !important;
   }
-  .p-xl-1 {
-    padding: 0.25rem !important;
+}
+@media (min-width: 66.5rem) {
+  .d-lg-block {
+    display: block !important;
   }
-  .pt-xl-1 {
-    padding-top: 0.25rem !important;
+  .d-lg-flex {
+    display: flex !important;
   }
-  .pr-xl-1 {
-    padding-right: 0.25rem !important;
+  .d-lg-inline {
+    display: inline !important;
   }
-  .pb-xl-1 {
-    padding-bottom: 0.25rem !important;
+  .d-lg-inline-block {
+    display: inline-block !important;
   }
-  .pl-xl-1 {
-    padding-left: 0.25rem !important;
+  .d-lg-none {
+    display: none !important;
   }
-  .px-xl-1 {
-    padding-right: 0.25rem !important;
-    padding-left: 0.25rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .py-xl-1 {
-    padding-top: 0.25rem !important;
-    padding-bottom: 0.25rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .p-xl-2 {
-    padding: 0.5rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .pt-xl-2 {
-    padding-top: 0.5rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .pr-xl-2 {
-    padding-right: 0.5rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .pb-xl-2 {
-    padding-bottom: 0.5rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .pl-xl-2 {
-    padding-left: 0.5rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .px-xl-2 {
-    padding-right: 0.5rem !important;
-    padding-left: 0.5rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .py-xl-2 {
-    padding-top: 0.5rem !important;
-    padding-bottom: 0.5rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .p-xl-3 {
-    padding: 0.75rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .pt-xl-3 {
-    padding-top: 0.75rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .pr-xl-3 {
-    padding-right: 0.75rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .pb-xl-3 {
-    padding-bottom: 0.75rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .pl-xl-3 {
-    padding-left: 0.75rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .px-xl-3 {
-    padding-right: 0.75rem !important;
-    padding-left: 0.75rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .py-xl-3 {
-    padding-top: 0.75rem !important;
-    padding-bottom: 0.75rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .p-xl-4 {
-    padding: 1rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .pt-xl-4 {
-    padding-top: 1rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .pr-xl-4 {
-    padding-right: 1rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .pb-xl-4 {
-    padding-bottom: 1rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .pl-xl-4 {
-    padding-left: 1rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .px-xl-4 {
-    padding-right: 1rem !important;
-    padding-left: 1rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .py-xl-4 {
-    padding-top: 1rem !important;
-    padding-bottom: 1rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .p-xl-5 {
-    padding: 1.5rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .pt-xl-5 {
-    padding-top: 1.5rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .pr-xl-5 {
-    padding-right: 1.5rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .pb-xl-5 {
-    padding-bottom: 1.5rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .pl-xl-5 {
-    padding-left: 1.5rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .px-xl-5 {
-    padding-right: 1.5rem !important;
-    padding-left: 1.5rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .py-xl-5 {
-    padding-top: 1.5rem !important;
-    padding-bottom: 1.5rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .p-xl-6 {
-    padding: 2rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .pt-xl-6 {
-    padding-top: 2rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .pr-xl-6 {
-    padding-right: 2rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .pb-xl-6 {
-    padding-bottom: 2rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .pl-xl-6 {
-    padding-left: 2rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .px-xl-6 {
-    padding-right: 2rem !important;
-    padding-left: 2rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .py-xl-6 {
-    padding-top: 2rem !important;
-    padding-bottom: 2rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .p-xl-7 {
-    padding: 2.5rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .pt-xl-7 {
-    padding-top: 2.5rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .pr-xl-7 {
-    padding-right: 2.5rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .pb-xl-7 {
-    padding-bottom: 2.5rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .pl-xl-7 {
-    padding-left: 2.5rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .px-xl-7 {
-    padding-right: 2.5rem !important;
-    padding-left: 2.5rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .py-xl-7 {
-    padding-top: 2.5rem !important;
-    padding-bottom: 2.5rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .p-xl-8 {
-    padding: 3rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .pt-xl-8 {
-    padding-top: 3rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .pr-xl-8 {
-    padding-right: 3rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .pb-xl-8 {
-    padding-bottom: 3rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .pl-xl-8 {
-    padding-left: 3rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .px-xl-8 {
-    padding-right: 3rem !important;
-    padding-left: 3rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .py-xl-8 {
-    padding-top: 3rem !important;
-    padding-bottom: 3rem !important;
+}
+@media (min-width: 87.5rem) {
+  .d-xl-block {
+    display: block !important;
   }
-  .p-xl-9 {
-    padding: 3.5rem !important;
+  .d-xl-flex {
+    display: flex !important;
   }
-  .pt-xl-9 {
-    padding-top: 3.5rem !important;
+  .d-xl-inline {
+    display: inline !important;
   }
-  .pr-xl-9 {
-    padding-right: 3.5rem !important;
+  .d-xl-inline-block {
+    display: inline-block !important;
   }
-  .pb-xl-9 {
-    padding-bottom: 3.5rem !important;
+  .d-xl-none {
+    display: none !important;
   }
-  .pl-xl-9 {
-    padding-left: 3.5rem !important;
+}
+.float-left {
+  float: left !important;
+}
+
+.float-right {
+  float: right !important;
+}
+
+.flex-justify-start {
+  justify-content: flex-start !important;
+}
+
+.flex-justify-end {
+  justify-content: flex-end !important;
+}
+
+.flex-justify-between {
+  justify-content: space-between !important;
+}
+
+.flex-justify-around {
+  justify-content: space-around !important;
+}
+
+.v-align-baseline {
+  vertical-align: baseline !important;
+}
+
+.v-align-bottom {
+  vertical-align: bottom !important;
+}
+
+.v-align-middle {
+  vertical-align: middle !important;
+}
+
+.v-align-text-bottom {
+  vertical-align: text-bottom !important;
+}
+
+.v-align-text-top {
+  vertical-align: text-top !important;
+}
+
+.v-align-top {
+  vertical-align: top !important;
+}
+
+.fs-1 {
+  font-size: 9px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-1 {
+    font-size: 10px !important;
   }
-  .px-xl-9 {
-    padding-right: 3.5rem !important;
-    padding-left: 3.5rem !important;
+}
+
+.fs-2 {
+  font-size: 11px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-2 {
+    font-size: 12px !important;
   }
-  .py-xl-9 {
-    padding-top: 3.5rem !important;
-    padding-bottom: 3.5rem !important;
+}
+
+.fs-3 {
+  font-size: 12px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-3 {
+    font-size: 14px !important;
   }
-  .p-xl-10 {
-    padding: 4rem !important;
+}
+
+.fs-4 {
+  font-size: 14px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-4 {
+    font-size: 16px !important;
   }
-  .pt-xl-10 {
-    padding-top: 4rem !important;
+}
+
+.fs-5 {
+  font-size: 16px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-5 {
+    font-size: 18px !important;
   }
-  .pr-xl-10 {
-    padding-right: 4rem !important;
+}
+
+.fs-6 {
+  font-size: 18px !important;
+}
+@media (min-width: 31.25rem) {
+  .fs-6 {
+    font-size: 24px !important;
+    line-height: 1.25;
   }
-  .pb-xl-10 {
-    padding-bottom: 4rem !important;
+}
+
+.fs-7 {
+  font-size: 24px !important;
+  line-height: 1.25;
+}
+@media (min-width: 31.25rem) {
+  .fs-7 {
+    font-size: 32px !important;
   }
-  .pl-xl-10 {
-    padding-left: 4rem !important;
+}
+
+.fs-8 {
+  font-size: 32px !important;
+  line-height: 1.25;
+}
+@media (min-width: 31.25rem) {
+  .fs-8 {
+    font-size: 36px !important;
   }
-  .px-xl-10 {
-    padding-right: 4rem !important;
-    padding-left: 4rem !important;
+}
+
+.fs-9 {
+  font-size: 36px !important;
+  line-height: 1.25;
+}
+@media (min-width: 31.25rem) {
+  .fs-9 {
+    font-size: 42px !important;
   }
-  .py-xl-10 {
-    padding-top: 4rem !important;
-    padding-bottom: 4rem !important;
+}
+
+.fs-10 {
+  font-size: 42px !important;
+  line-height: 1.25;
+}
+@media (min-width: 31.25rem) {
+  .fs-10 {
+    font-size: 48px !important;
   }
 }
+
+.fw-300 {
+  font-weight: 300 !important;
+}
+
+.fw-400 {
+  font-weight: 400 !important;
+}
+
+.fw-500 {
+  font-weight: 500 !important;
+}
+
+.fw-700 {
+  font-weight: 700 !important;
+}
+
+.lh-0 {
+  line-height: 0 !important;
+}
+
+.lh-default {
+  line-height: 1.4;
+}
+
+.lh-tight {
+  line-height: 1.25;
+}
+
+.ls-5 {
+  letter-spacing: 0.05em !important;
+}
+
+.ls-10 {
+  letter-spacing: 0.1em !important;
+}
+
+.ls-0 {
+  letter-spacing: 0 !important;
+}
+
+.text-uppercase {
+  text-transform: uppercase !important;
+}
+
+.list-style-none {
+  padding: 0 !important;
+  margin: 0 !important;
+  list-style: none !important;
+}
+.list-style-none li::before {
+  display: none !important;
+}
+
 @media print {
   .site-footer,
   .site-button,

From a7ce4e0b4558ac94294443c9a30a96849469ff7b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 28 Jan 2023 12:41:29 -0800
Subject: [PATCH 040/393] Add page on fixing latest MySql.Data bug.

---
 .../failed-add-reference-comerr.md            | 45 +++++++++++++++++++
 .../tutorials/migrating-from-connector-net.md |  5 ++-
 2 files changed, 49 insertions(+), 1 deletion(-)
 create mode 100644 docs/content/troubleshooting/failed-add-reference-comerr.md

diff --git a/docs/content/troubleshooting/failed-add-reference-comerr.md b/docs/content/troubleshooting/failed-add-reference-comerr.md
new file mode 100644
index 000000000..c7a8c7d4f
--- /dev/null
+++ b/docs/content/troubleshooting/failed-add-reference-comerr.md
@@ -0,0 +1,45 @@
+---
+date: 2023-01-28
+title: Failed to Add comerr
+customtitle: "Fix: Failed to add reference to 'comerr64' when installing MySql.Data"
+weight: 50
+menu:
+  main:
+    parent: troubleshooting
+---
+
+# Fix: Failed to add reference to 'comerr64' when installing MySql.Data
+
+## Problem
+
+When installing MySql.Data 8.0.32, you may receive the following error:
+
+```
+Install-Package : Failed to add reference to 'comerr64'.
+  Please make sure that the file is accessible, and that it is a valid assembly or COM component.
+At line:1 char:1
++ Install-Package MySql.Data
+```
+
+Or, when running `dotnet build`, you may receive the following warnings:
+
+```
+C:\Program Files\dotnet\sdk\7.0.100\Microsoft.Common.CurrentVersion.targets(2352,5):
+warning MSB3246: Resolved file has a bad image, no metadata, or is otherwise inaccessible. System.BadImageFormatException: PE image does not have metadata. [C:\MySql.csproj]
+warning MSB3246:    at System.Reflection.Metadata.MetadataReader.GetAssemblyName(String assemblyFile) [C:\MySql.csproj]
+warning MSB3246:    at Microsoft.Build.Shared.AssemblyNameExtension.GetAssemblyNameEx(String path) [C:\MySql.csproj]
+warning MSB3246:    at Microsoft.Build.Tasks.SystemState.GetAssemblyName(String path) [C:\MySql.csproj]
+warning MSB3246:    at Microsoft.Build.Tasks.ReferenceTable.SetPrimaryAssemblyReferenceItem(ITaskItem referenceAssemblyName) [C:\MySql.csproj]
+```
+
+## Cause
+
+This is [a bug](https://bugs.mysql.com/bug.php?id=109716) in the MySql.Data NuGet package introduced in version 8.0.32.
+Workarounds include switching to an SDK-style project (`.csproj` with `<PackageReference>` instead of `packages.config`), or downgrading to an earlier version of MySql.Data.
+
+## Fix
+
+A better long-term fix is to switch to MySqlConnector, which fixes this problem and [many other bugs](https://mysqlconnector.net/tutorials/migrating-from-connector-net/#fixed-bugs) in MySql.Data.
+To do this, uninstall the MySql.Data package and run `dotnet add package MySqlConnector` to replace it with MySqlConnector.
+
+For more information, see our guide on [migrating from MySql.Data to MySqlConnector](https://mysqlconnector.net/tutorials/migrating-from-connector-net/).
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index af2d58e54..7a4562f47 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -286,6 +286,7 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * ~~[#104910](https://bugs.mysql.com/bug.php?id=104910): `MySqlConnectionStringBuilder.TryGetValue` always returns `false`~~
 * ~~[#104913](https://bugs.mysql.com/bug.php?id=104913): Cannot execute stored procedure with backtick in name~~
 * ~~[#105209](https://bugs.mysql.com/bug.php?id=105209): Timespan value of zero can't be read with prepared command~~
+* ~~[#105516](https://bugs.mysql.com/bug.php?id=105516): Connector/Net doesn't recognize utf8mb3~~
 * ~~[#105728](https://bugs.mysql.com/bug.php?id=105728): Named command parameters override query attribute values~~
 * ~~[#105730](https://bugs.mysql.com/bug.php?id=105730): `MySqlCommand.Clone` doesn't clone attributes~~
 * ~~[#105768](https://bugs.mysql.com/bug.php?id=105768): `MySqlCommandBuilder` doesn't support tables with `BIGINT UNSIGNED` primary key~~
@@ -294,12 +295,14 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * ~~[#106243](https://bugs.mysql.com/bug.php?id=106243): `CancellationToken` doesn't cancel `MySqlConnection.OpenAsync`~~
 * ~~[#106244](https://bugs.mysql.com/bug.php?id=106244): `MySqlDataReader.GetFieldValue<Stream>` throws `InvalidCastException`~~
 * [#106247](https://bugs.mysql.com/bug.php?id=106247): Can't use ~~`MySqlDbType.Enum` or~~ `MySqlDbType.Set` with prepared command
+* [#107259](https://bugs.mysql.com/bug.php?id=107259): Character Set 'utf8mb3' is not supported by .NET Framework
 * [#108756](https://bugs.mysql.com/bug.php?id=108756): Can't insert negative number using prepared statement with `MySqlDbType.Int24`
 * [#108970](https://bugs.mysql.com/bug.php?id=108970): `MySqlConnectionStringBuilder.ContainsKey` method gives wrong result
 * [#109141](https://bugs.mysql.com/bug.php?id=109141): Insert of data into a table results in `System.ArgumentException`
 * [#109331](https://bugs.mysql.com/bug.php?id=109331): MySQL Connector/NET is incompatible with MariaDB 10.10 and later
 * [#109390](https://bugs.mysql.com/bug.php?id=109390): Transaction lock held after connection timeout exception
 * [#109476](https://bugs.mysql.com/bug.php?id=109476): `TransactionScope.Dispose` throws "Connection must be valid and open to rollback"
-* [#109670](https://bugs.mysql.com/bug.php?id=109670): Many MSB3246 errors when referencing MySql.Data 8.0.32
+* [#109670](https://bugs.mysql.com/bug.php?id=109670), [#109752](https://bugs.mysql.com/bug.php?id=109752): Many MSB3246 errors when referencing MySql.Data 8.0.32
 * [#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.
 * [#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.
+* [#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'

From bc56f1dea3d7a64ab17c884e4846aa0785074268 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 28 Jan 2023 12:41:56 -0800
Subject: [PATCH 041/393] Ignore Hugo lock file.

---
 docs/.gitignore | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/.gitignore b/docs/.gitignore
index c75eeccc8..644896509 100644
--- a/docs/.gitignore
+++ b/docs/.gitignore
@@ -1 +1,2 @@
 /public
+.hugo_build.lock

From 266abf2c2597936e7745f0a88cd3b685824d7f4d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 28 Jan 2023 13:13:17 -0800
Subject: [PATCH 042/393] Set og:description for all troubleshooting pages.

---
 docs/config.toml                                            | 2 +-
 docs/content/troubleshooting/aurora-freeze.md               | 2 ++
 docs/content/troubleshooting/connection-reuse.md            | 3 ++-
 docs/content/troubleshooting/datetime-storage.md            | 2 ++
 docs/content/troubleshooting/delimiter.md                   | 2 ++
 docs/content/troubleshooting/failed-add-reference-comerr.md | 5 +++--
 docs/content/troubleshooting/load-data-local-infile.md      | 3 ++-
 docs/content/troubleshooting/parameter-types.md             | 3 ++-
 docs/content/troubleshooting/retrieval-public-key.md        | 3 ++-
 docs/content/troubleshooting/transaction-usage.md           | 3 ++-
 docs/layouts/partials/header.html                           | 5 +++--
 11 files changed, 23 insertions(+), 10 deletions(-)

diff --git a/docs/config.toml b/docs/config.toml
index 59db4a349..ad251bb8b 100644
--- a/docs/config.toml
+++ b/docs/config.toml
@@ -15,7 +15,7 @@ disablePathToLower = true
       unsafe = true
 
 [params]
-  description = "Documentation for MySqlConnector: an Async MySQL Driver for .NET"
+  description = "MySqlConnector is a high-performance, asynchronous C# ADO.NET driver for MySQL Server, MariaDB, Percona Server, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL for MySQL and more."
   author = "MySqlConnector Authors"
 
 [taxonomies]
diff --git a/docs/content/troubleshooting/aurora-freeze.md b/docs/content/troubleshooting/aurora-freeze.md
index 664f4f6e7..83cecbea5 100644
--- a/docs/content/troubleshooting/aurora-freeze.md
+++ b/docs/content/troubleshooting/aurora-freeze.md
@@ -1,6 +1,8 @@
 ---
+lastmod: 2023-01-28
 date: 2021-11-25
 title: Aurora Freeze
+description: How to work around a hang when calling MySqlConnection.Open() with Amazon Aurora by setting Pipelining=False in the connection string.
 customtitle: "MySqlConnection.Open Freezes with Amazon Aurora"
 weight: 5
 menu:
diff --git a/docs/content/troubleshooting/connection-reuse.md b/docs/content/troubleshooting/connection-reuse.md
index 91e5c2f14..352c87653 100644
--- a/docs/content/troubleshooting/connection-reuse.md
+++ b/docs/content/troubleshooting/connection-reuse.md
@@ -1,7 +1,8 @@
 ---
-lastmod: 2018-09-29
+lastmod: 2023-01-28
 date: 2018-09-29
 title: Connection Reuse
+description: Invalid usages of MySqlConnection in multithreaded scenarios and how to fix them.
 weight: 10
 menu:
   main:
diff --git a/docs/content/troubleshooting/datetime-storage.md b/docs/content/troubleshooting/datetime-storage.md
index 302599b7a..7b82d71ab 100644
--- a/docs/content/troubleshooting/datetime-storage.md
+++ b/docs/content/troubleshooting/datetime-storage.md
@@ -1,6 +1,8 @@
 ---
+lastmod: 2023-01-28
 date: 2020-02-08
 title: DateTime Storage
+description: How to store .NET DateTime and DateTimeOffset values in MySQL using DATETIME and TIME columns.
 weight: 24
 menu:
   main:
diff --git a/docs/content/troubleshooting/delimiter.md b/docs/content/troubleshooting/delimiter.md
index 348c04b10..239a2f6c4 100644
--- a/docs/content/troubleshooting/delimiter.md
+++ b/docs/content/troubleshooting/delimiter.md
@@ -1,7 +1,9 @@
 ---
+lastmod: 2023-01-28
 date: 2021-07-27
 title: Delimiter
 customtitle: "Fix: Using DELIMITER in SQL"
+description: DELIMITER $$ is unnecessary when using MySqlConnector and should be removed from SQL statements.
 weight: 28
 menu:
   main:
diff --git a/docs/content/troubleshooting/failed-add-reference-comerr.md b/docs/content/troubleshooting/failed-add-reference-comerr.md
index c7a8c7d4f..75f400720 100644
--- a/docs/content/troubleshooting/failed-add-reference-comerr.md
+++ b/docs/content/troubleshooting/failed-add-reference-comerr.md
@@ -1,6 +1,7 @@
 ---
 date: 2023-01-28
 title: Failed to Add comerr
+description: How to fix the error "Failed to add reference to 'comerr64'" when installing MySql.Data using NuGet.
 customtitle: "Fix: Failed to add reference to 'comerr64' when installing MySql.Data"
 weight: 50
 menu:
@@ -39,7 +40,7 @@ Workarounds include switching to an SDK-style project (`.csproj` with `<PackageR
 
 ## Fix
 
-A better long-term fix is to switch to MySqlConnector, which fixes this problem and [many other bugs](https://mysqlconnector.net/tutorials/migrating-from-connector-net/#fixed-bugs) in MySql.Data.
+A better long-term fix is to [switch to MySqlConnector](/overview/installing), which fixes this problem and [many other bugs](/tutorials/migrating-from-connector-net/#fixed-bugs) in MySql.Data.
 To do this, uninstall the MySql.Data package and run `dotnet add package MySqlConnector` to replace it with MySqlConnector.
 
-For more information, see our guide on [migrating from MySql.Data to MySqlConnector](https://mysqlconnector.net/tutorials/migrating-from-connector-net/).
+For more information, see our guide on [migrating from MySql.Data to MySqlConnector](/tutorials/migrating-from-connector-net/).
diff --git a/docs/content/troubleshooting/load-data-local-infile.md b/docs/content/troubleshooting/load-data-local-infile.md
index 8d3b45d9a..d12e2d33e 100644
--- a/docs/content/troubleshooting/load-data-local-infile.md
+++ b/docs/content/troubleshooting/load-data-local-infile.md
@@ -1,8 +1,9 @@
 ---
-lastmod: 2019-08-21
+lastmod: 2023-01-28
 date: 2019-05-23
 title: Load Data Local Infile
 customtitle: "Fix: Using Load Data Local Infile"
+description: Add AllowLoadLocalInfile=true to the connection string to allow LOAD DATA LOCAL INFILE to succeed.
 weight: 30
 menu:
   main:
diff --git a/docs/content/troubleshooting/parameter-types.md b/docs/content/troubleshooting/parameter-types.md
index 628496c83..8085b2d9c 100644
--- a/docs/content/troubleshooting/parameter-types.md
+++ b/docs/content/troubleshooting/parameter-types.md
@@ -1,8 +1,9 @@
 ---
-lastmod: 2021-04-23
+lastmod: 2023-01-28
 date: 2021-01-15
 title: MySqlParameter Types
 customtitle: "Type of value supplied to MySqlParameter.Value isn’t supported"
+description: List of supported .NET types that can be stored in MySqlParameter.Value and what to do for unsupported types.
 weight: 35
 menu:
   main:
diff --git a/docs/content/troubleshooting/retrieval-public-key.md b/docs/content/troubleshooting/retrieval-public-key.md
index 75a145418..3c4be0bf9 100644
--- a/docs/content/troubleshooting/retrieval-public-key.md
+++ b/docs/content/troubleshooting/retrieval-public-key.md
@@ -1,8 +1,9 @@
 ---
-lastmod: 2019-07-30
+lastmod: 2023-01-28
 date: 2019-07-07
 title: Retrieval of Public Key
 customtitle: "Fix: Retrieval of the RSA public key is not enabled for insecure connections"
+description: Fixing 'Retrieval of the RSA public key is not enabled for insecure connections' error by using MySqlConnector and AllowPublicKeyRetrieval=True in the connection string.
 weight: 40
 menu:
   main:
diff --git a/docs/content/troubleshooting/transaction-usage.md b/docs/content/troubleshooting/transaction-usage.md
index bdb061207..4e224549a 100644
--- a/docs/content/troubleshooting/transaction-usage.md
+++ b/docs/content/troubleshooting/transaction-usage.md
@@ -1,8 +1,9 @@
 ---
-lastmod: 2021-09-21
+lastmod: 2023-01-28
 date: 2018-09-29
 title: Transaction Usage
 customtitle: "Fix: The transaction associated with this command is not the connection’s active transaction"
+description: How to fix the error "The transaction associated with this command is not the connection’s active transaction" by setting the Transaction property on MySqlCommand.
 weight: 20
 menu:
   main:
diff --git a/docs/layouts/partials/header.html b/docs/layouts/partials/header.html
index 5723cda88..3c59186c0 100644
--- a/docs/layouts/partials/header.html
+++ b/docs/layouts/partials/header.html
@@ -14,7 +14,8 @@
 
       gtag('config', 'UA-142489411-1');
     </script>
-    <meta name="description" content="{{ with .Description }}{{ . }}{{ else }}{{ with .Site.Params.description }}{{ . }}{{ end }}{{ end }}">
+    {{ .Scratch.Add "description" "" }}{{ if .Description }}{{ .Scratch.Set "description" .Description }}{{ else }}{{ .Scratch.Set "description" .Site.Params.description }}{{ end }}
+    <meta name="description" content="{{ .Scratch.Get "description" }}">
     {{ with .Site.Params.author }}<meta name="author" content="{{ . }}">{{ end }}
     <!--icons generated by http://www.favicon-generator.org/-->
     <link rel="apple-touch-icon" sizes="57x57" href="/img/icons/apple-icon-57x57.png">
@@ -39,7 +40,7 @@
     {{ .Scratch.Add "title" "" }}{{ if isset .Site.Data.titles .Title }}{{ .Scratch.Set "title" (index .Site.Data.titles .Title).title }}{{ else if isset .Page.Params "customtitle" }}{{ .Scratch.Set "title" .Page.Params.customtitle }}{{ else }}{{ .Scratch.Set "title" .Title}}{{end}}
     <title>{{ .Scratch.Get "title" }} - MySqlConnector</title>
     <meta property="og:title" content="{{ .Scratch.Get "title" }} - MySqlConnector" />
-    <meta property="og:description" content="MySqlConnector is a high-performance, asynchronous ADO.NET data provider for MySQL Server, MariaDB, Percona Server, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL for MySQL and more." />
+    <meta property="og:description" content="{{ .Scratch.Get "description" }}" />
     <meta property="og:type" content="website" />
     <meta property="og:image" content="https://mysqlconnector.net/img/icons/android-icon-192x192.png" />
     <meta property="og:image:type" content="image/png" />

From 732ad05292249d52c4e67af340cf4fb8e78265aa Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 28 Jan 2023 13:16:30 -0800
Subject: [PATCH 043/393] Fix IsSet Hugo warning.

---
 docs/layouts/partials/header.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/layouts/partials/header.html b/docs/layouts/partials/header.html
index 3c59186c0..f2c063118 100644
--- a/docs/layouts/partials/header.html
+++ b/docs/layouts/partials/header.html
@@ -37,7 +37,7 @@
     <meta name="theme-color" content="#ffffff">
     <!--end icons-->
 
-    {{ .Scratch.Add "title" "" }}{{ if isset .Site.Data.titles .Title }}{{ .Scratch.Set "title" (index .Site.Data.titles .Title).title }}{{ else if isset .Page.Params "customtitle" }}{{ .Scratch.Set "title" .Page.Params.customtitle }}{{ else }}{{ .Scratch.Set "title" .Title}}{{end}}
+    {{ .Scratch.Add "title" "" }}{{ if isset .Page.Params "customtitle" }}{{ .Scratch.Set "title" .Page.Params.customtitle }}{{ else }}{{ .Scratch.Set "title" .Title}}{{end}}
     <title>{{ .Scratch.Get "title" }} - MySqlConnector</title>
     <meta property="og:title" content="{{ .Scratch.Get "title" }} - MySqlConnector" />
     <meta property="og:description" content="{{ .Scratch.Get "description" }}" />

From e3d0fbbed28f5ec059e444c61892c5ee10128a2d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 28 Jan 2023 13:24:40 -0800
Subject: [PATCH 044/393] Add descriptions to overview pages.

---
 docs/content/overview/dbproviderfactories.md | 7 ++++---
 docs/content/overview/installing.md          | 8 ++++----
 docs/content/overview/schema-collections.md  | 7 ++++---
 docs/content/overview/use-with-orms.md       | 5 +++--
 docs/content/overview/using-addwithvalue.md  | 5 +++--
 docs/content/overview/version-history.md     | 3 ++-
 6 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/docs/content/overview/dbproviderfactories.md b/docs/content/overview/dbproviderfactories.md
index a2efe3166..751913f4e 100644
--- a/docs/content/overview/dbproviderfactories.md
+++ b/docs/content/overview/dbproviderfactories.md
@@ -1,15 +1,16 @@
 ---
-lastmod: 2020-06-14
+lastmod: 2023-01-28
 date: 2019-03-06
 menu:
   main:
     parent: getting started
 title: DbProviderFactories
+customtitle: "Using DbProviderFactories"
+description: How to install MySqlConnector into DbProviderFactories by using app.config or DbProviderFactories.RegisterFactory.
 weight: 15
 ---
 
-Using DbProviderFactories
-==========
+# Using DbProviderFactories
 
 MySqlConnector can be registered with `DbProviderFactories` and obtained via `DbProviderFactories.GetFactory("MySqlConnector")`, or by
 using the methods [described here](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/obtaining-a-dbproviderfactory).
diff --git a/docs/content/overview/installing.md b/docs/content/overview/installing.md
index b01ba35e4..c29f637b1 100644
--- a/docs/content/overview/installing.md
+++ b/docs/content/overview/installing.md
@@ -1,15 +1,15 @@
 ---
-lastmod: 2023-01-21
+lastmod: 2023-01-28
 date: 2016-10-16
+title: Installing
+description: How to install MySqlConnector from NuGet into your .NET application.
 menu:
   main:
     parent: getting started
-title: Installing
 weight: 10
 ---
 
-Installing
-==========
+# Installing
 
 The recommended way of installing MySqlConnector is through [NuGet](https://www.nuget.org/packages/MySqlConnector/).
 Note that if you are using the `MySql.Data` NuGet package, it must be uninstalled first.
diff --git a/docs/content/overview/schema-collections.md b/docs/content/overview/schema-collections.md
index db75bc68b..b9ef01ddd 100644
--- a/docs/content/overview/schema-collections.md
+++ b/docs/content/overview/schema-collections.md
@@ -1,11 +1,12 @@
 ---
+lastmod: 2023-01-28
 date: 2021-04-24
-lastmod: 2022-08-12
+title: Schema Collections
+customtitle: "Supported Schema Collections"
+description: Information about the schemas supported by MySqlConnection.GetSchema.
 menu:
   main:
     parent: getting started
-title: Schema Collections
-customtitle: "Supported Schema Collections"
 weight: 80
 ---
 
diff --git a/docs/content/overview/use-with-orms.md b/docs/content/overview/use-with-orms.md
index baf7bb988..162b38141 100644
--- a/docs/content/overview/use-with-orms.md
+++ b/docs/content/overview/use-with-orms.md
@@ -1,10 +1,11 @@
 ---
-lastmod: 2017-11-06
+lastmod: 2023-01-28
 date: 2016-10-16
+title: Use with ORMs
+description: A list of .NET object-relational mappers that work well with MySqlConnector.
 menu:
   main:
     parent: getting started
-title: Use with ORMs
 weight: 60
 ---
 
diff --git a/docs/content/overview/using-addwithvalue.md b/docs/content/overview/using-addwithvalue.md
index 54330de5f..1e476bde3 100644
--- a/docs/content/overview/using-addwithvalue.md
+++ b/docs/content/overview/using-addwithvalue.md
@@ -1,11 +1,12 @@
 ---
+lastmod: 2023-01-28
 date: 2020-01-06
-lastmod: 2021-07-04
+title: Using AddWithValue
 customtitle: "Start Using AddWithValue with MySQL"
+description: Why you should use Parameters.AddWithValue with MySqlConnector, and why it's OK to use with MySQL.
 menu:
   main:
     parent: getting started
-title: Using AddWithValue
 weight: 70
 ---
 
diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 6c859f8ed..fa4bac99c 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,10 +1,11 @@
 ---
-lastmod: 2023-01-01
+lastmod: 2023-01-28
 date: 2017-03-27
 menu:
   main:
     parent: getting started
 title: Version History
+description: Version history for the MySqlConnector NuGet package
 weight: 30
 ---
 

From 60641c49ca6dac04e5d34b1c83eb96ac357e18f3 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 28 Jan 2023 13:46:38 -0800
Subject: [PATCH 045/393] Add a Dapper tutorial.

---
 docs/content/tutorials/dapper.md | 98 ++++++++++++++++++++++++++++++++
 1 file changed, 98 insertions(+)
 create mode 100644 docs/content/tutorials/dapper.md

diff --git a/docs/content/tutorials/dapper.md b/docs/content/tutorials/dapper.md
new file mode 100644
index 000000000..64168d8dc
--- /dev/null
+++ b/docs/content/tutorials/dapper.md
@@ -0,0 +1,98 @@
+---
+date: 2023-01-28
+title: Dapper
+customtitle: "Tutorial: Connect to MySQL with Dapper"
+description: How to create a .NET application that connects to MySQL with MySqlConnector and Dapper.
+menu:
+  main:
+    parent: tutorials
+weight: 11
+---
+
+# Tutorial: Connect to MySQL with Dapper
+
+## Introduction
+
+[Dapper](https://github.com/DapperLib/Dapper/blob/main/Readme.md) is a popular "micro-ORM" for connecting to databases from .NET.
+It can be used with MySqlConnector to connect to MySQL and retrieve data.
+Here's how.
+
+## 1. Create Your Project
+
+If you don't already have a .NET project, create one using Visual Studio, or by running `dotnet new console` or `dotnet new webapi` at the command line.
+
+## 2. Install NuGet Packages
+
+You will need to install the following NuGet packages:
+
+* [MySqlConnector](https://www.nuget.org/packages/MySqlConnector/): `dotnet add package MySqlConnector`
+* [Dapper](https://www.nuget.org/packages/Dapper/): `dotnet add package Dapper`
+
+## 3. Build Your Connection String
+
+Build your connection string by substituting the appropriate values in this template:
+
+```
+Server=YOURSERVER; User ID=YOURUSERID; Password=YOURPASSWORD; Database=YOURDATABASE
+```
+
+In an ASP.NET Core Web Application or Web API, store this connection string in `appsettings.json`:
+
+```json
+{
+  ....
+  "ConnectionStrings": {
+    "Default": "Server=YOURSERVER; User ID=YOURUSERID; Password=YOURPASSWORD; Database=YOURDATABASE"
+  }
+}
+```
+
+In a console application, you can store this in a constant string:
+
+```csharp
+const string connectionString = "Server=YOURSERVER; User ID=YOURUSERID; Password=YOURPASSWORD; Database=YOURDATABASE";
+```
+
+## 4. Create Your Connection
+
+In an ASP.NET Core Web Application or Web API, you can use dependency injection to create a connection in `Program.cs`:
+
+```csharp
+builder.Services.AddTransient(x =>
+  new MySqlConnection(builder.Configuration.GetConnectionString("Default")));
+```
+
+In a console application, you can create a connection in your `Main` method:
+
+```csharp
+using var connection = new MySqlConnection(connectionString);
+```
+
+## 5. Query Your Database
+
+You can now use Dapper to query your database by using the extension methods it adds on `IDbConnection`:
+
+For ASP.NET Core with minimal APIs in .NET 7.0:
+
+```csharp
+app.MapGet("/users/{userId}", (int userId, [FromServices] MySqlConnection connection) =>
+{
+    var users = connection.Query<string>("select user_name from users where user_id = @userId", new { userId });
+    if (users.FirstOrDefault() is string userName)
+        return Results.Ok(new { Name = userName });
+    else
+        return Results.NotFound();
+});
+```
+
+For a console application:
+
+```csharp
+var userId = 1;
+var users = connection.Query<string>("select user_name from users where user_id = @userId", new { userId });
+Console.WriteLine($"Name: {users.FirstOrDefault()}");
+```
+
+## 6. Use More Dapper Features
+
+To learn about more advanced Dapper features, such as list support and multi-mapping, see the [Dapper documentation](https://github.com/DapperLib/Dapper/blob/main/Readme.md#features).

From 19a0a9a43ae19e7dc8c5c69972d9b78f33c2e0fb Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 28 Jan 2023 13:48:04 -0800
Subject: [PATCH 046/393] Add "C#" to the Dapper tutorial documentation.

---
 docs/content/tutorials/dapper.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/content/tutorials/dapper.md b/docs/content/tutorials/dapper.md
index 64168d8dc..8a727901a 100644
--- a/docs/content/tutorials/dapper.md
+++ b/docs/content/tutorials/dapper.md
@@ -1,21 +1,21 @@
 ---
 date: 2023-01-28
 title: Dapper
-customtitle: "Tutorial: Connect to MySQL with Dapper"
-description: How to create a .NET application that connects to MySQL with MySqlConnector and Dapper.
+customtitle: "Tutorial: Connect to MySQL with Dapper using C#"
+description: How to create a C# .NET application that connects to MySQL with MySqlConnector and Dapper.
 menu:
   main:
     parent: tutorials
 weight: 11
 ---
 
-# Tutorial: Connect to MySQL with Dapper
+# Tutorial: Connect to MySQL with Dapper using C#
 
 ## Introduction
 
 [Dapper](https://github.com/DapperLib/Dapper/blob/main/Readme.md) is a popular "micro-ORM" for connecting to databases from .NET.
 It can be used with MySqlConnector to connect to MySQL and retrieve data.
-Here's how.
+Here's how to do it using C#.
 
 ## 1. Create Your Project
 

From 84bc3c720c1246954216afded6357845db926146 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 29 Jan 2023 15:35:55 -0800
Subject: [PATCH 047/393] Add EF Core Tutorial.

---
 docs/content/tutorials/efcore.md  | 150 ++++++++++++++++++++++++++++++
 docs/content/tutorials/tracing.md |   2 +-
 2 files changed, 151 insertions(+), 1 deletion(-)
 create mode 100644 docs/content/tutorials/efcore.md

diff --git a/docs/content/tutorials/efcore.md b/docs/content/tutorials/efcore.md
new file mode 100644
index 000000000..45ef27a54
--- /dev/null
+++ b/docs/content/tutorials/efcore.md
@@ -0,0 +1,150 @@
+---
+date: 2023-01-29
+title: EF Core
+customtitle: "Tutorial: Connect to MySQL with Entity Framework Core in C#"
+description: How to create a C# .NET application that connects to MySQL using Entity Framework Core.
+menu:
+  main:
+    parent: tutorials
+weight: 12
+---
+
+# Tutorial: Connect to MySQL with Entity Framework Core
+
+## Introduction
+
+Entity Framework Core (EF Core) is Microsoft's open-source object/relational mapper that allows C# developers to work with a relational database using .NET objects.
+Pomelo.EntityFrameworkCore.MySql is an EF Core provider for MySQL.
+It uses MySqlConnector to provide the core database connectivity.
+
+## 1. Create Your Project
+
+In this tutorial, we'll use a console application to demonstrate the core concepts.
+Create a console application, then add the Pomelo.EntityFrameworkCore.MySql NuGet package:
+
+```
+dotnet new console -o EFCoreMySQL
+cd EFCoreMySQL
+dotnet add package Pomelo.EntityFrameworkCore.MySql
+ ```
+
+## 2. Build Your Connection String
+
+Build your connection string by substituting the appropriate values in this template:
+
+```
+Server=YOURSERVER; User ID=YOURUSERID; Password=YOURPASSWORD; Database=YOURDATABASE
+```
+
+In this tutorial application, we will store this in a constant string:
+
+```csharp
+const string connectionString = "Server=localhost; User ID=root; Password=pass; Database=blog";
+```
+
+## 3. Create a Database Context
+
+The `BlogDataContext` is used for accessing application data through Entity Framework.
+It derives from the Entity Framework `DbContext` class and has public properties for accessing data.
+
+The `OnConfiguring()` method is used to connect to MySQL by using `options.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));`.
+
+We define two model classes, `Author` and `Post`, which will be stored in our database.
+
+Add the following code to the end of `Program.cs`:
+
+```csharp
+public class BlogDataContext : DbContext
+{
+    static readonly string connectionString = "Server=localhost; User ID=root; Password=pass; Database=blog";
+
+    public DbSet<Author> Authors { get; set; }
+    public DbSet<Post> Posts { get; set; }
+
+    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+    {
+        optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString));
+    }
+}
+
+public class Post
+{
+    public int PostId { get; set; }
+    public string Title { get; set; }
+    public string Content { get; set; }
+    public Author Author { get; set; }
+}
+
+public class Author
+{
+    public int AuthorId { get; set; }
+    public string Name { get; set; }
+    public string Email { get; set; }
+
+    public List<Post> Posts { get; set; }
+}
+```
+
+## 4. Create Your Database
+
+Use [EF migrations](https://learn.microsoft.com/en-us/ef/core/managing-schemas/migrations/) to create your database by running the following commands:
+
+```
+dotnet tool install --global dotnet-ef
+dotnet add package Microsoft.EntityFrameworkCore.Design
+dotnet ef migrations add InitialCreate
+dotnet ef database update
+```
+
+## 5. Create and Save Data
+
+Use LINQ queries on the properties of the `BlogDataContext` class to create and save data.
+Add the following code to the top of `Program.cs` (before the `BlogDataContext` class):
+
+```csharp
+// create new blog posts
+using (var context = new BlogDataContext())
+{
+    var john = new Author { Name = "John T. Author", Email = "john@example.com" };
+    context.Authors.Add(john);
+
+    var jane = new Author { Name = "Jane Q. Hacker", Email = "jane@example.com" };
+    context.Authors.Add(jane);
+
+    var post = new Post { Title = "Hello World", Content = "I wrote an app using EF Core!", Author = jane };
+    context.Posts.Add(post);
+    post = new Post { Title = "How to use EF Core", Content = "It's pretty easy", Author = john };
+    context.Posts.Add(post);
+
+    context.SaveChanges();
+}
+
+// query the blog posts, using a join between the two tables
+using (var context = new BlogDataContext())
+{
+    var posts = context.Posts
+        .Include(p => p.Author)
+        .ToList();
+
+    foreach (var post in posts)
+    {
+        Console.WriteLine($"{post.Title} by {post.Author.Name}");
+    }
+}
+```
+
+## 6. Run Your Application
+
+Execute `dotnet run`. The program should run and print the following output:
+
+```
+How to use EF Core by John T. Author
+Hello World by Jane Q. Hacker
+```
+
+## Further Reading
+
+For more information, see:
+
+* [Entity Framework Core](https://learn.microsoft.com/en-us/ef/core/) (MSDN)
+* [Pomelo.EntityFrameworkCore.MySql README](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql/blob/master/README.md)
diff --git a/docs/content/tutorials/tracing.md b/docs/content/tutorials/tracing.md
index ee7243424..107645489 100644
--- a/docs/content/tutorials/tracing.md
+++ b/docs/content/tutorials/tracing.md
@@ -4,7 +4,7 @@ menu:
   main:
     parent: tutorials
 title: Tracing
-weight: 12
+weight: 13
 ---
 
 # Tracing

From e6497e0cf44707a246cb4a4a439577afdb62d74d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 29 Jan 2023 15:39:01 -0800
Subject: [PATCH 048/393] Add link to tutorials.

---
 docs/content/overview/use-with-orms.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/docs/content/overview/use-with-orms.md b/docs/content/overview/use-with-orms.md
index 162b38141..4285767e6 100644
--- a/docs/content/overview/use-with-orms.md
+++ b/docs/content/overview/use-with-orms.md
@@ -15,6 +15,7 @@ Use with ORMs
 This library is compatible with popular .NET ORMs including:
 
 * [Dapper](https://dapperlib.github.io/Dapper/) ([GitHub](https://github.com/DapperLib/Dapper), [NuGet](https://www.nuget.org/packages/Dapper))
+  * See [Tutorial: Connect to MySQL with Dapper](/tutorials/dapper/).
 * [FreeSql](https://freesql.net/) ([GitHub](https://github.com/dotnetcore/FreeSql), [NuGet](https://www.nuget.org/packages/FreeSql.Provider.MySqlConnector/))
 * [LINQ to DB](https://linq2db.github.io) ([GitHub](https://github.com/linq2db/linq2db), [NuGet](https://www.nuget.org/packages/linq2db.MySqlConnector))
 * [NHibernate](https://nhibernate.info/) ([GitHub](https://github.com/nhibernate/NHibernate.MySqlConnector), [NuGet](https://www.nuget.org/packages/NHibernate.Driver.MySqlConnector))
@@ -27,3 +28,4 @@ This library is compatible with popular .NET ORMs including:
 For Entity Framework support, use:
 
 * Pomelo.EntityFrameworkCore.MySql ([GitHub](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql), [NuGet](https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql))
+  * See [Tutorial: Connect to MySQL with Entity Framework Core](/tutorials/efcore/).

From 412e8224ae31f8a1d062db804600d3793bcfb065 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 29 Jan 2023 15:39:09 -0800
Subject: [PATCH 049/393] Word-wrap long lines.

---
 docs/content/tutorials/migrating-from-connector-net.md | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 7a4562f47..a1e742ba1 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -26,10 +26,16 @@ In a .NET Framework application, make the following `app.config` change to regis
 <system.data>
   <DbProviderFactories>
     <!-- REMOVE THIS -->
-    <!-- add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.20.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+    <!-- add name="MySQL Data Provider"
+      invariant="MySql.Data.MySqlClient"
+      description=".Net Framework Data Provider for MySQL"
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.20.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
-    <add name="MySqlConnector" invariant="MySqlConnector" description="MySQL Connector for .NET" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
+    <add name="MySqlConnector"
+      invariant="MySqlConnector"
+      description="MySQL Connector for .NET"
+      type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
   </DbProviderFactories>
 </system.data>
 ```

From 85ab5d927111e3dc080549a39143d5947ddf9424 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 30 Jan 2023 19:22:45 -0800
Subject: [PATCH 050/393] Add simpler potential fix for comerr64 issue.

---
 .../failed-add-reference-comerr.md            | 22 ++++++++++++++++---
 1 file changed, 19 insertions(+), 3 deletions(-)

diff --git a/docs/content/troubleshooting/failed-add-reference-comerr.md b/docs/content/troubleshooting/failed-add-reference-comerr.md
index 75f400720..1cefd5c88 100644
--- a/docs/content/troubleshooting/failed-add-reference-comerr.md
+++ b/docs/content/troubleshooting/failed-add-reference-comerr.md
@@ -1,4 +1,5 @@
 ---
+lastmod: 2023-01-30
 date: 2023-01-28
 title: Failed to Add comerr
 description: How to fix the error "Failed to add reference to 'comerr64'" when installing MySql.Data using NuGet.
@@ -36,11 +37,26 @@ warning MSB3246:    at Microsoft.Build.Tasks.ReferenceTable.SetPrimaryAssemblyRe
 ## Cause
 
 This is [a bug](https://bugs.mysql.com/bug.php?id=109716) in the MySql.Data NuGet package introduced in version 8.0.32.
-Workarounds include switching to an SDK-style project (`.csproj` with `<PackageReference>` instead of `packages.config`), or downgrading to an earlier version of MySql.Data.
 
 ## Fix
 
-A better long-term fix is to [switch to MySqlConnector](/overview/installing), which fixes this problem and [many other bugs](/tutorials/migrating-from-connector-net/#fixed-bugs) in MySql.Data.
-To do this, uninstall the MySql.Data package and run `dotnet add package MySqlConnector` to replace it with MySqlConnector.
+### Recommended Fix
+
+The recommended fix for this problem is to [switch to MySqlConnector](/overview/installing), which fixes this problem and [many other bugs](/tutorials/migrating-from-connector-net/#fixed-bugs) in MySql.Data.
+To do this, use **Tools** > **NuGet Package Manager** > **Manage NuGet Packages for Solution...** to uninstall MySql.Data and install MySqlConnector instead.
 
 For more information, see our guide on [migrating from MySql.Data to MySqlConnector](/tutorials/migrating-from-connector-net/).
+
+### Alternative Fix
+
+Users report that the problem only occurs when using `packages.config`.
+You may be able to fix the problem by switching to `<PackageReference>` elements.
+To do this, right-click `packages.config` in Solution Explorer and select **Migrate packages.config to PackageReference**.
+For more information see [Migrate from packages.config to PackageReference](https://learn.microsoft.com/en-us/nuget/consume-packages/migrate-packages-config-to-package-reference) at MSDN.
+
+After doing this, the installation of MySql.Data 8.0.32 should succeed.
+A full conversion to SDK-style projects should not be required.
+
+### Workaround
+
+A workaround is to downgrade to MySql.Data 8.0.31 or earlier.

From 55dc9ad722e0af3f9e7f0f750eee31e5d44de19a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 1 Feb 2023 06:06:23 -0800
Subject: [PATCH 051/393] Move Percona Server to the end of the list.

It likely less well-known than Google Cloud SQL for MySQL.
---
 docs/config.toml                         | 2 +-
 src/MySqlConnector/MySqlConnector.csproj | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/config.toml b/docs/config.toml
index ad251bb8b..47b118779 100644
--- a/docs/config.toml
+++ b/docs/config.toml
@@ -15,7 +15,7 @@ disablePathToLower = true
       unsafe = true
 
 [params]
-  description = "MySqlConnector is a high-performance, asynchronous C# ADO.NET driver for MySQL Server, MariaDB, Percona Server, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL for MySQL and more."
+  description = "MySqlConnector is a high-performance, asynchronous C# ADO.NET driver for MySQL Server, MariaDB, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL for MySQL, Percona Server and more."
   author = "MySqlConnector Authors"
 
 [taxonomies]
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 634e2f5e4..a0251e51e 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <TargetFrameworks>net462;net471;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
-    <Description>A truly async MySQL ADO.NET provider, supporting MySQL Server, MariaDB, Percona Server, Amazon Aurora, Azure Database for MySQL and more.</Description>
+    <Description>A truly async MySQL ADO.NET provider, supporting MySQL Server, MariaDB, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL, and more.</Description>
     <Copyright>Copyright 2016–2023 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
     <AssemblyName>MySqlConnector</AssemblyName>

From 11b723d452a909c7c8ab8e5b354f15f308a2f2b7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 10 Feb 2023 10:08:25 -0800
Subject: [PATCH 052/393] Add image dimensions to benchmark results.

---
 docs/content/home.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/content/home.md b/docs/content/home.md
index b663d8a49..523f263a7 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -58,7 +58,7 @@ while (await reader.ReadAsync())
 
 MySqlConnector outperforms Connector/NET (MySql.Data) on benchmarks:
 
-![Benchmark](https://files.logoscdn.com/v1/files/63673908/assets/13928411/content.png?signature=MVHBZxDfB0J-0Pueja8NtvuLD9A)
+<p><img src="https://files.logoscdn.com/v1/files/63673908/assets/13928411/content.png?signature=MVHBZxDfB0J-0Pueja8NtvuLD9A" alt="Benchmark results for MySql.Data vs MySqlConnector" style="width: 800px; height: 534px"></p>
 
 (Client: MySqlConnector 2.2.0, Ubuntu 20.04, .NET 7.0; Server: Azure Database for MySQL 8.0.28, TLS 1.2)
 

From 8d13465b3fd918cc45f45a14830be43f615cdde2 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 10 Feb 2023 10:14:22 -0800
Subject: [PATCH 053/393] Use attributes to specify image dimensions.

---
 docs/content/home.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/content/home.md b/docs/content/home.md
index 523f263a7..aee8de09f 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -58,7 +58,7 @@ while (await reader.ReadAsync())
 
 MySqlConnector outperforms Connector/NET (MySql.Data) on benchmarks:
 
-<p><img src="https://files.logoscdn.com/v1/files/63673908/assets/13928411/content.png?signature=MVHBZxDfB0J-0Pueja8NtvuLD9A" alt="Benchmark results for MySql.Data vs MySqlConnector" style="width: 800px; height: 534px"></p>
+<p><img src="https://files.logoscdn.com/v1/files/63673908/assets/13928411/content.png?signature=MVHBZxDfB0J-0Pueja8NtvuLD9A" alt="Benchmark results for MySql.Data vs MySqlConnector" width="800" height="534"></p>
 
 (Client: MySqlConnector 2.2.0, Ubuntu 20.04, .NET 7.0; Server: Azure Database for MySQL 8.0.28, TLS 1.2)
 

From 15f91a398144a862e9bc555387f6c5991e82d788 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 14 Feb 2023 06:51:02 -0800
Subject: [PATCH 054/393] Wrap long lines.

---
 docs/content/troubleshooting/transaction-usage.md | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/docs/content/troubleshooting/transaction-usage.md b/docs/content/troubleshooting/transaction-usage.md
index 4e224549a..4ed620d32 100644
--- a/docs/content/troubleshooting/transaction-usage.md
+++ b/docs/content/troubleshooting/transaction-usage.md
@@ -41,7 +41,8 @@ command.CommandText = "SELECT ...";
 // *** ADD THIS LINE ***
 command.Transaction = transaction;
 
-// otherwise, this will throw System.InvalidOperationException: The transaction associated with this command is not the connection's active transaction.
+// otherwise, this will throw System.InvalidOperationException: The transaction
+// associated with this command is not the connection's active transaction.
 command.ExecuteScalar();
 
 // ... remaining code
@@ -55,7 +56,8 @@ using var connection = new MySqlConnection(...);
 connection.Open();
 using var transaction = connection.BeginTransaction();
 
-// this will throw System.InvalidOperationException: The transaction associated with this command is not the connection's active transaction.
+// this will throw System.InvalidOperationException: The transaction
+// associated with this command is not the connection's active transaction.
 connection.Query("SELECT ...");
 
 // use this instead:

From c825c31dc0cce6a583ce85fbd1789c4729f5b120 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 14 Feb 2023 06:51:11 -0800
Subject: [PATCH 055/393] Fix unclosed tag.

---
 docs/content/connection-options.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/content/connection-options.md b/docs/content/connection-options.md
index 669d6d29f..71dcd15be 100644
--- a/docs/content/connection-options.md
+++ b/docs/content/connection-options.md
@@ -366,7 +366,7 @@ These are the other options that MySqlConnector supports. They are set to sensib
   <tr id="InteractiveSession">
     <td>Interactive Session, Interactive, InteractiveSession</td>
     <td>false</td>
-    <td>If <code>true</code>, the session <code>wait_timeout</code> variable is initialized from the global <code>interactive_timeout<code> value instead of the global <code>wait_timeout</code> value.</td>
+    <td>If <code>true</code>, the session <code>wait_timeout</code> variable is initialized from the global <code>interactive_timeout</code> value instead of the global <code>wait_timeout</code> value.</td>
   </tr>
   <tr id="Keepalive">
     <td>Keep Alive, Keepalive</td>

From c612d68a0b4944fb346a3d71dc7de67e615379a3 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 25 Feb 2023 12:54:22 -0800
Subject: [PATCH 056/393] Moved comparison to MySql.Data into migration doc.

---
 docs/content/home.md                          | 54 +------------------
 .../tutorials/migrating-from-connector-net.md | 27 +++++++---
 2 files changed, 22 insertions(+), 59 deletions(-)

diff --git a/docs/content/home.md b/docs/content/home.md
index aee8de09f..36a14f2a6 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-01-21
+lastmod: 2023-02-25
 date: 2016-10-16
 title: Home
 weight: 10
@@ -67,54 +67,4 @@ MySqlConnector outperforms Connector/NET (MySql.Data) on benchmarks:
 MySqlConnector is a clean-room reimplementation of the [MySQL Protocol](https://dev.mysql.com/doc/internals/en/client-server-protocol.html)
 and is not based on [Oracle’s MySQL Connector/NET](https://github.com/mysql/mysql-connector-net).
 
-* **Asynchronous:** True asynchronous I/O for maximum throughput
-* **Fast:** Optimized for speed and low memory usage
-* **Reliable:** Fixes [dozens of open bugs](/tutorials/migrating-from-connector-net/#fixed-bugs) in Oracle’s Connector/NET; passes all [ADO.NET Specification Tests](https://mysql-net.github.io/AdoNetResults/)
-* **Leading Edge:** First MySQL library to support .NET Core; uses the latest .NET features
-* **Open:** MIT license; development [happens on GitHub](https://github.com/mysql-net/MySqlConnector) with publicly visible roadmaps, issues, and PRs
-
-<table class="table table-striped table-hover" style="max-width: 650px">
-  <thead>
-    <th style="width:25%"></th>
-    <th style="width:25%">MySqlConnector</th>
-    <th style="width:25%">Oracle’s Connector/NET</th>
-    <th style="width:25%">MySqlConnector Advantage</th>
-  </thead>
-  <tr>
-    <td><strong>Async</strong></td>
-    <td><strong>Fully asynchronous</strong> I/O</td>
-    <td>Async calls map to synchronous I/O</td>
-    <td>Uses fewer thread pool threads; higher throughput</td>
-  </tr>
-  <tr>
-    <td><strong>Development</strong></td>
-    <td>
-      <strong>Open and Collaborative</strong> Development on
-      <a href="https://github.com/mysql-net/MySqlConnector">GitHub</a>
-    </td>
-    <td>
-      Closed Development Roadmap. Code is viewable on
-      <a href="https://github.com/mysql/mysql-connector-net">GitHub</a>,
-      some issues addressed in <a href="http://forums.mysql.com/list.php?38">forums</a>
-    </td>
-    <td>
-      <a href="https://github.com/mysql-net/MySqlConnector/issues?q=is%3Aissue+is%3Aopen+label%3A%22up+for+grabs%22">Get involved!</a> View
-      <a href="https://github.com/mysql-net/MySqlConnector/milestones">our roadmap</a>,
-      discuss <a href="https://github.com/mysql-net/MySqlConnector/issues">issues</a>,
-      contribute <a href="https://github.com/mysql-net/MySqlConnector/pulls">pull requests</a>
-    </td>
-  </tr>
-  <tr>
-    <td><strong>License</strong></td>
-    <td>
-      The <strong><a href="https://github.com/mysql-net/MySqlConnector/blob/master/LICENSE">MIT License</a></strong>
-    </td>
-    <td>
-      <a href="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html">GPLv2</a>
-      with <a href="http://www.mysql.com/about/legal/licensing/foss-exception/">FOSS Exception</a>; or
-      <a href="https://www.mysql.com/about/legal/licensing/oem/">commercial license</a>
-    </td>
-    <td>More Permissive</td>
-  </tr>
-</table>
-
+See [MySqlConnector vs MySql.Data](/tutorials/migrating-from-connector-net/) for reasons to switch to MySqlConnector and details on migrating.
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index a1e742ba1..656eba301 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -1,15 +1,28 @@
 ---
-lastmod: 2022-01-21
+lastmod: 2023-02-25
 date: 2016-10-16
 menu:
   main:
     parent: tutorials
-title: Migrating from Connector/NET
+title: Migrating from MySql.Data
+customtitle: MySqlConnector vs MySql.Data
 weight: 20
 ---
 
-Migrating from Connector/NET
-============================
+# MySqlConnector vs MySql.Data
+
+Compared to MySql.Data (i.e., Oracle's MySQL Connector/NET), MySqlConnector has the following benefits:
+
+* Asynchronous I/O support
+* Higher performance (see [benchmarks](/#performance)) and lower memory usage
+* More reliable (see [bugs fixed](#fixed-bugs))
+* Leading edge: first MySQL driver to support .NET Core, `DbBatch`, `DateOnly`, `DbDataSource` and other new .NET features
+* Better compatibility with non-MySQL databases ([MariaDB](https://bugs.mysql.com/bug.php?id=109331), Amazon Aurora, etc.)
+* Better license (MIT, not [GPL 2.0](https://github.com/mysql/mysql-connector-net/blob/8.0/LICENSE) with [Universal FOSS Exception](https://oss.oracle.com/licenses/universal-foss-exception/) or [commercial license](https://www.mysql.com/about/legal/licensing/oem/))
+* Faster pace of development (regular [NuGet releases](https://www.nuget.org/packages/MySqlConnector#versions-body-tab), not once per quarter)
+* Development happens in the open on GitHub
+
+## Migrating from Connector/NET
 
 ### Namespace
 
@@ -132,7 +145,7 @@ Connector/NET allows a `MySqlConnection` object to be reused after it has been d
 object to be created. See [#331](https://github.com/mysql-net/MySqlConnector/issues/331) for more details.
 
 The return value of `MySqlConnection.BeginTransactionAsync` has changed from `Task<MySqlTransaction>` to
-`ValueTask<MySqlTransaction>` to match the [standard API in .NET Core 3.0](https://github.com/dotnet/corefx/issues/35012).
+`ValueTask<MySqlTransaction>` to match the [standard API in .NET Core 3.0](https://github.com/dotnet/runtime/issues/28596).
 (This method does always perform I/O, so `ValueTask` is not an optimization for MySqlConnector.)
 
 ### MySqlConnectionStringBuilder
@@ -153,7 +166,7 @@ If `MySqlCommand.CommandType` is `CommandType.StoredProcedure`, the stored proce
 
 Connector/NET provides `MySqlDataAdapter.FillAsync`, `FillSchemaAsync`, and `UpdateAsync` methods, but these methods
 have a synchronous implementation. MySqlConnector only adds “Async” methods when they can be implemented asynchronously.
-This functionality depends on [dotnet/corefx#20658](https://github.com/dotnet/corefx/issues/20658) being implemented first.
+This functionality depends on [dotnet/runtime#22109](https://github.com/dotnet/runtime/issues/22109) being implemented first.
 To migrate code, change it to call the synchronous methods instead.
 
 ### MySqlGeometry
@@ -188,7 +201,7 @@ or used only as positional parameters if they’re unnamed.
 For consistency with other ADO.NET providers, MySqlConnector will throw `InvalidOperationException` (instead of `MySqlException`)
 for various precondition checks that indicate misuse of the API (and not a problem related to MySQL Server).
 
-### Fixed Bugs
+## Fixed Bugs
 
 The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~~Strikethrough~~ indicates bugs that have since been fixed in a newer version of Connector/NET, but were fixed first in MySqlConnector.)
 

From 81289313809fe3c3c50a0c27cf0ee49dd3fb143e Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 25 Feb 2023 16:19:03 -0800
Subject: [PATCH 057/393] Make code blocks scroll in narrow viewport.

---
 docs/assets/css/site.css | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/assets/css/site.css b/docs/assets/css/site.css
index aeda04d79..d9f00dc88 100644
--- a/docs/assets/css/site.css
+++ b/docs/assets/css/site.css
@@ -55,6 +55,7 @@ hr {
 pre {
   font-family: monospace, monospace; /* 1 */
   font-size: 1em; /* 2 */
+  overflow-x: auto;
 }
 
 /* Text-level semantics

From 85fa7c1b2d2a8c03a57b5d5b77b72f94c8fd4479 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 8 Mar 2023 07:20:54 +1300
Subject: [PATCH 058/393] Update NuGet API key for 2023.

---
 appveyor.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/appveyor.yml b/appveyor.yml
index 4248e6838..001c7edfe 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -37,7 +37,7 @@ artifacts:
 deploy:
 - provider: NuGet
   api_key:
-    secure: v9rtj48AfjN8dXyqo+jZzD+pjacn+9TfiZ6kMP2k9K0Wvz+XLhnD6gOCfrZjqrow
+    secure: SCNhz02UXcSKv6UVgAlhbzGb0u7PAQF4w1kx+cCGw1tm95QNhy2yiKLsLjjQ0j7l
   artifact: /.*MySqlConnector[0-9.]+(-(alpha|beta|rc)\.?[0-9.]+)?nupkg/
   skip_symbols: true
   on:

From da362f0b22f6e6c99d560b9b1177fa0dedd3280d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 10 Mar 2023 14:41:27 +1300
Subject: [PATCH 059/393] Update comerr64 article with fixed NuGet package.

---
 .../troubleshooting/failed-add-reference-comerr.md     | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/docs/content/troubleshooting/failed-add-reference-comerr.md b/docs/content/troubleshooting/failed-add-reference-comerr.md
index 1cefd5c88..08c63e5ac 100644
--- a/docs/content/troubleshooting/failed-add-reference-comerr.md
+++ b/docs/content/troubleshooting/failed-add-reference-comerr.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-01-30
+lastmod: 2023-03-10
 date: 2023-01-28
 title: Failed to Add comerr
 description: How to fix the error "Failed to add reference to 'comerr64'" when installing MySql.Data using NuGet.
@@ -40,6 +40,10 @@ This is [a bug](https://bugs.mysql.com/bug.php?id=109716) in the MySql.Data NuGe
 
 ## Fix
 
+A fix from Oracle is now available in [MySql.Data version 8.0.32.1](https://www.nuget.org/packages/MySql.Data/8.0.32.1).
+This is mentioned in the fix for [bug 109670](https://bugs.mysql.com/bug.php?id=109670).
+Should that not resolve the problem, there are two other fixes:
+
 ### Recommended Fix
 
 The recommended fix for this problem is to [switch to MySqlConnector](/overview/installing), which fixes this problem and [many other bugs](/tutorials/migrating-from-connector-net/#fixed-bugs) in MySql.Data.
@@ -56,7 +60,3 @@ For more information see [Migrate from packages.config to PackageReference](http
 
 After doing this, the installation of MySql.Data 8.0.32 should succeed.
 A full conversion to SDK-style projects should not be required.
-
-### Workaround
-
-A workaround is to downgrade to MySql.Data 8.0.31 or earlier.

From 7eea79bd461274d6bb3538faf5b9a9013c7740f9 Mon Sep 17 00:00:00 2001
From: Guillaume Boucher <gboucher90@users.noreply.github.com>
Date: Wed, 15 Mar 2023 11:39:59 +0100
Subject: [PATCH 060/393] Update ServerSession.cs

Signed-off-by: Guillaume Boucher <guillaume.boucher@gopigment.com>
---
 src/MySqlConnector/Core/ServerSession.cs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index f9a7e5335..3d6b2f3ac 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1083,7 +1083,11 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 						{
 							if (ioBehavior == IOBehavior.Asynchronous)
 							{
+#if NET5_0_OR_GREATER
+								await tcpClient.ConnectAsync(ipAddress, cs.Port, cancellationToken).ConfigureAwait(false);
+#else
 								await tcpClient.ConnectAsync(ipAddress, cs.Port).ConfigureAwait(false);
+#endif
 							}
 							else
 							{

From fd675d9a2b976b64618e36fa7e86d125ff7b54f3 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 17 Mar 2023 15:49:15 +1300
Subject: [PATCH 061/393] Update MinVer to 4.3.0.

---
 src/Directory.Build.props | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index b4ece5d17..56678b7b2 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -25,7 +25,7 @@
     <EmbedUntrackedSources>true</EmbedUntrackedSources>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <NoWarn>$(NoWarn);1591;CA1708;CA1835;CA2215;CA5397;NU5105;SYSLIB0039</NoWarn>
-    <MinVerDefaultPreReleasePhase>beta</MinVerDefaultPreReleasePhase>
+    <MinVerDefaultPreReleaseIdentifiers>beta.0</MinVerDefaultPreReleaseIdentifiers>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
@@ -39,7 +39,7 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
-    <PackageReference Include="MinVer" Version="4.2.0">
+    <PackageReference Include="MinVer" Version="4.3.0">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>

From 11e48efaa780097babb29ee10d1cd5a0a5d48430 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 17 Mar 2023 15:57:59 +1300
Subject: [PATCH 062/393] Standardise LangVersion and xunit assets.

---
 src/Directory.Build.props                           |  2 +-
 tests/Benchmark/Benchmark.csproj                    |  2 +-
 tests/Conformance.Tests/Conformance.Tests.csproj    |  9 ++++++---
 tests/IntegrationTests/IntegrationTests.csproj      |  9 ++++++---
 .../MySqlConnector.Tests.csproj                     | 13 ++++++-------
 5 files changed, 20 insertions(+), 15 deletions(-)

diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 56678b7b2..11784649d 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -17,7 +17,7 @@
     <PackageValidationBaselineVersion>2.0.0</PackageValidationBaselineVersion>
     <EnableStrictModeForCompatibleFrameworksInPackageValidation>true</EnableStrictModeForCompatibleFrameworksInPackageValidation>
     <EnableStrictModeForCompatibleTfms>true</EnableStrictModeForCompatibleTfms>
-    <LangVersion>preview</LangVersion>
+    <LangVersion>11.0</LangVersion>
     <AnalysisLevel>latest-all</AnalysisLevel>
     <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
     <RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
diff --git a/tests/Benchmark/Benchmark.csproj b/tests/Benchmark/Benchmark.csproj
index 596970480..3c401d1b2 100644
--- a/tests/Benchmark/Benchmark.csproj
+++ b/tests/Benchmark/Benchmark.csproj
@@ -2,7 +2,7 @@
   <PropertyGroup>
     <OutputType>Exe</OutputType>
     <TargetFramework>net7.0</TargetFramework>
-    <LangVersion>10.0</LangVersion>
+    <LangVersion>11.0</LangVersion>
   </PropertyGroup>
   <ItemGroup>
     <PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
diff --git a/tests/Conformance.Tests/Conformance.Tests.csproj b/tests/Conformance.Tests/Conformance.Tests.csproj
index 2e102acbb..bb3f7db21 100644
--- a/tests/Conformance.Tests/Conformance.Tests.csproj
+++ b/tests/Conformance.Tests/Conformance.Tests.csproj
@@ -7,14 +7,17 @@
     <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
     <AssemblyOriginatorKeyFile>..\..\MySqlConnector.snk</AssemblyOriginatorKeyFile>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-   <LangVersion>preview</LangVersion>
+   <LangVersion>11.0</LangVersion>
   </PropertyGroup>
 
  <ItemGroup>
     <PackageReference Include="AdoNet.Specification.Tests" Version="2.0.0-beta.1" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
     <PackageReference Include="xunit" Version="2.4.2" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+      <PrivateAssets>all</PrivateAssets>
+    </PackageReference>
     <ProjectReference Include="..\..\src\MySqlConnector\MySqlConnector.csproj" />
   </ItemGroup>
 
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index 9aef2ac8f..b43e9b4ed 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -22,15 +22,18 @@
     <ServerGarbageCollection>true</ServerGarbageCollection>
     <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection>
     <ThreadPoolMinThreads>64</ThreadPoolMinThreads>
-    <LangVersion>preview</LangVersion>
+    <LangVersion>11.0</LangVersion>
     <ImplicitUsings>enable</ImplicitUsings>
   </PropertyGroup>
 
   <ItemGroup>
     <!--testing packages-->
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
     <PackageReference Include="xunit" Version="2.4.2" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+      <PrivateAssets>all</PrivateAssets>
+    </PackageReference>
     <!--app packages-->
     <PackageReference Include="Dapper.StrongName" Version="2.0.123" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.1" />
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index faaaac4e0..364eac506 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -11,21 +11,20 @@
   </PropertyGroup>
 
   <PropertyGroup>
-    <VersionPrefix>0.1.0</VersionPrefix>
     <SignAssembly>true</SignAssembly>
     <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
     <AssemblyOriginatorKeyFile>..\..\MySqlConnector.snk</AssemblyOriginatorKeyFile>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-    <AssemblyName>MySqlConnector.Tests</AssemblyName>
-    <PackageId>MySqlConnector.Tests</PackageId>
-    <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
-    <LangVersion>preview</LangVersion>
+    <LangVersion>11.0</LangVersion>
   </PropertyGroup>
 
   <ItemGroup>
     <PackageReference Include="xunit" Version="2.4.2" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.4.1" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+      <PrivateAssets>all</PrivateAssets>
+    </PackageReference>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>

From d04e24a8190264d81efba49a4ce37f171f06a8d7 Mon Sep 17 00:00:00 2001
From: diego dupin <diego.dupin@mariadb.com>
Date: Thu, 30 Mar 2023 19:46:37 +0000
Subject: [PATCH 063/393] Optimize parameter encoding. (#1296)

Some types of parameters encoded as "text" or "length encoded text" are guaranteed to be ASCII. Using the ASCII encoder provides a significant performance improvement.

Signed-off-by: rusher <diego.dupin@gmail.com>
---
 src/MySqlConnector/MySqlParameter.cs          | 28 +++++++++----------
 .../Serialization/ByteBufferWriter.cs         | 16 +++++++++++
 2 files changed, 30 insertions(+), 14 deletions(-)

diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 38bb706fb..597bc376f 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -243,7 +243,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		}
 		else if (Value is decimal decimalValue)
 		{
-			writer.Write(decimalValue.ToString(CultureInfo.InvariantCulture));
+			writer.WriteAscii(decimalValue.ToString(CultureInfo.InvariantCulture));
 		}
 		else if (Value is short shortValue)
 		{
@@ -320,20 +320,20 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		else if (Value is float floatValue)
 		{
 			// NOTE: Utf8Formatter doesn't support "R"
-			writer.Write(floatValue.ToString("R", CultureInfo.InvariantCulture));
+			writer.WriteAscii(floatValue.ToString("R", CultureInfo.InvariantCulture));
 		}
 		else if (Value is double doubleValue)
 		{
 			// NOTE: Utf8Formatter doesn't support "R"
-			writer.Write(doubleValue.ToString("R", CultureInfo.InvariantCulture));
+			writer.WriteAscii(doubleValue.ToString("R", CultureInfo.InvariantCulture));
 		}
 		else if (Value is BigInteger bigInteger)
 		{
-			writer.Write(bigInteger.ToString(CultureInfo.InvariantCulture));
+			writer.WriteAscii(bigInteger.ToString(CultureInfo.InvariantCulture));
 		}
 		else if (Value is MySqlDecimal mySqlDecimal)
 		{
-			writer.Write(mySqlDecimal.ToString());
+			writer.WriteAscii(mySqlDecimal.ToString());
 		}
 		else if (Value is MySqlDateTime mySqlDateTimeValue)
 		{
@@ -344,7 +344,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 #else
 				var str = FormattableString.Invariant($"timestamp('{mySqlDateTimeValue.GetDateTime():yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
 #endif
-				writer.Write(str);
+				writer.WriteAscii(str);
 			}
 			else
 			{
@@ -354,7 +354,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 #if NET6_0_OR_GREATER
 		else if (Value is DateOnly dateOnlyValue)
 		{
-			writer.Write(string.Create(CultureInfo.InvariantCulture, stackalloc char[23], $"timestamp('{dateOnlyValue:yyyy'-'MM'-'dd}')"));
+			writer.WriteAscii(string.Create(CultureInfo.InvariantCulture, stackalloc char[23], $"timestamp('{dateOnlyValue:yyyy'-'MM'-'dd}')"));
 		}
 #endif
 		else if (Value is DateTime dateTimeValue)
@@ -369,7 +369,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 #else
 			var str = FormattableString.Invariant($"timestamp('{dateTimeValue:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
 #endif
-			writer.Write(str);
+			writer.WriteAscii(str);
 		}
 		else if (Value is DateTimeOffset dateTimeOffsetValue)
 		{
@@ -379,12 +379,12 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 #else
 			var str = FormattableString.Invariant($"timestamp('{dateTimeOffsetValue.UtcDateTime:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
 #endif
-			writer.Write(str);
+			writer.WriteAscii(str);
 		}
 #if NET6_0_OR_GREATER
 		else if (Value is TimeOnly timeOnlyValue)
 		{
-			writer.Write(string.Create(CultureInfo.InvariantCulture, stackalloc char[22], $"time '{timeOnlyValue:HH':'mm':'ss'.'ffffff}'"));
+			writer.WriteAscii(string.Create(CultureInfo.InvariantCulture, stackalloc char[22], $"time '{timeOnlyValue:HH':'mm':'ss'.'ffffff}'"));
 		}
 #endif
 		else if (Value is TimeSpan ts)
@@ -400,7 +400,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 #else
 			var str = FormattableString.Invariant($"{ts.Days * 24 + ts.Hours}:{ts:mm':'ss'.'ffffff}'");
 #endif
-			writer.Write(str);
+			writer.WriteAscii(str);
 		}
 		else if (Value is Guid guidValue)
 		{
@@ -628,11 +628,11 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
 		}
 		else if (Value is decimal decimalValue)
 		{
-			writer.WriteLengthEncodedString(decimalValue.ToString(CultureInfo.InvariantCulture));
+			writer.WriteLengthEncodedAsciiString(decimalValue.ToString(CultureInfo.InvariantCulture));
 		}
 		else if (Value is BigInteger bigInteger)
 		{
-			writer.WriteLengthEncodedString(bigInteger.ToString(CultureInfo.InvariantCulture));
+			writer.WriteLengthEncodedAsciiString(bigInteger.ToString(CultureInfo.InvariantCulture));
 		}
 		else if (Value is MySqlDateTime mySqlDateTimeValue)
 		{
@@ -643,7 +643,7 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
 		}
 		else if (Value is MySqlDecimal mySqlDecimal)
 		{
-			writer.WriteLengthEncodedString(mySqlDecimal.ToString());
+			writer.WriteLengthEncodedAsciiString(mySqlDecimal.ToString());
 		}
 #if NET6_0_OR_GREATER
 		else if (Value is DateOnly dateOnlyValue)
diff --git a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
index fd6e2d389..4f2338327 100644
--- a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
@@ -100,6 +100,9 @@ public void Write(ReadOnlySpan<byte> span)
 	}
 
 	public void Write(string value) => Write(value.AsSpan(), flush: true);
+
+	public void WriteAscii(string value) => WriteAscii(value.AsSpan());
+
 	public void Write(string value, int offset, int length) => Write(value.AsSpan(offset, length), flush: true);
 
 	public void Write(ReadOnlySpan<char> chars, bool flush)
@@ -126,6 +129,13 @@ public void Write(ReadOnlySpan<char> chars, bool flush)
 		}
 	}
 
+	public void WriteAscii(ReadOnlySpan<char> chars)
+	{
+		if (m_output.Length < chars.Length)
+			Reallocate(chars.Length - m_output.Length);
+		m_output = m_output[Encoding.ASCII.GetBytes(chars, m_output.Span)..];
+	}
+
 	public void WriteLengthEncodedString(StringBuilder stringBuilder)
 	{
 #if NETCOREAPP3_0_OR_GREATER
@@ -266,6 +276,12 @@ public static void WriteLengthEncodedString(this ByteBufferWriter writer, ReadOn
 		writer.Write(value, flush: true);
 	}
 
+	public static void WriteLengthEncodedAsciiString(this ByteBufferWriter writer, string value)
+	{
+		writer.WriteLengthEncodedInteger((ulong) value.Length);
+		writer.WriteAscii(value.AsSpan());
+	}
+
 	public static void WriteNullTerminatedString(this ByteBufferWriter writer, string value)
 	{
 		writer.Write(value);

From a58731deabd6ab0170cb70486ac1cb010b4b07e9 Mon Sep 17 00:00:00 2001
From: rusher <diego.dupin@gmail.com>
Date: Thu, 6 Apr 2023 18:45:32 +0200
Subject: [PATCH 064/393] Permit multi-authentication Authentication switch
 request can result in 3 type of response: * OK_Packet * ERR_Packet * another
 authentication switch request.

example with "create user mysqltest1 identified via mysql_native_password as password("good") OR unix_socket;" that might result with multiple authentication switch request.
since MariaDB 10.4.3 (https://mariadb.com/kb/en/create-user/#identified-viawith-authentication_plugin) and MySQL 8.0 (https://dev.mysql.com/doc/refman/8.0/en/create-user.html)

Signed-off-by: rusher <diego.dupin@gmail.com>
---
 .ci/server/init_ed25519.sql              |  5 ++++-
 src/MySqlConnector/Core/ServerSession.cs |  2 +-
 tests/IntegrationTests/ConnectAsync.cs   | 12 ++++++++++++
 3 files changed, 17 insertions(+), 2 deletions(-)

diff --git a/.ci/server/init_ed25519.sql b/.ci/server/init_ed25519.sql
index a36b23fe4..18b3c0678 100644
--- a/.ci/server/init_ed25519.sql
+++ b/.ci/server/init_ed25519.sql
@@ -1,3 +1,6 @@
 INSTALL SONAME 'auth_ed25519';
 CREATE USER 'ed25519user'@'%' IDENTIFIED VIA ed25519 USING PASSWORD('Ed255!9');
-GRANT ALL PRIVILEGES ON *.* TO 'ed25519user'@'%';
\ No newline at end of file
+GRANT ALL PRIVILEGES ON *.* TO 'ed25519user'@'%';
+
+CREATE USER 'multiAuthUser'@'%' IDENTIFIED VIA ed25519 USING PASSWORD('Ed255!9') OR mysql_native_password as password("secret");
+GRANT ALL PRIVILEGES ON *.* TO 'multiAuthUser'@'%';
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 3d6b2f3ac..0b79453ec 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -561,7 +561,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			// if server doesn't support the authentication fast path, it will send a new challenge
-			if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature)
+			while (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature)
 			{
 				payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			}
diff --git a/tests/IntegrationTests/ConnectAsync.cs b/tests/IntegrationTests/ConnectAsync.cs
index 342e5697b..45dcebbf3 100644
--- a/tests/IntegrationTests/ConnectAsync.cs
+++ b/tests/IntegrationTests/ConnectAsync.cs
@@ -439,6 +439,18 @@ public async Task Ed25519Authentication()
 		using var connection = new MySqlConnection(csb.ConnectionString);
 		await connection.OpenAsync();
 	}
+
+	[SkippableFact(ServerFeatures.Ed25519)]
+	public async Task MultiAuthentication()
+	{
+		Ed25519AuthenticationPlugin.Install();
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		csb.UserID = "multiAuthUser";
+		csb.Password = "secret";
+		csb.Database = null;
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+	}
 #endif
 
 	// To create a MariaDB GSSAPI user for a current user

From e5f8d2bb7b9c69a2da982400c4182a8de53f1c43 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 7 Apr 2023 10:33:49 -0700
Subject: [PATCH 065/393] Preserve original CancellationToken in exception.

Following the change in 7eea79bd, the exception may be thrown with a linked CancellationToken instead of the original one the user supplied. We preserve the API semantics by indicating cancellation from the user's token if possible.
---
 src/MySqlConnector/MySqlConnection.cs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index f22046ac4..b66217d19 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -427,9 +427,11 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 				m_hasBeenOpened = true;
 				SetState(ConnectionState.Open);
 			}
-			catch (OperationCanceledException)
+			catch (OperationCanceledException ex)
 			{
 				SetState(ConnectionState.Closed);
+				if (!cancellationToken.Equals(ex.CancellationToken))
+					cancellationToken.ThrowIfCancellationRequested();
 				throw;
 			}
 			catch (MySqlException)

From 10b1a1ed6e86eda038a378c45150f9e4a03afb6d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 7 Apr 2023 10:39:42 -0700
Subject: [PATCH 066/393] Suppress new warnings in .NET 8 Preview 2 SDK.

---
 .editorconfig | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/.editorconfig b/.editorconfig
index 96b4b958f..cca51b0ea 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -76,7 +76,10 @@ dotnet_diagnostic.CA1032.severity = none # Implement standard exception construc
 dotnet_diagnostic.CA1062.severity = silent # Validate arguments of public methods.
 dotnet_diagnostic.CA1307.severity = silent # Specify StringComparison for clarity.
 dotnet_diagnostic.CA1508.severity = silent # Avoid dead conditional code.
+dotnet_diagnostic.CA1510.severity = none # Use ArgumentNullException.ThrowIfNull.
+dotnet_diagnostic.CA1513.severity = none # Use ObjectDisposedException.ThrowIf.
 dotnet_diagnostic.CA1849.severity = none # Call async methods when in an async method.
+dotnet_diagnostic.CA1859.severity = silent # Use concrete return types for performance.
 dotnet_diagnostic.CA2000.severity = none # Use recommended Dispose pattern.
 dotnet_diagnostic.CA2100.severity = none # Review SQL queries for security vulnerabilities.
 dotnet_diagnostic.CA2213.severity = silent # Disposable fields should be disposed.

From ca2cf5156e4e016a6dd5999e1baad61a246904a8 Mon Sep 17 00:00:00 2001
From: Nickolay Batov <stilettk@gmail.com>
Date: Sat, 8 Apr 2023 08:09:35 +0300
Subject: [PATCH 067/393] pass Port and UserId to cancel connection

Signed-off-by: Nickolay Batov <stilettk@gmail.com>
---
 src/MySqlConnector/Core/ServerSession.cs | 3 +++
 src/MySqlConnector/MySqlConnection.cs    | 4 ++++
 2 files changed, 7 insertions(+)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 0b79453ec..76d33c006 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -59,6 +59,8 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public string? DatabaseOverride { get; set; }
 	public string HostName { get; private set; }
 	public IPAddress? IPAddress => (m_tcpClient?.Client.RemoteEndPoint as IPEndPoint)?.Address;
+	public int? Port => (m_tcpClient?.Client.RemoteEndPoint as IPEndPoint)?.Port;
+	public string UserID { get; private set; } = string.Empty;
 	public WeakReference<MySqlConnection>? OwningConnection { get; set; }
 	public bool SupportsComMulti => m_supportsComMulti;
 	public bool SupportsDeprecateEof => m_supportsDeprecateEof;
@@ -471,6 +473,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				AuthPluginData = initialHandshake.AuthPluginData;
 				m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0;
 				CancellationTimeout = cs.CancellationTimeout;
+				UserID = cs.UserID;
 
 				// set activity tags
 				{
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index b66217d19..84d2b8016 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -759,6 +759,10 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 			};
 			if (m_session?.IPAddress is { } ipAddress)
 				csb.Server = ipAddress.ToString();
+			if (m_session?.Port is { } port)
+				csb.Port = (uint) port;
+			if (m_session?.UserID is { Length: > 0 } userId)
+				csb.UserID = userId;
 			var cancellationTimeout = GetConnectionSettings().CancellationTimeout;
 			csb.ConnectionTimeout = cancellationTimeout < 1 ? 3u : (uint) cancellationTimeout;
 

From c453b3955550b38c4841a1594cdc6b6b343dac38 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 8 Apr 2023 15:34:18 -0700
Subject: [PATCH 068/393] Don't copy MySqlDataSource in CloneWith. Fixes #1306

If the new connection string ends up being exactly the same as the current one (i.e., this is a Clone), then copy the current MySqlDataSource (which will have the side-effect of reusing the same connection pool); otherwise, ignore the current MySqlDataSource so a new pool can be created as necessary.
---
 src/MySqlConnector/MySqlConnection.cs | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index b66217d19..859f155e3 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -704,7 +704,7 @@ public async Task DisposeAsync()
 		}
 	}
 
-	public MySqlConnection Clone() => new(this, m_connectionString, m_hasBeenOpened);
+	public MySqlConnection Clone() => new(this, m_dataSource, m_connectionString, m_hasBeenOpened);
 
 	object ICloneable.Clone() => Clone();
 
@@ -720,11 +720,13 @@ public async Task DisposeAsync()
 	public MySqlConnection CloneWith(string connectionString)
 	{
 		var newBuilder = new MySqlConnectionStringBuilder(connectionString ?? throw new ArgumentNullException(nameof(connectionString)));
-		var currentBuilder = new MySqlConnectionStringBuilder(m_connectionString);
+		var currentBuilder = GetConnectionSettings().ConnectionStringBuilder;
 		var shouldCopyPassword = newBuilder.Password.Length == 0 && (!newBuilder.PersistSecurityInfo || currentBuilder.PersistSecurityInfo);
 		if (shouldCopyPassword)
 			newBuilder.Password = currentBuilder.Password;
-		return new MySqlConnection(this, newBuilder.ConnectionString, m_hasBeenOpened && shouldCopyPassword && !currentBuilder.PersistSecurityInfo);
+		var newConnectionString = newBuilder.ConnectionString;
+		var dataSource = newConnectionString == currentBuilder.ConnectionString ? m_dataSource : null;
+		return new MySqlConnection(this, dataSource, newConnectionString, m_hasBeenOpened && shouldCopyPassword && !currentBuilder.PersistSecurityInfo);
 	}
 
 	internal ServerSession Session
@@ -973,10 +975,10 @@ internal void SetState(ConnectionState newState)
 		}
 	}
 
-	private MySqlConnection(MySqlConnection other, string connectionString, bool hasBeenOpened)
+	private MySqlConnection(MySqlConnection other, MySqlDataSource? dataSource, string connectionString, bool hasBeenOpened)
 		: this(connectionString, other.LoggingConfiguration)
 	{
-		m_dataSource = other.m_dataSource;
+		m_dataSource = dataSource;
 		m_hasBeenOpened = hasBeenOpened;
 		ProvideClientCertificatesCallback = other.ProvideClientCertificatesCallback;
 		ProvidePasswordCallback = other.ProvidePasswordCallback;

From e82f091e875d74cce89f46982f68f5b1e40fbca8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 8 Apr 2023 15:50:55 -0700
Subject: [PATCH 069/393] Simplify code for reusing port and user ID.

The IPAddress property was used just for cancellation, so it can be changed to an IPEndPoint (to provide both needed values: the IP address and port).

UserID can be assumed to have been set on the ServerSession if it is connected.
---
 src/MySqlConnector/Core/ServerSession.cs | 5 ++---
 src/MySqlConnector/MySqlConnection.cs    | 8 ++++----
 2 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 76d33c006..afa417928 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -58,9 +58,8 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public uint LastReturnedTicks { get; private set; }
 	public string? DatabaseOverride { get; set; }
 	public string HostName { get; private set; }
-	public IPAddress? IPAddress => (m_tcpClient?.Client.RemoteEndPoint as IPEndPoint)?.Address;
-	public int? Port => (m_tcpClient?.Client.RemoteEndPoint as IPEndPoint)?.Port;
-	public string UserID { get; private set; } = string.Empty;
+	public IPEndPoint? IPEndPoint => m_tcpClient?.Client.RemoteEndPoint as IPEndPoint;
+	public string? UserID { get; private set; }
 	public WeakReference<MySqlConnection>? OwningConnection { get; set; }
 	public bool SupportsComMulti => m_supportsComMulti;
 	public bool SupportsDeprecateEof => m_supportsDeprecateEof;
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 84d2b8016..8be3661b1 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -757,12 +757,12 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 				AutoEnlist = false,
 				Pooling = false,
 			};
-			if (m_session?.IPAddress is { } ipAddress)
+			if (m_session.IPEndPoint is { Address: { } ipAddress, Port: { } port })
+			{
 				csb.Server = ipAddress.ToString();
-			if (m_session?.Port is { } port)
 				csb.Port = (uint) port;
-			if (m_session?.UserID is { Length: > 0 } userId)
-				csb.UserID = userId;
+			}
+			csb.UserID = m_session.UserID;
 			var cancellationTimeout = GetConnectionSettings().CancellationTimeout;
 			csb.ConnectionTimeout = cancellationTimeout < 1 ? 3u : (uint) cancellationTimeout;
 

From b35721f77dbb8a53e1c5f916f33623d1db7d8518 Mon Sep 17 00:00:00 2001
From: diego dupin <diego.dupin@gmail.com>
Date: Thu, 13 Apr 2023 20:52:31 +0200
Subject: [PATCH 070/393] Permit skipping metadata for MariaDB (#1301)

Since MariaDB 10.6 (with https://jira.mariadb.org/browse/MDEV-19237), binary result-set skips sending metadata when they haven't changed. This avoids network data and parsing client-side.

Signed-off-by: rusher <diego.dupin@gmail.com>
---
 .../Core/CommandListPosition.cs               |  11 ++
 src/MySqlConnector/Core/PreparedStatement.cs  |   2 +-
 src/MySqlConnector/Core/ResultSet.cs          |  75 +++++++-----
 src/MySqlConnector/Core/ServerSession.cs      |   5 +-
 .../Core/SingleCommandPayloadCreator.cs       |  13 ++-
 src/MySqlConnector/Logging/Log.cs             |   4 +-
 src/MySqlConnector/MySqlDataReader.cs         |   2 +
 .../Protocol/Payloads/ColumnCountPayload.cs   |  28 +++++
 .../Payloads/HandshakeResponse41Payload.cs    |  45 ++++----
 .../Protocol/ProtocolCapabilities.cs          |  10 ++
 tests/IntegrationTests/BatchTests.cs          |  45 ++++++--
 tests/IntegrationTests/CommandTests.cs        | 107 ++++++++++++++++++
 tests/IntegrationTests/InsertTests.cs         |   3 +-
 .../MySqlConnector.Tests/MySqlDecimalTests.cs |   3 +-
 14 files changed, 282 insertions(+), 71 deletions(-)
 create mode 100644 src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs

diff --git a/src/MySqlConnector/Core/CommandListPosition.cs b/src/MySqlConnector/Core/CommandListPosition.cs
index 7e0188b24..24d9c0153 100644
--- a/src/MySqlConnector/Core/CommandListPosition.cs
+++ b/src/MySqlConnector/Core/CommandListPosition.cs
@@ -8,6 +8,7 @@ internal struct CommandListPosition
 	public CommandListPosition(IReadOnlyList<IMySqlCommand> commands)
 	{
 		Commands = commands;
+		PreparedStatements = null;
 		CommandIndex = 0;
 		PreparedStatementIndex = 0;
 	}
@@ -17,6 +18,11 @@ public CommandListPosition(IReadOnlyList<IMySqlCommand> commands)
 	/// </summary>
 	public IReadOnlyList<IMySqlCommand> Commands { get; }
 
+	/// <summary>
+	/// Associated prepared statements of commands
+	/// </summary>
+	public PreparedStatements? PreparedStatements;
+
 	/// <summary>
 	/// The index of the current command.
 	/// </summary>
@@ -26,4 +32,9 @@ public CommandListPosition(IReadOnlyList<IMySqlCommand> commands)
 	/// If the current command is a prepared statement, the index of the current prepared statement for that command.
 	/// </summary>
 	public int PreparedStatementIndex;
+
+	/// <summary>
+	/// Retrieve the last used prepared statement
+	/// </summary>
+	public PreparedStatement? LastUsedPreparedStatement;
 }
diff --git a/src/MySqlConnector/Core/PreparedStatement.cs b/src/MySqlConnector/Core/PreparedStatement.cs
index cd9db2891..b85bb9b8c 100644
--- a/src/MySqlConnector/Core/PreparedStatement.cs
+++ b/src/MySqlConnector/Core/PreparedStatement.cs
@@ -17,6 +17,6 @@ public PreparedStatement(int statementId, ParsedStatement statement, ColumnDefin
 
 	public int StatementId { get; }
 	public ParsedStatement Statement { get; }
-	public ColumnDefinitionPayload[]? Columns { get; }
+	public ColumnDefinitionPayload[]? Columns { get; internal set; }
 	public ColumnDefinitionPayload[]? Parameters { get; }
 }
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 4404e1fd5..abb6915fe 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -116,42 +116,55 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 				}
 				else
 				{
-					static int ReadColumnCount(ReadOnlySpan<byte> span)
+					var columnCountPacket = ColumnCountPayload.Create(payload.Span, Session.SupportsMetaSkip);
+					if (!columnCountPacket.MetadataFollows)
 					{
-						var reader = new ByteArrayReader(span);
-						var columnCount_ = (int) reader.ReadLengthEncodedInteger();
-						if (reader.BytesRemaining != 0)
-							throw new MySqlException("Unexpected data at end of column_count packet; see https://github.com/mysql-net/MySqlConnector/issues/324");
-						return columnCount_;
+						// reuse previous metadata
+						var preparedStatement = DataReader.CurrentPrepareStmt()!;
+						ColumnDefinitions = preparedStatement.Columns!;
+						ColumnTypes = new MySqlDbType[columnCountPacket.ColumnCount];
+						for (var column = 0; column < columnCountPacket.ColumnCount; column++)
+						{
+							ColumnTypes[column] = TypeMapper.ConvertToMySqlDbType(ColumnDefinitions[column],
+								Connection.TreatTinyAsBoolean, Connection.GuidFormat);
+						}
 					}
-					var columnCount = ReadColumnCount(payload.Span);
-
-					// reserve adequate space to hold a copy of all column definitions (but note that this can be resized below if we guess too small)
-					Utility.Resize(ref m_columnDefinitionPayloads, columnCount * 96);
+					else
+					{
+						// parse columns
+						// reserve adequate space to hold a copy of all column definitions (but note that this can be resized below if we guess too small)
+						Utility.Resize(ref m_columnDefinitionPayloads, columnCountPacket.ColumnCount * 96);
 
-					ColumnDefinitions = new ColumnDefinitionPayload[columnCount];
-					ColumnTypes = new MySqlDbType[columnCount];
+						ColumnDefinitions = new ColumnDefinitionPayload[columnCountPacket.ColumnCount];
+						ColumnTypes = new MySqlDbType[columnCountPacket.ColumnCount];
+						for (var column = 0; column < ColumnDefinitions.Length; column++)
+						{
+							payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
+							var payloadLength = payload.Span.Length;
+
+							// 'Session.ReceiveReplyAsync' reuses a shared buffer; make a copy so that the column definitions can always be safely read at any future point
+							if (m_columnDefinitionPayloadUsedBytes + payloadLength > m_columnDefinitionPayloads.Count)
+								Utility.Resize(ref m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes + payloadLength);
+							payload.Span.CopyTo(m_columnDefinitionPayloads.AsSpan(m_columnDefinitionPayloadUsedBytes));
+
+							var columnDefinition = ColumnDefinitionPayload.Create(new ResizableArraySegment<byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, payloadLength));
+							ColumnDefinitions[column] = columnDefinition;
+							ColumnTypes[column] = TypeMapper.ConvertToMySqlDbType(columnDefinition, Connection.TreatTinyAsBoolean, Connection.GuidFormat);
+							m_columnDefinitionPayloadUsedBytes += payloadLength;
+						}
 
-					for (var column = 0; column < ColumnDefinitions.Length; column++)
-					{
-						payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
-						var payloadLength = payload.Span.Length;
-
-						// 'Session.ReceiveReplyAsync' reuses a shared buffer; make a copy so that the column definitions can always be safely read at any future point
-						if (m_columnDefinitionPayloadUsedBytes + payloadLength > m_columnDefinitionPayloads.Count)
-							Utility.Resize(ref m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes + payloadLength);
-						payload.Span.CopyTo(m_columnDefinitionPayloads.AsSpan(m_columnDefinitionPayloadUsedBytes));
-
-						var columnDefinition = ColumnDefinitionPayload.Create(new ResizableArraySegment<byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, payloadLength));
-						ColumnDefinitions[column] = columnDefinition;
-						ColumnTypes[column] = TypeMapper.ConvertToMySqlDbType(columnDefinition, treatTinyAsBoolean: Connection.TreatTinyAsBoolean, guidFormat: Connection.GuidFormat);
-						m_columnDefinitionPayloadUsedBytes += payloadLength;
-					}
+						if (Session.SupportsMetaSkip)
+						{
+							// server support metadata skipping, but has resend them, so something has change since last prepare/execution
+							var preparedStatement = DataReader.CurrentPrepareStmt();
+							if (preparedStatement != null) preparedStatement.Columns = ColumnDefinitions;
+						}
 
-					if (!Session.SupportsDeprecateEof)
-					{
-						payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
-						EofPayload.Create(payload.Span);
+						if (!Session.SupportsDeprecateEof)
+						{
+							payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
+							EofPayload.Create(payload.Span);
+						}
 					}
 
 					if (ColumnDefinitions.Length == (Command?.OutParameters?.Count + 1) && ColumnDefinitions[0].Name == SingleCommandPayloadCreator.OutParameterSentinelColumnName)
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index afa417928..5ad3633c6 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -63,6 +63,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public WeakReference<MySqlConnection>? OwningConnection { get; set; }
 	public bool SupportsComMulti => m_supportsComMulti;
 	public bool SupportsDeprecateEof => m_supportsDeprecateEof;
+	public bool SupportsMetaSkip => m_supportsMetaSkip;
 	public bool SupportsQueryAttributes { get; private set; }
 	public bool SupportsSessionTrack => m_supportsSessionTrack;
 	public bool ProcAccessDenied { get; set; }
@@ -485,6 +486,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				m_supportsComMulti = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbComMulti) != 0;
 				m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
 				m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
+				m_supportsMetaSkip = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
 				SupportsQueryAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
 				m_supportsSessionTrack = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
 				var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0;
@@ -519,7 +521,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 					}
 				}
 
-				Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
+				Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, m_supportsMetaSkip, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
 
 				if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
 				{
@@ -1969,6 +1971,7 @@ protected override void OnStatementBegin(int index)
 	private bool m_supportsComMulti;
 	private bool m_supportsConnectionAttributes;
 	private bool m_supportsDeprecateEof;
+	private bool m_supportsMetaSkip;
 	private bool m_supportsSessionTrack;
 	private bool m_supportsPipelining;
 	private CharacterSet m_characterSet;
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index 0abe65b4d..27afe9889 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -1,7 +1,6 @@
 using MySqlConnector.Logging;
 using MySqlConnector.Protocol;
 using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
 
 namespace MySqlConnector.Core;
 
@@ -19,8 +18,8 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 			return false;
 
 		var command = commandListPosition.Commands[commandListPosition.CommandIndex];
-		var preparedStatements = command.TryGetPreparedStatements();
-		if (preparedStatements is null)
+		commandListPosition.PreparedStatements = command.TryGetPreparedStatements();
+		if (commandListPosition.PreparedStatements is null)
 		{
 			Log.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 
@@ -44,16 +43,18 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 			}
 
 			WriteQueryPayload(command, cachedProcedures, writer, appendSemicolon);
-
+			commandListPosition.LastUsedPreparedStatement = null;
 			commandListPosition.CommandIndex++;
 		}
 		else
 		{
 			writer.Write((byte) CommandKind.StatementExecute);
-			WritePreparedStatement(command, preparedStatements.Statements[commandListPosition.PreparedStatementIndex], writer);
+			commandListPosition.LastUsedPreparedStatement =
+				commandListPosition.PreparedStatements.Statements[commandListPosition.PreparedStatementIndex];
+			WritePreparedStatement(command, commandListPosition.LastUsedPreparedStatement, writer);
 
 			// advance to next prepared statement or next command
-			if (++commandListPosition.PreparedStatementIndex == preparedStatements.Statements.Count)
+			if (++commandListPosition.PreparedStatementIndex == commandListPosition.PreparedStatements.Statements.Count)
 			{
 				commandListPosition.CommandIndex++;
 				commandListPosition.PreparedStatementIndex = 0;
diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index c5e871feb..1afa47efd 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -40,8 +40,8 @@ internal static partial class Log
 	[LoggerMessage(EventIds.AutoDetectedAurora57, LogLevel.Debug, "Session {SessionId} auto-detected Aurora 5.7 at '{HostName}'; disabling pipelining")]
 	public static partial void AutoDetectedAurora57(ILogger logger, string sessionId, string hostName);
 
-	[LoggerMessage(EventIds.SessionMadeConnection, LogLevel.Debug, "Session {SessionId} made connection; server version {ServerVersion}; connection ID {ConnectionId}; supports: compression {SupportsCompression}, attributes {SupportsAttributes}, deprecate EOF {SupportsDeprecateEof}, SSL {SupportsSsl}, session track {SupportsSessionTrack}, pipelining {SupportsPipelining}, query attributes {SupportsQueryAttributes}")]
-	public static partial void SessionMadeConnection(ILogger logger, string sessionId, string serverVersion, int connectionId, bool supportsCompression, bool supportsAttributes, bool supportsDeprecateEof, bool supportsSsl, bool supportsSessionTrack, bool supportsPipelining, bool supportsQueryAttributes);
+	[LoggerMessage(EventIds.SessionMadeConnection, LogLevel.Debug, "Session {SessionId} made connection; server version {ServerVersion}; connection ID {ConnectionId}; supports: compression {SupportsCompression}, attributes {SupportsAttributes}, deprecate EOF {SupportsDeprecateEof}, metadata skip {supportsMetaSkip}, SSL {SupportsSsl}, session track {SupportsSessionTrack}, pipelining {SupportsPipelining}, query attributes {SupportsQueryAttributes}")]
+	public static partial void SessionMadeConnection(ILogger logger, string sessionId, string serverVersion, int connectionId, bool supportsCompression, bool supportsAttributes, bool supportsDeprecateEof, bool supportsMetaSkip, bool supportsSsl, bool supportsSessionTrack, bool supportsPipelining, bool supportsQueryAttributes);
 
 	[LoggerMessage(EventIds.ServerDoesNotSupportSsl, LogLevel.Error, "Session {SessionId} requires SSL but server doesn't support it")]
 	public static partial void ServerDoesNotSupportSsl(ILogger logger, string sessionId);
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 2912b4c74..50b6d921b 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -691,6 +691,8 @@ private void VerifyNotDisposed()
 			throw new InvalidOperationException("Can't call this method when MySqlDataReader is closed.");
 	}
 
+	internal PreparedStatement? CurrentPrepareStmt() => m_commandListPosition.LastUsedPreparedStatement;
+
 	private ResultSet GetResultSet()
 	{
 		VerifyNotDisposed();
diff --git a/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs b/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
new file mode 100644
index 000000000..e716cdf47
--- /dev/null
+++ b/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
@@ -0,0 +1,28 @@
+using MySqlConnector.Protocol.Serialization;
+
+namespace MySqlConnector.Protocol.Payloads;
+
+/// <summary>
+///     Helper class to parse Column count packet.
+///     https://mariadb.com/kb/en/result-set-packets/#column-count-packet
+///     Packet contains a columnCount, and - if capability MARIADB_CLIENT_CACHE_METADATA is set - a flag to indicate if metadata follows
+/// </summary>
+internal sealed class ColumnCountPayload
+{
+	private ColumnCountPayload(int columnCount, bool metadataFollows)
+	{
+		ColumnCount = columnCount;
+		MetadataFollows = metadataFollows;
+	}
+
+	public int ColumnCount { get; }
+	public bool MetadataFollows { get; }
+
+	public static ColumnCountPayload Create(ReadOnlySpan<byte> span, bool supportsMetaSkip)
+	{
+		var reader = new ByteArrayReader(span);
+		var columnCount = (int) reader.ReadLengthEncodedInteger();
+		var metadataFollows = supportsMetaSkip ? reader.ReadByte() == 1 : true;
+		return new ColumnCountPayload(columnCount, metadataFollows);
+	}
+}
diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
index 1acb02c9d..7707d5476 100644
--- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
@@ -9,25 +9,30 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 	{
 		var writer = new ByteBufferWriter();
 
-		writer.Write((int) (
-			ProtocolCapabilities.Protocol41 |
-			(cs.InteractiveSession ? (serverCapabilities & ProtocolCapabilities.Interactive) : 0) |
-			(serverCapabilities & ProtocolCapabilities.LongPassword) |
-			(serverCapabilities & ProtocolCapabilities.Transactions) |
-			ProtocolCapabilities.SecureConnection |
-			(serverCapabilities & ProtocolCapabilities.PluginAuth) |
-			(serverCapabilities & ProtocolCapabilities.PluginAuthLengthEncodedClientData) |
-			ProtocolCapabilities.MultiStatements |
-			ProtocolCapabilities.MultiResults |
-			(cs.AllowLoadLocalInfile ? ProtocolCapabilities.LocalFiles : 0) |
-			(string.IsNullOrWhiteSpace(cs.Database) ? 0 : ProtocolCapabilities.ConnectWithDatabase) |
-			(cs.UseAffectedRows ? 0 : ProtocolCapabilities.FoundRows) |
-			(useCompression ? ProtocolCapabilities.Compress : ProtocolCapabilities.None) |
-			(serverCapabilities & ProtocolCapabilities.ConnectionAttributes) |
-			(serverCapabilities & ProtocolCapabilities.SessionTrack) |
-			(serverCapabilities & ProtocolCapabilities.DeprecateEof) |
-			(serverCapabilities & ProtocolCapabilities.QueryAttributes) |
-			additionalCapabilities));
+		var clientCapabilities = (ProtocolCapabilities.Protocol41 |
+		                          (cs.InteractiveSession ? ProtocolCapabilities.Interactive : 0) |
+		                          ProtocolCapabilities.LongPassword |
+		                          ProtocolCapabilities.Transactions |
+		                          ProtocolCapabilities.SecureConnection |
+		                          ProtocolCapabilities.PluginAuth |
+		                          ProtocolCapabilities.PluginAuthLengthEncodedClientData |
+		                          ProtocolCapabilities.MultiStatements |
+		                          ProtocolCapabilities.MultiResults |
+		                          (cs.AllowLoadLocalInfile ? ProtocolCapabilities.LocalFiles : 0) |
+		                          (string.IsNullOrWhiteSpace(cs.Database)
+			                          ? 0
+			                          : ProtocolCapabilities.ConnectWithDatabase) |
+		                          (cs.UseAffectedRows ? 0 : ProtocolCapabilities.FoundRows) |
+		                          (useCompression ? ProtocolCapabilities.Compress : ProtocolCapabilities.None) |
+		                          ProtocolCapabilities.ConnectionAttributes |
+		                          ProtocolCapabilities.SessionTrack |
+		                          ProtocolCapabilities.DeprecateEof |
+		                          ProtocolCapabilities.QueryAttributes |
+		                          ProtocolCapabilities.MariaDbComMulti |
+		                          ProtocolCapabilities.MariaDbCacheMetadata |
+		                          additionalCapabilities) & serverCapabilities;
+
+		writer.Write((int) clientCapabilities);
 		writer.Write(0x4000_0000);
 		writer.Write((byte) characterSet);
 
@@ -38,7 +43,7 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 		if ((serverCapabilities & ProtocolCapabilities.LongPassword) == 0)
 		{
 			// MariaDB writes extended capabilities at the end of the padding
-			writer.Write((int) (((long) (serverCapabilities & ProtocolCapabilities.MariaDbComMulti)) >> 32));
+			writer.Write((int) ((ulong) clientCapabilities >> 32));
 		}
 		else
 		{
diff --git a/src/MySqlConnector/Protocol/ProtocolCapabilities.cs b/src/MySqlConnector/Protocol/ProtocolCapabilities.cs
index 5b0466f8d..fb6e42f31 100644
--- a/src/MySqlConnector/Protocol/ProtocolCapabilities.cs
+++ b/src/MySqlConnector/Protocol/ProtocolCapabilities.cs
@@ -147,4 +147,14 @@ internal enum ProtocolCapabilities : ulong
 	/// Support of array binding.
 	/// </summary>
 	MariaDbStatementBulkOperations = 0x4_0000_0000,
+
+	/// <summary>
+	/// metadata extended information
+	/// </summary>
+	MariaDbExtendedTypeInfo = 0x8_0000_0000,
+
+	/// <summary>
+	/// permit metadata caching
+	/// </summary>
+	MariaDbCacheMetadata = 0x10_0000_0000,
 }
diff --git a/tests/IntegrationTests/BatchTests.cs b/tests/IntegrationTests/BatchTests.cs
index f62af6e49..0755d67d9 100644
--- a/tests/IntegrationTests/BatchTests.cs
+++ b/tests/IntegrationTests/BatchTests.cs
@@ -180,7 +180,7 @@ public void ExecuteBatch(string suffix)
 		{
 			BatchCommands =
 			{
-				new MySqlBatchCommand("SELECT 1" + suffix),
+				new MySqlBatchCommand("SELECT 10; SELECT 1" + suffix),
 				new MySqlBatchCommand("SELECT 2" + suffix),
 				new MySqlBatchCommand("SELECT 3" + suffix),
 			},
@@ -198,12 +198,17 @@ public void ExecuteBatch(string suffix)
 		Assert.False(reader.Read());
 		Assert.True(reader.NextResult());
 
+		Assert.True(reader.Read());
+		total += reader.GetInt32(0);
+		Assert.False(reader.Read());
+		Assert.True(reader.NextResult());
+
 		Assert.True(reader.Read());
 		total += reader.GetInt32(0);
 		Assert.False(reader.Read());
 		Assert.False(reader.NextResult());
 
-		Assert.Equal(6, total);
+		Assert.Equal(16, total);
 	}
 
 	[Fact(Skip = "COM_MULTI")]
@@ -247,7 +252,7 @@ public void SingleRow(bool prepare)
 		{
 			BatchCommands =
 			{
-				new MySqlBatchCommand("SELECT id FROM batch_single_row ORDER BY id"),
+				new MySqlBatchCommand("SELECT id FROM batch_single_row ORDER BY id; SELECT 10"),
 				new MySqlBatchCommand("SELECT id FROM batch_single_row ORDER BY id"),
 			},
 		};
@@ -255,12 +260,36 @@ public void SingleRow(bool prepare)
 		if (prepare)
 			batch.Prepare();
 
-		using var reader = batch.ExecuteReader(CommandBehavior.SingleRow);
-		Assert.True(reader.Read());
-		Assert.Equal(1, reader.GetInt32(0));
-		Assert.False(reader.Read());
+		using (var reader = batch.ExecuteReader(CommandBehavior.SingleRow))
+		{
+			Assert.True(reader.Read());
+			Assert.Equal(1, reader.GetInt32(0));
+			Assert.False(reader.Read());
+			Assert.False(reader.NextResult());
+		}
+
+		using (var reader = batch.ExecuteReader())
+		{
+			Assert.True(reader.Read());
+			Assert.Equal(1, reader.GetInt32(0));
+			Assert.True(reader.Read());
+			Assert.Equal(2, reader.GetInt32(0));
+			Assert.True(reader.Read());
+			Assert.Equal(3, reader.GetInt32(0));
+			Assert.False(reader.Read());
+			Assert.True(reader.NextResult());
+			Assert.True(reader.Read());
+			Assert.Equal(10, reader.GetInt32(0));
+			Assert.True(reader.NextResult());
+			Assert.True(reader.Read());
+			Assert.Equal(1, reader.GetInt32(0));
+			Assert.True(reader.Read());
+			Assert.Equal(2, reader.GetInt32(0));
+			Assert.True(reader.Read());
+			Assert.Equal(3, reader.GetInt32(0));
+			Assert.False(reader.Read());
+		}
 
-		Assert.False(reader.NextResult());
 	}
 
 	[Fact]
diff --git a/tests/IntegrationTests/CommandTests.cs b/tests/IntegrationTests/CommandTests.cs
index 65d605e27..97ac20f2f 100644
--- a/tests/IntegrationTests/CommandTests.cs
+++ b/tests/IntegrationTests/CommandTests.cs
@@ -44,6 +44,113 @@ public void CreateCommandSetsConnection()
 		Assert.Equal(m_database.Connection, command.Connection);
 	}
 
+#if !MYSQL_DATA
+	[Fact]
+	public void SingleQueryWithPrepareReuse()
+	{
+		using var connection = new MySqlConnection(m_database.Connection.ConnectionString);
+		connection.Open();
+		using (var cmd = connection.CreateCommand())
+		{
+			cmd.CommandText = "select ?";
+			cmd.Prepare();
+			cmd.Parameters.Add(new() { Value = 1 });
+			using (var reader = cmd.ExecuteReader())
+			{
+				while (reader.Read())
+				{
+					Assert.Equal(1, reader.GetInt32(0));
+				}
+			}
+
+			cmd.Parameters.Clear();
+			cmd.Parameters.Add(new() { Value = 100 });
+			using (var reader = cmd.ExecuteReader())
+			{
+				while (reader.Read())
+				{
+					Assert.Equal(100, reader.GetInt32(0));
+				}
+			}
+		}
+
+		using (var cmd = connection.CreateCommand())
+		{
+			cmd.CommandText = "select ?";
+			cmd.Prepare();
+			cmd.Parameters.Add(new() { Value = 2 });
+			using var reader = cmd.ExecuteReader();
+			while (reader.Read())
+			{
+				Assert.Equal(2, reader.GetInt32(0));
+			}
+		}
+	}
+
+	[Fact]
+	public void MultiQueryWithPrepareReuse()
+	{
+		using var connection = new MySqlConnection(m_database.Connection.ConnectionString);
+		connection.Open();
+		using (var cmd = connection.CreateCommand())
+		{
+			cmd.CommandText = "select ?; select ? + 2";
+			cmd.Prepare();
+			cmd.Parameters.Add(new() { Value = 1 });
+			cmd.Parameters.Add(new() { Value = 4 });
+			using (var reader = cmd.ExecuteReader())
+			{
+				while (reader.Read())
+				{
+					Assert.Equal(1, reader.GetInt32(0));
+				}
+
+				Assert.True(reader.NextResult());
+				while (reader.Read())
+				{
+					Assert.Equal(6, reader.GetInt32(0));
+				}
+			}
+
+			cmd.Parameters.Clear();
+			cmd.Parameters.Add(new() { Value = 100 });
+			cmd.Parameters.Add(new() { Value = 400 });
+			using (var reader = cmd.ExecuteReader())
+			{
+				while (reader.Read())
+				{
+					Assert.Equal(100, reader.GetInt32(0));
+				}
+
+				Assert.True(reader.NextResult());
+				while (reader.Read())
+				{
+					Assert.Equal(402, reader.GetInt32(0));
+				}
+			}
+		}
+
+		using (var cmd = connection.CreateCommand())
+		{
+			cmd.CommandText = "select ?; select ? + 2";
+			cmd.Prepare();
+			cmd.Parameters.Add(new() { Value = 2 });
+			cmd.Parameters.Add(new() { Value = 5 });
+			using var reader = cmd.ExecuteReader();
+			while (reader.Read())
+			{
+				Assert.Equal(2, reader.GetInt32(0));
+			}
+
+			Assert.True(reader.NextResult());
+			while (reader.Read())
+			{
+				Assert.Equal(7, reader.GetInt32(0));
+			}
+		}
+	}
+#endif
+
 	[Fact]
 	public void CreateCommandDoesNotSetTransaction()
 	{
diff --git a/tests/IntegrationTests/InsertTests.cs b/tests/IntegrationTests/InsertTests.cs
index f90912c7f..8b75323c4 100644
--- a/tests/IntegrationTests/InsertTests.cs
+++ b/tests/IntegrationTests/InsertTests.cs
@@ -1,3 +1,4 @@
+using System.Globalization;
 using System.Numerics;
 #if MYSQL_DATA
 using MySql.Data.Types;
@@ -471,7 +472,7 @@ public void InsertMySqlDecimalAsDecimal(bool prepare)
 
 		using var reader = connection.ExecuteReader(@"select value from insert_mysql_decimal order by rowid;");
 		Assert.True(reader.Read());
-		var val = reader.GetValue(0).ToString();
+		var val = ((decimal) reader.GetValue(0)).ToString(CultureInfo.InvariantCulture);
 		Assert.Equal(value, val);
 	}
 #endif
diff --git a/tests/MySqlConnector.Tests/MySqlDecimalTests.cs b/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
index 03974650c..ed3070983 100644
--- a/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
@@ -1,4 +1,5 @@
 using System;
+using System.Globalization;
 using Xunit;
 
 namespace MySqlConnector.Tests;
@@ -90,7 +91,7 @@ public void TestValidFormatWithDecimalNegativeZero()
 		var value = "-0.2342323";
 		var decimalVal = new MySqlDecimal(value);
 		Assert.Equal(value, decimalVal.ToString());
-		Assert.Equal(decimal.Parse(value), decimalVal.Value);
+		Assert.Equal(decimal.Parse(value, CultureInfo.InvariantCulture), decimalVal.Value);
 	}
 
 	[Fact]

From e650af3ace270f1744e40b71dc34b38ea0e2c9b8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 13 Apr 2023 13:43:16 -0700
Subject: [PATCH 071/393] Clean up code style for metadata caching code.

- Rename abbreviated variable names.
- Be explicit about test expectations for return value of MySqlDataReader.Read.
- Reword some comments.
---
 src/MySqlConnector/Core/ResultSet.cs          | 10 +--
 src/MySqlConnector/Core/ServerSession.cs      |  7 +-
 src/MySqlConnector/Logging/Log.cs             |  4 +-
 src/MySqlConnector/MySqlDataReader.cs         |  2 +-
 .../Protocol/Payloads/ColumnCountPayload.cs   |  4 +-
 .../Protocol/ProtocolCapabilities.cs          |  2 +-
 tests/IntegrationTests/CommandTests.cs        | 73 ++++++++++---------
 7 files changed, 51 insertions(+), 51 deletions(-)

diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index abb6915fe..a12e96d65 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -116,11 +116,11 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 				}
 				else
 				{
-					var columnCountPacket = ColumnCountPayload.Create(payload.Span, Session.SupportsMetaSkip);
+					var columnCountPacket = ColumnCountPayload.Create(payload.Span, Session.SupportsCachedPreparedMetadata);
 					if (!columnCountPacket.MetadataFollows)
 					{
 						// reuse previous metadata
-						var preparedStatement = DataReader.CurrentPrepareStmt()!;
+						var preparedStatement = DataReader.LastUsedPreparedStatement!;
 						ColumnDefinitions = preparedStatement.Columns!;
 						ColumnTypes = new MySqlDbType[columnCountPacket.ColumnCount];
 						for (var column = 0; column < columnCountPacket.ColumnCount; column++)
@@ -153,10 +153,10 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 							m_columnDefinitionPayloadUsedBytes += payloadLength;
 						}
 
-						if (Session.SupportsMetaSkip)
+						if (Session.SupportsCachedPreparedMetadata)
 						{
-							// server support metadata skipping, but has resend them, so something has change since last prepare/execution
-							var preparedStatement = DataReader.CurrentPrepareStmt();
+							// server supports metadata caching, but has resent it, so something has changed since last prepare/execution
+							var preparedStatement = DataReader.LastUsedPreparedStatement;
 							if (preparedStatement != null) preparedStatement.Columns = ColumnDefinitions;
 						}
 
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 5ad3633c6..3282c881f 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -63,7 +63,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public WeakReference<MySqlConnection>? OwningConnection { get; set; }
 	public bool SupportsComMulti => m_supportsComMulti;
 	public bool SupportsDeprecateEof => m_supportsDeprecateEof;
-	public bool SupportsMetaSkip => m_supportsMetaSkip;
+	public bool SupportsCachedPreparedMetadata { get; private set; }
 	public bool SupportsQueryAttributes { get; private set; }
 	public bool SupportsSessionTrack => m_supportsSessionTrack;
 	public bool ProcAccessDenied { get; set; }
@@ -486,7 +486,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				m_supportsComMulti = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbComMulti) != 0;
 				m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
 				m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
-				m_supportsMetaSkip = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
+				SupportsCachedPreparedMetadata = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
 				SupportsQueryAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
 				m_supportsSessionTrack = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
 				var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0;
@@ -521,7 +521,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 					}
 				}
 
-				Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, m_supportsMetaSkip, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
+				Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
 
 				if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
 				{
@@ -1971,7 +1971,6 @@ protected override void OnStatementBegin(int index)
 	private bool m_supportsComMulti;
 	private bool m_supportsConnectionAttributes;
 	private bool m_supportsDeprecateEof;
-	private bool m_supportsMetaSkip;
 	private bool m_supportsSessionTrack;
 	private bool m_supportsPipelining;
 	private CharacterSet m_characterSet;
diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index 1afa47efd..35848069b 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -40,8 +40,8 @@ internal static partial class Log
 	[LoggerMessage(EventIds.AutoDetectedAurora57, LogLevel.Debug, "Session {SessionId} auto-detected Aurora 5.7 at '{HostName}'; disabling pipelining")]
 	public static partial void AutoDetectedAurora57(ILogger logger, string sessionId, string hostName);
 
-	[LoggerMessage(EventIds.SessionMadeConnection, LogLevel.Debug, "Session {SessionId} made connection; server version {ServerVersion}; connection ID {ConnectionId}; supports: compression {SupportsCompression}, attributes {SupportsAttributes}, deprecate EOF {SupportsDeprecateEof}, metadata skip {supportsMetaSkip}, SSL {SupportsSsl}, session track {SupportsSessionTrack}, pipelining {SupportsPipelining}, query attributes {SupportsQueryAttributes}")]
-	public static partial void SessionMadeConnection(ILogger logger, string sessionId, string serverVersion, int connectionId, bool supportsCompression, bool supportsAttributes, bool supportsDeprecateEof, bool supportsMetaSkip, bool supportsSsl, bool supportsSessionTrack, bool supportsPipelining, bool supportsQueryAttributes);
+	[LoggerMessage(EventIds.SessionMadeConnection, LogLevel.Debug, "Session {SessionId} made connection; server version {ServerVersion}; connection ID {ConnectionId}; supports: compression {SupportsCompression}, attributes {SupportsAttributes}, deprecate EOF {SupportsDeprecateEof}, cached metadata {SupportsCachedMetadata}, SSL {SupportsSsl}, session track {SupportsSessionTrack}, pipelining {SupportsPipelining}, query attributes {SupportsQueryAttributes}")]
+	public static partial void SessionMadeConnection(ILogger logger, string sessionId, string serverVersion, int connectionId, bool supportsCompression, bool supportsAttributes, bool supportsDeprecateEof, bool supportsCachedMetadata, bool supportsSsl, bool supportsSessionTrack, bool supportsPipelining, bool supportsQueryAttributes);
 
 	[LoggerMessage(EventIds.ServerDoesNotSupportSsl, LogLevel.Error, "Session {SessionId} requires SSL but server doesn't support it")]
 	public static partial void ServerDoesNotSupportSsl(ILogger logger, string sessionId);
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 50b6d921b..4b9753b34 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -691,7 +691,7 @@ private void VerifyNotDisposed()
 			throw new InvalidOperationException("Can't call this method when MySqlDataReader is closed.");
 	}
 
-	internal PreparedStatement? CurrentPrepareStmt() => m_commandListPosition.LastUsedPreparedStatement;
+	internal PreparedStatement? LastUsedPreparedStatement => m_commandListPosition.LastUsedPreparedStatement;
 
 	private ResultSet GetResultSet()
 	{
diff --git a/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs b/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
index e716cdf47..ea0116bca 100644
--- a/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
@@ -18,11 +18,11 @@ private ColumnCountPayload(int columnCount, bool metadataFollows)
 	public int ColumnCount { get; }
 	public bool MetadataFollows { get; }
 
-	public static ColumnCountPayload Create(ReadOnlySpan<byte> span, bool supportsMetaSkip)
+	public static ColumnCountPayload Create(ReadOnlySpan<byte> span, bool supportsOptionalMetadata)
 	{
 		var reader = new ByteArrayReader(span);
 		var columnCount = (int) reader.ReadLengthEncodedInteger();
-		var metadataFollows = supportsMetaSkip ? reader.ReadByte() == 1 : true;
+		var metadataFollows = !supportsOptionalMetadata || reader.ReadByte() == 1;
 		return new ColumnCountPayload(columnCount, metadataFollows);
 	}
 }
diff --git a/src/MySqlConnector/Protocol/ProtocolCapabilities.cs b/src/MySqlConnector/Protocol/ProtocolCapabilities.cs
index fb6e42f31..aca015562 100644
--- a/src/MySqlConnector/Protocol/ProtocolCapabilities.cs
+++ b/src/MySqlConnector/Protocol/ProtocolCapabilities.cs
@@ -154,7 +154,7 @@ internal enum ProtocolCapabilities : ulong
 	MariaDbExtendedTypeInfo = 0x8_0000_0000,
 
 	/// <summary>
-	/// permit metadata caching
+	/// Client supports caching metadata for prepared statements (MARIADB_CLIENT_CACHE_METADATA).
 	/// </summary>
 	MariaDbCacheMetadata = 0x10_0000_0000,
 }
diff --git a/tests/IntegrationTests/CommandTests.cs b/tests/IntegrationTests/CommandTests.cs
index 97ac20f2f..bec7385d9 100644
--- a/tests/IntegrationTests/CommandTests.cs
+++ b/tests/IntegrationTests/CommandTests.cs
@@ -57,20 +57,18 @@ public void SingleQueryWithPrepareReuse()
 			cmd.Parameters.Add(new() { Value = 1 });
 			using (var reader = cmd.ExecuteReader())
 			{
-				while (reader.Read())
-				{
-					Assert.Equal(1, reader.GetInt32(0));
-				}
+				Assert.True(reader.Read());
+				Assert.Equal(1, reader.GetInt32(0));
+				Assert.False(reader.Read());
 			}
 
 			cmd.Parameters.Clear();
 			cmd.Parameters.Add(new() { Value = 100 });
 			using (var reader = cmd.ExecuteReader())
 			{
-				while (reader.Read())
-				{
-					Assert.Equal(100, reader.GetInt32(0));
-				}
+				Assert.True(reader.Read());
+				Assert.Equal(100, reader.GetInt32(0));
+				Assert.False(reader.Read());
 			}
 		}
 
@@ -80,10 +78,9 @@ public void SingleQueryWithPrepareReuse()
 			cmd.Prepare();
 			cmd.Parameters.Add(new() { Value = 2 });
 			using var reader = cmd.ExecuteReader();
-			while (reader.Read())
-			{
-				Assert.Equal(2, reader.GetInt32(0));
-			}
+			Assert.True(reader.Read());
+			Assert.Equal(2, reader.GetInt32(0));
+			Assert.False(reader.Read());
 		}
 	}
 
@@ -100,16 +97,17 @@ public void MultiQueryWithPrepareReuse()
 			cmd.Parameters.Add(new() { Value = 4 });
 			using (var reader = cmd.ExecuteReader())
 			{
-				while (reader.Read())
-				{
-					Assert.Equal(1, reader.GetInt32(0));
-				}
+				Assert.True(reader.Read());
+				Assert.Equal(1, reader.GetInt32(0));
+				Assert.False(reader.Read());
 
 				Assert.True(reader.NextResult());
-				while (reader.Read())
-				{
-					Assert.Equal(6, reader.GetInt32(0));
-				}
+
+				Assert.True(reader.Read());
+				Assert.Equal(6, reader.GetInt32(0));
+				Assert.False(reader.Read());
+
+				Assert.False(reader.NextResult());
 			}
 
 			cmd.Parameters.Clear();
@@ -117,16 +115,17 @@ public void MultiQueryWithPrepareReuse()
 			cmd.Parameters.Add(new() { Value = 400 });
 			using (var reader = cmd.ExecuteReader())
 			{
-				while (reader.Read())
-				{
-					Assert.Equal(100, reader.GetInt32(0));
-				}
+				Assert.True(reader.Read());
+				Assert.Equal(100, reader.GetInt32(0));
+				Assert.False(reader.Read());
 
 				Assert.True(reader.NextResult());
-				while (reader.Read())
-				{
-					Assert.Equal(402, reader.GetInt32(0));
-				}
+
+				Assert.True(reader.Read());
+				Assert.Equal(402, reader.GetInt32(0));
+				Assert.False(reader.Read());
+
+				Assert.False(reader.NextResult());
 			}
 		}
 
@@ -137,16 +136,18 @@ public void MultiQueryWithPrepareReuse()
 			cmd.Parameters.Add(new() { Value = 2 });
 			cmd.Parameters.Add(new() { Value = 5 });
 			using var reader = cmd.ExecuteReader();
-			while (reader.Read())
-			{
-				Assert.Equal(2, reader.GetInt32(0));
-			}
+
+			Assert.True(reader.Read());
+			Assert.Equal(2, reader.GetInt32(0));
+			Assert.False(reader.Read());
 
 			Assert.True(reader.NextResult());
-			while (reader.Read())
-			{
-				Assert.Equal(7, reader.GetInt32(0));
-			}
+
+			Assert.True(reader.Read());
+			Assert.Equal(7, reader.GetInt32(0));
+			Assert.False(reader.Read());
+
+			Assert.False(reader.NextResult());
 		}
 	}
 #endif

From 1081028ca4b0547d5fe0d0909b5ef1b2b31163c8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 13 Apr 2023 14:04:12 -0700
Subject: [PATCH 072/393] Replace MariaDB 10.5 with 10.11 in integration tests.

MariaDB 10.11 is the latest LTS version.
---
 azure-pipelines.yml | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 38de95847..68af9f544 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -239,10 +239,6 @@ jobs:
         image: 'mysql:8.0'
         connectionStringExtra: ''
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
-      'MariaDB 10.5':
-        image: 'mariadb:10.5'
-        connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
       'MariaDB 10.6':
         image: 'mariadb:10.6'
         connectionStringExtra: ''
@@ -251,6 +247,10 @@ jobs:
         image: 'mariadb:10.10'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
+      'MariaDB 10.11':
+        image: 'mariadb:10.11'
+        connectionStringExtra: ''
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
   steps:
   - template: '.ci/integration-tests-steps.yml'
     parameters:

From b1aae10580560edf9b634243e244f3a23ce4328b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 13 Apr 2023 14:52:19 -0700
Subject: [PATCH 073/393] Be explicit about what CommandTimeout means.

---
 docs/content/overview/command-cancellation.md | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/docs/content/overview/command-cancellation.md b/docs/content/overview/command-cancellation.md
index be71122e4..fb1ce454d 100644
--- a/docs/content/overview/command-cancellation.md
+++ b/docs/content/overview/command-cancellation.md
@@ -23,6 +23,17 @@ The mechanisms for cancelling a command are:
 
 **CommandTimeout**. Each `MySqlCommand` has a `CommandTimeout` property that specifies a timeout in seconds. (This is initialized from the [`DefaultCommandTimeout`](/connection-options/#DefaultCommandTimeout) setting in the connection string.) If this timeout is not `0`, then a soft cancellation will be attempted when the timeout expires. If successful, the `ExecuteX(Async)` method will throw a `MySqlException` with `MySqlErrorCode.CommandTimeoutExpired`; its inner exception will be a `MySqlException` with `MySqlErrorCode.QueryInterrupted`.
 
+> **NOTE**
+>
+> `CommandTimeout` does not specify a timeout for the entire result set to be read.
+> Instead, as per [the documentation for `SqlCommand.CommandTimeout`](https://learn.microsoft.com/en-us/dotnet/api/system.data.sqlclient.sqlcommand.commandtimeout), it specifies "the cumulative time-out for all network packets that are read during the invocation of a method".
+> That is, the timeout is reset at the beginning of each call to a public API method, e.g., `MySqlDataReader.Read` or `MySqlDataReader.NextResult`.
+>
+> For methods such as `MySqlCommand.ExecuteNonQuery`, which perform all their work within a single public API method, then `CommandTimeout` does apply to the entirety of that operation.
+> However, it still only controls the cumulative timeout for all network packets, not the total time taken to execute the command.
+>
+> To stop a MySQL operation after a specified amount of time, use an `ExecuteXAsync(CancellationToken)` overload and pass a `CancellationToken` that is cancelled after the desired timeout.
+
 **CancellationTimeout**. The connection string can specify a [`CancellationTimeout`](/connection-options/#CancellationTimeout) timeout value. After `CommandTimeout` elapses (and a soft cancellation is started), the `CancellationTimeout` timer starts. If this times out, a hard cancellation is performed and the underlying network connection is closed. When this occurs, the `ExecuteX(Async)` method will throw a `MySqlException` with `MySqlErrorCode.CommandTimeoutExpired`.
 
 ### Notes

From b6d2792e2291a8918f02338b0eb69350b94ad221 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 13 Apr 2023 15:15:39 -0700
Subject: [PATCH 074/393] Add table of server compatibility. Fixes #1308

---
 README.md            |  5 +++++
 docs/content/home.md | 18 ++++++++++++++++++
 2 files changed, 23 insertions(+)

diff --git a/README.md b/README.md
index 8ff8f6852..6141a72b8 100644
--- a/README.md
+++ b/README.md
@@ -23,6 +23,11 @@ This library outperforms MySQL Connector/NET (`MySql.Data`) on benchmarks:
 
 (Client: MySqlConnector 2.2.0, Ubuntu 20.04, .NET 7.0; Server: Azure Database for MySQL 8.0.28, TLS 1.2)
 
+### Server Compatibility
+
+This library is compatible with [many MySQL-compatible servers](https://mysqlconnector.net/#server-compatibility).
+MySql.Data [only supports MySQL Server](https://bugs.mysql.com/bug.php?id=109331).
+
 ### Bug Fixes
 
 This library [fixes dozens of outstanding bugs](https://mysqlconnector.net/tutorials/migrating-from-connector-net/#fixed-bugs) in Connector/NET.
diff --git a/docs/content/home.md b/docs/content/home.md
index 36a14f2a6..5d35606e7 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -54,6 +54,24 @@ while (await reader.ReadAsync())
     Console.WriteLine(reader.GetString(0));
 ```
 
+## Server Compatibility
+
+MySqlConnector is compatible with the following servers.
+Version numbers in **bold** indicate versions that are regularly tested by the [integration tests](https://dev.azure.com/mysqlnet/MySqlConnector/_build?definitionId=2&_a=summary) run on every commit.
+
+Server  | Versions | Notes
+--- | --- | ---
+Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://mysqlconnector.net/troubleshooting/aurora-freeze/)
+Azure Database for MySQL | 5.7, **8.0** | Single Server and Flexible Server
+Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
+MariaDB | 10.x, 11.x (**10.6**, **10.10**, **10.11**) |
+MySQL | 5.5, **5.6**, **5.7**, **8.0** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 is EOL
+Percona Server | 5.6, 5.7, 8.0 |
+PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
+ProxySQL | 2.x | Some [compatibility issues](https://github.com/search?q=repo%3Amysql-net%2FMySqlConnector+proxysql&type=issues)
+SingleStoreDB | |
+TiDB | |
+
 ## Performance
 
 MySqlConnector outperforms Connector/NET (MySql.Data) on benchmarks:

From 795069b05dcb8907dee74d60560b47c97a0dd9dc Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 14 Apr 2023 09:50:27 -0700
Subject: [PATCH 075/393] Refactor code to avoid duplicate ColumnTypes
 initialisation.

---
 src/MySqlConnector/Core/PreparedStatement.cs |  2 +-
 src/MySqlConnector/Core/ResultSet.cs         | 27 ++++++++------------
 2 files changed, 12 insertions(+), 17 deletions(-)

diff --git a/src/MySqlConnector/Core/PreparedStatement.cs b/src/MySqlConnector/Core/PreparedStatement.cs
index b85bb9b8c..ea9622d55 100644
--- a/src/MySqlConnector/Core/PreparedStatement.cs
+++ b/src/MySqlConnector/Core/PreparedStatement.cs
@@ -17,6 +17,6 @@ public PreparedStatement(int statementId, ParsedStatement statement, ColumnDefin
 
 	public int StatementId { get; }
 	public ParsedStatement Statement { get; }
-	public ColumnDefinitionPayload[]? Columns { get; internal set; }
+	public ColumnDefinitionPayload[]? Columns { get; set; }
 	public ColumnDefinitionPayload[]? Parameters { get; }
 }
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index a12e96d65..159898c47 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -120,14 +120,9 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 					if (!columnCountPacket.MetadataFollows)
 					{
 						// reuse previous metadata
-						var preparedStatement = DataReader.LastUsedPreparedStatement!;
-						ColumnDefinitions = preparedStatement.Columns!;
-						ColumnTypes = new MySqlDbType[columnCountPacket.ColumnCount];
-						for (var column = 0; column < columnCountPacket.ColumnCount; column++)
-						{
-							ColumnTypes[column] = TypeMapper.ConvertToMySqlDbType(ColumnDefinitions[column],
-								Connection.TreatTinyAsBoolean, Connection.GuidFormat);
-						}
+						ColumnDefinitions = DataReader.LastUsedPreparedStatement!.Columns!;
+						if (ColumnDefinitions.Length != columnCountPacket.ColumnCount)
+							throw new InvalidOperationException($"Expected result set to have {ColumnDefinitions.Length} columns, but it contains {columnCountPacket.ColumnCount} columns");
 					}
 					else
 					{
@@ -136,7 +131,6 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 						Utility.Resize(ref m_columnDefinitionPayloads, columnCountPacket.ColumnCount * 96);
 
 						ColumnDefinitions = new ColumnDefinitionPayload[columnCountPacket.ColumnCount];
-						ColumnTypes = new MySqlDbType[columnCountPacket.ColumnCount];
 						for (var column = 0; column < ColumnDefinitions.Length; column++)
 						{
 							payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
@@ -149,16 +143,12 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 
 							var columnDefinition = ColumnDefinitionPayload.Create(new ResizableArraySegment<byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, payloadLength));
 							ColumnDefinitions[column] = columnDefinition;
-							ColumnTypes[column] = TypeMapper.ConvertToMySqlDbType(columnDefinition, Connection.TreatTinyAsBoolean, Connection.GuidFormat);
 							m_columnDefinitionPayloadUsedBytes += payloadLength;
 						}
 
-						if (Session.SupportsCachedPreparedMetadata)
-						{
-							// server supports metadata caching, but has resent it, so something has changed since last prepare/execution
-							var preparedStatement = DataReader.LastUsedPreparedStatement;
-							if (preparedStatement != null) preparedStatement.Columns = ColumnDefinitions;
-						}
+						// server supports metadata caching, but has re-sent it, so something has changed since last prepare/execution
+						if (Session.SupportsCachedPreparedMetadata && DataReader.LastUsedPreparedStatement is { } preparedStatement)
+							preparedStatement.Columns = ColumnDefinitions;
 
 						if (!Session.SupportsDeprecateEof)
 						{
@@ -167,6 +157,11 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 						}
 					}
 
+					// determine MySqlDbType for each column
+					ColumnTypes = new MySqlDbType[columnCountPacket.ColumnCount];
+					for (var column = 0; column < ColumnTypes.Length; column++)
+						ColumnTypes[column] = TypeMapper.ConvertToMySqlDbType(ColumnDefinitions[column], Connection.TreatTinyAsBoolean, Connection.GuidFormat);
+
 					if (ColumnDefinitions.Length == (Command?.OutParameters?.Count + 1) && ColumnDefinitions[0].Name == SingleCommandPayloadCreator.OutParameterSentinelColumnName)
 						ContainsCommandParameters = true;
 					WarningCount = 0;

From 5f72a2f741629fc5c2fa7781621315a329eb941d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 14 Apr 2023 09:55:47 -0700
Subject: [PATCH 076/393] Remove ColumnTypes array and compute MySqlDbType
 lazily.

Most MySqlDataReader methods don't need to know the MySqlDbType of a column, so computing it eagerly upfront is unnecessary work and an unnecessary allocation. Instead, compute it just in time for the few API method calls that require it.
---
 src/MySqlConnector/Core/ResultSet.cs  | 15 +++++----------
 src/MySqlConnector/Core/Row.cs        |  2 +-
 src/MySqlConnector/MySqlDataReader.cs |  2 +-
 3 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 159898c47..d1a1c9ed8 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -21,7 +21,6 @@ public void Reset()
 		// ResultSet can be re-used, so initialize everything
 		BufferState = ResultSetState.None;
 		ColumnDefinitions = null;
-		ColumnTypes = null;
 		WarningCount = 0;
 		State = ResultSetState.None;
 		ContainsCommandParameters = false;
@@ -57,7 +56,6 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 					if (ok.NewSchema is not null)
 						Connection.Session.DatabaseOverride = ok.NewSchema;
 					ColumnDefinitions = null;
-					ColumnTypes = null;
 					State = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0
 						? ResultSetState.NoMoreData
 						: ResultSetState.HasMoreData;
@@ -157,11 +155,6 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 						}
 					}
 
-					// determine MySqlDbType for each column
-					ColumnTypes = new MySqlDbType[columnCountPacket.ColumnCount];
-					for (var column = 0; column < ColumnTypes.Length; column++)
-						ColumnTypes[column] = TypeMapper.ConvertToMySqlDbType(ColumnDefinitions[column], Connection.TreatTinyAsBoolean, Connection.GuidFormat);
-
 					if (ColumnDefinitions.Length == (Command?.OutParameters?.Count + 1) && ColumnDefinitions[0].Name == SingleCommandPayloadCreator.OutParameterSentinelColumnName)
 						ContainsCommandParameters = true;
 					WarningCount = 0;
@@ -312,7 +305,7 @@ public string GetDataTypeName(int ordinal)
 		if (ordinal < 0 || ordinal >= ColumnDefinitions.Length)
 			throw new IndexOutOfRangeException($"value must be between 0 and {ColumnDefinitions.Length - 1}");
 
-		var mySqlDbType = ColumnTypes![ordinal];
+		var mySqlDbType = GetColumnType(ordinal);
 		if (mySqlDbType == MySqlDbType.String)
 			return string.Format(CultureInfo.InvariantCulture, "CHAR({0})", ColumnDefinitions[ordinal].ColumnLength / ProtocolUtility.GetBytesPerCharacter(ColumnDefinitions[ordinal].CharacterSet));
 		return TypeMapper.Instance.GetColumnTypeMetadata(mySqlDbType).SimpleDataTypeName;
@@ -325,12 +318,15 @@ public Type GetFieldType(int ordinal)
 		if (ordinal < 0 || ordinal >= ColumnDefinitions.Length)
 			throw new IndexOutOfRangeException($"value must be between 0 and {ColumnDefinitions.Length - 1}");
 
-		var type = TypeMapper.Instance.GetColumnTypeMetadata(ColumnTypes![ordinal]).DbTypeMapping.ClrType;
+		var type = TypeMapper.Instance.GetColumnTypeMetadata(GetColumnType(ordinal)).DbTypeMapping.ClrType;
 		if (Connection.AllowZeroDateTime && type == typeof(DateTime))
 			type = typeof(MySqlDateTime);
 		return type;
 	}
 
+	public MySqlDbType GetColumnType(int ordinal) =>
+		TypeMapper.ConvertToMySqlDbType(ColumnDefinitions![ordinal], Connection.TreatTinyAsBoolean, Connection.GuidFormat);
+
 	public int FieldCount => ColumnDefinitions?.Length ?? 0;
 
 	public bool HasRows
@@ -374,7 +370,6 @@ public Row GetCurrentRow()
 
 	public ResultSetState BufferState { get; private set; }
 	public ColumnDefinitionPayload[]? ColumnDefinitions { get; private set; }
-	public MySqlDbType[]? ColumnTypes { get; private set; }
 	public int WarningCount { get; private set; }
 	public ResultSetState State { get; private set; }
 	public bool ContainsCommandParameters { get; private set; }
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index d4661f245..79a062a22 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -184,7 +184,7 @@ public int GetInt32(int ordinal)
 			and not ColumnType.Int24 and not ColumnType.Long and not ColumnType.Longlong
 			and not ColumnType.Bit and not ColumnType.Year)
 		{
-			throw new InvalidCastException($"Can't convert {ResultSet.ColumnTypes![ordinal]} to Int32");
+			throw new InvalidCastException($"Can't convert {ResultSet.GetColumnType(ordinal)} to Int32");
 		}
 
 		var data = m_data.Slice(m_dataOffsets[ordinal], m_dataLengths[ordinal]).Span;
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 4b9753b34..b4091b832 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -354,7 +354,7 @@ public ReadOnlyCollection<DbColumn> GetColumnSchema()
 		var hasNoSchema = columnDefinitions is null || m_resultSet.ContainsCommandParameters;
 		return hasNoSchema ? new List<DbColumn>().AsReadOnly() :
 			columnDefinitions!
-				.Select((c, n) => (DbColumn) new MySqlDbColumn(n, c, Connection!.AllowZeroDateTime, GetResultSet().ColumnTypes![n]))
+				.Select((c, n) => (DbColumn) new MySqlDbColumn(n, c, Connection!.AllowZeroDateTime, GetResultSet().GetColumnType(n)))
 				.ToList().AsReadOnly();
 	}
 

From 3e746d8af2fc7e17bee26dbbc7503f7b3570eb1f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 14 Apr 2023 10:44:56 -0700
Subject: [PATCH 077/393] Convert most payload classes to readonly structs.

These classes are very small (just one or two fields) and are typically scoped to a single method (i.e., stack frame) so do not need to live on the heap. Making them structs should avoid some unnecessary allocations.
---
 src/MySqlConnector/Core/ServerSession.cs                    | 6 +++---
 .../Payloads/AuthenticationMethodSwitchRequestPayload.cs    | 2 +-
 .../Protocol/Payloads/AuthenticationMoreDataPayload.cs      | 2 +-
 .../Protocol/Payloads/CachingSha2ServerResponsePayload.cs   | 2 +-
 src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs  | 2 +-
 src/MySqlConnector/Protocol/Payloads/EofPayload.cs          | 2 +-
 src/MySqlConnector/Protocol/Payloads/ErrorPayload.cs        | 2 +-
 src/MySqlConnector/Protocol/Payloads/LocalInfilePayload.cs  | 2 +-
 .../Protocol/Payloads/StatementPrepareResponsePayload.cs    | 2 +-
 9 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 44d274449..a86fe2ee2 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -736,7 +736,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 			if (!m_isSecureConnection && password.Length != 0)
 			{
 				var publicKey = await GetRsaPublicKeyAsync(switchRequest.Name, cs, ioBehavior, cancellationToken).ConfigureAwait(false);
-				return await SendEncryptedPasswordAsync(switchRequest, publicKey, password, ioBehavior, cancellationToken).ConfigureAwait(false);
+				return await SendEncryptedPasswordAsync(switchRequest.Data, publicKey, password, ioBehavior, cancellationToken).ConfigureAwait(false);
 			}
 			else
 			{
@@ -776,7 +776,7 @@ private async Task<PayloadData> SendClearPasswordAsync(string password, IOBehavi
 	}
 
 	private async Task<PayloadData> SendEncryptedPasswordAsync(
-		AuthenticationMethodSwitchRequestPayload switchRequest,
+		byte[] switchRequestData,
 		string rsaPublicKey,
 		string password,
 		IOBehavior ioBehavior,
@@ -814,7 +814,7 @@ private async Task<PayloadData> SendEncryptedPasswordAsync(
 		Array.Resize(ref passwordBytes, passwordBytes.Length + 1);
 
 		// XOR the password bytes with the challenge
-		AuthPluginData = Utility.TrimZeroByte(switchRequest.Data);
+		AuthPluginData = Utility.TrimZeroByte(switchRequestData);
 		for (var i = 0; i < passwordBytes.Length; i++)
 			passwordBytes[i] ^= AuthPluginData[i % AuthPluginData.Length];
 
diff --git a/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs b/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs
index dc2ec9233..c79aedd81 100644
--- a/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs
@@ -4,7 +4,7 @@
 
 namespace MySqlConnector.Protocol.Payloads;
 
-internal sealed class AuthenticationMethodSwitchRequestPayload
+internal readonly struct AuthenticationMethodSwitchRequestPayload
 {
 	public string Name { get; }
 	public byte[] Data { get; }
diff --git a/src/MySqlConnector/Protocol/Payloads/AuthenticationMoreDataPayload.cs b/src/MySqlConnector/Protocol/Payloads/AuthenticationMoreDataPayload.cs
index 0346ed443..c5e27f9a9 100644
--- a/src/MySqlConnector/Protocol/Payloads/AuthenticationMoreDataPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/AuthenticationMoreDataPayload.cs
@@ -2,7 +2,7 @@
 
 namespace MySqlConnector.Protocol.Payloads;
 
-internal sealed class AuthenticationMoreDataPayload
+internal readonly struct AuthenticationMoreDataPayload
 {
 	public byte[] Data { get; }
 
diff --git a/src/MySqlConnector/Protocol/Payloads/CachingSha2ServerResponsePayload.cs b/src/MySqlConnector/Protocol/Payloads/CachingSha2ServerResponsePayload.cs
index 24a27cdc1..698eaba75 100644
--- a/src/MySqlConnector/Protocol/Payloads/CachingSha2ServerResponsePayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/CachingSha2ServerResponsePayload.cs
@@ -2,7 +2,7 @@
 
 namespace MySqlConnector.Protocol.Payloads;
 
-internal sealed class CachingSha2ServerResponsePayload
+internal readonly struct CachingSha2ServerResponsePayload
 {
 	public const byte Signature = 0x01;
 
diff --git a/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs b/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
index ea0116bca..551093d59 100644
--- a/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
@@ -7,7 +7,7 @@ namespace MySqlConnector.Protocol.Payloads;
 ///     https://mariadb.com/kb/en/result-set-packets/#column-count-packet
 ///     Packet contains a columnCount, and - if capability MARIADB_CLIENT_CACHE_METADATA is set - a flag to indicate if metadata follows
 /// </summary>
-internal sealed class ColumnCountPayload
+internal readonly struct ColumnCountPayload
 {
 	private ColumnCountPayload(int columnCount, bool metadataFollows)
 	{
diff --git a/src/MySqlConnector/Protocol/Payloads/EofPayload.cs b/src/MySqlConnector/Protocol/Payloads/EofPayload.cs
index f58d57960..79fff182b 100644
--- a/src/MySqlConnector/Protocol/Payloads/EofPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/EofPayload.cs
@@ -3,7 +3,7 @@
 namespace MySqlConnector.Protocol.Payloads;
 
 // See https://dev.mysql.com/doc/internals/en/packet-EOF_Packet.html
-internal sealed class EofPayload
+internal readonly struct EofPayload
 {
 	public int WarningCount { get; }
 	public ServerStatus ServerStatus { get; }
diff --git a/src/MySqlConnector/Protocol/Payloads/ErrorPayload.cs b/src/MySqlConnector/Protocol/Payloads/ErrorPayload.cs
index 628e47d2d..d87523d55 100644
--- a/src/MySqlConnector/Protocol/Payloads/ErrorPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/ErrorPayload.cs
@@ -5,7 +5,7 @@
 namespace MySqlConnector.Protocol.Payloads;
 
 // See https://dev.mysql.com/doc/internals/en/packet-ERR_Packet.html
-internal sealed class ErrorPayload
+internal readonly struct ErrorPayload
 {
 	public int ErrorCode { get; }
 	public string State { get; }
diff --git a/src/MySqlConnector/Protocol/Payloads/LocalInfilePayload.cs b/src/MySqlConnector/Protocol/Payloads/LocalInfilePayload.cs
index e68068b4c..72badfcbd 100644
--- a/src/MySqlConnector/Protocol/Payloads/LocalInfilePayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/LocalInfilePayload.cs
@@ -5,7 +5,7 @@
 namespace MySqlConnector.Protocol.Payloads;
 
 // See https://dev.mysql.com/doc/internals/en/com-query-response.html#local-infile-request
-internal sealed class LocalInfilePayload
+internal readonly struct LocalInfilePayload
 {
 	public const byte Signature = 0xFB;
 
diff --git a/src/MySqlConnector/Protocol/Payloads/StatementPrepareResponsePayload.cs b/src/MySqlConnector/Protocol/Payloads/StatementPrepareResponsePayload.cs
index 197598453..da77783e9 100644
--- a/src/MySqlConnector/Protocol/Payloads/StatementPrepareResponsePayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/StatementPrepareResponsePayload.cs
@@ -2,7 +2,7 @@
 
 namespace MySqlConnector.Protocol.Payloads;
 
-internal sealed class StatementPrepareResponsePayload
+internal readonly struct StatementPrepareResponsePayload
 {
 	public int StatementId { get; }
 	public int ColumnCount { get; }

From 256fa1d879b652e11f2af9e2d9e8cb64765321de Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 14 Apr 2023 10:56:24 -0700
Subject: [PATCH 078/393] Release 2.3.0 Beta 2.

---
 docs/content/overview/version-history.md | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index fa4bac99c..6849b4693 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -11,6 +11,19 @@ weight: 30
 
 # Version History
 
+### 2.3.0 Beta 2
+
+* Support skipping metadata for prepared statements with MariaDB 10.6 and later: [#1301](https://github.com/mysql-net/MySqlConnector/pull/1301).
+* Support multiple authentication methods when connecting: [#1303](https://github.com/mysql-net/MySqlConnector/pull/1303).
+* Recycle `MySqlDataReader` objects: [#1277](https://github.com/mysql-net/MySqlConnector/issues/1277).
+* Optimize parameter encoding for ASCII strings: [#1296](https://github.com/mysql-net/MySqlConnector/pull/1296).
+* Use `TcpClient.ConnectAsync` overload with `CancellationToken` on .NET 5.0 and later: [#1291](https://github.com/mysql-net/MySqlConnector/pull/1291).
+* Fix cancellation when using a redirected connection: [#1305](https://github.com/mysql-net/MySqlConnector/pull/1305).
+* Fix `MySqlConnection.CloneWith` for connections created from a `MySqlDataSource`: [#1306](https://github.com/mysql-net/MySqlConnector/issues/1306).
+* Work around ephemeral PEM bug on Windows: [#1278](https://github.com/mysql-net/MySqlConnector/issues/1278).
+* Remove some unnecessary allocations.
+* Thanks to [Guillaume Boucher](https://github.com/gboucher90),  [Diego Dupin](https://github.com/rusher), and [Nickolay Batov](https://github.com/stilettk) for contributions to this release.
+
 ### 2.3.0 Beta 1
 
 * `MySqlDataSource` is now available for all TFMs, not just .NET 7.0: [#1269](https://github.com/mysql-net/MySqlConnector/pull/1269).

From 8df43a2897dac335e06c1c0cbff0442a65150331 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 15 Apr 2023 13:24:42 -0700
Subject: [PATCH 079/393] Improve description for connection options page.

---
 docs/content/connection-options.md       | 5 +++--
 docs/content/overview/version-history.md | 2 +-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/docs/content/connection-options.md b/docs/content/connection-options.md
index 71dcd15be..466cb732c 100644
--- a/docs/content/connection-options.md
+++ b/docs/content/connection-options.md
@@ -1,8 +1,9 @@
 ---
-lastmod: 2022-08-13
+lastmod: 2023-04-15
 date: 2016-10-16
 title: Connection Options
-customtitle: MySQL Connection String for C# .NET Core Programs
+customtitle: MySQL Connection String Options for .NET/C#
+description: A MySqlConnection is configured using a connection string. The connection string options are key/value pairs described on this page.
 weight: 30
 menu:
   main:
diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 6849b4693..ede6cbc6e 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-01-28
+lastmod: 2023-04-14
 date: 2017-03-27
 menu:
   main:

From 2bbbadf7d47515528f46f057c89b10db1e5b8865 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 15 Apr 2023 18:05:52 -0700
Subject: [PATCH 080/393] Use pattern matching.

---
 src/MySqlConnector/MySqlConnectionStringBuilder.cs | 14 ++++----------
 1 file changed, 4 insertions(+), 10 deletions(-)

diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index 241187008..40a901a96 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -811,21 +811,15 @@ public bool UseXaTransactions
 	/// </summary>
 	/// <param name="keyword">The option name.</param>
 	/// <returns><c>true</c> if an option with that name is set; otherwise, <c>false</c>.</returns>
-	public override bool ContainsKey(string keyword)
-	{
-		var option = MySqlConnectionStringOption.TryGetOptionForKey(keyword);
-		return option is object && base.ContainsKey(option.Key);
-	}
+	public override bool ContainsKey(string keyword) =>
+		MySqlConnectionStringOption.TryGetOptionForKey(keyword) is { } option && base.ContainsKey(option.Key);
 
 	/// <summary>
 	/// Removes the option with the specified name.
 	/// </summary>
 	/// <param name="keyword">The option name.</param>
-	public override bool Remove(string keyword)
-	{
-		var option = MySqlConnectionStringOption.TryGetOptionForKey(keyword);
-		return option is object && base.Remove(option.Key);
-	}
+	public override bool Remove(string keyword) =>
+		MySqlConnectionStringOption.TryGetOptionForKey(keyword) is { } option && base.Remove(option.Key);
 
 	/// <summary>
 	/// Retrieves an option value by name.

From 90901a5623955cbd67c0a70de833d623aa90c7f4 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 15 Apr 2023 18:09:15 -0700
Subject: [PATCH 081/393] Remove unused usings.

---
 .../Chaos.NaCl/Internal/Ed25519Ref10/FieldElement.cs          | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs          | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/base.cs                  | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/d.cs                     | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/d2.cs                    | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/fe_0.cs                  | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/fe_1.cs                  | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/fe_invert.cs             | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/fe_isnegative.cs         | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/ge_madd.cs               | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p2.cs         | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p3.cs         | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/ge_p2_dbl.cs             | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_0.cs               | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_dbl.cs             | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_to_p2.cs           | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_tobytes.cs         | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/ge_precomp_0.cs          | 2 --
 .../Chaos.NaCl/Internal/Ed25519Ref10/sc_clamp.cs              | 2 --
 src/MySqlConnector/Core/ConnectionPool.cs                     | 1 -
 src/MySqlConnector/Core/ConnectionSettings.cs                 | 1 -
 src/MySqlConnector/MySqlBulkLoader.cs                         | 1 -
 src/MySqlConnector/MySqlCommand.cs                            | 2 --
 src/MySqlConnector/MySqlConnection.cs                         | 1 -
 src/MySqlConnector/MySqlConnectionStringBuilder.cs            | 2 --
 src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs          | 2 --
 src/MySqlConnector/Protocol/Payloads/QueryPayload.cs          | 2 --
 src/MySqlConnector/Utilities/ActivitySourceHelper.cs          | 2 --
 src/MySqlConnector/Utilities/Utility.cs                       | 1 -
 tests/IntegrationTests/AppConfig.cs                           | 1 -
 tests/IntegrationTests/DataTypes.cs                           | 1 -
 tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs   | 1 -
 tests/MySqlConnector.Tests/MySqlAttributeTests.cs             | 1 -
 tests/MySqlConnector.Tests/SkipCITheoryAttribute.cs           | 4 ----
 34 files changed, 61 deletions(-)

diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/FieldElement.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/FieldElement.cs
index 50c9290e1..71d014b0d 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/FieldElement.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/FieldElement.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal struct FieldElement
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs
index 0553b10f9..9ae034b6e 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 /*
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/base.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/base.cs
index 4d828baeb..a2d173b89 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/base.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/base.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class LookupTables
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/d.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/d.cs
index 55d09cd2f..e5a50872a 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/d.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/d.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class LookupTables
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/d2.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/d2.cs
index 049062e50..d340f03b3 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/d2.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/d2.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class LookupTables
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_0.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_0.cs
index da5393b16..b31e91ee4 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_0.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_0.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class FieldOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_1.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_1.cs
index 4ff20137c..4a6c6701f 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_1.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_1.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class FieldOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_invert.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_invert.cs
index ae9176602..cf20b1a15 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_invert.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_invert.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class FieldOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_isnegative.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_isnegative.cs
index 9a2fd3944..cd47876d4 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_isnegative.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/fe_isnegative.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class FieldOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_madd.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_madd.cs
index 810669e74..e0b1c2118 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_madd.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_madd.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class GroupOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p2.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p2.cs
index ff09ec999..e4be46778 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p2.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p2.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class GroupOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p3.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p3.cs
index 0fa3380af..55b18d579 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p3.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p1p1_to_p3.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class GroupOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p2_dbl.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p2_dbl.cs
index 4570fc290..24cc1175f 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p2_dbl.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p2_dbl.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class GroupOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_0.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_0.cs
index 420603c40..1706e9e8c 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_0.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_0.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class GroupOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_dbl.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_dbl.cs
index 41d01b4c0..dd05ed907 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_dbl.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_dbl.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class GroupOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_to_p2.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_to_p2.cs
index 14054a0fe..7015e681f 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_to_p2.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_to_p2.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class GroupOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_tobytes.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_tobytes.cs
index ce810cbbd..bd93e9b9b 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_tobytes.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_p3_tobytes.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class GroupOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_precomp_0.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_precomp_0.cs
index 2d89e38f2..61a716f40 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_precomp_0.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/ge_precomp_0.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class GroupOperations
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sc_clamp.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sc_clamp.cs
index 5495024ab..e0f556023 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sc_clamp.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sc_clamp.cs
@@ -1,5 +1,3 @@
-using System;
-
 namespace Chaos.NaCl.Internal.Ed25519Ref10;
 
 internal static partial class ScalarOperations
diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 7d94a6f6d..82bb7dfb4 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -1,6 +1,5 @@
 using System.Collections.Concurrent;
 using System.Diagnostics;
-using System.Globalization;
 using System.Net;
 using System.Security.Authentication;
 using Microsoft.Extensions.Logging;
diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index f2f0aaf20..dada980d9 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -1,6 +1,5 @@
 using System.Net.Security;
 using System.Security.Authentication;
-using MySqlConnector.Logging;
 using MySqlConnector.Utilities;
 
 namespace MySqlConnector.Core;
diff --git a/src/MySqlConnector/MySqlBulkLoader.cs b/src/MySqlConnector/MySqlBulkLoader.cs
index b69d06fb6..45aff9ac4 100644
--- a/src/MySqlConnector/MySqlBulkLoader.cs
+++ b/src/MySqlConnector/MySqlBulkLoader.cs
@@ -2,7 +2,6 @@
 using System.Globalization;
 using System.Text;
 using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
 
 namespace MySqlConnector;
 
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index b98c96b44..9bdfa4b19 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -1,8 +1,6 @@
-using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
-using MySqlConnector.Logging;
 using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 254bff1a8..ea917583d 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -1,4 +1,3 @@
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index 40a901a96..df62505ad 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -2,9 +2,7 @@
 using System.ComponentModel;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
-using System.Linq;
 using System.Text.RegularExpressions;
-using MySqlConnector.Utilities;
 
 namespace MySqlConnector;
 
diff --git a/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs b/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs
index 66a6e503f..898b0b4f8 100644
--- a/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs
@@ -1,5 +1,3 @@
-using MySqlConnector.Utilities;
-
 namespace MySqlConnector.Protocol.Payloads;
 
 internal static class EmptyPayload
diff --git a/src/MySqlConnector/Protocol/Payloads/QueryPayload.cs b/src/MySqlConnector/Protocol/Payloads/QueryPayload.cs
index 71714d74f..8299536bf 100644
--- a/src/MySqlConnector/Protocol/Payloads/QueryPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/QueryPayload.cs
@@ -1,5 +1,3 @@
-using System.Text;
-
 namespace MySqlConnector.Protocol.Payloads;
 
 internal static class QueryPayload
diff --git a/src/MySqlConnector/Utilities/ActivitySourceHelper.cs b/src/MySqlConnector/Utilities/ActivitySourceHelper.cs
index 6cfe95bf6..55ba7a24d 100644
--- a/src/MySqlConnector/Utilities/ActivitySourceHelper.cs
+++ b/src/MySqlConnector/Utilities/ActivitySourceHelper.cs
@@ -1,8 +1,6 @@
-using System.Collections.Generic;
 using System.Diagnostics;
 using System.Globalization;
 using System.Reflection;
-using MySqlConnector.Core;
 
 namespace MySqlConnector.Utilities;
 
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 6da68bcc7..2f026ffc1 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -1,7 +1,6 @@
 using System.Buffers;
 using System.Buffers.Text;
 using System.Diagnostics.CodeAnalysis;
-using System.Globalization;
 using System.Net;
 using System.Reflection;
 using System.Runtime.InteropServices;
diff --git a/tests/IntegrationTests/AppConfig.cs b/tests/IntegrationTests/AppConfig.cs
index 995cbc497..5c0ea34e6 100644
--- a/tests/IntegrationTests/AppConfig.cs
+++ b/tests/IntegrationTests/AppConfig.cs
@@ -1,4 +1,3 @@
-using System.Reflection;
 using Microsoft.Extensions.Configuration;
 
 namespace IntegrationTests;
diff --git a/tests/IntegrationTests/DataTypes.cs b/tests/IntegrationTests/DataTypes.cs
index cee22e809..9569d1b7e 100644
--- a/tests/IntegrationTests/DataTypes.cs
+++ b/tests/IntegrationTests/DataTypes.cs
@@ -1,5 +1,4 @@
 using System.Globalization;
-using System.Reflection;
 #if MYSQL_DATA
 using MySql.Data.Types;
 #endif
diff --git a/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs b/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs
index e81091986..d1f1726db 100644
--- a/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs
@@ -1,7 +1,6 @@
 #if MYSQL_DATA
 using MySql.Data.MySqlClient;
 #endif
-using System;
 using Xunit;
 
 namespace MySqlConnector.Tests;
diff --git a/tests/MySqlConnector.Tests/MySqlAttributeTests.cs b/tests/MySqlConnector.Tests/MySqlAttributeTests.cs
index bb8f21c3a..cefbbbfd6 100644
--- a/tests/MySqlConnector.Tests/MySqlAttributeTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlAttributeTests.cs
@@ -1,7 +1,6 @@
 #if MYSQL_DATA
 using MySql.Data.MySqlClient;
 #endif
-using System;
 using Xunit;
 
 namespace MySqlConnector.Tests;
diff --git a/tests/MySqlConnector.Tests/SkipCITheoryAttribute.cs b/tests/MySqlConnector.Tests/SkipCITheoryAttribute.cs
index 13eeac6a7..f76639588 100644
--- a/tests/MySqlConnector.Tests/SkipCITheoryAttribute.cs
+++ b/tests/MySqlConnector.Tests/SkipCITheoryAttribute.cs
@@ -1,8 +1,4 @@
 using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
 using Xunit;
 
 namespace MySqlConnector.Tests;

From 56ef2238f532b8228e7199506556495bbd842be0 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 16 Apr 2023 07:43:00 -0700
Subject: [PATCH 082/393] Add link to schema documentation.

---
 src/MySqlConnector/MySqlConnection.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index ea917583d..c213e684f 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -589,14 +589,14 @@ private static async Task ClearPoolAsync(MySqlConnection connection, IOBehavior
 	/// <summary>
 	/// Returns schema information for the data source of this <see cref="MySqlConnection"/>.
 	/// </summary>
-	/// <param name="collectionName">The name of the schema to return.</param>
+	/// <param name="collectionName">The name of the schema to return. See <a href="https://mysqlconnector.net/overview/schema-collections/">Supported Schema Collections</a> for the list of supported schema names.</param>
 	/// <returns>A <see cref="DataTable"/> containing schema information.</returns>
 	public override DataTable GetSchema(string collectionName) => GetSchemaProvider().GetSchemaAsync(IOBehavior.Synchronous, collectionName, default, default).GetAwaiter().GetResult();
 
 	/// <summary>
 	/// Returns schema information for the data source of this <see cref="MySqlConnection"/>.
 	/// </summary>
-	/// <param name="collectionName">The name of the schema to return.</param>
+	/// <param name="collectionName">The name of the schema to return. See <a href="https://mysqlconnector.net/overview/schema-collections/">Supported Schema Collections</a> for the list of supported schema names.</param>
 	/// <param name="restrictionValues">The restrictions to apply to the schema.</param>
 	/// <returns>A <see cref="DataTable"/> containing schema information.</returns>
 	public override DataTable GetSchema(string collectionName, string?[] restrictionValues) => GetSchemaProvider().GetSchemaAsync(IOBehavior.Synchronous, collectionName, restrictionValues, default).GetAwaiter().GetResult();

From a293c2e9bb90574f27bb2e29cb68f8ba5ffced8f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 20 Mar 2023 07:42:51 +1300
Subject: [PATCH 083/393] Add .NET 8.0 target.

Replace .NET 7 tests with .NET 8.
---
 .ci/build-steps.yml                           | 21 ++++++-------
 .ci/conformance-test-steps.yml                |  6 ++--
 .ci/integration-tests-steps.yml               | 14 ++++-----
 .ci/mysqlconnector-tests-steps.yml            |  7 +++--
 appveyor.yml                                  |  1 +
 azure-pipelines.yml                           | 30 +++++++++++--------
 src/MySqlConnector/Core/ServerSession.cs      |  2 ++
 src/MySqlConnector/MySqlConnector.csproj      |  2 +-
 .../Conformance.Tests.csproj                  |  2 +-
 .../IntegrationTests/IntegrationTests.csproj  |  6 ++--
 .../MySqlConnector.Tests.csproj               |  2 +-
 .../SchemaCollectionGenerator.csproj          |  2 +-
 12 files changed, 50 insertions(+), 45 deletions(-)

diff --git a/.ci/build-steps.yml b/.ci/build-steps.yml
index 95cfc5553..a021ba623 100644
--- a/.ci/build-steps.yml
+++ b/.ci/build-steps.yml
@@ -6,9 +6,10 @@ steps:
     contents: 'config.json'
     targetFolder: 'tests/IntegrationTests'
 - task: UseDotNet@2
-  displayName: 'Install .NET Core'
+  displayName: 'Install .NET'
   inputs:
     version: $(DotNetCoreSdkVersion)
+    includePreviewVersions: true
 
 - task: DotNetCoreCLI@2
   displayName: 'Restore packages'
@@ -26,37 +27,37 @@ steps:
   displayName: 'Publish MySqlConnector.Tests'
   inputs:
     command: 'publish'
-    arguments: '-c Release -f net7.0 --no-build tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj'
+    arguments: '-c Release -f net8.0 --no-build tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
 - task: PublishPipelineArtifact@0
   inputs:
-    artifactName: 'MySqlConnector.Tests-7.0-$(Agent.OS)'
-    targetPath: 'tests/MySqlConnector.Tests/bin/Release/net7.0/publish'
+    artifactName: 'MySqlConnector.Tests-8.0-$(Agent.OS)'
+    targetPath: 'tests/MySqlConnector.Tests/bin/Release/net8.0/publish'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish Conformance.Tests'
   inputs:
     command: 'publish'
-    arguments: '-c Release -f net7.0 --no-build tests/Conformance.Tests/Conformance.Tests.csproj'
+    arguments: '-c Release -f net8.0 --no-build tests/Conformance.Tests/Conformance.Tests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
 - task: PublishPipelineArtifact@0
   inputs:
-    artifactName: 'Conformance.Tests-7.0-$(Agent.OS)'
-    targetPath: 'tests/Conformance.Tests/bin/Release/net7.0/publish'
+    artifactName: 'Conformance.Tests-8.0-$(Agent.OS)'
+    targetPath: 'tests/Conformance.Tests/bin/Release/net8.0/publish'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish IntegrationTests (7.0)'
   inputs:
     command: 'publish'
-    arguments: '-c Release -f net7.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
+    arguments: '-c Release -f net8.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
 - task: PublishPipelineArtifact@0
   inputs:
-    artifactName: 'IntegrationTests-net7.0-$(Agent.OS)'
-    targetPath: 'tests/IntegrationTests/bin/Release/net7.0/publish'
+    artifactName: 'IntegrationTests-net8.0-$(Agent.OS)'
+    targetPath: 'tests/IntegrationTests/bin/Release/net8.0/publish'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish IntegrationTests (6.0)'
diff --git a/.ci/conformance-test-steps.yml b/.ci/conformance-test-steps.yml
index 28d38cbc0..02d700623 100644
--- a/.ci/conformance-test-steps.yml
+++ b/.ci/conformance-test-steps.yml
@@ -10,14 +10,14 @@ steps:
 - task: DownloadPipelineArtifact@0
   condition: always()
   inputs:
-    artifactName: 'Conformance.Tests-7.0-$(Agent.OS)'
-    targetPath: '$(Build.BinariesDirectory)/7.0'
+    artifactName: 'Conformance.Tests-8.0-$(Agent.OS)'
+    targetPath: '$(Build.BinariesDirectory)/8.0'
 - task: DotNetCoreCLI@2
   displayName: 'Conformance Tests'
   inputs:
     command: 'custom'
     custom: 'vstest'
-    arguments: '$(Build.BinariesDirectory)/7.0/Conformance.Tests.dll /logger:trx'
+    arguments: '$(Build.BinariesDirectory)/8.0/Conformance.Tests.dll /logger:trx'
   env:
     CONNECTION_STRING: ${{ parameters.connectionString }}
 - task: PublishTestResults@2
diff --git a/.ci/integration-tests-steps.yml b/.ci/integration-tests-steps.yml
index 4e89b919c..d56e29c11 100644
--- a/.ci/integration-tests-steps.yml
+++ b/.ci/integration-tests-steps.yml
@@ -8,19 +8,15 @@ steps:
 - bash: ${{ format('.ci/docker-run.sh {0} 3300 {1}', parameters.image, parameters.unsupportedFeatures) }}
   displayName: 'Start Docker container'
 - task: UseDotNet@2
-  displayName: 'Install .NET Core 3.1'
-  inputs:
-    version: 3.1.x
-    packageType: runtime
-- task: UseDotNet@2
-  displayName: 'Install .NET Core 6.0'
+  displayName: 'Install .NET 6.0'
   inputs:
     version: 6.0.x
     packageType: runtime
 - task: UseDotNet@2
-  displayName: 'Install .NET Core'
+  displayName: 'Install .NET'
   inputs:
     version: $(DotNetCoreSdkVersion)
+    includePreviewVersions: true
 - template: 'conformance-test-steps.yml'
   parameters:
     image: ${{ parameters.image }}
@@ -30,7 +26,7 @@ steps:
     image: ${{ parameters.image }}
     unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
     connectionString: server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=required;DefaultCommandTimeout=3600;certificate file=$(Build.Repository.LocalPath)/.ci/server/certs/ssl-client.pfx;${{ parameters.connectionStringExtra }}
-    platform: 'net7.0'
+    platform: 'net8.0'
     description: 'SSL'
 - template: 'integration-test-steps.yml'
   parameters:
@@ -52,4 +48,4 @@ steps:
     unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
     connectionString: server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=required;DefaultCommandTimeout=3600;certificate file=$(Build.Repository.LocalPath)/.ci/server/certs/ssl-client.pfx;${{ parameters.connectionStringExtra }}
     platform: 'net6.0'
-    description: 'SSL'
\ No newline at end of file
+    description: 'SSL'
diff --git a/.ci/mysqlconnector-tests-steps.yml b/.ci/mysqlconnector-tests-steps.yml
index 6e25b9f90..ca4b1ce8f 100644
--- a/.ci/mysqlconnector-tests-steps.yml
+++ b/.ci/mysqlconnector-tests-steps.yml
@@ -1,11 +1,12 @@
 steps:
 - task: UseDotNet@2
-  displayName: 'Install .NET Core'
+  displayName: 'Install .NET'
   inputs:
     version: $(DotNetCoreSdkVersion)
+    includePreviewVersions: true
 - task: DownloadPipelineArtifact@0
   inputs:
-    artifactName: 'MySqlConnector.Tests-7.0-$(Agent.OS)'
+    artifactName: 'MySqlConnector.Tests-8.0-$(Agent.OS)'
     targetPath: $(System.DefaultWorkingDirectory)
 - task: DotNetCoreCLI@2
   displayName: 'Run MySqlConnector.Tests'
@@ -17,4 +18,4 @@ steps:
   inputs:
     testResultsFormat: VSTest
     testResultsFiles: '**/*.trx'
-    testRunTitle: 'MySqlConnector.Tests-7.0-$(Agent.OS)'
\ No newline at end of file
+    testRunTitle: 'MySqlConnector.Tests-8.0-$(Agent.OS)'
diff --git a/appveyor.yml b/appveyor.yml
index 001c7edfe..4055b0efd 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -11,6 +11,7 @@ install:
   - ps: Invoke-WebRequest -Uri "https://dot.net/v1/dotnet-install.ps1" -OutFile "install-dotnet.ps1"
   - ps: .\install-dotnet.ps1 -Channel 6.0 -InstallDir "dotnetcli"
   - ps: .\install-dotnet.ps1 -Channel 7.0 -InstallDir "dotnetcli"
+  - ps: .\install-dotnet.ps1 -Channel 8.0 -InstallDir "dotnetcli"
 build_script:
 - dotnet --info
 before_test:
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 68af9f544..2007b4da1 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -1,5 +1,5 @@
 variables:
-  DotNetCoreSdkVersion: '7.x'
+  DotNetCoreSdkVersion: '8.x'
   NUGET_PACKAGES: '$(Pipeline.Workspace)/.nuget/packages'
 
 jobs:
@@ -41,13 +41,14 @@ jobs:
       contents: 'config.json'
       targetFolder: 'tests/IntegrationTests'
   - task: UseDotNet@2
-    displayName: 'Install .NET Core 7.0'
+    displayName: 'Install .NET 7.0'
     inputs:
       version: '7.0.x'
   - task: UseDotNet@2
-    displayName: 'Install .NET Core'
+    displayName: 'Install .NET'
     inputs:
       version: $(DotNetCoreSdkVersion)
+      includePreviewVersions: true
   - task: DotNetCoreCLI@2
     displayName: 'MySql.Data unit tests'
     inputs:
@@ -113,14 +114,15 @@ jobs:
       targetType: inline
       script: "mysql -u$(Azure MySQL Root User) -p$(Azure MySQL Root Password) -h '$(Azure MySQL Host)' -e 'create schema mysqltest_$(Build.BuildId) collate utf8mb4_0900_ai_ci;'"
   - task: UseDotNet@2
-    displayName: 'Install .NET Core 7.0'
+    displayName: 'Install .NET 7.0'
     inputs:
       version: 7.0.x
       packageType: runtime
   - task: UseDotNet@2
-    displayName: 'Install .NET Core'
+    displayName: 'Install .NET'
     inputs:
       version: $(DotNetCoreSdkVersion)
+      includePreviewVersions: true
   - task: PowerShell@2
     displayName: 'Copy Azure config'
     inputs:
@@ -131,11 +133,11 @@ jobs:
     inputs:
       command: 'restore'
   - task: DotNetCoreCLI@2
-    displayName: 'Integration tests (net7.0)'
+    displayName: 'Integration tests (net8.0)'
     inputs:
       command: 'test'
       projects: 'tests/IntegrationTests/IntegrationTests.csproj'
-      arguments: '-c Release -f net7.0 --no-restore'
+      arguments: '-c Release -f net8.0 --no-restore'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'Azure', 'net6.0', 'SSL') }}
     env:
       DATA__UNSUPPORTEDFEATURES: 'CachingSha2Password,Ed25519,GlobalLog,KnownCertificateAuthority,QueryAttributes,RsaEncryption,Sha256Password,StreamingResults,Timeout,Tls11,Tls13,UnixDomainSocket,ZeroDateTime'
@@ -154,9 +156,10 @@ jobs:
   steps:
   - template: '.ci/install-mysql-windows.yml'
   - task: UseDotNet@2
-    displayName: 'Install .NET Core'
+    displayName: 'Install .NET'
     inputs:
       version: $(DotNetCoreSdkVersion)
+      includePreviewVersions: true
   - task: CopyFiles@2
     displayName: 'Copy config.json'
     inputs:
@@ -167,18 +170,18 @@ jobs:
     displayName: 'Remove target frameworks'
     inputs:
       targetType: 'inline'
-      script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''<TargetFrameworks>.*</TargetFrameworks>'', ''<TargetFrameworks>net472;net7.0</TargetFrameworks>'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
+      script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''<TargetFrameworks>.*</TargetFrameworks>'', ''<TargetFrameworks>net472;net8.0</TargetFrameworks>'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
   - task: DotNetCoreCLI@2
     displayName: 'Restore packages'
     inputs:
       command: 'restore'
   - task: DotNetCoreCLI@2
-    displayName: 'Integration tests (net472/net7.0)'
+    displayName: 'Integration tests (net472/net8.0)'
     inputs:
       command: 'test'
       projects: 'tests/IntegrationTests/IntegrationTests.csproj'
       arguments: '-c Release --no-restore'
-      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net472/net7.0', 'No SSL') }}
+      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net472/net8.0', 'No SSL') }}
     env:
       DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True'
@@ -190,14 +193,15 @@ jobs:
   steps:
   - template: '.ci/install-mysql-windows.yml'
   - task: UseDotNet@2
-    displayName: 'Install .NET Core 6.0'
+    displayName: 'Install .NET 6.0'
     inputs:
       version: 6.0.x
       packageType: runtime
   - task: UseDotNet@2
-    displayName: 'Install .NET Core'
+    displayName: 'Install .NET'
     inputs:
       version: $(DotNetCoreSdkVersion)
+      includePreviewVersions: true
   - script: copy .ci\config\config.json tests\IntegrationTests\config.json
     displayName: 'Copy config.json'
   - task: PowerShell@2
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index a86fe2ee2..99b58cadc 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1493,8 +1493,10 @@ caCertificateChain is not null &&
 		};
 
 #if NETCOREAPP3_0_OR_GREATER
+#pragma warning disable CA1416 // Validate platform compatibility
 		if (cs.TlsCipherSuites is { Count: > 0 })
 			clientAuthenticationOptions.CipherSuitesPolicy = new CipherSuitesPolicy(cs.TlsCipherSuites);
+#pragma warning restore CA1416 // Validate platform compatibility
 #endif
 
 		try
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index a0251e51e..095e18223 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;net471;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
+    <TargetFrameworks>net462;net471;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0</TargetFrameworks>
     <Description>A truly async MySQL ADO.NET provider, supporting MySQL Server, MariaDB, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL, and more.</Description>
     <Copyright>Copyright 2016–2023 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
diff --git a/tests/Conformance.Tests/Conformance.Tests.csproj b/tests/Conformance.Tests/Conformance.Tests.csproj
index bb3f7db21..459074505 100644
--- a/tests/Conformance.Tests/Conformance.Tests.csproj
+++ b/tests/Conformance.Tests/Conformance.Tests.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
  <PropertyGroup>
-    <TargetFrameworks>net7.0</TargetFrameworks>
+    <TargetFrameworks>net8.0</TargetFrameworks>
     <VersionPrefix>0.1.0</VersionPrefix>
     <SignAssembly>true</SignAssembly>
     <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index b43e9b4ed..4736082b6 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -1,12 +1,12 @@
-﻿<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup Condition=" '$(Configuration)' != 'MySqlData' ">
-    <TargetFrameworks>net462;net472;net6.0;net7.0</TargetFrameworks>
+    <TargetFrameworks>net462;net472;net6.0;net8.0</TargetFrameworks>
     <CheckEolTargetFramework>false</CheckEolTargetFramework>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <TargetFrameworks>net7.0</TargetFrameworks>
+    <TargetFrameworks>net8.0</TargetFrameworks>
     <DefineConstants>MYSQL_DATA</DefineConstants>
     <NoWarn>$(NoWarn);MSB3246</NoWarn>
   </PropertyGroup>
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 364eac506..adfacb0a3 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup Condition=" '$(Configuration)' != 'MySqlData' ">
-    <TargetFrameworks>net481;net7.0</TargetFrameworks>
+    <TargetFrameworks>net481;net8.0</TargetFrameworks>
   </PropertyGroup>
 
  <PropertyGroup Condition=" '$(Configuration)' == 'MySqlData' ">
diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
index 5334b4ac3..80b38af2d 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
+++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net7.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
   </PropertyGroup>

From 275b3e8c0890a0be0196eaa92c96c90fdfeb4854 Mon Sep 17 00:00:00 2001
From: rusher <diego.dupin@gmail.com>
Date: Thu, 20 Apr 2023 15:32:23 +0200
Subject: [PATCH 084/393] MariaDB version parsing evolution fix #1259

Since MariaDB server 11.0.1 (MDEV-28910) won't have "5.5.5-" prefix anymore.
This PR propose to evolve server identification accordingly based on 2 principles:
* version is stripped of "5.5.5-" prefix and identified as MariaDB (The only server that use 'x.y.z-' prefix is percona, but first percona 5.5 server was 5.5.7). MySQL use "x.y.z", alibaba and aurora use x.y.z.a notation)
* server version string contains "MariaDB"

(All mariadb connectors use/will use that exact same behavior for mariadb identification)

Signed-off-by: rusher <diego.dupin@gmail.com>
---
 src/MySqlConnector/Core/CachedProcedure.cs    |  6 +-
 src/MySqlConnector/Core/ServerSession.cs      |  4 +-
 src/MySqlConnector/Core/ServerVersion.cs      | 40 ++++----------
 .../ServerVersionTests.cs                     | 55 ++++++++-----------
 4 files changed, 41 insertions(+), 64 deletions(-)

diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index f070384cb..865499bd2 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -12,7 +12,9 @@ internal sealed class CachedProcedure
 	public static async Task<CachedProcedure?> FillAsync(IOBehavior ioBehavior, MySqlConnection connection, string schema, string component, ILogger logger, CancellationToken cancellationToken)
 	{
 		// try to use mysql.proc first, as it is much faster
-		if (connection.Session.ServerVersion.Version < ServerVersions.RemovesMySqlProcTable && !connection.Session.ProcAccessDenied)
+		if (!connection.Session.ServerVersion.MariaDb
+		    && connection.Session.ServerVersion.Version < ServerVersions.RemovesMySqlProcTable
+		    && !connection.Session.ProcAccessDenied)
 		{
 			try
 			{
@@ -127,7 +129,7 @@ internal MySqlParameterCollection AlignParamsWithDb(MySqlParameterCollection? pa
 			if (!alignParam.HasSetDbType)
 				alignParam.MySqlDbType = cachedParam.MySqlDbType;
 
-			// cached parameters are oredered by ordinal position
+			// cached parameters are ordered by ordinal position
 			alignedParams.Add(alignParam);
 		}
 
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index a86fe2ee2..f9877dccb 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -610,7 +610,9 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 			ClearPreparedStatements();
 
 			PayloadData payload;
-			if (DatabaseOverride is null && (ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0 || ServerVersion.MariaDbVersion?.CompareTo(ServerVersions.MariaDbSupportsResetConnection) >= 0))
+			if (DatabaseOverride is null
+			    && ((!ServerVersion.MariaDb && ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0)
+			    || (ServerVersion.MariaDb && ServerVersion.Version.CompareTo(ServerVersions.MariaDbSupportsResetConnection) >= 0)))
 			{
 				if (m_supportsPipelining)
 				{
diff --git a/src/MySqlConnector/Core/ServerVersion.cs b/src/MySqlConnector/Core/ServerVersion.cs
index d7a55f1ba..bedc09957 100644
--- a/src/MySqlConnector/Core/ServerVersion.cs
+++ b/src/MySqlConnector/Core/ServerVersion.cs
@@ -9,6 +9,16 @@ internal sealed class ServerVersion
 	public ServerVersion(ReadOnlySpan<byte> versionString)
 	{
 		OriginalString = Encoding.ASCII.GetString(versionString);
+		if (versionString.IndexOf("5.5.5-"u8) == 0)
+		{
+			// for MariaDB < 11.0.1
+			versionString = versionString[6..];
+			MariaDb = true;
+		}
+		else if (versionString.IndexOf("MariaDB"u8) != -1)
+		{
+			MariaDb = true;
+		}
 
 		var minor = 0;
 		var build = 0;
@@ -34,39 +44,11 @@ public ServerVersion(ReadOnlySpan<byte> versionString)
 		}
 
 		Version = new Version(major, minor, build);
-
-		// check for MariaDB version appended to a fake MySQL version
-		if (versionString is [ 0x2D, .. ])
-		{
-			versionString = versionString[1..];
-			ReadOnlySpan<byte> mariaDb = "-MariaDB"u8;
-			var mariaDbIndex = versionString.IndexOf(mariaDb);
-			if (mariaDbIndex != -1)
-			{
-				var totalBytesRead = 0;
-				if (Utf8Parser.TryParse(versionString, out major, out bytesConsumed) && versionString[bytesConsumed] == 0x2E)
-				{
-					versionString = versionString[(bytesConsumed + 1)..];
-					totalBytesRead += bytesConsumed + 1;
-					if (Utf8Parser.TryParse(versionString, out minor, out bytesConsumed) && versionString[bytesConsumed] == 0x2E)
-					{
-						versionString = versionString[(bytesConsumed + 1)..];
-						totalBytesRead += bytesConsumed + 1;
-						if (Utf8Parser.TryParse(versionString, out build, out bytesConsumed) && versionString[bytesConsumed] == 0x2D)
-						{
-							totalBytesRead += bytesConsumed;
-							if (totalBytesRead == mariaDbIndex)
-								MariaDbVersion = new(major, minor, build);
-						}
-					}
-				}
-			}
-		}
 	}
 
+	public bool MariaDb { get; }
 	public string OriginalString { get; }
 	public Version Version { get; }
-	public Version? MariaDbVersion { get; }
 
 	public static ServerVersion Empty { get; } = new();
 
diff --git a/tests/MySqlConnector.Tests/ServerVersionTests.cs b/tests/MySqlConnector.Tests/ServerVersionTests.cs
index dfcb62836..0cdff24c9 100644
--- a/tests/MySqlConnector.Tests/ServerVersionTests.cs
+++ b/tests/MySqlConnector.Tests/ServerVersionTests.cs
@@ -8,41 +8,32 @@ namespace MySqlConnector.Tests;
 public class ServerVersionTests
 {
 	[Theory]
-	[InlineData("5.5.5-10.1.38-MariaDB-1~bionic", "5.5.5", "10.1.38")]
-	[InlineData("5.5.5-10-MariaDB", "5.5.5", null)]
-	[InlineData("5.5.5-10.2-MariaDB", "5.5.5", null)]
-	[InlineData("5.5.5-10.2.13-MariaDB", "5.5.5", "10.2.13")]
-	[InlineData("5.5.5-10.2.19-MariaDB-1:10.2.19+maria~bionic", "5.5.5", "10.2.19")]
-	[InlineData("5.5.5-10.3.13-MariaDB-1:10.3.13+maria~bionic", "5.5.5", "10.3.13")]
-	[InlineData("5.7.21-log", "5.7.21", null)]
-	[InlineData("8.0.13", "8.0.13", null)]
-	[InlineData("5.7.25-28", "5.7.25", null)]
-	[InlineData("5.7.25-", "5.7.25", null)]
-	[InlineData("5.7.25-10.2.3", "5.7.25", null)]
-	[InlineData("5.7.25-MariaDB-10.2.3", "5.7.25", null)]
-	[InlineData("5.7.25-10.2.3-10.3.19-MariaDB-1", "5.7.25", null)]
-	[InlineData("a.b.c", "0.0.0", null)]
-	[InlineData("1", "1.0.0", null)]
-	[InlineData("1.", "1.0.0", null)]
-	[InlineData("1.2", "1.2.0", null)]
-	[InlineData("1.2.", "1.2.0", null)]
-	[InlineData("1.2.3", "1.2.3", null)]
-	[InlineData("1.2.3.", "1.2.3", null)]
-	[InlineData("1.2.3-", "1.2.3", null)]
-	public void ParseServerVersion(string input, string expectedString, string expectedMariaDbString)
+	[InlineData("5.5.5-10.1.38-MariaDB-1~bionic", "10.1.38", true)]
+	[InlineData("5.5.5-10.2.13-MariaDB", "10.2.13", true)]
+	[InlineData("5.5.5-10.2.19-MariaDB-1:10.2.19+maria~bionic", "10.2.19", true)]
+	[InlineData("5.5.5-10.3.13-MariaDB-1:10.3.13+maria~bionic", "10.3.13", true)]
+	[InlineData("11.0.1-MariaDB-1:11.0.1+maria~bionic", "11.0.1", true)]
+	[InlineData("10.3.13-MariaDB-1:10.3.13+maria~bionic", "10.3.13", true)]
+	[InlineData("5.7.21-log", "5.7.21", false)]
+	[InlineData("8.0.13", "8.0.13", false)]
+	[InlineData("5.7.25-28", "5.7.25", false)]
+	[InlineData("5.7.25-", "5.7.25", false)]
+	[InlineData("5.7.25-10.2.3", "5.7.25", false)]
+	[InlineData("5.7.25-MariaDB-10.2.3", "5.7.25", true)]
+	[InlineData("5.7.25-10.2.3-10.3.19-MariaDB-1", "5.7.25", true)]
+	[InlineData("a.b.c", "0.0.0", false)]
+	[InlineData("1", "1.0.0", false)]
+	[InlineData("1.", "1.0.0", false)]
+	[InlineData("1.2", "1.2.0", false)]
+	[InlineData("1.2.", "1.2.0", false)]
+	[InlineData("1.2.3", "1.2.3", false)]
+	[InlineData("1.2.3.", "1.2.3", false)]
+	[InlineData("1.2.3-", "1.2.3", false)]
+	public void ParseServerVersion(string input, string expectedString, bool expectedMariaDb)
 	{
 		var serverVersion = new ServerVersion(Encoding.UTF8.GetBytes(input));
 		var expected = Version.Parse(expectedString);
 		Assert.Equal(expected, serverVersion.Version);
-
-		if (expectedMariaDbString is null)
-		{
-			Assert.Equal(default(Version), serverVersion.MariaDbVersion);
-		}
-		else
-		{
-			var expectedMariaDb = Version.Parse(expectedMariaDbString);
-			Assert.Equal(expectedMariaDb, serverVersion.MariaDbVersion);
-		}
+		Assert.Equal(expectedMariaDb, serverVersion.MariaDb);
 	}
 }

From c76c08761dc66de1865383a68e65245cb72fa8f9 Mon Sep 17 00:00:00 2001
From: MaceWindu <MaceWindu@users.noreply.github.com>
Date: Mon, 24 Apr 2023 14:28:19 +0200
Subject: [PATCH 085/393] Update migrating-from-connector-net.md

Unexpected, but Oracle added async implementation. But as expected, not everything works properly - cancellation token is not respected.

Also following issues are marked as fixed, but I will leave it to you to decide as I didn't tested it:
- https://bugs.mysql.com/bug.php?id=92465
- https://bugs.mysql.com/bug.php?id=93370
- https://bugs.mysql.com/bug.php?id=97061
- https://bugs.mysql.com/bug.php?id=99091
- https://bugs.mysql.com/bug.php?id=106247
- https://bugs.mysql.com/bug.php?id=109683

And this one now is hidden:
- https://bugs.mysql.com/bug.php?id=97473
---
 docs/content/tutorials/migrating-from-connector-net.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 656eba301..cb11f8e1c 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -210,7 +210,7 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * [#50773](https://bugs.mysql.com/bug.php?id=50773): Can’t use multiple connections within one TransactionScope
 * [#61477](https://bugs.mysql.com/bug.php?id=61477): `ColumnOrdinal` in schema table is 1-based
 * ~~[#66476](https://bugs.mysql.com/bug.php?id=66476), [#106368](https://bugs.mysql.com/bug.php?id=106368): Connection pool uses queue instead of stack~~
-* [#70111](https://bugs.mysql.com/bug.php?id=70111): `Async` methods execute synchronously
+* ~[#70111](https://bugs.mysql.com/bug.php?id=70111): `Async` methods execute synchronously~
 * ~~[#70686](https://bugs.mysql.com/bug.php?id=70686): `TIME(3)` and `TIME(6)` fields serialize milliseconds incorrectly~~
 * [#72494](https://bugs.mysql.com/bug.php?id=72494), [#83330](https://bugs.mysql.com/bug.php?id=83330): EndOfStreamException inserting large blob with UseCompression=True
 * [#73610](https://bugs.mysql.com/bug.php?id=73610): Invalid password exception has wrong number
@@ -321,7 +321,7 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * [#109331](https://bugs.mysql.com/bug.php?id=109331): MySQL Connector/NET is incompatible with MariaDB 10.10 and later
 * [#109390](https://bugs.mysql.com/bug.php?id=109390): Transaction lock held after connection timeout exception
 * [#109476](https://bugs.mysql.com/bug.php?id=109476): `TransactionScope.Dispose` throws "Connection must be valid and open to rollback"
-* [#109670](https://bugs.mysql.com/bug.php?id=109670), [#109752](https://bugs.mysql.com/bug.php?id=109752): Many MSB3246 errors when referencing MySql.Data 8.0.32
+* ~[#109670](https://bugs.mysql.com/bug.php?id=109670), [#109752](https://bugs.mysql.com/bug.php?id=109752): Many MSB3246 errors when referencing MySql.Data 8.0.32~
 * [#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.
 * [#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.
 * [#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'

From 89e2d45b4c5f1432ee8a90656cb7172f9e4df1b9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 24 Apr 2023 12:11:13 -0700
Subject: [PATCH 086/393] Update to MySql.Data 8.0.33.

Some of the bugs now marked as fixed were fixed in earlier builds of MySql.Data, but the tests and documentation weren't kept up-to-date.
---
 README.md                                     | 12 ++++-----
 .../tutorials/migrating-from-connector-net.md | 27 ++++++++++---------
 tests/IntegrationTests/AppConfig.cs           |  4 +++
 tests/IntegrationTests/CommandTimeoutTests.cs |  6 ++---
 tests/IntegrationTests/ConnectAsync.cs        | 19 +++----------
 tests/IntegrationTests/ConnectionTests.cs     |  2 +-
 tests/IntegrationTests/InsertTests.cs         |  4 +--
 .../IntegrationTests/IntegrationTests.csproj  |  2 +-
 tests/IntegrationTests/ParameterCollection.cs |  2 +-
 .../IntegrationTests/PreparedCommandTests.cs  |  4 ---
 tests/IntegrationTests/app.config             |  2 +-
 .../MySqlConnector.Tests.csproj               |  2 +-
 12 files changed, 37 insertions(+), 49 deletions(-)

diff --git a/README.md b/README.md
index 6141a72b8..a79528558 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Async MySQL Connector for .NET and .NET Core
+# MySQL Connector for .NET and .NET Core
 
 This is an [ADO.NET](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/) data
 provider for [MySQL](https://www.mysql.com/). It provides implementations of
@@ -9,12 +9,6 @@ Complete documentation is available at the [MySqlConnector Documentation Website
 
 ## Why Use This Library?
 
-### Async Support
-
-This library implements true asynchronous I/O for database operations, without blocking
-(or using `Task.Run` to run synchronous methods on a background thread). This greatly
-improves the throughput of a web server that performs database operations.
-
 ### Performance
 
 This library outperforms MySQL Connector/NET (`MySql.Data`) on benchmarks:
@@ -32,6 +26,10 @@ MySql.Data [only supports MySQL Server](https://bugs.mysql.com/bug.php?id=109331
 
 This library [fixes dozens of outstanding bugs](https://mysqlconnector.net/tutorials/migrating-from-connector-net/#fixed-bugs) in Connector/NET.
 
+### Cutting Edge
+
+This library implements the latest ADO.NET APIs, from async (introduced in .NET Framework 4.5), through `DbBatch` (.NET 6.0) and `DbDataSource` (.NET 7.0).
+
 ### License
 
 This library is [MIT-licensed](LICENSE) and may be freely distributed with commercial software.
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index cb11f8e1c..0df2bdb6a 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-02-25
+lastmod: 2023-04-24
 date: 2016-10-16
 menu:
   main:
@@ -13,9 +13,8 @@ weight: 20
 
 Compared to MySql.Data (i.e., Oracle's MySQL Connector/NET), MySqlConnector has the following benefits:
 
-* Asynchronous I/O support
 * Higher performance (see [benchmarks](/#performance)) and lower memory usage
-* More reliable (see [bugs fixed](#fixed-bugs))
+* More reliable, particularly when using `Async` methods (see [bugs fixed](#fixed-bugs))
 * Leading edge: first MySQL driver to support .NET Core, `DbBatch`, `DateOnly`, `DbDataSource` and other new .NET features
 * Better compatibility with non-MySQL databases ([MariaDB](https://bugs.mysql.com/bug.php?id=109331), Amazon Aurora, etc.)
 * Better license (MIT, not [GPL 2.0](https://github.com/mysql/mysql-connector-net/blob/8.0/LICENSE) with [Universal FOSS Exception](https://oss.oracle.com/licenses/universal-foss-exception/) or [commercial license](https://www.mysql.com/about/legal/licensing/oem/))
@@ -42,7 +41,7 @@ In a .NET Framework application, make the following `app.config` change to regis
     <!-- add name="MySQL Data Provider"
       invariant="MySql.Data.MySqlClient"
       description=".Net Framework Data Provider for MySQL"
-      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.20.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.33.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
     <add name="MySqlConnector"
@@ -252,7 +251,7 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * [#91754](https://bugs.mysql.com/bug.php?id=91754): Inserting 16MiB `BLOB` shifts it by four bytes when prepared
 * ~~[#91770](https://bugs.mysql.com/bug.php?id=91770): `TIME(n)` column loses microseconds with prepared command~~
 * [#92367](https://bugs.mysql.com/bug.php?id=92367): `MySqlDataReader.GetDateTime` and `GetValue` return inconsistent values
-* [#92465](https://bugs.mysql.com/bug.php?id=92465): “There is already an open DataReader” `MySqlException` thrown from `TransactionScope.Dispose`
+* ~~[#92465](https://bugs.mysql.com/bug.php?id=92465): “There is already an open DataReader” `MySqlException` thrown from `TransactionScope.Dispose`~~
 * ~~[#92734](https://bugs.mysql.com/bug.php?id=92734): `MySqlParameter.Clone` doesn't copy all property values~~
 * ~~[#92789](https://bugs.mysql.com/bug.php?id=92789): Illegal connection attributes written for non-ASCII values~~
 * ~~[#92912](https://bugs.mysql.com/bug.php?id=92912): `MySqlDbType.LongText` values encoded incorrectly with prepared statements~~
@@ -260,7 +259,7 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * [#93047](https://bugs.mysql.com/bug.php?id=93047): `MySqlDataAdapter` throws timeout exception when an error occurs
 * ~~[#93202](https://bugs.mysql.com/bug.php?id=93202): Connector runs `SHOW VARIABLES` when connection is made~~
 * [#93220](https://bugs.mysql.com/bug.php?id=93220): Can’t call FUNCTION when parameter name contains parentheses
-* [#93370](https://bugs.mysql.com/bug.php?id=93370): `MySqlParameterCollection.Add` precondition check isn't consistent
+* ~~[#93370](https://bugs.mysql.com/bug.php?id=93370): `MySqlParameterCollection.Add` precondition check isn't consistent~~
 * ~~[#93374](https://bugs.mysql.com/bug.php?id=93374): `MySqlDataReader.GetStream` throws `IndexOutOfRangeException`~~
 * [#93825](https://bugs.mysql.com/bug.php?id=93825): `MySqlException` loses data when serialized
 * ~~[#94075](https://bugs.mysql.com/bug.php?id=94075): `MySqlCommand.Cancel` throws exception~~
@@ -275,15 +274,15 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * [#96500](https://bugs.mysql.com/bug.php?id=96500): `MySqlDataReader.GetFieldValue<MySqlGeometry>` throws `InvalidCastException`
 * [#96636](https://bugs.mysql.com/bug.php?id=96636): `MySqlConnection.Open()` slow under load when using SSL
 * [#96717](https://bugs.mysql.com/bug.php?id=96717): Not compatible with MySQL Server 5.0
-* [#97061](https://bugs.mysql.com/bug.php?id=97061): `MySqlCommand.LastInsertedId` returns 0 after executing multiple statements
+* ~~[#97061](https://bugs.mysql.com/bug.php?id=97061): `MySqlCommand.LastInsertedId` returns 0 after executing multiple statements~~
 * [#97067](https://bugs.mysql.com/bug.php?id=97067): Aggregate functions on BIT(n) columns return wrong result
 * ~~[#97300](https://bugs.mysql.com/bug.php?id=97300): `GetSchemaTable()` returns table for stored procedure with output parameters~~
 * ~~[#97448](https://bugs.mysql.com/bug.php?id=97448): Connecting fails if more than one IP is found in DNS for a named host~~
-* [#97473](https://bugs.mysql.com/bug.php?id=97473): `MySqlConnection.Clone` discloses connection password
+* ~~[#97473](https://bugs.mysql.com/bug.php?id=97473): `MySqlConnection.Clone` discloses connection password~~
 * [#97738](https://bugs.mysql.com/bug.php?id=97738): Cannot use PEM files when account uses `require subject`
 * [#97872](https://bugs.mysql.com/bug.php?id=97872): `KeepAlive` in connection string throws exception on .NET Core
 * ~~[#98322](https://bugs.mysql.com/bug.php?id=98322): `new MySqlConnection(null)` throws `NullReferenceException`~~
-* [#99091](https://bugs.mysql.com/bug.php?id=99091): Unexpected return value getting integer for `TINYINT(1)` column
+* ~~[#99091](https://bugs.mysql.com/bug.php?id=99091): Unexpected return value getting integer for `TINYINT(1)` column~~
 * ~~[#99793](https://bugs.mysql.com/bug.php?id=99793): Prepared stored procedure command doesn't verify parameter types~~
 * ~~[#100159](https://bugs.mysql.com/bug.php?id=100159): SQL with DateTime parameter returns String value~~
 * ~~[#100208](https://bugs.mysql.com/bug.php?id=100208): `GetSchema("Procedures")` returns `ROUTINE_DEFINITION` of `"System.Byte[]"`~~
@@ -313,7 +312,7 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * ~~[#106242](https://bugs.mysql.com/bug.php?id=106242): `MySqlConnection.Open` throws `AggregateException` instead of `MySqlException`~~
 * ~~[#106243](https://bugs.mysql.com/bug.php?id=106243): `CancellationToken` doesn't cancel `MySqlConnection.OpenAsync`~~
 * ~~[#106244](https://bugs.mysql.com/bug.php?id=106244): `MySqlDataReader.GetFieldValue<Stream>` throws `InvalidCastException`~~
-* [#106247](https://bugs.mysql.com/bug.php?id=106247): Can't use ~~`MySqlDbType.Enum` or~~ `MySqlDbType.Set` with prepared command
+* ~~[#106247](https://bugs.mysql.com/bug.php?id=106247): Can't use ~~`MySqlDbType.Enum` or~~ `MySqlDbType.Set` with prepared command~~
 * [#107259](https://bugs.mysql.com/bug.php?id=107259): Character Set 'utf8mb3' is not supported by .NET Framework
 * [#108756](https://bugs.mysql.com/bug.php?id=108756): Can't insert negative number using prepared statement with `MySqlDbType.Int24`
 * [#108970](https://bugs.mysql.com/bug.php?id=108970): `MySqlConnectionStringBuilder.ContainsKey` method gives wrong result
@@ -323,5 +322,9 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * [#109476](https://bugs.mysql.com/bug.php?id=109476): `TransactionScope.Dispose` throws "Connection must be valid and open to rollback"
 * ~[#109670](https://bugs.mysql.com/bug.php?id=109670), [#109752](https://bugs.mysql.com/bug.php?id=109752): Many MSB3246 errors when referencing MySql.Data 8.0.32~
 * [#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.
-* [#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.
-* [#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'
+* ~~[#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.~~
+* ~~[#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'~~
+* [110717](https://bugs.mysql.com/bug.php?id=110717): Opening two MySqlConnections simultaneously can crash
+* [110789](https://bugs.mysql.com/bug.php?id=110789): `OpenAsync` throws unhandled exception from thread pool
+* [110790](https://bugs.mysql.com/bug.php?id=110790): `ExecuteReaderAsync` hangs instead of cancelling query after `CommandTimeout`
+* [110791](https://bugs.mysql.com/bug.php?id=110791): `OpenAsync(CancellationToken)` doesn't throw for cancelled token
diff --git a/tests/IntegrationTests/AppConfig.cs b/tests/IntegrationTests/AppConfig.cs
index 5c0ea34e6..faabbcf0c 100644
--- a/tests/IntegrationTests/AppConfig.cs
+++ b/tests/IntegrationTests/AppConfig.cs
@@ -1,5 +1,9 @@
 using Microsoft.Extensions.Configuration;
 
+#if MYSQL_DATA
+[assembly: CollectionBehavior(DisableTestParallelization = true)]
+#endif
+
 namespace IntegrationTests;
 
 public static class AppConfig
diff --git a/tests/IntegrationTests/CommandTimeoutTests.cs b/tests/IntegrationTests/CommandTimeoutTests.cs
index 3806b3193..4c6c9eadc 100644
--- a/tests/IntegrationTests/CommandTimeoutTests.cs
+++ b/tests/IntegrationTests/CommandTimeoutTests.cs
@@ -72,7 +72,7 @@ public void CommandTimeoutWithSleepSync()
 		Assert.Equal(connectionState, m_connection.State);
 	}
 
-	[SkippableFact(ServerFeatures.CancelSleepSuccessfully | ServerFeatures.Timeout)]
+	[SkippableFact(ServerFeatures.CancelSleepSuccessfully | ServerFeatures.Timeout, MySqlData = "https://bugs.mysql.com/bug.php?id=110790")]
 	public async Task CommandTimeoutWithSleepAsync()
 	{
 		var connectionState = m_connection.State;
@@ -170,7 +170,7 @@ public void MultipleCommandTimeoutWithSleepSync()
 		Assert.Equal(connectionState, m_connection.State);
 	}
 
-	[SkippableFact(ServerFeatures.CancelSleepSuccessfully | ServerFeatures.Timeout)]
+	[SkippableFact(ServerFeatures.CancelSleepSuccessfully | ServerFeatures.Timeout, MySqlData = "https://bugs.mysql.com/bug.php?id=110790")]
 	public async Task MultipleCommandTimeoutWithSleepAsync()
 	{
 		var connectionState = m_connection.State;
@@ -273,7 +273,7 @@ public void TransactionCommandTimeoutWithSleepSync()
 		Assert.Equal(connectionState, m_connection.State);
 	}
 
-	[SkippableFact(ServerFeatures.CancelSleepSuccessfully)]
+	[SkippableFact(ServerFeatures.CancelSleepSuccessfully, MySqlData = "https://bugs.mysql.com/bug.php?id=110790")]
 	public async Task TransactionCommandTimeoutWithSleepAsync()
 	{
 		var connectionState = m_connection.State;
diff --git a/tests/IntegrationTests/ConnectAsync.cs b/tests/IntegrationTests/ConnectAsync.cs
index 45dcebbf3..1f339cc04 100644
--- a/tests/IntegrationTests/ConnectAsync.cs
+++ b/tests/IntegrationTests/ConnectAsync.cs
@@ -51,18 +51,14 @@ public async Task ConnectBadPassword()
 		Assert.Equal(ConnectionState.Closed, connection.State);
 	}
 
-	[Fact]
+	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=110791")]
 	public async Task ConnectCanceled()
 	{
 		using var cts = new CancellationTokenSource();
 		cts.Cancel();
 
 		using var connection = new MySqlConnection(AppConfig.ConnectionString);
-#if MYSQL_DATA
-		await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await connection.OpenAsync(cts.Token));
-#else
 		await Assert.ThrowsAsync<OperationCanceledException>(async () => await connection.OpenAsync(cts.Token));
-#endif
 		await connection.OpenAsync();
 		Assert.Equal(ConnectionState.Open, connection.State);
 	}
@@ -117,7 +113,7 @@ public async Task ConnectKeepAlive()
 		await Task.Delay(3000);
 	}
 
-	[SkippableFact(ServerFeatures.Timeout)]
+	[SkippableFact(ServerFeatures.Timeout, MySqlData = "https://bugs.mysql.com/bug.php?id=110789")]
 	public async Task ConnectTimeoutAsync()
 	{
 		var csb = new MySqlConnectionStringBuilder
@@ -135,7 +131,6 @@ public async Task ConnectTimeoutAsync()
 		TestUtilities.AssertDuration(stopwatch, 1900, 1500);
 	}
 
-
 	[SkippableFact(ServerFeatures.Timeout, MySqlData = "https://bugs.mysql.com/bug.php?id=94760")]
 	public async Task ConnectTimeoutAsyncCancellationToken()
 	{
@@ -536,7 +531,7 @@ public async Task UnixDomainSocket()
 		Assert.Equal(ConnectionState.Open, connection.State);
 	}
 
-	[Fact]
+	[SkippableFact(MySqlData = "Not raised")]
 	public async Task DisposeAsyncRaisesDisposed()
 	{
 		var disposedCount = 0;
@@ -549,11 +544,3 @@ public async Task DisposeAsyncRaisesDisposed()
 
 	readonly DatabaseFixture m_database;
 }
-
-#if MYSQL_DATA
-internal static class MySqlDataConnectionHelpers
-{
-	// MySql.Data capitalizes the 'B' in 'Database'
-	public static Task ChangeDatabaseAsync(this MySqlConnection connection, string databaseName) => connection.ChangeDataBaseAsync(databaseName);
-}
-#endif
diff --git a/tests/IntegrationTests/ConnectionTests.cs b/tests/IntegrationTests/ConnectionTests.cs
index 1b9336333..e086a9580 100644
--- a/tests/IntegrationTests/ConnectionTests.cs
+++ b/tests/IntegrationTests/ConnectionTests.cs
@@ -188,7 +188,7 @@ public void CloneIsClosed()
 		Assert.Equal(ConnectionState.Closed, connection2.State);
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=97473")]
+	[Fact]
 	public void CloneDoesNotDisclosePassword()
 	{
 		using var connection = new MySqlConnection(AppConfig.ConnectionString);
diff --git a/tests/IntegrationTests/InsertTests.cs b/tests/IntegrationTests/InsertTests.cs
index 8b75323c4..b47a1539b 100644
--- a/tests/IntegrationTests/InsertTests.cs
+++ b/tests/IntegrationTests/InsertTests.cs
@@ -114,7 +114,7 @@ public async Task LastInsertedIdBeforeSelect()
 		}
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=109683")]
+	[Fact]
 	public async Task LastInsertedIdTwoInserts()
 	{
 		await m_database.Connection.ExecuteAsync(@"drop table if exists insert_ai;
@@ -134,7 +134,7 @@ public async Task LastInsertedIdTwoInserts()
 		}
 	}
 
-	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=97061")]
+	[Fact]
 	public async Task LastInsertedIdLockTables()
 	{
 		await m_database.Connection.ExecuteAsync(@"drop table if exists insert_ai;
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index b43e9b4ed..6ec889108 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -55,7 +55,7 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <PackageReference Include="MySql.Data" Version="8.0.32" />
+    <PackageReference Include="MySql.Data" Version="8.0.33" />
     <Using Include="MySql.Data.MySqlClient" />
   </ItemGroup>
 
diff --git a/tests/IntegrationTests/ParameterCollection.cs b/tests/IntegrationTests/ParameterCollection.cs
index 9c5d36939..5e1b54c98 100644
--- a/tests/IntegrationTests/ParameterCollection.cs
+++ b/tests/IntegrationTests/ParameterCollection.cs
@@ -86,7 +86,7 @@ public void FindByNameIgnoringCase(string parameterName)
 	[InlineData("@test")]
 	[InlineData("@TEST")]
 	[InlineData("test")]
-#if !MYSQL_DATA // https://bugs.mysql.com/bug.php?id=93370
+#if !MYSQL_DATA // doesn't support quoted parameter names
 	[InlineData("@'test'")]
 	[InlineData("@`TEST`")]
 #endif
diff --git a/tests/IntegrationTests/PreparedCommandTests.cs b/tests/IntegrationTests/PreparedCommandTests.cs
index 7f25508f2..3a976cf56 100644
--- a/tests/IntegrationTests/PreparedCommandTests.cs
+++ b/tests/IntegrationTests/PreparedCommandTests.cs
@@ -397,10 +397,6 @@ public static IEnumerable<object[]> GetInsertAndQueryData()
 			yield return new object[] { isPrepared, "DATE", new DateTime(2018, 7, 23), MySqlDbType.Date };
 			yield return new object[] { isPrepared, "DATETIME(3)", new DateTime(2018, 7, 23, 20, 46, 52, 123), MySqlDbType.DateTime };
 			yield return new object[] { isPrepared, "ENUM('small', 'medium', 'large')", "medium", MySqlDbType.Enum };
-#if !MYSQL_DATA
-			// https://bugs.mysql.com/bug.php?id=106247
-			yield return new object[] { isPrepared, "SET('one','two','four','eight')", "two,eight", MySqlDbType.Set };
-#endif
 #if !MYSQL_DATA
 			yield return new object[] { isPrepared, "BOOL", true, MySqlDbType.Bool };
 #else
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index 21445d991..4e4e23d52 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -3,7 +3,7 @@
   <system.data>
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.32.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.33.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 364eac506..1f7be92fb 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -36,7 +36,7 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <PackageReference Include="MySql.Data" Version="8.0.32" />
+    <PackageReference Include="MySql.Data" Version="8.0.33" />
     <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
   </ItemGroup>
 

From 5f317de4ba3e6e93b32a325bb5cddf1e974137fe Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 13 May 2023 10:18:05 -0700
Subject: [PATCH 087/393] Move MySqlParameterTests to unit tests project.

These don't depend on the server being used and don't need to be run as part of the integration tests.
---
 .../MySqlParameterTests.cs}                           | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)
 rename tests/{IntegrationTests/ParameterTests.cs => MySqlConnector.Tests/MySqlParameterTests.cs} (98%)

diff --git a/tests/IntegrationTests/ParameterTests.cs b/tests/MySqlConnector.Tests/MySqlParameterTests.cs
similarity index 98%
rename from tests/IntegrationTests/ParameterTests.cs
rename to tests/MySqlConnector.Tests/MySqlParameterTests.cs
index 6c76dcf74..f2bc84ec4 100644
--- a/tests/IntegrationTests/ParameterTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterTests.cs
@@ -1,6 +1,13 @@
-namespace IntegrationTests;
+using System.Data;
+using System.Data.Common;
+#if MYSQL_DATA
+using MySql.Data.MySqlClient;
+#endif
+using Xunit;
+
+namespace MySqlConnector.Tests;
 
-public class ParameterTests
+public class MySqlParameterTests
 {
 	[Theory]
 	[InlineData(DbType.Byte, MySqlDbType.UByte)]

From b6fc78ebec6b54d1bf5de87e9d9dd26e031f33e9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 13 May 2023 10:48:41 -0700
Subject: [PATCH 088/393] Use global usings.

---
 tests/MySqlConnector.Tests/ByteBufferWriterTests.cs  |  1 -
 tests/MySqlConnector.Tests/CachedProcedureTests.cs   |  4 ----
 tests/MySqlConnector.Tests/CancellationTests.cs      |  7 -------
 tests/MySqlConnector.Tests/ConnectionTests.cs        |  7 -------
 tests/MySqlConnector.Tests/DbProviderFactoryTests.cs |  2 --
 tests/MySqlConnector.Tests/FakeMySqlServer.cs        |  4 ----
 .../FakeMySqlServerConnection.cs                     |  4 ----
 tests/MySqlConnector.Tests/LoadBalancerTests.cs      |  4 ----
 .../MySqlAttributeCollectionTests.cs                 |  5 -----
 tests/MySqlConnector.Tests/MySqlAttributeTests.cs    |  5 -----
 .../MySqlConnectionStringBuilderTests.cs             |  6 ------
 .../MySqlConnector.Tests/MySqlConnector.Tests.csproj | 12 ++++++++++++
 tests/MySqlConnector.Tests/MySqlDateTimeTests.cs     |  5 -----
 tests/MySqlConnector.Tests/MySqlDecimalTests.cs      |  2 --
 tests/MySqlConnector.Tests/MySqlExceptionTests.cs    |  5 -----
 tests/MySqlConnector.Tests/MySqlHelperTests.cs       |  5 -----
 .../MySqlParameterCollectionNameToIndexTests.cs      |  2 --
 .../MySqlParameterCollectionTests.cs                 |  6 ------
 tests/MySqlConnector.Tests/MySqlParameterTests.cs    |  7 -------
 tests/MySqlConnector.Tests/NormalizeTests.cs         |  3 ---
 tests/MySqlConnector.Tests/ServerVersionTests.cs     |  3 ---
 tests/MySqlConnector.Tests/SkipCITheoryAttribute.cs  |  3 ---
 tests/MySqlConnector.Tests/StatementPreparerTests.cs |  4 ----
 tests/MySqlConnector.Tests/TypeMapperTests.cs        |  6 ------
 tests/MySqlConnector.Tests/UtilityTests.cs           |  2 --
 25 files changed, 12 insertions(+), 102 deletions(-)

diff --git a/tests/MySqlConnector.Tests/ByteBufferWriterTests.cs b/tests/MySqlConnector.Tests/ByteBufferWriterTests.cs
index 0906f5623..01beb3aba 100644
--- a/tests/MySqlConnector.Tests/ByteBufferWriterTests.cs
+++ b/tests/MySqlConnector.Tests/ByteBufferWriterTests.cs
@@ -1,5 +1,4 @@
 using MySqlConnector.Protocol.Serialization;
-using Xunit;
 
 namespace MySqlConnector.Tests;
 
diff --git a/tests/MySqlConnector.Tests/CachedProcedureTests.cs b/tests/MySqlConnector.Tests/CachedProcedureTests.cs
index 1a590e7b9..c715b46c8 100644
--- a/tests/MySqlConnector.Tests/CachedProcedureTests.cs
+++ b/tests/MySqlConnector.Tests/CachedProcedureTests.cs
@@ -1,7 +1,3 @@
-using System.Collections.Generic;
-using MySqlConnector.Core;
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class CachedProcedureTests
diff --git a/tests/MySqlConnector.Tests/CancellationTests.cs b/tests/MySqlConnector.Tests/CancellationTests.cs
index de904b738..aa5bc8af2 100644
--- a/tests/MySqlConnector.Tests/CancellationTests.cs
+++ b/tests/MySqlConnector.Tests/CancellationTests.cs
@@ -1,11 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Diagnostics;
 using System.Net.Sockets;
-using System.Threading;
-using System.Threading.Tasks;
-using Xunit;
 
 namespace MySqlConnector.Tests;
 
diff --git a/tests/MySqlConnector.Tests/ConnectionTests.cs b/tests/MySqlConnector.Tests/ConnectionTests.cs
index 81c5b3ed5..6d3072832 100644
--- a/tests/MySqlConnector.Tests/ConnectionTests.cs
+++ b/tests/MySqlConnector.Tests/ConnectionTests.cs
@@ -1,10 +1,3 @@
-using System;
-using System.Data;
-using System.Diagnostics;
-using System.Threading;
-using System.Threading.Tasks;
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class ConnectionTests : IDisposable
diff --git a/tests/MySqlConnector.Tests/DbProviderFactoryTests.cs b/tests/MySqlConnector.Tests/DbProviderFactoryTests.cs
index 3b28ac22e..0b85f6a3e 100644
--- a/tests/MySqlConnector.Tests/DbProviderFactoryTests.cs
+++ b/tests/MySqlConnector.Tests/DbProviderFactoryTests.cs
@@ -1,8 +1,6 @@
 #if MYSQL_DATA
-using MySql.Data.MySqlClient;
 using MySqlConnectorFactory = MySql.Data.MySqlClient.MySqlClientFactory;
 #endif
-using Xunit;
 
 namespace MySqlConnector.Tests;
 
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServer.cs b/tests/MySqlConnector.Tests/FakeMySqlServer.cs
index 951f506ed..d40c0b457 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServer.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServer.cs
@@ -1,9 +1,5 @@
-using System;
-using System.Collections.Generic;
 using System.Net;
 using System.Net.Sockets;
-using System.Threading;
-using System.Threading.Tasks;
 
 namespace MySqlConnector.Tests;
 
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
index bd6677b94..a76063d36 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
@@ -1,11 +1,7 @@
-using System;
 using System.IO;
-using System.Linq;
 using System.Net.Sockets;
 using System.Text;
 using System.Text.RegularExpressions;
-using System.Threading;
-using System.Threading.Tasks;
 using MySqlConnector.Protocol;
 
 namespace MySqlConnector.Tests;
diff --git a/tests/MySqlConnector.Tests/LoadBalancerTests.cs b/tests/MySqlConnector.Tests/LoadBalancerTests.cs
index cbf18bd27..0dcfa86cb 100644
--- a/tests/MySqlConnector.Tests/LoadBalancerTests.cs
+++ b/tests/MySqlConnector.Tests/LoadBalancerTests.cs
@@ -1,7 +1,3 @@
-using System.Linq;
-using MySqlConnector.Core;
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class LoadBalancerTests
diff --git a/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs b/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs
index d1f1726db..b245dcbf0 100644
--- a/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs
@@ -1,8 +1,3 @@
-#if MYSQL_DATA
-using MySql.Data.MySqlClient;
-#endif
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class MySqlAttributeCollectionTests
diff --git a/tests/MySqlConnector.Tests/MySqlAttributeTests.cs b/tests/MySqlConnector.Tests/MySqlAttributeTests.cs
index cefbbbfd6..a218caf1e 100644
--- a/tests/MySqlConnector.Tests/MySqlAttributeTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlAttributeTests.cs
@@ -1,8 +1,3 @@
-#if MYSQL_DATA
-using MySql.Data.MySqlClient;
-#endif
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class MySqlAttributeTests
diff --git a/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs b/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
index 0f61413df..708e9e417 100644
--- a/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
@@ -1,11 +1,5 @@
-using System;
 using System.ComponentModel;
 using System.Globalization;
-using System.Linq;
-#if MYSQL_DATA
-using MySql.Data.MySqlClient;
-#endif
-using Xunit;
 
 namespace MySqlConnector.Tests;
 
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 1f7be92fb..70688abdb 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -29,15 +29,27 @@
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
+    <Using Include="System" />
+    <Using Include="System.Collections.Generic" />
+    <Using Include="System.Data" />
+    <Using Include="System.Data.Common" />
+    <Using Include="System.Diagnostics" />
+    <Using Include="System.Linq" />
+    <Using Include="System.Threading" />
+    <Using Include="System.Threading.Tasks" />
+    <Using Include="Xunit" />
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' != 'MySqlData' ">
     <ProjectReference Include="..\..\src\MySqlConnector\MySqlConnector.csproj" />
+    <Using Include="MySqlConnector.Core" />
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
     <PackageReference Include="MySql.Data" Version="8.0.33" />
     <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
+    <Using Include="MySql.Data.MySqlClient" />
+    <Using Include="MySql.Data.Types" />
   </ItemGroup>
 
 </Project>
diff --git a/tests/MySqlConnector.Tests/MySqlDateTimeTests.cs b/tests/MySqlConnector.Tests/MySqlDateTimeTests.cs
index 9638a5c45..837dd0592 100644
--- a/tests/MySqlConnector.Tests/MySqlDateTimeTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlDateTimeTests.cs
@@ -1,9 +1,4 @@
-using System;
 using System.Globalization;
-#if MYSQL_DATA
-using MySql.Data.Types;
-#endif
-using Xunit;
 
 namespace MySqlConnector.Tests;
 
diff --git a/tests/MySqlConnector.Tests/MySqlDecimalTests.cs b/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
index ed3070983..8a32827c2 100644
--- a/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
@@ -1,6 +1,4 @@
-using System;
 using System.Globalization;
-using Xunit;
 
 namespace MySqlConnector.Tests;
 
diff --git a/tests/MySqlConnector.Tests/MySqlExceptionTests.cs b/tests/MySqlConnector.Tests/MySqlExceptionTests.cs
index f7d7b8778..e5ea7e766 100644
--- a/tests/MySqlConnector.Tests/MySqlExceptionTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlExceptionTests.cs
@@ -1,8 +1,3 @@
-#if MYSQL_DATA
-using MySql.Data.MySqlClient;
-#endif
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class MySqlExceptionTests
diff --git a/tests/MySqlConnector.Tests/MySqlHelperTests.cs b/tests/MySqlConnector.Tests/MySqlHelperTests.cs
index 986815585..127d8273c 100644
--- a/tests/MySqlConnector.Tests/MySqlHelperTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlHelperTests.cs
@@ -1,8 +1,3 @@
-#if MYSQL_DATA
-using MySql.Data.MySqlClient;
-#endif
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class MySqlHelperTests
diff --git a/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs b/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs
index 5ab63400b..4a1127f61 100644
--- a/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs
@@ -1,5 +1,3 @@
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class MySqlParameterCollectionNameToIndexTests
diff --git a/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs b/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs
index 80934c44b..1f1ccf0f6 100644
--- a/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs
@@ -1,9 +1,3 @@
-#if MYSQL_DATA
-using MySql.Data.MySqlClient;
-#endif
-using System;
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class MySqlParameterCollectionTests
diff --git a/tests/MySqlConnector.Tests/MySqlParameterTests.cs b/tests/MySqlConnector.Tests/MySqlParameterTests.cs
index f2bc84ec4..f0a556f40 100644
--- a/tests/MySqlConnector.Tests/MySqlParameterTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterTests.cs
@@ -1,10 +1,3 @@
-using System.Data;
-using System.Data.Common;
-#if MYSQL_DATA
-using MySql.Data.MySqlClient;
-#endif
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class MySqlParameterTests
diff --git a/tests/MySqlConnector.Tests/NormalizeTests.cs b/tests/MySqlConnector.Tests/NormalizeTests.cs
index 9f7527312..f0c365479 100644
--- a/tests/MySqlConnector.Tests/NormalizeTests.cs
+++ b/tests/MySqlConnector.Tests/NormalizeTests.cs
@@ -1,6 +1,3 @@
-using MySqlConnector.Core;
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class NormalizeTests
diff --git a/tests/MySqlConnector.Tests/ServerVersionTests.cs b/tests/MySqlConnector.Tests/ServerVersionTests.cs
index faaf0d11a..cb41661e9 100644
--- a/tests/MySqlConnector.Tests/ServerVersionTests.cs
+++ b/tests/MySqlConnector.Tests/ServerVersionTests.cs
@@ -1,7 +1,4 @@
-using System;
 using System.Text;
-using MySqlConnector.Core;
-using Xunit;
 
 namespace MySqlConnector.Tests;
 
diff --git a/tests/MySqlConnector.Tests/SkipCITheoryAttribute.cs b/tests/MySqlConnector.Tests/SkipCITheoryAttribute.cs
index f76639588..07544b7de 100644
--- a/tests/MySqlConnector.Tests/SkipCITheoryAttribute.cs
+++ b/tests/MySqlConnector.Tests/SkipCITheoryAttribute.cs
@@ -1,6 +1,3 @@
-using System;
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class SkipCITheoryAttribute : TheoryAttribute
diff --git a/tests/MySqlConnector.Tests/StatementPreparerTests.cs b/tests/MySqlConnector.Tests/StatementPreparerTests.cs
index 8805e1504..4d5565ac7 100644
--- a/tests/MySqlConnector.Tests/StatementPreparerTests.cs
+++ b/tests/MySqlConnector.Tests/StatementPreparerTests.cs
@@ -1,12 +1,8 @@
-using System;
-using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Text;
-using MySqlConnector.Core;
 using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
-using Xunit;
 
 namespace MySqlConnector.Tests;
 
diff --git a/tests/MySqlConnector.Tests/TypeMapperTests.cs b/tests/MySqlConnector.Tests/TypeMapperTests.cs
index afb3d49dd..02713033e 100644
--- a/tests/MySqlConnector.Tests/TypeMapperTests.cs
+++ b/tests/MySqlConnector.Tests/TypeMapperTests.cs
@@ -1,9 +1,3 @@
-using System;
-using System.Data;
-using System.Linq;
-using MySqlConnector.Core;
-using Xunit;
-
 namespace MySqlConnector.Tests;
 
 public class TypeMapperTests
diff --git a/tests/MySqlConnector.Tests/UtilityTests.cs b/tests/MySqlConnector.Tests/UtilityTests.cs
index d31cade55..6df2da131 100644
--- a/tests/MySqlConnector.Tests/UtilityTests.cs
+++ b/tests/MySqlConnector.Tests/UtilityTests.cs
@@ -1,9 +1,7 @@
-using System;
 using System.Globalization;
 using System.Security.Cryptography;
 using System.Text;
 using MySqlConnector.Utilities;
-using Xunit;
 
 namespace MySqlConnector.Tests;
 

From 881e5706471f4349a045f3c2d3fd0ab046a6cad2 Mon Sep 17 00:00:00 2001
From: diego dupin <diego.dupin@gmail.com>
Date: Sun, 14 May 2023 01:48:15 +0000
Subject: [PATCH 089/393] Support per-query variables for CommandBehavior
 (#1312)

MariaDB and Percona server permits per-query variables
(see https://mariadb.com/kb/en/set-statement/ and https://www.percona.com/blog/using-per-query-variable-statements-in-percona-server/)

This corresponds to commands like: SET STATEMENT <variable=value> FOR <statement>

This uses per-query variables when using CommandBehavior.SchemaOnly/ CommandBehavior.SingleRow in place of multiple statements, which helps improve performance.

Signed-off-by: rusher <diego.dupin@gmail.com>
Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
Co-authored-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Core/ConcatenatedCommandPayloadCreator.cs |  8 ++--
 src/MySqlConnector/Core/ServerSession.cs      |  1 +
 src/MySqlConnector/Core/ServerVersions.cs     |  3 ++
 .../Core/SingleCommandPayloadCreator.cs       | 39 +++++++++++--------
 src/MySqlConnector/MySqlConnection.cs         |  1 +
 5 files changed, 32 insertions(+), 20 deletions(-)

diff --git a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
index 51617c2b0..6cf0443be 100644
--- a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
@@ -32,10 +32,12 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 			var command = commandListPosition.Commands[commandListPosition.CommandIndex];
 			Log.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 
-			isComplete = SingleCommandPayloadCreator.WriteQueryPayload(command, cachedProcedures, writer, commandListPosition.CommandIndex < commandListPosition.Commands.Count - 1 || appendSemicolon);
+			isComplete = SingleCommandPayloadCreator.WriteQueryPayload(command, cachedProcedures, writer,
+				commandListPosition.CommandIndex < commandListPosition.Commands.Count - 1 || appendSemicolon,
+				commandListPosition.CommandIndex == 0,
+				commandListPosition.CommandIndex == commandListPosition.Commands.Count - 1);
 			commandListPosition.CommandIndex++;
-		}
-		while (commandListPosition.CommandIndex < commandListPosition.Commands.Count && isComplete);
+		} while (commandListPosition.CommandIndex < commandListPosition.Commands.Count && isComplete);
 
 		return true;
 	}
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 822dd7235..6f2f81402 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -48,6 +48,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 
 	public string Id { get; }
 	public ServerVersion ServerVersion { get; set; }
+	public bool SupportsPerQueryVariables => ServerVersion.IsMariaDb && ServerVersion.Version >= ServerVersions.MariaDbSupportsPerQueryVariables;
 	public int ActiveCommandId { get; private set; }
 	public int CancellationTimeout { get; private set; }
 	public int ConnectionId { get; set; }
diff --git a/src/MySqlConnector/Core/ServerVersions.cs b/src/MySqlConnector/Core/ServerVersions.cs
index 6e732e00a..fc8e498f5 100644
--- a/src/MySqlConnector/Core/ServerVersions.cs
+++ b/src/MySqlConnector/Core/ServerVersions.cs
@@ -16,4 +16,7 @@ internal static class ServerVersions
 
 	// https://ocelot.ca/blog/blog/2017/08/22/no-more-mysql-proc-in-mysql-8-0/
 	public static readonly Version RemovesMySqlProcTable = new(8, 0, 0);
+
+	// https://mariadb.com/kb/en/set-statement/
+	public static readonly Version MariaDbSupportsPerQueryVariables = new(10, 1, 2);
 }
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index 27afe9889..9c51ca5a3 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -42,7 +42,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 				Log.QueryAttributesNotSupported(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 			}
 
-			WriteQueryPayload(command, cachedProcedures, writer, appendSemicolon);
+			WriteQueryPayload(command, cachedProcedures, writer, appendSemicolon, isFirstCommand: true, isLastCommand: true);
 			commandListPosition.LastUsedPreparedStatement = null;
 			commandListPosition.CommandIndex++;
 		}
@@ -70,9 +70,11 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 	/// <param name="cachedProcedures">The cached procedures.</param>
 	/// <param name="writer">The output writer.</param>
 	/// <param name="appendSemicolon">Whether a statement-separating semicolon should be appended if it's missing.</param>
+	/// <param name="isFirstCommand">Whether this command is the first one.</param>
+	/// <param name="isLastCommand">Whether this command is the last one.</param>
 	/// <returns><c>true</c> if a complete command was written; otherwise, <c>false</c>.</returns>
-	public static bool WriteQueryPayload(IMySqlCommand command, IDictionary<string, CachedProcedure?> cachedProcedures, ByteBufferWriter writer, bool appendSemicolon) =>
-		(command.CommandType == CommandType.StoredProcedure) ? WriteStoredProcedure(command, cachedProcedures, writer) : WriteCommand(command, writer, appendSemicolon);
+	public static bool WriteQueryPayload(IMySqlCommand command, IDictionary<string, CachedProcedure?> cachedProcedures, ByteBufferWriter writer, bool appendSemicolon, bool isFirstCommand, bool isLastCommand) =>
+		(command.CommandType == CommandType.StoredProcedure) ? WriteStoredProcedure(command, cachedProcedures, writer) : WriteCommand(command, writer, appendSemicolon, isFirstCommand, isLastCommand);
 
 	private static void WritePreparedStatement(IMySqlCommand command, PreparedStatement preparedStatement, ByteBufferWriter writer)
 	{
@@ -242,27 +244,30 @@ private static bool WriteStoredProcedure(IMySqlCommand command, IDictionary<stri
 		return preparer.ParseAndBindParameters(writer);
 	}
 
-	private static bool WriteCommand(IMySqlCommand command, ByteBufferWriter writer, bool appendSemicolon)
+	private static bool WriteCommand(IMySqlCommand command, ByteBufferWriter writer, bool appendSemicolon, bool isFirstCommand, bool isLastCommand)
 	{
 		var isSchemaOnly = (command.CommandBehavior & CommandBehavior.SchemaOnly) != 0;
 		var isSingleRow = (command.CommandBehavior & CommandBehavior.SingleRow) != 0;
-		if (isSchemaOnly)
+		if ((isSchemaOnly || isSingleRow) && isFirstCommand)
 		{
-			ReadOnlySpan<byte> setSqlSelectLimit0 = "SET sql_select_limit=0;\n"u8;
-			writer.Write(setSqlSelectLimit0);
-		}
-		else if (isSingleRow)
-		{
-			ReadOnlySpan<byte> setSqlSelectLimit1 = "SET sql_select_limit=1;\n"u8;
-			writer.Write(setSqlSelectLimit1);
+			writer.Write((command.Connection!.SupportsPerQueryVariables, isSingleRow) switch
+			{
+				// server doesn't support per-query variables; use multi-statements
+				(false, false) => "SET sql_select_limit=0;\n"u8,
+				(false, true) => "SET sql_select_limit=1;\n"u8,
+
+				// server supports per-query variables; use SET STATEMENT
+				(true, false) => "SET STATEMENT sql_select_limit=0 FOR "u8,
+				(true, true) => "SET STATEMENT sql_select_limit=1 FOR "u8,
+			});
 		}
+
 		var preparer = new StatementPreparer(command.CommandText!, command.RawParameters, command.CreateStatementPreparerOptions() | ((appendSemicolon || isSchemaOnly || isSingleRow) ? StatementPreparerOptions.AppendSemicolon : StatementPreparerOptions.None));
 		var isComplete = preparer.ParseAndBindParameters(writer);
-		if (isComplete && (isSchemaOnly || isSingleRow))
-		{
-			ReadOnlySpan<byte> clearSqlSelectLimit = "\nSET sql_select_limit=default;"u8;
-			writer.Write(clearSqlSelectLimit);
-		}
+
+		if ((isSchemaOnly || isSingleRow) && isLastCommand && isComplete && !command.Connection!.SupportsPerQueryVariables)
+			writer.Write("\nSET sql_select_limit=default;"u8);
+
 		return isComplete;
 	}
 }
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index c213e684f..0d59b2270 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -862,6 +862,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 
 	internal int? ActiveCommandId => m_session?.ActiveCommandId;
 
+	internal bool SupportsPerQueryVariables => m_session?.SupportsPerQueryVariables ?? false;
 	internal bool HasActiveReader => m_activeReader is not null;
 
 	internal void SetActiveReader(MySqlDataReader dataReader)

From a32a73bdc6f4276b32a75554c7dbec9a0f8d373c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 27 May 2023 07:14:53 -0700
Subject: [PATCH 090/393] Mark serialization APIs obsolete in .NET 8 Preview 4.

https://aka.ms/dotnet-warnings/SYSLIB0051
Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/MySqlConversionException.cs | 3 +++
 src/MySqlConnector/MySqlException.cs           | 6 ++++++
 src/MySqlConnector/MySqlProtocolException.cs   | 3 +++
 3 files changed, 12 insertions(+)

diff --git a/src/MySqlConnector/MySqlConversionException.cs b/src/MySqlConnector/MySqlConversionException.cs
index 130c3cc54..1e8a4ad67 100644
--- a/src/MySqlConnector/MySqlConversionException.cs
+++ b/src/MySqlConnector/MySqlConversionException.cs
@@ -17,6 +17,9 @@ internal MySqlConversionException(string message)
 	{
 	}
 
+#if NET8_0_OR_GREATER
+	[Obsolete(DiagnosticId = "SYSLIB0051")]
+#endif
 	private MySqlConversionException(SerializationInfo info, StreamingContext context)
 		: base(info, context)
 	{
diff --git a/src/MySqlConnector/MySqlException.cs b/src/MySqlConnector/MySqlException.cs
index 89c9aa5e1..0032f7a7e 100644
--- a/src/MySqlConnector/MySqlException.cs
+++ b/src/MySqlConnector/MySqlException.cs
@@ -39,6 +39,9 @@ public sealed class MySqlException : DbException
 	public bool IsTransient => IsErrorTransient(ErrorCode);
 #endif
 
+#if NET8_0_OR_GREATER
+	[Obsolete(DiagnosticId = "SYSLIB0051")]
+#endif
 	private MySqlException(SerializationInfo info, StreamingContext context)
 		: base(info, context)
 	{
@@ -52,6 +55,9 @@ private MySqlException(SerializationInfo info, StreamingContext context)
 	/// </summary>
 	/// <param name="info">The <see cref="SerializationInfo"/> that will be set.</param>
 	/// <param name="context">The context.</param>
+#if NET8_0_OR_GREATER
+	[Obsolete("Do not use legacy serialization infrastructure APIs", DiagnosticId = "SYSLIB0051")]
+#endif
 	public override void GetObjectData(SerializationInfo info, StreamingContext context)
 	{
 		base.GetObjectData(info, context);
diff --git a/src/MySqlConnector/MySqlProtocolException.cs b/src/MySqlConnector/MySqlProtocolException.cs
index dd51a6e76..f4f4ffbfd 100644
--- a/src/MySqlConnector/MySqlProtocolException.cs
+++ b/src/MySqlConnector/MySqlProtocolException.cs
@@ -17,6 +17,9 @@ public sealed class MySqlProtocolException : InvalidOperationException
 	internal static MySqlProtocolException CreateForPacketOutOfOrder(int expectedSequenceNumber, int packetSequenceNumber) =>
 		new MySqlProtocolException($"Packet received out-of-order. Expected {expectedSequenceNumber:d}; got {packetSequenceNumber:d}.");
 
+#if NET8_0_OR_GREATER
+	[Obsolete(DiagnosticId = "SYSLIB0051")]
+#endif
 	private MySqlProtocolException(SerializationInfo info, StreamingContext context)
 		: base(info, context)
 	{

From d93e4f8ed97626389788ba9005810dcae06ba4d6 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 27 May 2023 07:43:25 -0700
Subject: [PATCH 091/393] Use FrozenDictionary to store option keys.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../MySqlConnectionStringBuilder.cs           | 135 ++++++++++--------
 1 file changed, 75 insertions(+), 60 deletions(-)

diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index df62505ad..7f8f0184f 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -1,4 +1,7 @@
 using System.Collections;
+#if NET8_0_OR_GREATER
+using System.Collections.Frozen;
+#endif
 using System.ComponentModel;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
@@ -970,10 +973,10 @@ protected MySqlConnectionStringOption(IReadOnlyList<string> keys)
 		m_keys = keys;
 	}
 
-	private static void AddOption(MySqlConnectionStringOption option)
+	private static void AddOption(Dictionary<string, MySqlConnectionStringOption> options, MySqlConnectionStringOption option)
 	{
-		foreach (string key in option.m_keys)
-			s_options.Add(key, option);
+		foreach (var key in option.m_keys)
+			options.Add(key, option);
 		OptionNames.Add(option.m_keys[0]);
 	}
 
@@ -981,75 +984,76 @@ private static void AddOption(MySqlConnectionStringOption option)
 #pragma warning disable CA1810 // Initialize reference type static fields inline
 	static MySqlConnectionStringOption()
 	{
-		s_options = new(StringComparer.OrdinalIgnoreCase);
+		var options = new Dictionary<string, MySqlConnectionStringOption>(StringComparer.OrdinalIgnoreCase);
 
 		// Base Options
-		AddOption(Server = new(
+#pragma warning disable SA1118 // Parameter should not span multiple lines
+		AddOption(options, Server = new(
 			keys: new[] { "Server", "Host", "Data Source", "DataSource", "Address", "Addr", "Network Address" },
 			defaultValue: ""));
 
-		AddOption(Port = new(
+		AddOption(options, Port = new(
 			keys: new[] { "Port" },
 			defaultValue: 3306u));
 
-		AddOption(UserID = new(
+		AddOption(options, UserID = new(
 			keys: new[] { "User ID", "UserID", "Username", "Uid", "User name", "User" },
 			defaultValue: ""));
 
-		AddOption(Password = new(
+		AddOption(options, Password = new(
 			keys: new[] { "Password", "pwd" },
 			defaultValue: ""));
 
-		AddOption(Database = new(
+		AddOption(options, Database = new(
 			keys: new[] { "Database", "Initial Catalog" },
 			defaultValue: ""));
 
-		AddOption(LoadBalance = new(
+		AddOption(options, LoadBalance = new(
 			keys: new[] { "Load Balance", "LoadBalance" },
 			defaultValue: MySqlLoadBalance.RoundRobin));
 
-		AddOption(ConnectionProtocol = new(
+		AddOption(options, ConnectionProtocol = new(
 			keys: new[] { "Connection Protocol", "ConnectionProtocol", "Protocol" },
 			defaultValue: MySqlConnectionProtocol.Socket));
 
-		AddOption(PipeName = new(
+		AddOption(options, PipeName = new(
 			keys: new[] { "Pipe Name", "PipeName", "Pipe" },
 			defaultValue: "MYSQL"));
 
 		// SSL/TLS Options
-		AddOption(SslMode = new(
+		AddOption(options, SslMode = new(
 			keys: new[] { "SSL Mode", "SslMode" },
 			defaultValue: MySqlSslMode.Preferred));
 
-		AddOption(CertificateFile = new(
+		AddOption(options, CertificateFile = new(
 			keys: new[] { "Certificate File", "CertificateFile" },
 			defaultValue: ""));
 
-		AddOption(CertificatePassword = new(
+		AddOption(options, CertificatePassword = new(
 			keys: new[] { "Certificate Password", "CertificatePassword" },
 			defaultValue: ""));
 
-		AddOption(CertificateStoreLocation = new(
+		AddOption(options, CertificateStoreLocation = new(
 			keys: new[] { "Certificate Store Location", "CertificateStoreLocation" },
 			defaultValue: MySqlCertificateStoreLocation.None));
 
-		AddOption(CertificateThumbprint = new(
+		AddOption(options, CertificateThumbprint = new(
 			keys: new[] { "Certificate Thumbprint", "CertificateThumbprint", "Certificate Thumb Print" },
 			defaultValue: ""));
 
-		AddOption(SslCert = new(
+		AddOption(options, SslCert = new(
 			keys: new[] { "SSL Cert", "SslCert", "Ssl-Cert" },
 			defaultValue: ""));
 
-		AddOption(SslKey = new(
+		AddOption(options, SslKey = new(
 			keys: new[] { "SSL Key", "SslKey", "Ssl-Key" },
 			defaultValue: ""));
 
-		AddOption(SslCa = new(
+		AddOption(options, SslCa = new(
 			keys: new[] { "SSL CA", "CACertificateFile", "CA Certificate File", "SslCa", "Ssl-Ca" },
 			defaultValue: ""));
 
-		AddOption(TlsVersion = new(
+		AddOption(options, TlsVersion = new(
 			keys: new[] { "TLS Version", "TlsVersion", "Tls-Version" },
 			defaultValue: "",
 			coerce: value =>
@@ -1092,73 +1096,73 @@ static MySqlConnectionStringOption()
 				return coercedValue;
 			}));
 
-		AddOption(TlsCipherSuites = new(
+		AddOption(options, TlsCipherSuites = new(
 			keys: new[] { "TLS Cipher Suites", "TlsCipherSuites" },
 			defaultValue: ""));
 
 		// Connection Pooling Options
-		AddOption(Pooling = new(
+		AddOption(options, Pooling = new(
 			keys: new[] { "Pooling" },
 			defaultValue: true));
 
-		AddOption(ConnectionLifeTime = new(
+		AddOption(options, ConnectionLifeTime = new(
 			keys: new[] { "Connection Lifetime", "ConnectionLifeTime" },
 			defaultValue: 0u));
 
-		AddOption(ConnectionReset = new(
+		AddOption(options, ConnectionReset = new(
 			keys: new[] { "Connection Reset", "ConnectionReset" },
 			defaultValue: true));
 
-		AddOption(DeferConnectionReset = new(
+		AddOption(options, DeferConnectionReset = new(
 			keys: new[] { "Defer Connection Reset", "DeferConnectionReset" },
 			defaultValue: true));
 
-		AddOption(ConnectionIdlePingTime = new(
+		AddOption(options, ConnectionIdlePingTime = new(
 			keys: new[] { "Connection Idle Ping Time", "ConnectionIdlePingTime" },
 			defaultValue: 0u));
 
-		AddOption(ConnectionIdleTimeout = new(
+		AddOption(options, ConnectionIdleTimeout = new(
 			keys: new[] { "Connection Idle Timeout", "ConnectionIdleTimeout" },
 			defaultValue: 180u));
 
-		AddOption(MinimumPoolSize = new(
+		AddOption(options, MinimumPoolSize = new(
 			keys: new[] { "Minimum Pool Size", "Min Pool Size", "MinimumPoolSize", "minpoolsize" },
 			defaultValue: 0u));
 
-		AddOption(MaximumPoolSize = new(
+		AddOption(options, MaximumPoolSize = new(
 			keys: new[] { "Maximum Pool Size", "Max Pool Size", "MaximumPoolSize", "maxpoolsize" },
 			defaultValue: 100u));
 
-		AddOption(DnsCheckInterval = new(
+		AddOption(options, DnsCheckInterval = new(
 			keys: new[] { "DNS Check Interval", "DnsCheckInterval" },
 			defaultValue: 0u));
 
 		// Other Options
-		AddOption(AllowLoadLocalInfile = new(
+		AddOption(options, AllowLoadLocalInfile = new(
 			keys: new[] { "Allow Load Local Infile", "AllowLoadLocalInfile" },
 			defaultValue: false));
 
-		AddOption(AllowPublicKeyRetrieval = new(
+		AddOption(options, AllowPublicKeyRetrieval = new(
 			keys: new[] { "Allow Public Key Retrieval", "AllowPublicKeyRetrieval" },
 			defaultValue: false));
 
-		AddOption(AllowUserVariables = new(
+		AddOption(options, AllowUserVariables = new(
 			keys: new[] { "Allow User Variables", "AllowUserVariables" },
 			defaultValue: false));
 
-		AddOption(AllowZeroDateTime = new(
+		AddOption(options, AllowZeroDateTime = new(
 			keys: new[] { "Allow Zero DateTime", "AllowZeroDateTime" },
 			defaultValue: false));
 
-		AddOption(ApplicationName = new(
+		AddOption(options, ApplicationName = new(
 			keys: new[] { "Application Name", "ApplicationName" },
 			defaultValue: ""));
 
-		AddOption(AutoEnlist = new(
+		AddOption(options, AutoEnlist = new(
 			keys: new[] { "Auto Enlist", "AutoEnlist" },
 			defaultValue: true));
 
-		AddOption(CancellationTimeout = new(
+		AddOption(options, CancellationTimeout = new(
 			keys: new[] { "Cancellation Timeout", "CancellationTimeout" },
 			defaultValue: 2,
 			coerce: x =>
@@ -1168,93 +1172,100 @@ static MySqlConnectionStringOption()
 				return x;
 			}));
 
-		AddOption(CharacterSet = new(
+		AddOption(options, CharacterSet = new(
 			keys: new[] { "Character Set", "CharSet", "CharacterSet" },
 			defaultValue: ""));
 
-		AddOption(ConnectionTimeout = new(
+		AddOption(options, ConnectionTimeout = new(
 			keys: new[] { "Connection Timeout", "ConnectionTimeout", "Connect Timeout" },
 			defaultValue: 15u));
 
-		AddOption(ConvertZeroDateTime = new(
+		AddOption(options, ConvertZeroDateTime = new(
 			keys: new[] { "Convert Zero DateTime", "ConvertZeroDateTime" },
 			defaultValue: false));
 
-		AddOption(DateTimeKind = new(
+		AddOption(options, DateTimeKind = new(
 			keys: new[] { "DateTime Kind", "DateTimeKind" },
 			defaultValue: MySqlDateTimeKind.Unspecified));
 
-		AddOption(DefaultCommandTimeout = new(
+		AddOption(options, DefaultCommandTimeout = new(
 			keys: new[] { "Default Command Timeout", "DefaultCommandTimeout", "Command Timeout" },
 			defaultValue: 30u));
 
-		AddOption(ForceSynchronous = new(
+		AddOption(options, ForceSynchronous = new(
 			keys: new[] { "Force Synchronous", "ForceSynchronous" },
 			defaultValue: false));
 
-		AddOption(GuidFormat = new(
+		AddOption(options, GuidFormat = new(
 			keys: new[] { "GUID Format", "GuidFormat" },
 			defaultValue: MySqlGuidFormat.Default));
 
-		AddOption(IgnoreCommandTransaction = new(
+		AddOption(options, IgnoreCommandTransaction = new(
 			keys: new[] { "Ignore Command Transaction", "IgnoreCommandTransaction" },
 			defaultValue: false));
 
-		AddOption(IgnorePrepare = new(
+		AddOption(options, IgnorePrepare = new(
 			keys: new[] { "Ignore Prepare", "IgnorePrepare" },
 			defaultValue: false));
 
-		AddOption(InteractiveSession = new(
+		AddOption(options, InteractiveSession = new(
 			keys: new[] { "Interactive Session", "InteractiveSession", "Interactive" },
 			defaultValue: false));
 
-		AddOption(Keepalive = new(
+		AddOption(options, Keepalive = new(
 			keys: new[] { "Keep Alive", "Keepalive" },
 			defaultValue: 0u));
 
-		AddOption(NoBackslashEscapes = new(
+		AddOption(options, NoBackslashEscapes = new(
 			keys: new[] { "No Backslash Escapes", "NoBackslashEscapes" },
 			defaultValue: false));
 
-		AddOption(OldGuids = new(
+		AddOption(options, OldGuids = new(
 			keys: new[] { "Old Guids", "OldGuids" },
 			defaultValue: false));
 
-		AddOption(PersistSecurityInfo = new(
+		AddOption(options, PersistSecurityInfo = new(
 			keys: new[] { "Persist Security Info", "PersistSecurityInfo" },
 			defaultValue: false));
 
-		AddOption(Pipelining = new(
+		AddOption(options, Pipelining = new(
 			keys: new[] { "Pipelining" },
 			defaultValue: true));
 
-		AddOption(ServerRedirectionMode = new(
+		AddOption(options, ServerRedirectionMode = new(
 			keys: new[] { "Server Redirection Mode", "ServerRedirectionMode" },
 			defaultValue: MySqlServerRedirectionMode.Disabled));
 
-		AddOption(ServerRsaPublicKeyFile = new(
+		AddOption(options, ServerRsaPublicKeyFile = new(
 			keys: new[] { "Server RSA Public Key File", "ServerRsaPublicKeyFile" },
 			defaultValue: ""));
 
-		AddOption(ServerSPN = new(
+		AddOption(options, ServerSPN = new(
 			keys: new[] { "Server SPN", "ServerSPN" },
 			defaultValue: ""));
 
-		AddOption(TreatTinyAsBoolean = new(
+		AddOption(options, TreatTinyAsBoolean = new(
 			keys: new[] { "Treat Tiny As Boolean", "TreatTinyAsBoolean" },
 			defaultValue: true));
 
-		AddOption(UseAffectedRows = new(
+		AddOption(options, UseAffectedRows = new(
 			keys: new[] { "Use Affected Rows", "UseAffectedRows" },
 			defaultValue: false));
 
-		AddOption(UseCompression = new(
+		AddOption(options, UseCompression = new(
 			keys: new[] { "Use Compression", "Compress", "UseCompression" },
 			defaultValue: false));
 
-		AddOption(UseXaTransactions = new(
+		AddOption(options, UseXaTransactions = new(
 			keys: new[] { "Use XA Transactions", "UseXaTransactions" },
 			defaultValue: true));
+#pragma warning restore SA1118 // Parameter should not span multiple lines
+
+#if NET8_0_OR_GREATER
+		s_options = options.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase, optimizeForReading: true);
+#else
+		s_options = options;
+#endif
 	}
 
 	private const string c_tlsVersionsRegexPattern = @"\s*TLS( ?v?(1|1\.?0|1\.?1|1\.?2|1\.?3))?$";
@@ -1265,7 +1276,11 @@ static MySqlConnectionStringOption()
 	private static Regex TlsVersionsRegex() => s_tlsVersionsRegex;
 	private static readonly Regex s_tlsVersionsRegex = new(c_tlsVersionsRegexPattern, RegexOptions.IgnoreCase);
 #endif
+#if NET8_0_OR_GREATER
+	private static readonly FrozenDictionary<string, MySqlConnectionStringOption> s_options;
+#else
 	private static readonly Dictionary<string, MySqlConnectionStringOption> s_options;
+#endif
 
 	private readonly IReadOnlyList<string> m_keys;
 }

From 584c6b1157551f7663e9498c531fd8d3f4a9188a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 28 May 2023 17:50:18 -0700
Subject: [PATCH 092/393] Remove Azure MySQL integration tests.

The Azure Credits for Open Source sponsorship is expiring on May 31st.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 README.md            |  3 ---
 azure-pipelines.yml  | 46 --------------------------------------------
 docs/content/home.md |  2 +-
 3 files changed, 1 insertion(+), 50 deletions(-)

diff --git a/README.md b/README.md
index a79528558..aecd0c19e 100644
--- a/README.md
+++ b/README.md
@@ -104,6 +104,3 @@ Development of MySqlConnector is supported by:
 
 [Faithlife](https://faithlife.com/about) ([View jobs](https://faithlife.com/careers))
 
-[![Microsoft Azure](https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Microsoft_Azure.svg/75px-Microsoft_Azure.svg.png)](https://azure.microsoft.com/en-us/overview/open-source/)
-
-[Azure Credits for Open Source](https://opensource.microsoft.com/azure-credits)
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 68af9f544..a98e16645 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -101,52 +101,6 @@ jobs:
       image: 'mysql:8.0'
       connectionString: 'server=localhost;port=3306;user id=mysqltest;password=test;database=conformance;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
 
-- job: azure_mysql_integration_tests
-  condition: ne('True', variables['System.PullRequest.IsFork'])
-  displayName: 'Azure MySQL Integration Tests'
-  pool:
-    vmimage: 'windows-latest'
-  steps:
-  - task: PowerShell@2
-    displayName: Create Database
-    inputs:
-      targetType: inline
-      script: "mysql -u$(Azure MySQL Root User) -p$(Azure MySQL Root Password) -h '$(Azure MySQL Host)' -e 'create schema mysqltest_$(Build.BuildId) collate utf8mb4_0900_ai_ci;'"
-  - task: UseDotNet@2
-    displayName: 'Install .NET Core 7.0'
-    inputs:
-      version: 7.0.x
-      packageType: runtime
-  - task: UseDotNet@2
-    displayName: 'Install .NET Core'
-    inputs:
-      version: $(DotNetCoreSdkVersion)
-  - task: PowerShell@2
-    displayName: 'Copy Azure config'
-    inputs:
-      targetType: inline
-      script: Copy-Item -Path ".\.ci\config\config.ssl.json" -Destination ".\tests\IntegrationTests\config.json"
-  - task: DotNetCoreCLI@2
-    displayName: 'Restore packages'
-    inputs:
-      command: 'restore'
-  - task: DotNetCoreCLI@2
-    displayName: 'Integration tests (net7.0)'
-    inputs:
-      command: 'test'
-      projects: 'tests/IntegrationTests/IntegrationTests.csproj'
-      arguments: '-c Release -f net7.0 --no-restore'
-      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'Azure', 'net6.0', 'SSL') }}
-    env:
-      DATA__UNSUPPORTEDFEATURES: 'CachingSha2Password,Ed25519,GlobalLog,KnownCertificateAuthority,QueryAttributes,RsaEncryption,Sha256Password,StreamingResults,Timeout,Tls11,Tls13,UnixDomainSocket,ZeroDateTime'
-      DATA__CONNECTIONSTRING: "$(AzureConnectionString);database=mysqltest_$(Build.BuildId);ssl mode=Required;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True"
-  - task: PowerShell@2
-    displayName: Drop Database
-    condition: always()
-    inputs:
-      targetType: inline
-      script: "mysql -u$(Azure MySQL Root User) -p$(Azure MySQL Root Password) -h '$(Azure MySQL Host)' -e 'drop schema if exists mysqltest_$(Build.BuildId);'"
-
 - job: windows_integration_tests_1
   displayName: 'Windows Integration Tests (Part 1)'
   pool:
diff --git a/docs/content/home.md b/docs/content/home.md
index 5d35606e7..50481a17a 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -62,7 +62,7 @@ Version numbers in **bold** indicate versions that are regularly tested by the [
 Server  | Versions | Notes
 --- | --- | ---
 Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://mysqlconnector.net/troubleshooting/aurora-freeze/)
-Azure Database for MySQL | 5.7, **8.0** | Single Server and Flexible Server
+Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
 MariaDB | 10.x, 11.x (**10.6**, **10.10**, **10.11**) |
 MySQL | 5.5, **5.6**, **5.7**, **8.0** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 is EOL

From c1604acbee08238a0016ab9364f308ded729b2bd Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 1 Jun 2023 22:13:34 -0700
Subject: [PATCH 093/393] Use ArtifactsPath to store build output.

---
 .gitignore                |  1 +
 appveyor.yml              | 10 ++--------
 src/Directory.Build.props |  1 +
 3 files changed, 4 insertions(+), 8 deletions(-)

diff --git a/.gitignore b/.gitignore
index b58dd54e1..e72e818f0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,6 @@
 # ignore output folders
 BenchmarkDotNet.Artifacts/
+artifacts/
 bin/
 build/
 obj/
diff --git a/appveyor.yml b/appveyor.yml
index 4055b0efd..1f30f422b 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -22,19 +22,13 @@ test_script:
 - ps: .\.ci\test.ps1
 after_test:
 - cmd: |-
-    dotnet pack src\MySqlConnector\MySqlConnector.csproj -c Release
-    dotnet pack src\MySqlConnector.Authentication.Ed25519\MySqlConnector.Authentication.Ed25519.csproj -c Release
-    dotnet pack src\MySqlConnector.DependencyInjection\MySqlConnector.DependencyInjection.csproj -c Release
-    dotnet pack src\MySqlConnector.Logging.log4net\MySqlConnector.Logging.log4net.csproj -c Release
-    dotnet pack src\MySqlConnector.Logging.Microsoft.Extensions.Logging\MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj -c Release
-    dotnet pack src\MySqlConnector.Logging.NLog\MySqlConnector.Logging.NLog.csproj -c Release
-    dotnet pack src\MySqlConnector.Logging.Serilog\MySqlConnector.Logging.Serilog.csproj -c Release
+    dotnet pack -c Release
 notifications:
 - provider: Slack
   incoming_webhook:
     secure: SRJ5fYQE4tvelyqeX3Lkv0gXta3O2pl4/+wNaqmqmcFkmYBe+U31T5YCGhipBOVhHBIZLevihOJZ78sFVJrmAFV5bHgtX/VPIKEXN+7ytis=
 artifacts:
-- path: src\*\bin\Release\*.nupkg
+- path: artifacts\package\release\*.nupkg
 deploy:
 - provider: NuGet
   api_key:
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 11784649d..fe86ab8b9 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -26,6 +26,7 @@
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
     <NoWarn>$(NoWarn);1591;CA1708;CA1835;CA2215;CA5397;NU5105;SYSLIB0039</NoWarn>
     <MinVerDefaultPreReleaseIdentifiers>beta.0</MinVerDefaultPreReleaseIdentifiers>
+    <ArtifactsPath>$(MSBuildThisFileDirectory)..\artifacts</ArtifactsPath>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">

From 2ecd74d0fa1c12cff6f5e338d677f8dac4b5ba5a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 1 Jun 2023 23:03:13 -0700
Subject: [PATCH 094/393] Use IUtf8SpanFormattable.

See https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-preview-4/#libraries-utf8-improvements.
---
 src/MySqlConnector/MySqlParameter.cs | 48 ++++++++++++++++++++++++++++
 1 file changed, 48 insertions(+)

diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 597bc376f..80f7144d1 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -4,6 +4,9 @@
 using System.Globalization;
 using System.Numerics;
 using System.Text;
+#if NET8_0_OR_GREATER
+using System.Text.Unicode;
+#endif
 using MySqlConnector.Core;
 using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
@@ -243,7 +246,12 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		}
 		else if (Value is decimal decimalValue)
 		{
+#if NET8_0_OR_GREATER
+			decimalValue.TryFormat(writer.GetSpan(31), out var bytesWritten, default, CultureInfo.InvariantCulture);
+			writer.Advance(bytesWritten);
+#else
 			writer.WriteAscii(decimalValue.ToString(CultureInfo.InvariantCulture));
+#endif
 		}
 		else if (Value is short shortValue)
 		{
@@ -319,13 +327,23 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		}
 		else if (Value is float floatValue)
 		{
+#if NET8_0_OR_GREATER
+			floatValue.TryFormat(writer.GetSpan(14), out var bytesWritten, "R", CultureInfo.InvariantCulture);
+			writer.Advance(bytesWritten);
+#else
 			// NOTE: Utf8Formatter doesn't support "R"
 			writer.WriteAscii(floatValue.ToString("R", CultureInfo.InvariantCulture));
+#endif
 		}
 		else if (Value is double doubleValue)
 		{
+#if NET8_0_OR_GREATER
+			doubleValue.TryFormat(writer.GetSpan(24), out var bytesWritten, "R", CultureInfo.InvariantCulture);
+			writer.Advance(bytesWritten);
+#else
 			// NOTE: Utf8Formatter doesn't support "R"
 			writer.WriteAscii(doubleValue.ToString("R", CultureInfo.InvariantCulture));
+#endif
 		}
 		else if (Value is BigInteger bigInteger)
 		{
@@ -339,12 +357,17 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		{
 			if (mySqlDateTimeValue.IsValidDateTime)
 			{
+#if NET8_0_OR_GREATER
+				Utf8.TryWrite(writer.GetSpan(39), CultureInfo.InvariantCulture, $"timestamp('{mySqlDateTimeValue.GetDateTime():yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')", out var bytesWritten);
+				writer.Advance(bytesWritten);
+#else
 #if NET6_0_OR_GREATER
 				var str = string.Create(CultureInfo.InvariantCulture, stackalloc char[39], $"timestamp('{mySqlDateTimeValue.GetDateTime():yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
 #else
 				var str = FormattableString.Invariant($"timestamp('{mySqlDateTimeValue.GetDateTime():yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
 #endif
 				writer.WriteAscii(str);
+#endif
 			}
 			else
 			{
@@ -354,7 +377,12 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 #if NET6_0_OR_GREATER
 		else if (Value is DateOnly dateOnlyValue)
 		{
+#if NET8_0_OR_GREATER
+			Utf8.TryWrite(writer.GetSpan(23), CultureInfo.InvariantCulture, $"timestamp('{dateOnlyValue:yyyy'-'MM'-'dd}')", out var bytesWritten);
+			writer.Advance(bytesWritten);
+#else
 			writer.WriteAscii(string.Create(CultureInfo.InvariantCulture, stackalloc char[23], $"timestamp('{dateOnlyValue:yyyy'-'MM'-'dd}')"));
+#endif
 		}
 #endif
 		else if (Value is DateTime dateTimeValue)
@@ -364,27 +392,42 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 			else if ((options & StatementPreparerOptions.DateTimeLocal) != 0 && dateTimeValue.Kind == DateTimeKind.Utc)
 				throw new MySqlException($"DateTime.Kind must not be Utc when DateTimeKind setting is Local (parameter name: {ParameterName})");
 
+#if NET8_0_OR_GREATER
+			Utf8.TryWrite(writer.GetSpan(39), CultureInfo.InvariantCulture, $"timestamp('{dateTimeValue:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')", out var bytesWritten);
+			writer.Advance(bytesWritten);
+#else
 #if NET6_0_OR_GREATER
 			var str = string.Create(CultureInfo.InvariantCulture, stackalloc char[39], $"timestamp('{dateTimeValue:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
 #else
 			var str = FormattableString.Invariant($"timestamp('{dateTimeValue:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
 #endif
 			writer.WriteAscii(str);
+#endif
 		}
 		else if (Value is DateTimeOffset dateTimeOffsetValue)
 		{
 			// store as UTC as it will be read as such when deserialized from a timespan column
+#if NET8_0_OR_GREATER
+			Utf8.TryWrite(writer.GetSpan(39), CultureInfo.InvariantCulture, $"timestamp('{dateTimeOffsetValue.UtcDateTime:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')", out var bytesWritten);
+			writer.Advance(bytesWritten);
+#else
 #if NET6_0_OR_GREATER
 			var str = string.Create(CultureInfo.InvariantCulture, stackalloc char[39], $"timestamp('{dateTimeOffsetValue.UtcDateTime:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
 #else
 			var str = FormattableString.Invariant($"timestamp('{dateTimeOffsetValue.UtcDateTime:yyyy'-'MM'-'dd' 'HH':'mm':'ss'.'ffffff}')");
 #endif
 			writer.WriteAscii(str);
+#endif
 		}
 #if NET6_0_OR_GREATER
 		else if (Value is TimeOnly timeOnlyValue)
 		{
+#if NET8_0_OR_GREATER
+			Utf8.TryWrite(writer.GetSpan(22), CultureInfo.InvariantCulture, $"time '{timeOnlyValue:HH':'mm':'ss'.'ffffff}'", out var bytesWritten);
+			writer.Advance(bytesWritten);
+#else
 			writer.WriteAscii(string.Create(CultureInfo.InvariantCulture, stackalloc char[22], $"time '{timeOnlyValue:HH':'mm':'ss'.'ffffff}'"));
+#endif
 		}
 #endif
 		else if (Value is TimeSpan ts)
@@ -395,12 +438,17 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 				writer.Write((byte) '-');
 				ts = TimeSpan.FromTicks(-ts.Ticks);
 			}
+#if NET8_0_OR_GREATER
+			Utf8.TryWrite(writer.GetSpan(17), CultureInfo.InvariantCulture, $"{ts.Days * 24 + ts.Hours}:{ts:mm':'ss'.'ffffff}'", out var bytesWritten);
+			writer.Advance(bytesWritten);
+#else
 #if NET6_0_OR_GREATER
 			var str = string.Create(CultureInfo.InvariantCulture, stackalloc char[17], $"{ts.Days * 24 + ts.Hours}:{ts:mm':'ss'.'ffffff}'");
 #else
 			var str = FormattableString.Invariant($"{ts.Days * 24 + ts.Hours}:{ts:mm':'ss'.'ffffff}'");
 #endif
 			writer.WriteAscii(str);
+#endif
 		}
 		else if (Value is Guid guidValue)
 		{

From 4bc694a55be6379829251974ab8d706626daff25 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 1 Jun 2023 23:05:00 -0700
Subject: [PATCH 095/393] Inline temporary local variables.

---
 src/MySqlConnector/Core/ServerSession.cs |  3 +--
 src/MySqlConnector/Core/TextRow.cs       | 10 ++++------
 src/MySqlConnector/MySqlParameter.cs     |  7 ++-----
 3 files changed, 7 insertions(+), 13 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 64f84f005..7c272dee5 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1406,7 +1406,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 				// find the index of each individual certificate in the file (assuming there may be multiple certificates concatenated together)
 				for (var index = 0; index != -1;)
 				{
-					var nextIndex = Utility.FindNextIndex(certificateBytes, index + 1, BeginCertificateBytes);
+					var nextIndex = Utility.FindNextIndex(certificateBytes, index + 1, "-----BEGIN CERTIFICATE-----"u8);
 					try
 					{
 						// load the certificate at this index; note that 'new X509Certificate' stops at the end of the first certificate it loads
@@ -1955,7 +1955,6 @@ protected override void OnStatementBegin(int index)
 	[LoggerMessage(EventIds.ExpectedSessionState6, LogLevel.Error, "Session {SessionId} should have state {ExpectedState1} or {ExpectedState2} or {ExpectedState3} or {ExpectedState4} or {ExpectedState5} or {ExpectedState6} but was {SessionState}")]
 	private static partial void ExpectedSessionState6(ILogger logger, string sessionId, State expectedState1, State expectedState2, State expectedState3, State expectedState4, State expectedState5, State expectedState6, State sessionState);
 
-	private static ReadOnlySpan<byte> BeginCertificateBytes => "-----BEGIN CERTIFICATE-----"u8;
 	private static readonly PayloadData s_setNamesUtf8NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8;"u8);
 	private static readonly PayloadData s_setNamesUtf8mb4NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8mb4;"u8);
 	private static readonly PayloadData s_setNamesUtf8WithAttributesPayload = QueryPayload.Create(true, "SET NAMES utf8;"u8);
diff --git a/src/MySqlConnector/Core/TextRow.cs b/src/MySqlConnector/Core/TextRow.cs
index c8c172e02..a5c43766e 100644
--- a/src/MySqlConnector/Core/TextRow.cs
+++ b/src/MySqlConnector/Core/TextRow.cs
@@ -98,10 +98,9 @@ protected override object GetValueCore(ReadOnlySpan<byte> data, ColumnDefinition
 		case ColumnType.Float:
 			if (Utf8Parser.TryParse(data, out float floatValue, out var floatBytesConsumed) && floatBytesConsumed == data.Length)
 				return floatValue;
-			ReadOnlySpan<byte> floatInfinity = "-inf"u8;
-			if (data.SequenceEqual(floatInfinity))
+			if (data.SequenceEqual("-inf"u8))
 				return float.NegativeInfinity;
-			if (data.SequenceEqual(floatInfinity.Slice(1)))
+			if (data.SequenceEqual("inf"u8))
 				return float.PositiveInfinity;
 			if (data.SequenceEqual("nan"u8))
 				return float.NaN;
@@ -110,10 +109,9 @@ protected override object GetValueCore(ReadOnlySpan<byte> data, ColumnDefinition
 		case ColumnType.Double:
 			if (Utf8Parser.TryParse(data, out double doubleValue, out var doubleBytesConsumed) && doubleBytesConsumed == data.Length)
 				return doubleValue;
-			ReadOnlySpan<byte> doubleInfinity = "-inf"u8;
-			if (data.SequenceEqual(doubleInfinity))
+			if (data.SequenceEqual("-inf"u8))
 				return double.NegativeInfinity;
-			if (data.SequenceEqual(doubleInfinity.Slice(1)))
+			if (data.SequenceEqual("inf"u8))
 				return double.PositiveInfinity;
 			if (data.SequenceEqual("nan"u8))
 				return double.NaN;
diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 80f7144d1..07b59fa8b 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -196,8 +196,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 
 		if (Value is null || Value == DBNull.Value)
 		{
-			ReadOnlySpan<byte> nullBytes = "NULL"u8;
-			writer.Write(nullBytes);
+			writer.Write("NULL"u8);
 		}
 		else if (Value is string stringValue)
 		{
@@ -321,9 +320,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		}
 		else if (Value is bool boolValue)
 		{
-			ReadOnlySpan<byte> trueBytes = "true"u8;
-			ReadOnlySpan<byte> falseBytes = "false"u8;
-			writer.Write(boolValue ? trueBytes : falseBytes);
+			writer.Write(boolValue ? "true"u8 : "false"u8);
 		}
 		else if (Value is float floatValue)
 		{

From e94ddb9efc30e49bc5039654182f81e966d8c456 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 2 Jun 2023 05:47:42 -0700
Subject: [PATCH 096/393] Remove loop from integer formatting methods.

---
 .../Serialization/ByteBufferWriter.cs         | 26 +++++--------------
 1 file changed, 7 insertions(+), 19 deletions(-)

diff --git a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
index 4f2338327..4bd24cb00 100644
--- a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
@@ -180,49 +180,37 @@ public void WriteLengthEncodedString(StringBuilder stringBuilder)
 
 	public void WriteString(short value)
 	{
-		int bytesWritten;
-		while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten))
-			Reallocate();
+		Utf8Formatter.TryFormat(value, GetSpan(6), out var bytesWritten);
 		m_output = m_output[bytesWritten..];
 	}
 
 	public void WriteString(ushort value)
 	{
-		int bytesWritten;
-		while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten))
-			Reallocate();
+		Utf8Formatter.TryFormat(value, GetSpan(5), out var bytesWritten);
 		m_output = m_output[bytesWritten..];
 	}
 
 	public void WriteString(int value)
 	{
-		int bytesWritten;
-		while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten))
-			Reallocate();
+		Utf8Formatter.TryFormat(value, GetSpan(11), out var bytesWritten);
 		m_output = m_output[bytesWritten..];
 	}
 
 	public void WriteString(uint value)
 	{
-		int bytesWritten;
-		while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten))
-			Reallocate();
+		Utf8Formatter.TryFormat(value, GetSpan(10), out var bytesWritten);
 		m_output = m_output[bytesWritten..];
 	}
 
 	public void WriteString(long value)
 	{
-		int bytesWritten;
-		while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten))
-			Reallocate();
+		Utf8Formatter.TryFormat(value, GetSpan(20), out var bytesWritten);
 		m_output = m_output[bytesWritten..];
 	}
 
 	public void WriteString(ulong value)
 	{
-		int bytesWritten;
-		while (!Utf8Formatter.TryFormat(value, m_output.Span, out bytesWritten))
-			Reallocate();
+		Utf8Formatter.TryFormat(value, GetSpan(20), out var bytesWritten);
 		m_output = m_output[bytesWritten..];
 	}
 
@@ -230,7 +218,7 @@ private void Reallocate(int additional = 0)
 	{
 		var usedLength = Position;
 		var newBuffer = ArrayPool<byte>.Shared.Rent(Math.Max(usedLength + additional, m_buffer.Length * 2));
-		Buffer.BlockCopy(m_buffer, 0, newBuffer, 0, usedLength);
+		m_buffer.AsSpan(0, usedLength).CopyTo(newBuffer);
 		ArrayPool<byte>.Shared.Return(m_buffer);
 		m_buffer = newBuffer;
 		m_output = new(m_buffer, usedLength, m_buffer.Length - usedLength);

From 451a65716198a6b3ef813d008c583ae2dd3e347c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 3 Jun 2023 06:55:33 -0700
Subject: [PATCH 097/393] Fix error calculating required buffer length.

The 'additional' parameter should specify the number of bytes to be added to the current output, not the additional length to be added to the buffer.
---
 src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
index 4f2338327..0e95365df 100644
--- a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
@@ -132,7 +132,7 @@ public void Write(ReadOnlySpan<char> chars, bool flush)
 	public void WriteAscii(ReadOnlySpan<char> chars)
 	{
 		if (m_output.Length < chars.Length)
-			Reallocate(chars.Length - m_output.Length);
+			Reallocate(chars.Length);
 		m_output = m_output[Encoding.ASCII.GetBytes(chars, m_output.Span)..];
 	}
 

From 89767d772647b2540847ff91c335748c02b5dab6 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 4 Jun 2023 16:17:00 -0700
Subject: [PATCH 098/393] Fix or suppress CA1859 concrete type warnings.

---
 .editorconfig                              |  1 -
 src/MySqlConnector/Core/CachedProcedure.cs |  2 ++
 src/MySqlConnector/Core/ConnectionPool.cs  | 18 +++++++++---------
 src/MySqlConnector/Core/ServerSession.cs   |  2 +-
 src/MySqlConnector/MySqlBatch.cs           |  3 ++-
 src/MySqlConnector/MySqlBulkCopy.cs        |  2 +-
 src/MySqlConnector/MySqlBulkLoader.cs      |  2 +-
 7 files changed, 16 insertions(+), 14 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index cca51b0ea..675b99587 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -79,7 +79,6 @@ dotnet_diagnostic.CA1508.severity = silent # Avoid dead conditional code.
 dotnet_diagnostic.CA1510.severity = none # Use ArgumentNullException.ThrowIfNull.
 dotnet_diagnostic.CA1513.severity = none # Use ObjectDisposedException.ThrowIf.
 dotnet_diagnostic.CA1849.severity = none # Call async methods when in an async method.
-dotnet_diagnostic.CA1859.severity = silent # Use concrete return types for performance.
 dotnet_diagnostic.CA2000.severity = none # Use recommended Dispose pattern.
 dotnet_diagnostic.CA2100.severity = none # Review SQL queries for security vulnerabilities.
 dotnet_diagnostic.CA2213.severity = silent # Disposable fields should be disposed.
diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index 11d24cc4b..cffe35c7b 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Text;
 using System.Text.RegularExpressions;
@@ -233,6 +234,7 @@ private static CachedParameter CreateCachedParameter(int ordinal, string? direct
 
 	private string FullyQualified => $"`{m_schema}`.`{m_component}`";
 
+	[SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance", Justification = "Avoid mutable static field")]
 	private static readonly IReadOnlyDictionary<string, string> s_typeMapping = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
 	{
 		{ "BOOL", "TINYINT" },
diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 82bb7dfb4..3d89e5bbc 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -546,18 +546,18 @@ public static async Task ClearPoolsAsync(IOBehavior ioBehavior, CancellationToke
 	{
 		foreach (var pool in GetAllPools())
 			await pool.ClearAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-	}
 
-	private static IReadOnlyList<ConnectionPool> GetAllPools()
-	{
-		var pools = new List<ConnectionPool>(s_pools.Count);
-		var uniquePools = new HashSet<ConnectionPool>();
-		foreach (var pool in s_pools.Values)
+		static List<ConnectionPool> GetAllPools()
 		{
-			if (pool is not null && uniquePools.Add(pool))
-				pools.Add(pool);
+			var pools = new List<ConnectionPool>(s_pools.Count);
+			var uniquePools = new HashSet<ConnectionPool>();
+			foreach (var pool in s_pools.Values)
+			{
+				if (pool is not null && uniquePools.Add(pool))
+					pools.Add(pool);
+			}
+			return pools;
 		}
-		return pools;
 	}
 
 	private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration, ConnectionSettings cs)
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 7c272dee5..da081d943 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1852,7 +1852,7 @@ private byte[] CreateConnectionAttributes(string programName)
 		return payload.Memory.ToArray();
 	}
 
-	private Exception CreateExceptionForErrorPayload(ReadOnlySpan<byte> span)
+	private MySqlException CreateExceptionForErrorPayload(ReadOnlySpan<byte> span)
 	{
 		var errorPayload = ErrorPayload.Create(span);
 		Log.ErrorPayload(m_logger, Id, errorPayload.ErrorCode, errorPayload.State, errorPayload.Message);
diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index a762e1bad..507fb00bd 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -130,6 +130,7 @@ public async Task<MySqlDataReader> ExecuteReaderAsync(CancellationToken cancella
 #if NET6_0_OR_GREATER
 	protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
 #else
+	[SuppressMessage("Performance", "CA1859:Use concrete types when possible for improved performance", Justification = "Matches .NET 6.0 override")]
 	private DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
 #endif
 	{
@@ -351,7 +352,7 @@ private bool NeedsPrepare(out Exception? exception)
 		return exception is null && !Connection!.IgnorePrepare;
 	}
 
-	private Exception? GetExceptionForInvalidCommands()
+	private InvalidOperationException? GetExceptionForInvalidCommands()
 	{
 		foreach (var command in BatchCommands)
 		{
diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs
index 2c0c52dd5..5aead4b65 100644
--- a/src/MySqlConnector/MySqlBulkCopy.cs
+++ b/src/MySqlConnector/MySqlBulkCopy.cs
@@ -706,7 +706,7 @@ static bool WriteBytes(ReadOnlySpan<byte> value, ref int inputIndex, Span<byte>
 	}
 
 	private static readonly char[] s_specialCharacters = new char[] { '\t', '\\', '\n' };
-	private static readonly Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
+	private static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
 
 	private readonly MySqlConnection m_connection;
 	private readonly MySqlTransaction? m_transaction;
diff --git a/src/MySqlConnector/MySqlBulkLoader.cs b/src/MySqlConnector/MySqlBulkLoader.cs
index 45aff9ac4..3e91c8fdc 100644
--- a/src/MySqlConnector/MySqlBulkLoader.cs
+++ b/src/MySqlConnector/MySqlBulkLoader.cs
@@ -281,7 +281,7 @@ private string CreateSql()
 		return sb.ToString();
 	}
 
-	private static Stream CreateFileStream(string fileName)
+	private static FileStream CreateFileStream(string fileName)
 	{
 		try
 		{

From af48af1962ef7a79b9f274ca134bf3f0e3852aec Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 6 Jun 2023 21:02:48 -0700
Subject: [PATCH 099/393] Specify default C# solution.

This is for the latest C# (or C# Dev Kit) VSCode extension (currently in Preview).
---
 .vscode/settings.json | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/.vscode/settings.json b/.vscode/settings.json
index 0bf1718e7..663c360cb 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -6,5 +6,6 @@
 	// Controls if the editor will insert spaces for tabs. Accepted values:  "auto", true, false. If set to "auto", the value will be guessed when a file is opened.
 	"editor.insertSpaces": false,
 
-	"files.trimTrailingWhitespace": true
+	"files.trimTrailingWhitespace": true,
+	"dotnet.defaultSolution": "MySqlConnector.sln"
 }
\ No newline at end of file

From f53882e071e7878f15e21ad2f3914f332c746adc Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 6 Jun 2023 21:07:17 -0700
Subject: [PATCH 100/393] Add Name to MySqlDataSource.

This will be to provide a friendly name instead of using the full connection string to identify a pool. It can also disambiguate two pools created with the same connection string.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ConnectionPool.cs    | 13 ++++++++-----
 src/MySqlConnector/Logging/EventIds.cs       |  6 ++++--
 src/MySqlConnector/Logging/Log.cs            | 14 ++++++++++----
 src/MySqlConnector/MySqlDataSource.cs        | 18 +++++++++++++-----
 src/MySqlConnector/MySqlDataSourceBuilder.cs | 13 +++++++++++++
 5 files changed, 48 insertions(+), 16 deletions(-)

diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 82bb7dfb4..007870e44 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -13,6 +13,8 @@ internal sealed class ConnectionPool : IDisposable
 {
 	public int Id { get; }
 
+	public string? Name { get; }
+
 	public ConnectionSettings ConnectionSettings { get; }
 
 	public SslProtocols SslProtocols { get; set; }
@@ -464,7 +466,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 		return session;
 	}
 
-	public static ConnectionPool? CreatePool(string connectionString, MySqlConnectorLoggingConfiguration loggingConfiguration)
+	public static ConnectionPool? CreatePool(string connectionString, MySqlConnectorLoggingConfiguration loggingConfiguration, string? name)
 	{
 		// parse connection string and check for 'Pooling' setting; return 'null' if pooling is disabled
 		var connectionStringBuilder = new MySqlConnectionStringBuilder(connectionString);
@@ -473,13 +475,13 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 
 		// force a new pool to be created, ignoring the cache
 		var connectionSettings = new ConnectionSettings(connectionStringBuilder);
-		var pool = new ConnectionPool(loggingConfiguration, connectionSettings);
+		var pool = new ConnectionPool(loggingConfiguration, connectionSettings, name);
 		pool.StartReaperTask();
 		pool.StartDnsCheckTimer();
 		return pool;
 	}
 
-	// Gets an existing ConnectionPool, creating it if it's missing. If 'createIfNotFound' is false, then 'loggingConfiguration'
+	// Gets an existing (unnamed) ConnectionPool, creating it if it's missing. If 'createIfNotFound' is false, then 'loggingConfiguration'
 	// may be set to null; otherwise, it must be provided.
 	public static ConnectionPool? GetPool(string connectionString, MySqlConnectorLoggingConfiguration? loggingConfiguration, bool createIfNotFound = true)
 	{
@@ -521,7 +523,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 
 		// create a new pool and attempt to insert it; if someone else beats us to it, just use their value
 		var connectionSettings = new ConnectionSettings(connectionStringBuilder);
-		var newPool = new ConnectionPool(loggingConfiguration!, connectionSettings);
+		var newPool = new ConnectionPool(loggingConfiguration!, connectionSettings, name: null);
 		pool = s_pools.GetOrAdd(normalizedConnectionString, newPool);
 
 		if (pool == newPool)
@@ -560,11 +562,12 @@ private static IReadOnlyList<ConnectionPool> GetAllPools()
 		return pools;
 	}
 
-	private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration, ConnectionSettings cs)
+	private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration, ConnectionSettings cs, string? name)
 	{
 		m_logger = loggingConfiguration.PoolLogger;
 		m_connectionLogger = loggingConfiguration.ConnectionLogger;
 		ConnectionSettings = cs;
+		Name = name;
 		SslProtocols = cs.TlsVersions;
 		m_generation = 0;
 		m_cleanSemaphore = new(1);
diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index 9851298f1..eb3318e75 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -3,8 +3,10 @@ namespace MySqlConnector.Logging;
 internal static class EventIds
 {
 	// DataSource events, 1000-1099
-	public const int DataSourceCreatedWithPool = 1000;
-	public const int DataSourceCreatedWithoutPool = 1001;
+	public const int DataSourceCreatedWithPoolWithName = 1000;
+	public const int DataSourceCreatedWithoutPoolWithName = 1001;
+	public const int DataSourceCreatedWithPoolWithoutName = 1002;
+	public const int DataSourceCreatedWithoutPoolWithoutName = 1003;
 
 	// DataSource errors, 1100-1199
 	public const int PeriodicPasswordProviderFailed = 1100;
diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index 35848069b..420a61994 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -7,11 +7,17 @@ namespace MySqlConnector.Logging;
 
 internal static partial class Log
 {
-	[LoggerMessage(EventIds.DataSourceCreatedWithPool, LogLevel.Information, "Data source {DataSourceId} created with pool {PoolId}")]
-	public static partial void DataSourceCreatedWithPool(ILogger logger, int dataSourceId, int poolId);
+	[LoggerMessage(EventIds.DataSourceCreatedWithPoolWithName, LogLevel.Information, "Data source {DataSourceId} created with pool {PoolId} and name {DataSourceName}")]
+	public static partial void DataSourceCreatedWithPoolWithName(ILogger logger, int dataSourceId, int poolId, string dataSourceName);
 
-	[LoggerMessage(EventIds.DataSourceCreatedWithoutPool, LogLevel.Information, "Data source {DataSourceId} created with no pool")]
-	public static partial void DataSourceCreatedWithoutPool(ILogger logger, int dataSourceId);
+	[LoggerMessage(EventIds.DataSourceCreatedWithoutPoolWithName, LogLevel.Information, "Data source {DataSourceId} created with name {DataSourceName} and no pool")]
+	public static partial void DataSourceCreatedWithoutPoolWithName(ILogger logger, int dataSourceId, string dataSourceName);
+
+	[LoggerMessage(EventIds.DataSourceCreatedWithPoolWithoutName, LogLevel.Information, "Data source {DataSourceId} created with pool {PoolId} and no name")]
+	public static partial void DataSourceCreatedWithPoolWithoutName(ILogger logger, int dataSourceId, int poolId);
+
+	[LoggerMessage(EventIds.DataSourceCreatedWithoutPoolWithoutName, LogLevel.Information, "Data source {DataSourceId} created with no pool and no name")]
+	public static partial void DataSourceCreatedWithoutPoolWithoutName(ILogger logger, int dataSourceId);
 
 	[LoggerMessage(EventIds.PeriodicPasswordProviderFailed, LogLevel.Error, "Periodic password provider for data source {DataSourceId} failed: {ExceptionMessage}")]
 	public static partial void PeriodicPasswordProviderFailed(ILogger logger, Exception exception, int dataSourceId, string exceptionMessage);
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index c6a14d66f..c3499ef0e 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -18,12 +18,13 @@ public sealed class MySqlDataSource : DbDataSource
 	/// <param name="connectionString">The connection string for the MySQL Server. This parameter is required.</param>
 	/// <exception cref="ArgumentNullException">Thrown if <paramref name="connectionString"/> is <c>null</c>.</exception>
 	public MySqlDataSource(string connectionString)
-		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, default, default)
+		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, default, default)
 	{
 	}
 
 	internal MySqlDataSource(string connectionString,
 		MySqlConnectorLoggingConfiguration loggingConfiguration,
+		string? name,
 		Func<X509CertificateCollection, ValueTask>? clientCertificatesCallback,
 		RemoteCertificateValidationCallback? remoteCertificateValidationCallback,
 		Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? periodicPasswordProvider,
@@ -32,16 +33,21 @@ internal MySqlDataSource(string connectionString,
 	{
 		m_connectionString = connectionString;
 		LoggingConfiguration = loggingConfiguration;
+		Name = name;
 		m_clientCertificatesCallback = clientCertificatesCallback;
 		m_remoteCertificateValidationCallback = remoteCertificateValidationCallback;
 		m_logger = loggingConfiguration.DataSourceLogger;
 
-		Pool = ConnectionPool.CreatePool(m_connectionString, LoggingConfiguration);
+		Pool = ConnectionPool.CreatePool(m_connectionString, LoggingConfiguration, name);
 		m_id = Interlocked.Increment(ref s_lastId);
-		if (Pool is not null)
-			Log.DataSourceCreatedWithPool(m_logger, m_id, Pool.Id);
+		if (Pool is not null && Name is not null)
+			Log.DataSourceCreatedWithPoolWithName(m_logger, m_id, Pool.Id, Name);
+		else if (Pool is not null)
+			Log.DataSourceCreatedWithPoolWithoutName(m_logger, m_id, Pool.Id);
+		else if (Name is not null)
+			Log.DataSourceCreatedWithoutPoolWithName(m_logger, m_id, Name);
 		else
-			Log.DataSourceCreatedWithoutPool(m_logger, m_id);
+			Log.DataSourceCreatedWithoutPoolWithoutName(m_logger, m_id);
 
 		if (periodicPasswordProvider is not null)
 		{
@@ -186,6 +192,8 @@ private async Task RefreshPassword()
 
 	internal MySqlConnectorLoggingConfiguration LoggingConfiguration { get; }
 
+	internal string? Name { get; }
+
 	private string ProvidePasswordFromField(MySqlProvidePasswordContext context) => m_password!;
 
 	private string ProvidePasswordFromInitialRefreshTask(MySqlProvidePasswordContext context)
diff --git a/src/MySqlConnector/MySqlDataSourceBuilder.cs b/src/MySqlConnector/MySqlDataSourceBuilder.cs
index 628e6375d..dcdb05edd 100644
--- a/src/MySqlConnector/MySqlDataSourceBuilder.cs
+++ b/src/MySqlConnector/MySqlDataSourceBuilder.cs
@@ -31,6 +31,17 @@ public MySqlDataSourceBuilder UseLoggerFactory(ILoggerFactory? loggerFactory)
 		return this;
 	}
 
+	/// <summary>
+	/// Sets the name of the <see cref="MySqlDataSource"/> that will be created.
+	/// </summary>
+	/// <param name="name">The data source name.</param>
+	/// <returns>This builder, so that method calls can be chained.</returns>
+	public MySqlDataSourceBuilder UseName(string? name)
+	{
+		m_name = name;
+		return this;
+	}
+
 	/// <summary>
 	/// Sets the callback used to provide client certificates for connecting to a server.
 	/// </summary>
@@ -83,6 +94,7 @@ public MySqlDataSource Build()
 		var loggingConfiguration = m_loggerFactory is null ? MySqlConnectorLoggingConfiguration.NullConfiguration : new(m_loggerFactory);
 		return new(ConnectionStringBuilder.ConnectionString,
 			loggingConfiguration,
+			m_name,
 			m_clientCertificatesCallback,
 			m_remoteCertificateValidationCallback,
 			m_periodicPasswordProvider,
@@ -97,6 +109,7 @@ public MySqlDataSource Build()
 	public MySqlConnectionStringBuilder ConnectionStringBuilder { get; }
 
 	private ILoggerFactory? m_loggerFactory;
+	private string? m_name;
 	private Func<X509CertificateCollection, ValueTask>? m_clientCertificatesCallback;
 	private RemoteCertificateValidationCallback? m_remoteCertificateValidationCallback;
 	private Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? m_periodicPasswordProvider;

From a18e4d1e6637d8e590b9773d8d7674c57b4aaa5b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 13 Jun 2023 21:18:51 -0700
Subject: [PATCH 101/393] Update to .NET 8 Preview 5.

Fix CA1861 analyzer warning. The new code is just as inefficient on .NET Framework (as it still allocates a temporary array), but will call an efficient overload on recent .NET.
---
 src/MySqlConnector/Core/CachedProcedure.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index cffe35c7b..17b6c4466 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -202,7 +202,7 @@ internal static string ParseDataType(string sql, out bool unsigned, out int leng
 			sql = s_length.Replace(sql, "");
 		}
 
-		var list = sql.Trim().Split(new[] { ' ' });
+		var list = sql.Trim().Split(' ');
 		var type = string.Empty;
 
 		if (list.Length < 2 || !s_typeMapping.TryGetValue(list[0] + ' ' + list[1], out type))

From 647094ab4044c705b1a9843174e6fb0bf632b6e6 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 13 Jun 2023 21:18:51 -0700
Subject: [PATCH 102/393] Fix build error if .NET 8 SDK is installed.

Fix CA1861 analyzer warning. The new code is just as inefficient on .NET Framework (as it still allocates a temporary array), but will call an efficient overload on recent .NET.

(cherry picked from commit a18e4d1e6637d8e590b9773d8d7674c57b4aaa5b)
---
 src/MySqlConnector/Core/CachedProcedure.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index 11d24cc4b..c3bad0846 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -201,7 +201,7 @@ internal static string ParseDataType(string sql, out bool unsigned, out int leng
 			sql = s_length.Replace(sql, "");
 		}
 
-		var list = sql.Trim().Split(new[] { ' ' });
+		var list = sql.Trim().Split(' ');
 		var type = string.Empty;
 
 		if (list.Length < 2 || !s_typeMapping.TryGetValue(list[0] + ' ' + list[1], out type))

From 2910f6d2929981e1d1a7f600097096ab69616a86 Mon Sep 17 00:00:00 2001
From: rusher <diego.dupin@gmail.com>
Date: Fri, 16 Jun 2023 12:14:30 +0200
Subject: [PATCH 103/393] Faster parsing for multiple rows

When result-set contains multiple rows, in order to get field value, the columns details are checked for each field parsing.

This PR permit to set parsing method once for the result-set.
This doesn't change much in terms of performance, but still not negligible.

Performance results:

```
[Benchmark]
public async Task getInt64()
{
	using var cmd = Connection.CreateCommand();
	cmd.CommandText = "SELECT * FROM seq_1_to_100000";
	using var reader = await cmd.ExecuteReaderAsync();
	long total = 0;
	do
	{
		while (await reader.ReadAsync())
		{
			total += reader.GetInt64(0);
		}
	} while (await reader.NextResultAsync());
}
```

Initial results:

```
|   Method |        Library |     Mean |    Error |
|--------- |--------------- |---------:|---------:|
| getInt64 | MySqlConnector | 17.91 ms | 0.149 ms |
```

PR results:

```
|   Method |        Library |     Mean |    Error |
|--------- |--------------- |---------:|---------:|
| getInt64 | MySqlConnector | 17.59 ms | 0.125 ms |
```
---
 .../BinaryBooleanColumnReader.cs              |  20 ++
 .../BinaryDateTimeColumnReader.cs             |  74 +++++++
 .../ColumnReaders/BinaryDoubleColumnReader.cs |  21 ++
 .../ColumnReaders/BinaryFloatColumnReader.cs  |  21 ++
 .../BinarySignedInt16ColumnReader.cs          |  21 ++
 .../BinarySignedInt32ColumnReader.cs          |  21 ++
 .../BinarySignedInt64ColumnReader.cs          |  21 ++
 .../BinarySignedInt8ColumnReader.cs           |  20 ++
 .../ColumnReaders/BinaryTimeColumnReader.cs   |  44 ++++
 .../BinaryUnsignedInt16ColumnReader.cs        |  21 ++
 .../BinaryUnsignedInt32ColumnReader.cs        |  21 ++
 .../BinaryUnsignedInt64ColumnReader.cs        |  21 ++
 .../BinaryUnsignedInt8ColumnReader.cs         |  20 ++
 .../ColumnReaders/BinaryYearColumnReader.cs   |  21 ++
 .../ColumnReaders/BitColumnReader.cs          |  61 ++++++
 .../ColumnReaders/BytesColumnReader.cs        |  20 ++
 .../ColumnReaders/ColumnReaderFactory.cs      | 204 ++++++++++++++++++
 .../ColumnReaders/DecimalColumnReader.cs      |  24 +++
 .../ColumnReaders/Guid16ColumnReader.cs       |  28 +++
 .../ColumnReaders/Guid32ColumnReader.cs       |  25 +++
 .../ColumnReaders/Guid36ColumnReader.cs       |  25 +++
 .../ColumnReaders/GuidBytesColumnReader.cs    |  24 +++
 .../ColumnReaders/IColumnReader.cs            |   9 +
 .../ColumnReaders/NullColumnReader.cs         |  25 +++
 .../ColumnReaders/StringColumnReader.cs       |  21 ++
 .../ColumnReaders/TextBooleanColumnReader.cs  |  20 ++
 .../ColumnReaders/TextDateTimeColumnReader.cs | 108 ++++++++++
 .../ColumnReaders/TextDoubleColumnReader.cs   |  30 +++
 .../ColumnReaders/TextFloatColumnReader.cs    |  30 +++
 .../TextSignedInt16ColumnReader.cs            |  24 +++
 .../TextSignedInt32ColumnReader.cs            |  20 ++
 .../TextSignedInt64ColumnReader.cs            |  24 +++
 .../TextSignedInt8ColumnReader.cs             |  28 +++
 .../ColumnReaders/TextTimeColumnReader.cs     |  20 ++
 .../TextUnsignedInt16ColumnReader.cs          |  24 +++
 .../TextUnsignedInt32ColumnReader.cs          |  24 +++
 .../TextUnsignedInt64ColumnReader.cs          |  24 +++
 .../TextUnsignedInt8ColumnReader.cs           |  28 +++
 .../TimeSwapBinary16ColumnReader.cs           |  28 +++
 src/MySqlConnector/Core/BinaryRow.cs          | 172 +--------------
 src/MySqlConnector/Core/ResultSet.cs          |  10 +-
 src/MySqlConnector/Core/Row.cs                |  72 ++-----
 src/MySqlConnector/Core/TextRow.cs            | 120 +----------
 tests/Benchmark/Benchmark.csproj              |   7 +-
 44 files changed, 1290 insertions(+), 356 deletions(-)
 create mode 100644 src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BitColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/IColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/NullColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/StringColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
 create mode 100644 src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs

diff --git a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
new file mode 100644
index 000000000..3654d4472
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
@@ -0,0 +1,20 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinaryBooleanColumnReader : IColumnReader
+{
+	internal static BinaryBooleanColumnReader Instance { get; } = new BinaryBooleanColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return data[0] != 0;
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return data[0] != 0 ? 1 : 0;
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
new file mode 100644
index 000000000..f24614204
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
@@ -0,0 +1,74 @@
+namespace MySqlConnector.ColumnReaders;
+
+using System;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using System.Text;
+using MySqlConnector.Core;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinaryDateTimeColumnReader : IColumnReader
+{
+	private bool allowZeroDateTime;
+	private bool convertZeroDateTime;
+	private DateTimeKind dateTimeKind;
+	internal BinaryDateTimeColumnReader(MySqlConnection connection)
+	{
+		this.allowZeroDateTime = connection.AllowZeroDateTime;
+		this.convertZeroDateTime = connection.ConvertZeroDateTime;
+		this.dateTimeKind = connection.DateTimeKind;
+	}
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (data.Length == 0)
+		{
+			if (this.convertZeroDateTime)
+				return DateTime.MinValue;
+			if (this.allowZeroDateTime)
+				return default(MySqlDateTime);
+			throw new InvalidCastException("Unable to convert MySQL date/time to System.DateTime.");
+		}
+
+		int year = data[0] + data[1] * 256;
+		int month = data[2];
+		int day = data[3];
+
+		int hour, minute, second;
+		if (data.Length <= 4)
+		{
+			hour = 0;
+			minute = 0;
+			second = 0;
+		}
+		else
+		{
+			hour = data[4];
+			minute = data[5];
+			second = data[6];
+		}
+
+		var microseconds = data.Length <= 7 ? 0 : MemoryMarshal.Read<int>(data[7..]);
+
+		try
+		{
+			return this.allowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
+#if NET7_0_OR_GREATER
+				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, this.dateTimeKind);
+#else
+				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, this.dateTimeKind).AddTicks(microseconds % 1000 * 10);
+#endif
+		}
+		catch (Exception ex)
+		{
+			throw new FormatException($"Couldn't interpret value as a valid DateTime: {Encoding.UTF8.GetString(data)}", ex);
+		}
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
new file mode 100644
index 000000000..153e8c846
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
@@ -0,0 +1,21 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinaryDoubleColumnReader : IColumnReader
+{
+	internal static BinaryDoubleColumnReader Instance { get; } = new BinaryDoubleColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return MemoryMarshal.Read<double>(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
new file mode 100644
index 000000000..9502260ba
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
@@ -0,0 +1,21 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinaryFloatColumnReader : IColumnReader
+{
+	internal static BinaryFloatColumnReader Instance { get; } = new BinaryFloatColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return MemoryMarshal.Read<float>(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
new file mode 100644
index 000000000..d2f02ab2d
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
@@ -0,0 +1,21 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinarySignedInt16ColumnReader : IColumnReader
+{
+	internal static BinarySignedInt16ColumnReader Instance { get; } = new BinarySignedInt16ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return MemoryMarshal.Read<short>(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return (int) MemoryMarshal.Read<short>(data);
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
new file mode 100644
index 000000000..7b75e7141
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
@@ -0,0 +1,21 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinarySignedInt32ColumnReader : IColumnReader
+{
+	internal static BinarySignedInt32ColumnReader Instance { get; } = new BinarySignedInt32ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return MemoryMarshal.Read<int>(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return MemoryMarshal.Read<int>(data);
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
new file mode 100644
index 000000000..72e196c60
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
@@ -0,0 +1,21 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinarySignedInt64ColumnReader : IColumnReader
+{
+	internal static BinarySignedInt64ColumnReader Instance { get; } = new BinarySignedInt64ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return MemoryMarshal.Read<long>(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return checked((int) MemoryMarshal.Read<long>(data));
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
new file mode 100644
index 000000000..ca492e399
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
@@ -0,0 +1,20 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinarySignedInt8ColumnReader : IColumnReader
+{
+	internal static BinarySignedInt8ColumnReader Instance { get; } = new BinarySignedInt8ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return (sbyte) data[0];
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return (int) (sbyte) data[0];
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
new file mode 100644
index 000000000..52333ea82
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
@@ -0,0 +1,44 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinaryTimeColumnReader : IColumnReader
+{
+	internal static BinaryTimeColumnReader Instance { get; } = new BinaryTimeColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (data.Length == 0)
+			return TimeSpan.Zero;
+
+		var isNegative = data[0];
+		var days = MemoryMarshal.Read<int>(data[1..]);
+		var hours = (int) data[5];
+		var minutes = (int) data[6];
+		var seconds = (int) data[7];
+		var microseconds = data.Length == 8 ? 0 : MemoryMarshal.Read<int>(data[8..]);
+
+		if (isNegative != 0)
+		{
+			days = -days;
+			hours = -hours;
+			minutes = -minutes;
+			seconds = -seconds;
+			microseconds = -microseconds;
+		}
+
+#if NET7_0_OR_GREATER
+		return new TimeSpan(days, hours, minutes, seconds, microseconds / 1000, microseconds % 1000);
+#else
+		return new TimeSpan(days, hours, minutes, seconds) + TimeSpan.FromTicks(microseconds * 10);
+#endif
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
new file mode 100644
index 000000000..ad3b8f3f3
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
@@ -0,0 +1,21 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinaryUnsignedInt16ColumnReader : IColumnReader
+{
+	internal static BinaryUnsignedInt16ColumnReader Instance { get; } = new BinaryUnsignedInt16ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return MemoryMarshal.Read<ushort>(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return (int) MemoryMarshal.Read<ushort>(data);
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
new file mode 100644
index 000000000..fc73983b1
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
@@ -0,0 +1,21 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinaryUnsignedInt32ColumnReader : IColumnReader
+{
+	internal static BinaryUnsignedInt32ColumnReader Instance { get; } = new BinaryUnsignedInt32ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return MemoryMarshal.Read<uint>(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return checked((int) MemoryMarshal.Read<uint>(data));
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
new file mode 100644
index 000000000..4eef811e5
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
@@ -0,0 +1,21 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinaryUnsignedInt64ColumnReader : IColumnReader
+{
+	internal static BinaryUnsignedInt64ColumnReader Instance { get; } = new BinaryUnsignedInt64ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return MemoryMarshal.Read<ulong>(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return checked((int) MemoryMarshal.Read<ulong>(data));
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
new file mode 100644
index 000000000..36c4c2f2c
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
@@ -0,0 +1,20 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinaryUnsignedInt8ColumnReader : IColumnReader
+{
+	internal static BinaryUnsignedInt8ColumnReader Instance { get; } = new BinaryUnsignedInt8ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return (byte) data[0];
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return (int) (byte) data[0];
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
new file mode 100644
index 000000000..a5317bc10
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
@@ -0,0 +1,21 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BinaryYearColumnReader : IColumnReader
+{
+	internal static BinaryYearColumnReader Instance { get; } = new BinaryYearColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return (int) MemoryMarshal.Read<short>(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return (int) MemoryMarshal.Read<short>(data);
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
new file mode 100644
index 000000000..d61f08508
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
@@ -0,0 +1,61 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BitColumnReader : IColumnReader
+{
+	internal static BitColumnReader Instance { get; } = new BitColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if ((columnDefinition.ColumnFlags & ColumnFlags.Binary) == 0)
+		{
+			// when the Binary flag IS NOT set, the BIT column is transmitted as MSB byte array
+			ulong bitValue = 0;
+			for (int i = 0; i < data.Length; i++)
+				bitValue = bitValue * 256 + data[i];
+			return bitValue;
+		}
+		else if (columnDefinition.ColumnLength <= 5 && data.Length == 1 && data[0] < (byte) (1 << (int) columnDefinition.ColumnLength))
+		{
+			// a server bug may return the data as binary even when we expect text: https://github.com/mysql-net/MySqlConnector/issues/713
+			// in this case, the data can't possibly be an ASCII digit, so assume it's the binary serialisation of BIT(n) where n <= 5
+			return (ulong) data[0];
+		}
+		else
+		{
+			// when the Binary flag IS set, the BIT column is transmitted as text
+			return !Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		}
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if ((columnDefinition.ColumnFlags & ColumnFlags.Binary) == 0)
+		{
+			// when the Binary flag IS NOT set, the BIT column is transmitted as MSB byte array
+			ulong bitValue = 0;
+			for (int i = 0; i < data.Length; i++)
+				bitValue = bitValue * 256 + data[i];
+			return checked((int) bitValue);
+		}
+		else if (columnDefinition.ColumnLength <= 5 && data.Length == 1 && data[0] < (byte) (1 << (int) columnDefinition.ColumnLength))
+		{
+			// a server bug may return the data as binary even when we expect text: https://github.com/mysql-net/MySqlConnector/issues/713
+			// in this case, the data can't possibly be an ASCII digit, so assume it's the binary serialisation of BIT(n) where n <= 5
+			return checked((int) data[0]);
+		}
+		else
+		{
+			// when the Binary flag IS set, the BIT column is transmitted as text
+			if (!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length)
+			{
+				throw new FormatException();
+			}
+			return checked((int) value);
+		}
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs b/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
new file mode 100644
index 000000000..234bf886c
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
@@ -0,0 +1,20 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class BytesColumnReader : IColumnReader
+{
+	internal static BytesColumnReader Instance { get; } = new BytesColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return data.ToArray();
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
new file mode 100644
index 000000000..aa6e0e79a
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
@@ -0,0 +1,204 @@
+namespace MySqlConnector.ColumnReaders;
+using MySqlConnector.Protocol;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+
+internal class ColumnReaderFactory
+{
+	internal static IColumnReader GetReader(bool binary, ColumnDefinitionPayload columnDefinition, MySqlConnection connection)
+	{
+		var isUnsigned = (columnDefinition.ColumnFlags & ColumnFlags.Unsigned) != 0;
+		if (binary)
+		{
+			switch (columnDefinition.ColumnType)
+			{
+				case ColumnType.Tiny:
+					if (connection.TreatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned)
+						return BinaryBooleanColumnReader.Instance;
+					return isUnsigned ? BinaryUnsignedInt8ColumnReader.Instance : BinarySignedInt8ColumnReader.Instance;
+
+				case ColumnType.Int24:
+				case ColumnType.Long:
+					return isUnsigned ? BinaryUnsignedInt32ColumnReader.Instance : BinarySignedInt32ColumnReader.Instance;
+
+				case ColumnType.Longlong:
+					return isUnsigned ? BinaryUnsignedInt64ColumnReader.Instance : BinarySignedInt64ColumnReader.Instance;
+
+				case ColumnType.Bit:
+					return BitColumnReader.Instance;
+
+				case ColumnType.String:
+					if (connection.GuidFormat == MySqlGuidFormat.Char36
+					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
+						return Guid36ColumnReader.Instance;
+					if (connection.GuidFormat == MySqlGuidFormat.Char32
+					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
+						return Guid32ColumnReader.Instance;
+					goto case ColumnType.VarString;
+
+				case ColumnType.VarString:
+				case ColumnType.VarChar:
+				case ColumnType.TinyBlob:
+				case ColumnType.Blob:
+				case ColumnType.MediumBlob:
+				case ColumnType.LongBlob:
+				case ColumnType.Enum:
+				case ColumnType.Set:
+					if (columnDefinition.CharacterSet == CharacterSet.Binary)
+					{
+						var guidFormat = connection.GuidFormat;
+						if ((guidFormat is MySqlGuidFormat.Binary16 or MySqlGuidFormat.TimeSwapBinary16
+							    or MySqlGuidFormat.LittleEndianBinary16) && columnDefinition.ColumnLength == 16)
+						{
+							switch (guidFormat)
+							{
+							case MySqlGuidFormat.Binary16:
+								return Guid16ColumnReader.Instance;
+							case MySqlGuidFormat.TimeSwapBinary16:
+								return TimeSwapBinary16ColumnReader.Instance;
+							default:
+								return GuidBytesColumnReader.Instance;
+							}
+						}
+						return BytesColumnReader.Instance;
+					}
+					return StringColumnReader.Instance;
+
+				case ColumnType.Json:
+					return StringColumnReader.Instance;
+
+				case ColumnType.Short:
+					return isUnsigned ?
+						BinaryUnsignedInt16ColumnReader.Instance :
+						BinarySignedInt16ColumnReader.Instance;
+
+				case ColumnType.Date:
+				case ColumnType.DateTime:
+				case ColumnType.NewDate:
+				case ColumnType.Timestamp:
+					return new BinaryDateTimeColumnReader(connection);
+
+				case ColumnType.Time:
+					return BinaryTimeColumnReader.Instance;
+
+				case ColumnType.Year:
+					return BinaryYearColumnReader.Instance;
+
+				case ColumnType.Float:
+					return BinaryFloatColumnReader.Instance;
+
+				case ColumnType.Double:
+					return BinaryDoubleColumnReader.Instance;
+
+				case ColumnType.Decimal:
+				case ColumnType.NewDecimal:
+					return DecimalColumnReader.Instance;
+
+				case ColumnType.Geometry:
+					return BytesColumnReader.Instance;
+
+				case ColumnType.Null:
+					return NullColumnReader.Instance;
+
+				default:
+					throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
+			}
+		}
+		else
+		{
+			switch (columnDefinition.ColumnType)
+			{
+				case ColumnType.Tiny:
+					if (connection.TreatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned)
+						return TextBooleanColumnReader.Instance;
+					return isUnsigned ? TextUnsignedInt8ColumnReader.Instance : TextSignedInt8ColumnReader.Instance;
+
+				case ColumnType.Int24:
+				case ColumnType.Long:
+					return isUnsigned ? TextUnsignedInt32ColumnReader.Instance : TextSignedInt32ColumnReader.Instance;
+
+				case ColumnType.Longlong:
+					return isUnsigned ?
+						TextUnsignedInt64ColumnReader.Instance :
+						TextSignedInt64ColumnReader.Instance;
+
+				case ColumnType.Bit:
+					return BitColumnReader.Instance;
+
+				case ColumnType.String:
+					if (connection.GuidFormat == MySqlGuidFormat.Char36
+					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
+						return Guid36ColumnReader.Instance;
+					if (connection.GuidFormat == MySqlGuidFormat.Char32
+					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
+						return Guid32ColumnReader.Instance;
+					goto case ColumnType.VarString;
+
+				case ColumnType.VarString:
+				case ColumnType.VarChar:
+				case ColumnType.TinyBlob:
+				case ColumnType.Blob:
+				case ColumnType.MediumBlob:
+				case ColumnType.LongBlob:
+				case ColumnType.Enum:
+				case ColumnType.Set:
+					if (columnDefinition.CharacterSet == CharacterSet.Binary)
+					{
+						var guidFormat = connection.GuidFormat;
+						if ((guidFormat is MySqlGuidFormat.Binary16 or MySqlGuidFormat.TimeSwapBinary16
+							    or MySqlGuidFormat.LittleEndianBinary16) && columnDefinition.ColumnLength == 16)
+						{
+							switch (guidFormat)
+							{
+								case MySqlGuidFormat.Binary16:
+									return Guid16ColumnReader.Instance;
+								case MySqlGuidFormat.TimeSwapBinary16:
+									return TimeSwapBinary16ColumnReader.Instance;
+								default:
+									return GuidBytesColumnReader.Instance;
+							}
+						}
+						return BytesColumnReader.Instance;
+					}
+					return StringColumnReader.Instance;
+
+				case ColumnType.Json:
+					return StringColumnReader.Instance;
+
+				case ColumnType.Short:
+					return isUnsigned ? TextUnsignedInt16ColumnReader.Instance : TextSignedInt16ColumnReader.Instance;
+
+				case ColumnType.Date:
+				case ColumnType.DateTime:
+				case ColumnType.NewDate:
+				case ColumnType.Timestamp:
+					return new TextDateTimeColumnReader(connection);
+
+				case ColumnType.Time:
+					return TextTimeColumnReader.Instance;
+
+				case ColumnType.Float:
+					return TextFloatColumnReader.Instance;
+
+				case ColumnType.Double:
+					return TextDoubleColumnReader.Instance;
+
+				case ColumnType.Decimal:
+				case ColumnType.NewDecimal:
+					return DecimalColumnReader.Instance;
+
+				case ColumnType.Year:
+					return TextSignedInt32ColumnReader.Instance;
+
+				case ColumnType.Geometry:
+					return BytesColumnReader.Instance;
+
+				case ColumnType.Null:
+					return NullColumnReader.Instance;
+
+				default:
+					throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
+			}
+		}
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
new file mode 100644
index 000000000..85a7e18cd
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
@@ -0,0 +1,24 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class DecimalColumnReader : IColumnReader
+{
+	internal static DecimalColumnReader Instance { get; } = new DecimalColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) && bytesConsumed == data.Length ? decimalValue : throw new FormatException();
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (!Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) || bytesConsumed != data.Length)
+		{
+			throw new FormatException();
+		}
+		return (int) decimalValue;
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
new file mode 100644
index 000000000..977ecc67c
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
@@ -0,0 +1,28 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class Guid16ColumnReader : IColumnReader
+{
+	internal static Guid16ColumnReader Instance { get; } = new Guid16ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		return new Guid(stackalloc byte[16] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
+#else
+		return new Guid(new[]
+		{
+			data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9],
+			data[10], data[11], data[12], data[13], data[14], data[15],
+		});
+#endif
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
new file mode 100644
index 000000000..8f6c4b2c8
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
@@ -0,0 +1,25 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class Guid32ColumnReader : IColumnReader
+{
+	internal static Guid32ColumnReader Instance { get; } = new Guid32ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return Utf8Parser.TryParse(data, out Guid guid, out int guid32BytesConsumed, 'N') &&
+		       guid32BytesConsumed == 32
+			? guid
+			: throw new FormatException(
+				$"Could not parse CHAR(32) value as Guid: {Encoding.UTF8.GetString(data)}");
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs b/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
new file mode 100644
index 000000000..0bab53d97
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
@@ -0,0 +1,25 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class Guid36ColumnReader : IColumnReader
+{
+	internal static Guid36ColumnReader Instance { get; } = new Guid36ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return Utf8Parser.TryParse(data, out Guid guid, out int guid36BytesConsumed, 'D') &&
+		       guid36BytesConsumed == 36
+			? guid
+			: throw new FormatException(
+				$"Could not parse CHAR(36) value as Guid: {Encoding.UTF8.GetString(data)}");
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
new file mode 100644
index 000000000..6957f97c9
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
@@ -0,0 +1,24 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class GuidBytesColumnReader : IColumnReader
+{
+	internal static GuidBytesColumnReader Instance { get; } = new GuidBytesColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		return new Guid(data);
+#else
+		return new Guid(data.ToArray());
+#endif
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/IColumnReader.cs b/src/MySqlConnector/ColumnReaders/IColumnReader.cs
new file mode 100644
index 000000000..4d172f533
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/IColumnReader.cs
@@ -0,0 +1,9 @@
+namespace MySqlConnector.ColumnReaders;
+using MySqlConnector.Protocol.Payloads;
+
+internal interface IColumnReader
+{
+	object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
+
+	int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
+}
diff --git a/src/MySqlConnector/ColumnReaders/NullColumnReader.cs b/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
new file mode 100644
index 000000000..ba83221ed
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
@@ -0,0 +1,25 @@
+namespace MySqlConnector.ColumnReaders;
+
+using System;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using System.Text;
+using MySqlConnector.Core;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class NullColumnReader : IColumnReader
+{
+	internal static NullColumnReader Instance { get; } = new NullColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return DBNull.Value;
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/StringColumnReader.cs b/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
new file mode 100644
index 000000000..b0f88b996
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
@@ -0,0 +1,21 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class StringColumnReader : IColumnReader
+{
+	internal static StringColumnReader Instance { get; } = new StringColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return Encoding.UTF8.GetString(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
new file mode 100644
index 000000000..7527fb93f
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
@@ -0,0 +1,20 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextBooleanColumnReader : IColumnReader
+{
+	internal static TextBooleanColumnReader Instance { get; } = new TextBooleanColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return data[0] != (byte) '0';
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return data[0] != (byte) '0' ? 1 : 0;
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
new file mode 100644
index 000000000..92d6b6b56
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
@@ -0,0 +1,108 @@
+namespace MySqlConnector.ColumnReaders;
+
+using System;
+using System.Buffers.Text;
+using System.Runtime.InteropServices;
+using System.Text;
+using MySqlConnector.Core;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextDateTimeColumnReader : IColumnReader
+{
+	private bool allowZeroDateTime;
+	private bool convertZeroDateTime;
+	private DateTimeKind dateTimeKind;
+	internal TextDateTimeColumnReader(MySqlConnection connection)
+	{
+		this.allowZeroDateTime = connection.AllowZeroDateTime;
+		this.convertZeroDateTime = connection.ConvertZeroDateTime;
+		this.dateTimeKind = connection.DateTimeKind;
+	}
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		Exception? exception = null;
+		if (!Utf8Parser.TryParse(data, out int year, out var bytesConsumed) || bytesConsumed != 4)
+			goto InvalidDateTime;
+		if (data.Length < 5 || data[4] != 45)
+			goto InvalidDateTime;
+		if (!Utf8Parser.TryParse(data[5..], out int month, out bytesConsumed) || bytesConsumed != 2)
+			goto InvalidDateTime;
+		if (data.Length < 8 || data[7] != 45)
+			goto InvalidDateTime;
+		if (!Utf8Parser.TryParse(data[8..], out int day, out bytesConsumed) || bytesConsumed != 2)
+			goto InvalidDateTime;
+
+		if (year == 0 && month == 0 && day == 0)
+		{
+			if (this.convertZeroDateTime)
+				return DateTime.MinValue;
+			if (this.allowZeroDateTime)
+				return default(MySqlDateTime);
+			throw new InvalidCastException("Unable to convert MySQL date/time to System.DateTime, set AllowZeroDateTime=True or ConvertZeroDateTime=True in the connection string. See https://mysqlconnector.net/connection-options/");
+		}
+
+		int hour, minute, second, microseconds;
+		if (data.Length == 10)
+		{
+			hour = 0;
+			minute = 0;
+			second = 0;
+			microseconds = 0;
+		}
+		else
+		{
+			if (data[10] != 32)
+				goto InvalidDateTime;
+			if (!Utf8Parser.TryParse(data[11..], out hour, out bytesConsumed) || bytesConsumed != 2)
+				goto InvalidDateTime;
+			if (data.Length < 14 || data[13] != 58)
+				goto InvalidDateTime;
+			if (!Utf8Parser.TryParse(data[14..], out minute, out bytesConsumed) || bytesConsumed != 2)
+				goto InvalidDateTime;
+			if (data.Length < 17 || data[16] != 58)
+				goto InvalidDateTime;
+			if (!Utf8Parser.TryParse(data[17..], out second, out bytesConsumed) || bytesConsumed != 2)
+				goto InvalidDateTime;
+
+			if (data.Length == 19)
+			{
+				microseconds = 0;
+			}
+			else
+			{
+				if (data[19] != 46)
+					goto InvalidDateTime;
+
+				if (!Utf8Parser.TryParse(data[20..], out microseconds, out bytesConsumed) || bytesConsumed != data.Length - 20)
+					goto InvalidDateTime;
+				for (; bytesConsumed < 6; bytesConsumed++)
+					microseconds *= 10;
+			}
+		}
+
+		try
+		{
+			return this.allowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
+#if NET7_0_OR_GREATER
+				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, this.dateTimeKind);
+#else
+				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, this.dateTimeKind).AddTicks(microseconds % 1000 * 10);
+#endif
+		}
+		catch (Exception ex)
+		{
+			exception = ex;
+		}
+
+InvalidDateTime:
+		throw new FormatException($"Couldn't interpret value as a valid DateTime: {Encoding.UTF8.GetString(data)}", exception);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
new file mode 100644
index 000000000..16727a1d6
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
@@ -0,0 +1,30 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextDoubleColumnReader : IColumnReader
+{
+	internal static TextDoubleColumnReader Instance { get; } = new TextDoubleColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (Utf8Parser.TryParse(data, out double doubleValue, out var doubleBytesConsumed) && doubleBytesConsumed == data.Length)
+			return doubleValue;
+		ReadOnlySpan<byte> doubleInfinity = "-inf"u8;
+		if (data.SequenceEqual(doubleInfinity))
+			return double.NegativeInfinity;
+		if (data.SequenceEqual(doubleInfinity.Slice(1)))
+			return double.PositiveInfinity;
+		if (data.SequenceEqual("nan"u8))
+			return double.NaN;
+		throw new FormatException($"Couldn't parse value as double: {Encoding.UTF8.GetString(data)}");
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
new file mode 100644
index 000000000..9f6340ecb
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
@@ -0,0 +1,30 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using System.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextFloatColumnReader : IColumnReader
+{
+	internal static TextFloatColumnReader Instance { get; } = new TextFloatColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (Utf8Parser.TryParse(data, out float floatValue, out var floatBytesConsumed) && floatBytesConsumed == data.Length)
+			return floatValue;
+		ReadOnlySpan<byte> floatInfinity = "-inf"u8;
+		if (data.SequenceEqual(floatInfinity))
+			return float.NegativeInfinity;
+		if (data.SequenceEqual(floatInfinity.Slice(1)))
+			return float.PositiveInfinity;
+		if (data.SequenceEqual("nan"u8))
+			return float.NaN;
+		throw new FormatException($"Couldn't parse value as float: {Encoding.UTF8.GetString(data)}");
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
new file mode 100644
index 000000000..63260e63e
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
@@ -0,0 +1,24 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextSignedInt16ColumnReader : IColumnReader
+{
+	internal static TextSignedInt16ColumnReader Instance { get; } = new TextSignedInt16ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return !Utf8Parser.TryParse(data, out short value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
+		{
+			throw new FormatException();
+		}
+		return value;
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
new file mode 100644
index 000000000..d9b760083
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
@@ -0,0 +1,20 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextSignedInt32ColumnReader : IColumnReader
+{
+	internal static TextSignedInt32ColumnReader Instance { get; } = new TextSignedInt32ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return !Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return !Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
new file mode 100644
index 000000000..ea2f0f222
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
@@ -0,0 +1,24 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextSignedInt64ColumnReader : IColumnReader
+{
+	internal static TextSignedInt64ColumnReader Instance { get; } = new TextSignedInt64ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return !Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length)
+		{
+			throw new FormatException();
+		}
+		return checked((int) value);
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
new file mode 100644
index 000000000..75ee8bef8
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
@@ -0,0 +1,28 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextSignedInt8ColumnReader : IColumnReader
+{
+	internal static TextSignedInt8ColumnReader Instance { get; } = new TextSignedInt8ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
+		{
+			throw new FormatException();
+		}
+		return (sbyte) value;
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
+		{
+			throw new FormatException();
+		}
+		return value;
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
new file mode 100644
index 000000000..b294b1791
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
@@ -0,0 +1,20 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextTimeColumnReader : IColumnReader
+{
+	internal static TextTimeColumnReader Instance { get; } = new TextTimeColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return Utility.ParseTimeSpan(data);
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
new file mode 100644
index 000000000..cd4fbf164
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
@@ -0,0 +1,24 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextUnsignedInt16ColumnReader : IColumnReader
+{
+	internal static TextUnsignedInt16ColumnReader Instance { get; } = new TextUnsignedInt16ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return !Utf8Parser.TryParse(data, out ushort value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
+		{
+			throw new FormatException();
+		}
+		return value;
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
new file mode 100644
index 000000000..a86250281
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
@@ -0,0 +1,24 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextUnsignedInt32ColumnReader : IColumnReader
+{
+	internal static TextUnsignedInt32ColumnReader Instance { get; } = new TextUnsignedInt32ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return !Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (!Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length)
+		{
+			throw new FormatException();
+		}
+		return checked((int) value);
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
new file mode 100644
index 000000000..16335f95a
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
@@ -0,0 +1,24 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextUnsignedInt64ColumnReader : IColumnReader
+{
+	internal static TextUnsignedInt64ColumnReader Instance { get; } = new TextUnsignedInt64ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		return !Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length)
+		{
+			throw new FormatException();
+		}
+		return checked((int) value);
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
new file mode 100644
index 000000000..8e4bf07ff
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
@@ -0,0 +1,28 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TextUnsignedInt8ColumnReader : IColumnReader
+{
+	internal static TextUnsignedInt8ColumnReader Instance { get; } = new TextUnsignedInt8ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
+		{
+			throw new FormatException();
+		}
+		return (byte) value;
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
+		{
+			throw new FormatException();
+		}
+		return value;
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs
new file mode 100644
index 000000000..29f011162
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs
@@ -0,0 +1,28 @@
+namespace MySqlConnector.ColumnReaders;
+using System.Buffers.Text;
+using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
+
+internal sealed class TimeSwapBinary16ColumnReader : IColumnReader
+{
+	internal static TimeSwapBinary16ColumnReader Instance { get; } = new TimeSwapBinary16ColumnReader();
+
+	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		return new Guid(stackalloc byte[16] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
+#else
+		return new Guid(new[]
+		{
+			data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9],
+			data[10], data[11], data[12], data[13], data[14], data[15],
+		});
+#endif
+	}
+
+	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/Core/BinaryRow.cs b/src/MySqlConnector/Core/BinaryRow.cs
index cf984a11b..cebc6b397 100644
--- a/src/MySqlConnector/Core/BinaryRow.cs
+++ b/src/MySqlConnector/Core/BinaryRow.cs
@@ -11,7 +11,7 @@ namespace MySqlConnector.Core;
 internal sealed class BinaryRow : Row
 {
 	public BinaryRow(ResultSet resultSet)
-		: base(resultSet)
+		: base(true, resultSet)
 	{
 	}
 
@@ -60,174 +60,4 @@ protected override void GetDataOffsets(ReadOnlySpan<byte> data, int[] dataOffset
 			reader.Offset += dataLengths[column];
 		}
 	}
-
-	protected override int GetInt32Core(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		var isUnsigned = (columnDefinition.ColumnFlags & ColumnFlags.Unsigned) != 0;
-		return columnDefinition.ColumnType switch
-		{
-			ColumnType.Tiny => isUnsigned ? (int) data[0] : (sbyte) data[0],
-			ColumnType.Decimal or ColumnType.NewDecimal => Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) && bytesConsumed == data.Length ? checked((int) decimalValue) : throw new FormatException(),
-			ColumnType.Int24 or ColumnType.Long => isUnsigned ? checked((int) MemoryMarshal.Read<uint>(data)) : MemoryMarshal.Read<int>(data),
-			ColumnType.Longlong => isUnsigned ? checked((int) MemoryMarshal.Read<ulong>(data)) : checked((int) MemoryMarshal.Read<long>(data)),
-			ColumnType.Short => isUnsigned ? (int) MemoryMarshal.Read<ushort>(data) : MemoryMarshal.Read<short>(data),
-			ColumnType.Year => MemoryMarshal.Read<short>(data),
-			_ => throw new FormatException(),
-		};
-	}
-
-	protected override object GetValueCore(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		var isUnsigned = (columnDefinition.ColumnFlags & ColumnFlags.Unsigned) != 0;
-		switch (columnDefinition.ColumnType)
-		{
-		case ColumnType.Tiny:
-			if (Connection.TreatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned)
-				return data[0] != 0;
-			return isUnsigned ? (object) data[0] : (sbyte) data[0];
-
-		case ColumnType.Int24:
-		case ColumnType.Long:
-			return isUnsigned ? (object) MemoryMarshal.Read<uint>(data) : MemoryMarshal.Read<int>(data);
-
-		case ColumnType.Longlong:
-			return isUnsigned ? (object) MemoryMarshal.Read<ulong>(data) : MemoryMarshal.Read<long>(data);
-
-		case ColumnType.Bit:
-			return ReadBit(data, columnDefinition);
-
-		case ColumnType.String:
-			if (Connection.GuidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
-				return Utf8Parser.TryParse(data, out Guid guid, out int guid36BytesConsumed, 'D') && guid36BytesConsumed == 36 ? guid : throw new FormatException($"Could not parse CHAR(36) value as Guid: {Encoding.UTF8.GetString(data)}");
-			if (Connection.GuidFormat == MySqlGuidFormat.Char32 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
-				return Utf8Parser.TryParse(data, out Guid guid, out int guid32BytesConsumed, 'N') && guid32BytesConsumed == 32 ? guid : throw new FormatException($"Could not parse CHAR(32) value as Guid: {Encoding.UTF8.GetString(data)}");
-			goto case ColumnType.VarString;
-
-		case ColumnType.VarString:
-		case ColumnType.VarChar:
-		case ColumnType.TinyBlob:
-		case ColumnType.Blob:
-		case ColumnType.MediumBlob:
-		case ColumnType.LongBlob:
-		case ColumnType.Enum:
-		case ColumnType.Set:
-			if (columnDefinition.CharacterSet == CharacterSet.Binary)
-			{
-				var guidFormat = Connection.GuidFormat;
-				if ((guidFormat is MySqlGuidFormat.Binary16 or MySqlGuidFormat.TimeSwapBinary16 or MySqlGuidFormat.LittleEndianBinary16) && columnDefinition.ColumnLength == 16)
-					return CreateGuidFromBytes(guidFormat, data);
-
-				return data.ToArray();
-			}
-			return Encoding.UTF8.GetString(data);
-
-		case ColumnType.Json:
-			return Encoding.UTF8.GetString(data);
-
-		case ColumnType.Short:
-			return isUnsigned ? (object) MemoryMarshal.Read<ushort>(data) : MemoryMarshal.Read<short>(data);
-
-		case ColumnType.Date:
-		case ColumnType.DateTime:
-		case ColumnType.NewDate:
-		case ColumnType.Timestamp:
-			return ReadDateTime(data);
-
-		case ColumnType.Time:
-			return ReadTimeSpan(data);
-
-		case ColumnType.Year:
-			return (int) MemoryMarshal.Read<short>(data);
-
-		case ColumnType.Float:
-			return MemoryMarshal.Read<float>(data);
-
-		case ColumnType.Double:
-			return MemoryMarshal.Read<double>(data);
-
-		case ColumnType.Decimal:
-		case ColumnType.NewDecimal:
-			return Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) && bytesConsumed == data.Length ? decimalValue : throw new FormatException();
-
-		case ColumnType.Geometry:
-			return data.ToArray();
-
-		default:
-			throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
-		}
-	}
-
-	private object ReadDateTime(ReadOnlySpan<byte> value)
-	{
-		if (value.Length == 0)
-		{
-			if (Connection.ConvertZeroDateTime)
-				return DateTime.MinValue;
-			if (Connection.AllowZeroDateTime)
-				return default(MySqlDateTime);
-			throw new InvalidCastException("Unable to convert MySQL date/time to System.DateTime.");
-		}
-
-		int year = value[0] + value[1] * 256;
-		int month = value[2];
-		int day = value[3];
-
-		int hour, minute, second;
-		if (value.Length <= 4)
-		{
-			hour = 0;
-			minute = 0;
-			second = 0;
-		}
-		else
-		{
-			hour = value[4];
-			minute = value[5];
-			second = value[6];
-		}
-
-		var microseconds = value.Length <= 7 ? 0 : MemoryMarshal.Read<int>(value[7..]);
-
-		try
-		{
-			return Connection.AllowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
-#if NET7_0_OR_GREATER
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, Connection.DateTimeKind);
-#else
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, Connection.DateTimeKind).AddTicks(microseconds % 1000 * 10);
-#endif
-		}
-		catch (Exception ex)
-		{
-			throw new FormatException($"Couldn't interpret value as a valid DateTime: {Encoding.UTF8.GetString(value)}", ex);
-		}
-	}
-
-	private static TimeSpan ReadTimeSpan(ReadOnlySpan<byte> value)
-	{
-		if (value.Length == 0)
-			return TimeSpan.Zero;
-
-		var isNegative = value[0];
-		var days = MemoryMarshal.Read<int>(value[1..]);
-		var hours = (int) value[5];
-		var minutes = (int) value[6];
-		var seconds = (int) value[7];
-		var microseconds = value.Length == 8 ? 0 : MemoryMarshal.Read<int>(value[8..]);
-
-		if (isNegative != 0)
-		{
-			days = -days;
-			hours = -hours;
-			minutes = -minutes;
-			seconds = -seconds;
-			microseconds = -microseconds;
-		}
-
-#if NET7_0_OR_GREATER
-		return new TimeSpan(days, hours, minutes, seconds, microseconds / 1000, microseconds % 1000);
-#else
-		return new TimeSpan(days, hours, minutes, seconds) + TimeSpan.FromTicks(microseconds * 10);
-#endif
-	}
 }
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index d1a1c9ed8..2e466b463 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -147,12 +147,12 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 						// server supports metadata caching, but has re-sent it, so something has changed since last prepare/execution
 						if (Session.SupportsCachedPreparedMetadata && DataReader.LastUsedPreparedStatement is { } preparedStatement)
 							preparedStatement.Columns = ColumnDefinitions;
+					}
 
-						if (!Session.SupportsDeprecateEof)
-						{
-							payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
-							EofPayload.Create(payload.Span);
-						}
+					if (!Session.SupportsDeprecateEof)
+					{
+						payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
+						EofPayload.Create(payload.Span);
 					}
 
 					if (ColumnDefinitions.Length == (Command?.OutParameters?.Count + 1) && ColumnDefinitions[0].Name == SingleCommandPayloadCreator.OutParameterSentinelColumnName)
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 79a062a22..36deee9dd 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -2,6 +2,7 @@
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text;
+using MySqlConnector.ColumnReaders;
 using MySqlConnector.Protocol;
 using MySqlConnector.Protocol.Payloads;
 using MySqlConnector.Utilities;
@@ -35,7 +36,7 @@ public object GetValue(int ordinal)
 
 		var data = m_data.Slice(m_dataOffsets[ordinal], m_dataLengths[ordinal]).Span;
 		var columnDefinition = ResultSet.ColumnDefinitions[ordinal];
-		return GetValueCore(data, columnDefinition);
+		return columnReaders[ordinal].ReadValue(data, columnDefinition);
 	}
 
 	public bool GetBoolean(int ordinal)
@@ -172,41 +173,17 @@ public short GetInt16(int ordinal)
 
 	public int GetInt32(int ordinal)
 	{
-		if (ordinal < 0 || ordinal >= ResultSet.ColumnDefinitions!.Length || m_dataOffsets[ordinal] == -1)
-			return (int) GetValue(ordinal);
+		if (ordinal < 0 || ordinal >= ResultSet.ColumnDefinitions!.Length)
+			throw new ArgumentOutOfRangeException(nameof(ordinal), $"value must be between 0 and {ResultSet.ColumnDefinitions!.Length - 1}");
 
-		var columnDefinition = ResultSet.ColumnDefinitions[ordinal];
-		if (columnDefinition.ColumnType is ColumnType.Decimal or ColumnType.NewDecimal)
-		{
-			return (int) (decimal) GetValue(ordinal);
-		}
-		else if (columnDefinition.ColumnType is not ColumnType.Tiny and not ColumnType.Short
-			and not ColumnType.Int24 and not ColumnType.Long and not ColumnType.Longlong
-			and not ColumnType.Bit and not ColumnType.Year)
-		{
-			throw new InvalidCastException($"Can't convert {ResultSet.GetColumnType(ordinal)} to Int32");
-		}
+		if (m_dataOffsets[ordinal] == -1)
+			throw new InvalidCastException("Couldn't interpret null value as int");
 
 		var data = m_data.Slice(m_dataOffsets[ordinal], m_dataLengths[ordinal]).Span;
-
-		if (columnDefinition.ColumnType == ColumnType.Bit)
-			return checked((int) ReadBit(data, columnDefinition));
-
-		var result = GetInt32Core(data, columnDefinition);
-		if (columnDefinition.ColumnType == ColumnType.Tiny &&
-			Connection.TreatTinyAsBoolean &&
-			columnDefinition.ColumnLength == 1 &&
-			(columnDefinition.ColumnFlags & ColumnFlags.Unsigned) == 0 &&
-			result != 0)
-		{
-			// coerce all non-zero TINYINT(1) results to 1, since it represents a BOOL value
-			result = 1;
-		}
-		return result;
+		var columnDefinition = ResultSet.ColumnDefinitions[ordinal];
+		return columnReaders[ordinal].ReadInt32(data, columnDefinition);
 	}
 
-	protected abstract int GetInt32Core(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
-
 	public long GetInt64(int ordinal)
 	{
 		var value = GetValue(ordinal);
@@ -393,17 +370,17 @@ public int GetValues(object[] values)
 
 	public object this[string name] => GetValue(ResultSet.GetOrdinal(name));
 
-	protected Row(ResultSet resultSet)
+	protected Row(bool binary, ResultSet resultSet)
 	{
 		ResultSet = resultSet;
 		m_dataOffsets = new int[ResultSet.ColumnDefinitions!.Length];
 		m_dataLengths = new int[ResultSet.ColumnDefinitions.Length];
+		columnReaders = Array.ConvertAll(ResultSet.ColumnDefinitions,
+			new Converter<ColumnDefinitionPayload, IColumnReader>(column => ColumnReaderFactory.GetReader(binary, column, resultSet.Connection)));
 	}
 
 	protected abstract Row CloneCore();
 
-	protected abstract object GetValueCore(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
-
 	protected abstract void GetDataOffsets(ReadOnlySpan<byte> data, int[] dataOffsets, int[] dataLengths);
 
 	protected ResultSet ResultSet { get; }
@@ -506,32 +483,6 @@ protected static unsafe Guid CreateGuidFromBytes(MySqlGuidFormat guidFormat, Rea
 #endif
 		};
 
-	protected static ulong ReadBit(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if ((columnDefinition.ColumnFlags & ColumnFlags.Binary) == 0)
-		{
-			// when the Binary flag IS NOT set, the BIT column is transmitted as MSB byte array
-			ulong bitValue = 0;
-			for (int i = 0; i < data.Length; i++)
-				bitValue = bitValue * 256 + data[i];
-			return bitValue;
-		}
-		else if (columnDefinition.ColumnLength <= 5 && data.Length == 1 && data[0] < (byte) (1 << (int) columnDefinition.ColumnLength))
-		{
-			// a server bug may return the data as binary even when we expect text: https://github.com/mysql-net/MySqlConnector/issues/713
-			// in this case, the data can't possibly be an ASCII digit, so assume it's the binary serialisation of BIT(n) where n <= 5
-			return (ulong) data[0];
-		}
-		else
-		{
-			// when the Binary flag IS set, the BIT column is transmitted as text
-			return ParseUInt64(data);
-		}
-	}
-
-	protected static ulong ParseUInt64(ReadOnlySpan<byte> data) =>
-		!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-
 	private void CheckBinaryColumn(int ordinal)
 	{
 		if (m_dataOffsets[ordinal] == -1)
@@ -567,4 +518,5 @@ private static void CheckBufferArguments<T>(long dataOffset, T[] buffer, int buf
 	private readonly int[] m_dataOffsets;
 	private readonly int[] m_dataLengths;
 	private ReadOnlyMemory<byte> m_data;
+	private IColumnReader[] columnReaders;
 }
diff --git a/src/MySqlConnector/Core/TextRow.cs b/src/MySqlConnector/Core/TextRow.cs
index c8c172e02..b80cdbb04 100644
--- a/src/MySqlConnector/Core/TextRow.cs
+++ b/src/MySqlConnector/Core/TextRow.cs
@@ -10,7 +10,7 @@ namespace MySqlConnector.Core;
 internal sealed class TextRow : Row
 {
 	public TextRow(ResultSet resultSet)
-		: base(resultSet)
+		: base(false, resultSet)
 	{
 	}
 
@@ -27,122 +27,4 @@ protected override void GetDataOffsets(ReadOnlySpan<byte> data, int[] dataOffset
 			reader.Offset += dataLengths[column];
 		}
 	}
-
-	protected override int GetInt32Core(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new OverflowException() : value;
-
-	protected override object GetValueCore(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		var isUnsigned = (columnDefinition.ColumnFlags & ColumnFlags.Unsigned) != 0;
-		switch (columnDefinition.ColumnType)
-		{
-		case ColumnType.Tiny:
-			var value = ParseInt32(data);
-			if (Connection.TreatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned)
-				return value != 0;
-			return isUnsigned ? (object) (byte) value : (sbyte) value;
-
-		case ColumnType.Int24:
-		case ColumnType.Long:
-			return isUnsigned ? (object) ParseUInt32(data) : ParseInt32(data);
-
-		case ColumnType.Longlong:
-			return isUnsigned ? (object) ParseUInt64(data) : ParseInt64(data);
-
-		case ColumnType.Bit:
-			return ReadBit(data, columnDefinition);
-
-		case ColumnType.String:
-			if (Connection.GuidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
-				return Utf8Parser.TryParse(data, out Guid guid, out int guid36BytesConsumed, 'D') && guid36BytesConsumed == 36 ? guid : throw new FormatException($"Could not parse CHAR(36) value as Guid: {Encoding.UTF8.GetString(data)}");
-			if (Connection.GuidFormat == MySqlGuidFormat.Char32 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
-				return Utf8Parser.TryParse(data, out Guid guid, out int guid32BytesConsumed, 'N') && guid32BytesConsumed == 32 ? guid : throw new FormatException($"Could not parse CHAR(32) value as Guid: {Encoding.UTF8.GetString(data)}");
-			goto case ColumnType.VarString;
-
-		case ColumnType.VarString:
-		case ColumnType.VarChar:
-		case ColumnType.TinyBlob:
-		case ColumnType.Blob:
-		case ColumnType.MediumBlob:
-		case ColumnType.LongBlob:
-		case ColumnType.Enum:
-		case ColumnType.Set:
-			if (columnDefinition.CharacterSet == CharacterSet.Binary)
-			{
-				var guidFormat = Connection.GuidFormat;
-				if ((guidFormat is MySqlGuidFormat.Binary16 or MySqlGuidFormat.TimeSwapBinary16 or MySqlGuidFormat.LittleEndianBinary16) && columnDefinition.ColumnLength == 16)
-					return CreateGuidFromBytes(guidFormat, data);
-
-				return data.ToArray();
-			}
-			return Encoding.UTF8.GetString(data);
-
-		case ColumnType.Json:
-			return Encoding.UTF8.GetString(data);
-
-		case ColumnType.Short:
-			return isUnsigned ? (object) ParseUInt16(data) : ParseInt16(data);
-
-		case ColumnType.Date:
-		case ColumnType.DateTime:
-		case ColumnType.NewDate:
-		case ColumnType.Timestamp:
-			return ParseDateTime(data);
-
-		case ColumnType.Time:
-			return Utility.ParseTimeSpan(data);
-
-		case ColumnType.Year:
-			return ParseInt32(data);
-
-		case ColumnType.Float:
-			if (Utf8Parser.TryParse(data, out float floatValue, out var floatBytesConsumed) && floatBytesConsumed == data.Length)
-				return floatValue;
-			ReadOnlySpan<byte> floatInfinity = "-inf"u8;
-			if (data.SequenceEqual(floatInfinity))
-				return float.NegativeInfinity;
-			if (data.SequenceEqual(floatInfinity.Slice(1)))
-				return float.PositiveInfinity;
-			if (data.SequenceEqual("nan"u8))
-				return float.NaN;
-			throw new FormatException($"Couldn't parse value as float: {Encoding.UTF8.GetString(data)}");
-
-		case ColumnType.Double:
-			if (Utf8Parser.TryParse(data, out double doubleValue, out var doubleBytesConsumed) && doubleBytesConsumed == data.Length)
-				return doubleValue;
-			ReadOnlySpan<byte> doubleInfinity = "-inf"u8;
-			if (data.SequenceEqual(doubleInfinity))
-				return double.NegativeInfinity;
-			if (data.SequenceEqual(doubleInfinity.Slice(1)))
-				return double.PositiveInfinity;
-			if (data.SequenceEqual("nan"u8))
-				return double.NaN;
-			throw new FormatException($"Couldn't parse value as double: {Encoding.UTF8.GetString(data)}");
-
-		case ColumnType.Decimal:
-		case ColumnType.NewDecimal:
-			return Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) && bytesConsumed == data.Length ? decimalValue : throw new FormatException();
-
-		case ColumnType.Geometry:
-			return data.ToArray();
-
-		default:
-			throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
-		}
-	}
-
-	private static short ParseInt16(ReadOnlySpan<byte> data) =>
-		!Utf8Parser.TryParse(data, out short value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-
-	private static ushort ParseUInt16(ReadOnlySpan<byte> data) =>
-		!Utf8Parser.TryParse(data, out ushort value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-
-	private static int ParseInt32(ReadOnlySpan<byte> data) =>
-		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-
-	private static uint ParseUInt32(ReadOnlySpan<byte> data) =>
-		!Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-
-	private static long ParseInt64(ReadOnlySpan<byte> data) =>
-		!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/tests/Benchmark/Benchmark.csproj b/tests/Benchmark/Benchmark.csproj
index 3c401d1b2..b668e841c 100644
--- a/tests/Benchmark/Benchmark.csproj
+++ b/tests/Benchmark/Benchmark.csproj
@@ -6,7 +6,12 @@
   </PropertyGroup>
   <ItemGroup>
     <PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
-    <PackageReference Include="MySqlConnector" Version="2.2.0-rc.1" />
+    <PackageReference Include="MySqlConnector" Version="2.2.6" />
     <PackageReference Include="MySql.Data" Version="8.0.32" />
   </ItemGroup>
+
+<!--  <ItemGroup>-->
+<!--    <ProjectReference Include="..\..\src\MySqlConnector\MySqlConnector.csproj"/>-->
+<!--    <Using Include="MySqlConnector.Core"/>-->
+<!--  </ItemGroup>-->
 </Project>

From 3ada2766b07079dbb6e22ca5b2329a19cd13ddb6 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 12:21:03 -0700
Subject: [PATCH 104/393] Move using directives above namespace. Remove unused
 usings.

Enforce code style checks in the build to make this a build error.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .editorconfig                                         |  2 ++
 src/Directory.Build.props                             |  1 +
 .../ColumnReaders/BinaryBooleanColumnReader.cs        |  6 ++----
 .../ColumnReaders/BinaryDateTimeColumnReader.cs       |  8 ++------
 .../ColumnReaders/BinaryDoubleColumnReader.cs         |  6 ++----
 .../ColumnReaders/BinaryFloatColumnReader.cs          |  6 ++----
 .../ColumnReaders/BinarySignedInt16ColumnReader.cs    |  6 ++----
 .../ColumnReaders/BinarySignedInt32ColumnReader.cs    |  6 ++----
 .../ColumnReaders/BinarySignedInt64ColumnReader.cs    |  6 ++----
 .../ColumnReaders/BinarySignedInt8ColumnReader.cs     |  6 ++----
 .../ColumnReaders/BinaryTimeColumnReader.cs           |  6 ++----
 .../ColumnReaders/BinaryUnsignedInt16ColumnReader.cs  |  6 ++----
 .../ColumnReaders/BinaryUnsignedInt32ColumnReader.cs  |  6 ++----
 .../ColumnReaders/BinaryUnsignedInt64ColumnReader.cs  |  6 ++----
 .../ColumnReaders/BinaryUnsignedInt8ColumnReader.cs   |  6 ++----
 .../ColumnReaders/BinaryYearColumnReader.cs           |  6 ++----
 src/MySqlConnector/ColumnReaders/BitColumnReader.cs   |  5 ++---
 src/MySqlConnector/ColumnReaders/BytesColumnReader.cs |  6 ++----
 .../ColumnReaders/ColumnReaderFactory.cs              |  3 ++-
 .../ColumnReaders/DecimalColumnReader.cs              |  5 ++---
 .../ColumnReaders/Guid16ColumnReader.cs               |  6 ++----
 .../ColumnReaders/Guid32ColumnReader.cs               |  4 ++--
 .../ColumnReaders/Guid36ColumnReader.cs               |  4 ++--
 .../ColumnReaders/GuidBytesColumnReader.cs            |  6 ++----
 src/MySqlConnector/ColumnReaders/IColumnReader.cs     |  3 ++-
 src/MySqlConnector/ColumnReaders/NullColumnReader.cs  | 11 ++---------
 .../ColumnReaders/StringColumnReader.cs               |  5 ++---
 .../ColumnReaders/TextBooleanColumnReader.cs          |  6 ++----
 .../ColumnReaders/TextDateTimeColumnReader.cs         |  8 ++------
 .../ColumnReaders/TextDoubleColumnReader.cs           |  4 ++--
 .../ColumnReaders/TextFloatColumnReader.cs            |  4 ++--
 .../ColumnReaders/TextSignedInt16ColumnReader.cs      |  5 ++---
 .../ColumnReaders/TextSignedInt32ColumnReader.cs      |  5 ++---
 .../ColumnReaders/TextSignedInt64ColumnReader.cs      |  5 ++---
 .../ColumnReaders/TextSignedInt8ColumnReader.cs       |  5 ++---
 .../ColumnReaders/TextTimeColumnReader.cs             |  5 ++---
 .../ColumnReaders/TextUnsignedInt16ColumnReader.cs    |  5 ++---
 .../ColumnReaders/TextUnsignedInt32ColumnReader.cs    |  5 ++---
 .../ColumnReaders/TextUnsignedInt64ColumnReader.cs    |  5 ++---
 .../ColumnReaders/TextUnsignedInt8ColumnReader.cs     |  5 ++---
 .../ColumnReaders/TimeSwapBinary16ColumnReader.cs     |  6 ++----
 src/MySqlConnector/Core/BinaryRow.cs                  |  5 -----
 src/MySqlConnector/Core/TextRow.cs                    |  5 -----
 43 files changed, 81 insertions(+), 149 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index cca51b0ea..6531ccd44 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -84,6 +84,8 @@ dotnet_diagnostic.CA2000.severity = none # Use recommended Dispose pattern.
 dotnet_diagnostic.CA2100.severity = none # Review SQL queries for security vulnerabilities.
 dotnet_diagnostic.CA2213.severity = silent # Disposable fields should be disposed.
 dotnet_diagnostic.CA5398.severity = none # Avoid hardcoded SslProtocols values.
+dotnet_diagnostic.IDE0003.severity = silent # Name can be simplified
+dotnet_diagnostic.IDE0065.severity = error # Using directives must be placed outside of a namespace declaration.
 dotnet_diagnostic.SA1003.severity = none # Operator must not be followed by whitespace.
 dotnet_diagnostic.SA1008.severity = none # Opening parenthesis must not be preceded by a space.
 dotnet_diagnostic.SA1009.severity = none # Closing parenthesis must not be followed by a space.
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 11784649d..53a9d1697 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -21,6 +21,7 @@
     <AnalysisLevel>latest-all</AnalysisLevel>
     <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
     <RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
+    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
     <PublishRepositoryUrl>true</PublishRepositoryUrl>
     <EmbedUntrackedSources>true</EmbedUntrackedSources>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
diff --git a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
index 3654d4472..817eca91f 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
@@ -1,8 +1,6 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryBooleanColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
index f24614204..97f8c4847 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
@@ -1,14 +1,10 @@
-namespace MySqlConnector.ColumnReaders;
-
-using System;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using System.Text;
-using MySqlConnector.Core;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
+namespace MySqlConnector.ColumnReaders;
+
 internal sealed class BinaryDateTimeColumnReader : IColumnReader
 {
 	private bool allowZeroDateTime;
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
index 153e8c846..6e4a4dc4e 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
@@ -1,9 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryDoubleColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
index 9502260ba..42e8a613b 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
@@ -1,9 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryFloatColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
index d2f02ab2d..10441cfa1 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
@@ -1,9 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinarySignedInt16ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
index 7b75e7141..de99f351f 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
@@ -1,9 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinarySignedInt32ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
index 72e196c60..0f8872de4 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
@@ -1,9 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinarySignedInt64ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
index ca492e399..7aaa811ca 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
@@ -1,8 +1,6 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinarySignedInt8ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
index 52333ea82..ceb6bbba8 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
@@ -1,9 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryTimeColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
index ad3b8f3f3..83d89030e 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
@@ -1,9 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryUnsignedInt16ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
index fc73983b1..d8d5498cc 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
@@ -1,9 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryUnsignedInt32ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
index 4eef811e5..ba33fbde6 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
@@ -1,9 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryUnsignedInt64ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
index 36c4c2f2c..76b514f2c 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
@@ -1,8 +1,6 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryUnsignedInt8ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
index a5317bc10..312a297e6 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
@@ -1,9 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryYearColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
index d61f08508..b5c64d4a2 100644
--- a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
@@ -1,9 +1,8 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using MySqlConnector.Protocol;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BitColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs b/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
index 234bf886c..23929c346 100644
--- a/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
@@ -1,8 +1,6 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BytesColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
index aa6e0e79a..bbd4eaf27 100644
--- a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
+++ b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
@@ -1,8 +1,9 @@
-namespace MySqlConnector.ColumnReaders;
 using MySqlConnector.Protocol;
 using MySqlConnector.Protocol.Payloads;
 using MySqlConnector.Protocol.Serialization;
 
+namespace MySqlConnector.ColumnReaders;
+
 internal class ColumnReaderFactory
 {
 	internal static IColumnReader GetReader(bool binary, ColumnDefinitionPayload columnDefinition, MySqlConnection connection)
diff --git a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
index 85a7e18cd..a039eded6 100644
--- a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
@@ -1,8 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class DecimalColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
index 977ecc67c..d1c02a21a 100644
--- a/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
@@ -1,8 +1,6 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class Guid16ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
index 8f6c4b2c8..aee8e5824 100644
--- a/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
@@ -1,10 +1,10 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using System.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
+namespace MySqlConnector.ColumnReaders;
+
 internal sealed class Guid32ColumnReader : IColumnReader
 {
 	internal static Guid32ColumnReader Instance { get; } = new Guid32ColumnReader();
diff --git a/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs b/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
index 0bab53d97..d822ccd28 100644
--- a/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
@@ -1,10 +1,10 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using System.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
+namespace MySqlConnector.ColumnReaders;
+
 internal sealed class Guid36ColumnReader : IColumnReader
 {
 	internal static Guid36ColumnReader Instance { get; } = new Guid36ColumnReader();
diff --git a/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
index 6957f97c9..729630753 100644
--- a/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
@@ -1,8 +1,6 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class GuidBytesColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/IColumnReader.cs b/src/MySqlConnector/ColumnReaders/IColumnReader.cs
index 4d172f533..dbb3facd5 100644
--- a/src/MySqlConnector/ColumnReaders/IColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/IColumnReader.cs
@@ -1,6 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
 using MySqlConnector.Protocol.Payloads;
 
+namespace MySqlConnector.ColumnReaders;
+
 internal interface IColumnReader
 {
 	object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
diff --git a/src/MySqlConnector/ColumnReaders/NullColumnReader.cs b/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
index ba83221ed..d91f91ce1 100644
--- a/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
@@ -1,13 +1,6 @@
-namespace MySqlConnector.ColumnReaders;
-
-using System;
-using System.Buffers.Text;
-using System.Runtime.InteropServices;
-using System.Text;
-using MySqlConnector.Core;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class NullColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/StringColumnReader.cs b/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
index b0f88b996..fa574b6ca 100644
--- a/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
@@ -1,10 +1,9 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using System.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
+namespace MySqlConnector.ColumnReaders;
+
 internal sealed class StringColumnReader : IColumnReader
 {
 	internal static StringColumnReader Instance { get; } = new StringColumnReader();
diff --git a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
index 7527fb93f..f8b1db419 100644
--- a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
@@ -1,8 +1,6 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextBooleanColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
index 92d6b6b56..616e62577 100644
--- a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
@@ -1,14 +1,10 @@
-namespace MySqlConnector.ColumnReaders;
-
-using System;
 using System.Buffers.Text;
-using System.Runtime.InteropServices;
 using System.Text;
-using MySqlConnector.Core;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
+namespace MySqlConnector.ColumnReaders;
+
 internal sealed class TextDateTimeColumnReader : IColumnReader
 {
 	private bool allowZeroDateTime;
diff --git a/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
index 16727a1d6..0307f4615 100644
--- a/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
@@ -1,10 +1,10 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using System.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
+namespace MySqlConnector.ColumnReaders;
+
 internal sealed class TextDoubleColumnReader : IColumnReader
 {
 	internal static TextDoubleColumnReader Instance { get; } = new TextDoubleColumnReader();
diff --git a/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
index 9f6340ecb..3aed4fd8b 100644
--- a/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
@@ -1,10 +1,10 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using System.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
+namespace MySqlConnector.ColumnReaders;
+
 internal sealed class TextFloatColumnReader : IColumnReader
 {
 	internal static TextFloatColumnReader Instance { get; } = new TextFloatColumnReader();
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
index 63260e63e..f727d71a3 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
@@ -1,8 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextSignedInt16ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
index d9b760083..8967338bd 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
@@ -1,8 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextSignedInt32ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
index ea2f0f222..b00444d2f 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
@@ -1,8 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextSignedInt64ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
index 75ee8bef8..06915f3f6 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
@@ -1,8 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextSignedInt8ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
index b294b1791..88769651f 100644
--- a/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
@@ -1,9 +1,8 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
+namespace MySqlConnector.ColumnReaders;
+
 internal sealed class TextTimeColumnReader : IColumnReader
 {
 	internal static TextTimeColumnReader Instance { get; } = new TextTimeColumnReader();
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
index cd4fbf164..4fe317133 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
@@ -1,8 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextUnsignedInt16ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
index a86250281..53ac0d23c 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
@@ -1,8 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextUnsignedInt32ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
index 16335f95a..800465646 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
@@ -1,8 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextUnsignedInt64ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
index 8e4bf07ff..0512f6887 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
@@ -1,8 +1,7 @@
-namespace MySqlConnector.ColumnReaders;
 using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextUnsignedInt8ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs
index 29f011162..8b87c6007 100644
--- a/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs
@@ -1,8 +1,6 @@
-namespace MySqlConnector.ColumnReaders;
-using System.Buffers.Text;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
+
+namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TimeSwapBinary16ColumnReader : IColumnReader
 {
diff --git a/src/MySqlConnector/Core/BinaryRow.cs b/src/MySqlConnector/Core/BinaryRow.cs
index cebc6b397..fdcd6a637 100644
--- a/src/MySqlConnector/Core/BinaryRow.cs
+++ b/src/MySqlConnector/Core/BinaryRow.cs
@@ -1,10 +1,5 @@
-using System.Buffers.Text;
-using System.Runtime.InteropServices;
-using System.Text;
 using MySqlConnector.Protocol;
-using MySqlConnector.Protocol.Payloads;
 using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
 
 namespace MySqlConnector.Core;
 
diff --git a/src/MySqlConnector/Core/TextRow.cs b/src/MySqlConnector/Core/TextRow.cs
index b80cdbb04..ee5794c72 100644
--- a/src/MySqlConnector/Core/TextRow.cs
+++ b/src/MySqlConnector/Core/TextRow.cs
@@ -1,9 +1,4 @@
-using System.Buffers.Text;
-using System.Text;
-using MySqlConnector.Protocol;
-using MySqlConnector.Protocol.Payloads;
 using MySqlConnector.Protocol.Serialization;
-using MySqlConnector.Utilities;
 
 namespace MySqlConnector.Core;
 

From 10a3c08a9d7cbea5a2de1a57c6cb048cb96390d7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 12:28:16 -0700
Subject: [PATCH 105/393] Make fields readonly.

Fields should be placed at the end of the class. Field names should have a "m_" prefix.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .editorconfig                                 |  1 -
 .../BinaryDateTimeColumnReader.cs             | 23 ++++++++++---------
 .../ColumnReaders/TextDateTimeColumnReader.cs | 23 ++++++++++---------
 3 files changed, 24 insertions(+), 23 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index 6531ccd44..f4cc4ec50 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -84,7 +84,6 @@ dotnet_diagnostic.CA2000.severity = none # Use recommended Dispose pattern.
 dotnet_diagnostic.CA2100.severity = none # Review SQL queries for security vulnerabilities.
 dotnet_diagnostic.CA2213.severity = silent # Disposable fields should be disposed.
 dotnet_diagnostic.CA5398.severity = none # Avoid hardcoded SslProtocols values.
-dotnet_diagnostic.IDE0003.severity = silent # Name can be simplified
 dotnet_diagnostic.IDE0065.severity = error # Using directives must be placed outside of a namespace declaration.
 dotnet_diagnostic.SA1003.severity = none # Operator must not be followed by whitespace.
 dotnet_diagnostic.SA1008.severity = none # Opening parenthesis must not be preceded by a space.
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
index 97f8c4847..a9a5a6f2a 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
@@ -7,23 +7,20 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryDateTimeColumnReader : IColumnReader
 {
-	private bool allowZeroDateTime;
-	private bool convertZeroDateTime;
-	private DateTimeKind dateTimeKind;
 	internal BinaryDateTimeColumnReader(MySqlConnection connection)
 	{
-		this.allowZeroDateTime = connection.AllowZeroDateTime;
-		this.convertZeroDateTime = connection.ConvertZeroDateTime;
-		this.dateTimeKind = connection.DateTimeKind;
+		m_allowZeroDateTime = connection.AllowZeroDateTime;
+		m_convertZeroDateTime = connection.ConvertZeroDateTime;
+		m_dateTimeKind = connection.DateTimeKind;
 	}
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (data.Length == 0)
 		{
-			if (this.convertZeroDateTime)
+			if (m_convertZeroDateTime)
 				return DateTime.MinValue;
-			if (this.allowZeroDateTime)
+			if (m_allowZeroDateTime)
 				return default(MySqlDateTime);
 			throw new InvalidCastException("Unable to convert MySQL date/time to System.DateTime.");
 		}
@@ -50,11 +47,11 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 
 		try
 		{
-			return this.allowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
+			return m_allowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
 #if NET7_0_OR_GREATER
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, this.dateTimeKind);
+				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, m_dateTimeKind);
 #else
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, this.dateTimeKind).AddTicks(microseconds % 1000 * 10);
+				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, m_dateTimeKind).AddTicks(microseconds % 1000 * 10);
 #endif
 		}
 		catch (Exception ex)
@@ -67,4 +64,8 @@ public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefi
 	{
 		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
 	}
+
+	private readonly bool m_allowZeroDateTime;
+	private readonly bool m_convertZeroDateTime;
+	private readonly DateTimeKind m_dateTimeKind;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
index 616e62577..437366a49 100644
--- a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
@@ -7,14 +7,11 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextDateTimeColumnReader : IColumnReader
 {
-	private bool allowZeroDateTime;
-	private bool convertZeroDateTime;
-	private DateTimeKind dateTimeKind;
 	internal TextDateTimeColumnReader(MySqlConnection connection)
 	{
-		this.allowZeroDateTime = connection.AllowZeroDateTime;
-		this.convertZeroDateTime = connection.ConvertZeroDateTime;
-		this.dateTimeKind = connection.DateTimeKind;
+		m_allowZeroDateTime = connection.AllowZeroDateTime;
+		m_convertZeroDateTime = connection.ConvertZeroDateTime;
+		m_dateTimeKind = connection.DateTimeKind;
 	}
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
@@ -33,9 +30,9 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 
 		if (year == 0 && month == 0 && day == 0)
 		{
-			if (this.convertZeroDateTime)
+			if (m_convertZeroDateTime)
 				return DateTime.MinValue;
-			if (this.allowZeroDateTime)
+			if (m_allowZeroDateTime)
 				return default(MySqlDateTime);
 			throw new InvalidCastException("Unable to convert MySQL date/time to System.DateTime, set AllowZeroDateTime=True or ConvertZeroDateTime=True in the connection string. See https://mysqlconnector.net/connection-options/");
 		}
@@ -81,11 +78,11 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 
 		try
 		{
-			return this.allowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
+			return m_allowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
 #if NET7_0_OR_GREATER
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, this.dateTimeKind);
+				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, m_dateTimeKind);
 #else
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, this.dateTimeKind).AddTicks(microseconds % 1000 * 10);
+				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, m_dateTimeKind).AddTicks(microseconds % 1000 * 10);
 #endif
 		}
 		catch (Exception ex)
@@ -101,4 +98,8 @@ public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefi
 	{
 		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
 	}
+
+	private readonly bool m_allowZeroDateTime;
+	private readonly bool m_convertZeroDateTime;
+	private readonly DateTimeKind m_dateTimeKind;
 }

From 1cc81efbb218269a98eaf23e4e53ae469a707e40 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 12:33:18 -0700
Subject: [PATCH 106/393] Enforce use of readonly fields.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .editorconfig                  | 1 +
 src/MySqlConnector/Core/Row.cs | 8 ++++----
 2 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index f4cc4ec50..ee0f4b83f 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -84,6 +84,7 @@ dotnet_diagnostic.CA2000.severity = none # Use recommended Dispose pattern.
 dotnet_diagnostic.CA2100.severity = none # Review SQL queries for security vulnerabilities.
 dotnet_diagnostic.CA2213.severity = silent # Disposable fields should be disposed.
 dotnet_diagnostic.CA5398.severity = none # Avoid hardcoded SslProtocols values.
+dotnet_diagnostic.IDE0044.severity = error # Make fields readonly.
 dotnet_diagnostic.IDE0065.severity = error # Using directives must be placed outside of a namespace declaration.
 dotnet_diagnostic.SA1003.severity = none # Operator must not be followed by whitespace.
 dotnet_diagnostic.SA1008.severity = none # Opening parenthesis must not be preceded by a space.
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 36deee9dd..3a0da6b6b 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -36,7 +36,7 @@ public object GetValue(int ordinal)
 
 		var data = m_data.Slice(m_dataOffsets[ordinal], m_dataLengths[ordinal]).Span;
 		var columnDefinition = ResultSet.ColumnDefinitions[ordinal];
-		return columnReaders[ordinal].ReadValue(data, columnDefinition);
+		return m_columnReaders[ordinal].ReadValue(data, columnDefinition);
 	}
 
 	public bool GetBoolean(int ordinal)
@@ -181,7 +181,7 @@ public int GetInt32(int ordinal)
 
 		var data = m_data.Slice(m_dataOffsets[ordinal], m_dataLengths[ordinal]).Span;
 		var columnDefinition = ResultSet.ColumnDefinitions[ordinal];
-		return columnReaders[ordinal].ReadInt32(data, columnDefinition);
+		return m_columnReaders[ordinal].ReadInt32(data, columnDefinition);
 	}
 
 	public long GetInt64(int ordinal)
@@ -375,7 +375,7 @@ protected Row(bool binary, ResultSet resultSet)
 		ResultSet = resultSet;
 		m_dataOffsets = new int[ResultSet.ColumnDefinitions!.Length];
 		m_dataLengths = new int[ResultSet.ColumnDefinitions.Length];
-		columnReaders = Array.ConvertAll(ResultSet.ColumnDefinitions,
+		m_columnReaders = Array.ConvertAll(ResultSet.ColumnDefinitions,
 			new Converter<ColumnDefinitionPayload, IColumnReader>(column => ColumnReaderFactory.GetReader(binary, column, resultSet.Connection)));
 	}
 
@@ -517,6 +517,6 @@ private static void CheckBufferArguments<T>(long dataOffset, T[] buffer, int buf
 
 	private readonly int[] m_dataOffsets;
 	private readonly int[] m_dataLengths;
+	private readonly IColumnReader[] m_columnReaders;
 	private ReadOnlyMemory<byte> m_data;
-	private IColumnReader[] columnReaders;
 }

From 4d0560412933a87d351478b08052510743d3ae46 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 13:15:29 -0700
Subject: [PATCH 107/393] Make constructors/singletons public.

Use target-typed new.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs   | 2 +-
 src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs  | 2 +-
 src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs    | 2 +-
 src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs     | 2 +-
 .../ColumnReaders/BinarySignedInt16ColumnReader.cs              | 2 +-
 .../ColumnReaders/BinarySignedInt32ColumnReader.cs              | 2 +-
 .../ColumnReaders/BinarySignedInt64ColumnReader.cs              | 2 +-
 .../ColumnReaders/BinarySignedInt8ColumnReader.cs               | 2 +-
 src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs      | 2 +-
 .../ColumnReaders/BinaryUnsignedInt16ColumnReader.cs            | 2 +-
 .../ColumnReaders/BinaryUnsignedInt32ColumnReader.cs            | 2 +-
 .../ColumnReaders/BinaryUnsignedInt64ColumnReader.cs            | 2 +-
 .../ColumnReaders/BinaryUnsignedInt8ColumnReader.cs             | 2 +-
 src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs      | 2 +-
 src/MySqlConnector/ColumnReaders/BitColumnReader.cs             | 2 +-
 src/MySqlConnector/ColumnReaders/BytesColumnReader.cs           | 2 +-
 src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs         | 2 +-
 src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs          | 2 +-
 src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs          | 2 +-
 src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs          | 2 +-
 src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs       | 2 +-
 src/MySqlConnector/ColumnReaders/NullColumnReader.cs            | 2 +-
 src/MySqlConnector/ColumnReaders/StringColumnReader.cs          | 2 +-
 src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs     | 2 +-
 src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs    | 2 +-
 src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs      | 2 +-
 src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs       | 2 +-
 src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs | 2 +-
 src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs | 2 +-
 src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs | 2 +-
 src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs  | 2 +-
 src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs        | 2 +-
 .../ColumnReaders/TextUnsignedInt16ColumnReader.cs              | 2 +-
 .../ColumnReaders/TextUnsignedInt32ColumnReader.cs              | 2 +-
 .../ColumnReaders/TextUnsignedInt64ColumnReader.cs              | 2 +-
 .../ColumnReaders/TextUnsignedInt8ColumnReader.cs               | 2 +-
 .../ColumnReaders/TimeSwapBinary16ColumnReader.cs               | 2 +-
 37 files changed, 37 insertions(+), 37 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
index 817eca91f..fb4278dda 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
@@ -4,7 +4,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryBooleanColumnReader : IColumnReader
 {
-	internal static BinaryBooleanColumnReader Instance { get; } = new BinaryBooleanColumnReader();
+	public static BinaryBooleanColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
index a9a5a6f2a..5f14eb514 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
@@ -7,7 +7,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryDateTimeColumnReader : IColumnReader
 {
-	internal BinaryDateTimeColumnReader(MySqlConnection connection)
+	public BinaryDateTimeColumnReader(MySqlConnection connection)
 	{
 		m_allowZeroDateTime = connection.AllowZeroDateTime;
 		m_convertZeroDateTime = connection.ConvertZeroDateTime;
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
index 6e4a4dc4e..b6e4d0b8c 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryDoubleColumnReader : IColumnReader
 {
-	internal static BinaryDoubleColumnReader Instance { get; } = new BinaryDoubleColumnReader();
+	public static BinaryDoubleColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
index 42e8a613b..480504c4f 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryFloatColumnReader : IColumnReader
 {
-	internal static BinaryFloatColumnReader Instance { get; } = new BinaryFloatColumnReader();
+	public static BinaryFloatColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
index 10441cfa1..ead1c24f8 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinarySignedInt16ColumnReader : IColumnReader
 {
-	internal static BinarySignedInt16ColumnReader Instance { get; } = new BinarySignedInt16ColumnReader();
+	public static BinarySignedInt16ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
index de99f351f..0408ddf82 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinarySignedInt32ColumnReader : IColumnReader
 {
-	internal static BinarySignedInt32ColumnReader Instance { get; } = new BinarySignedInt32ColumnReader();
+	public static BinarySignedInt32ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
index 0f8872de4..8b97addd9 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinarySignedInt64ColumnReader : IColumnReader
 {
-	internal static BinarySignedInt64ColumnReader Instance { get; } = new BinarySignedInt64ColumnReader();
+	public static BinarySignedInt64ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
index 7aaa811ca..24beb34b6 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
@@ -4,7 +4,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinarySignedInt8ColumnReader : IColumnReader
 {
-	internal static BinarySignedInt8ColumnReader Instance { get; } = new BinarySignedInt8ColumnReader();
+	public static BinarySignedInt8ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
index ceb6bbba8..bb6c529ad 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryTimeColumnReader : IColumnReader
 {
-	internal static BinaryTimeColumnReader Instance { get; } = new BinaryTimeColumnReader();
+	public static BinaryTimeColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
index 83d89030e..5fed7cf46 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryUnsignedInt16ColumnReader : IColumnReader
 {
-	internal static BinaryUnsignedInt16ColumnReader Instance { get; } = new BinaryUnsignedInt16ColumnReader();
+	public static BinaryUnsignedInt16ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
index d8d5498cc..451370b4c 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryUnsignedInt32ColumnReader : IColumnReader
 {
-	internal static BinaryUnsignedInt32ColumnReader Instance { get; } = new BinaryUnsignedInt32ColumnReader();
+	public static BinaryUnsignedInt32ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
index ba33fbde6..86dc8d6e3 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryUnsignedInt64ColumnReader : IColumnReader
 {
-	internal static BinaryUnsignedInt64ColumnReader Instance { get; } = new BinaryUnsignedInt64ColumnReader();
+	public static BinaryUnsignedInt64ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
index 76b514f2c..1850a41eb 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
@@ -4,7 +4,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryUnsignedInt8ColumnReader : IColumnReader
 {
-	internal static BinaryUnsignedInt8ColumnReader Instance { get; } = new BinaryUnsignedInt8ColumnReader();
+	public static BinaryUnsignedInt8ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
index 312a297e6..386242594 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BinaryYearColumnReader : IColumnReader
 {
-	internal static BinaryYearColumnReader Instance { get; } = new BinaryYearColumnReader();
+	public static BinaryYearColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
index b5c64d4a2..f8cab81fc 100644
--- a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
@@ -6,7 +6,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BitColumnReader : IColumnReader
 {
-	internal static BitColumnReader Instance { get; } = new BitColumnReader();
+	public static BitColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs b/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
index 23929c346..2c35dd940 100644
--- a/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
@@ -4,7 +4,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class BytesColumnReader : IColumnReader
 {
-	internal static BytesColumnReader Instance { get; } = new BytesColumnReader();
+	public static BytesColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
index a039eded6..86381fc74 100644
--- a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class DecimalColumnReader : IColumnReader
 {
-	internal static DecimalColumnReader Instance { get; } = new DecimalColumnReader();
+	public static DecimalColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
index d1c02a21a..de01f9152 100644
--- a/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
@@ -4,7 +4,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class Guid16ColumnReader : IColumnReader
 {
-	internal static Guid16ColumnReader Instance { get; } = new Guid16ColumnReader();
+	public static Guid16ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
index aee8e5824..f64dd2cf2 100644
--- a/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
@@ -7,7 +7,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class Guid32ColumnReader : IColumnReader
 {
-	internal static Guid32ColumnReader Instance { get; } = new Guid32ColumnReader();
+	public static Guid32ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs b/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
index d822ccd28..51b516b4c 100644
--- a/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
@@ -7,7 +7,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class Guid36ColumnReader : IColumnReader
 {
-	internal static Guid36ColumnReader Instance { get; } = new Guid36ColumnReader();
+	public static Guid36ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
index 729630753..5de55e139 100644
--- a/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
@@ -4,7 +4,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class GuidBytesColumnReader : IColumnReader
 {
-	internal static GuidBytesColumnReader Instance { get; } = new GuidBytesColumnReader();
+	public static GuidBytesColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/NullColumnReader.cs b/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
index d91f91ce1..27bc1ec84 100644
--- a/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
@@ -4,7 +4,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class NullColumnReader : IColumnReader
 {
-	internal static NullColumnReader Instance { get; } = new NullColumnReader();
+	public static NullColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/StringColumnReader.cs b/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
index fa574b6ca..16a95810d 100644
--- a/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
@@ -6,7 +6,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class StringColumnReader : IColumnReader
 {
-	internal static StringColumnReader Instance { get; } = new StringColumnReader();
+	public static StringColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
index f8b1db419..1a27af530 100644
--- a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
@@ -4,7 +4,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextBooleanColumnReader : IColumnReader
 {
-	internal static TextBooleanColumnReader Instance { get; } = new TextBooleanColumnReader();
+	public static TextBooleanColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
index 437366a49..2cd01800c 100644
--- a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
@@ -7,7 +7,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextDateTimeColumnReader : IColumnReader
 {
-	internal TextDateTimeColumnReader(MySqlConnection connection)
+	public TextDateTimeColumnReader(MySqlConnection connection)
 	{
 		m_allowZeroDateTime = connection.AllowZeroDateTime;
 		m_convertZeroDateTime = connection.ConvertZeroDateTime;
diff --git a/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
index 0307f4615..fabdadf01 100644
--- a/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
@@ -7,7 +7,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextDoubleColumnReader : IColumnReader
 {
-	internal static TextDoubleColumnReader Instance { get; } = new TextDoubleColumnReader();
+	public static TextDoubleColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
index 3aed4fd8b..07cb77338 100644
--- a/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
@@ -7,7 +7,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextFloatColumnReader : IColumnReader
 {
-	internal static TextFloatColumnReader Instance { get; } = new TextFloatColumnReader();
+	public static TextFloatColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
index f727d71a3..207a520c7 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextSignedInt16ColumnReader : IColumnReader
 {
-	internal static TextSignedInt16ColumnReader Instance { get; } = new TextSignedInt16ColumnReader();
+	public static TextSignedInt16ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
index 8967338bd..059203bc8 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextSignedInt32ColumnReader : IColumnReader
 {
-	internal static TextSignedInt32ColumnReader Instance { get; } = new TextSignedInt32ColumnReader();
+	public static TextSignedInt32ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
index b00444d2f..494cb9423 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextSignedInt64ColumnReader : IColumnReader
 {
-	internal static TextSignedInt64ColumnReader Instance { get; } = new TextSignedInt64ColumnReader();
+	public static TextSignedInt64ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
index 06915f3f6..5e4c3435b 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextSignedInt8ColumnReader : IColumnReader
 {
-	internal static TextSignedInt8ColumnReader Instance { get; } = new TextSignedInt8ColumnReader();
+	public static TextSignedInt8ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
index 88769651f..e905663c9 100644
--- a/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextTimeColumnReader : IColumnReader
 {
-	internal static TextTimeColumnReader Instance { get; } = new TextTimeColumnReader();
+	public static TextTimeColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
index 4fe317133..eefbb3312 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextUnsignedInt16ColumnReader : IColumnReader
 {
-	internal static TextUnsignedInt16ColumnReader Instance { get; } = new TextUnsignedInt16ColumnReader();
+	public static TextUnsignedInt16ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
index 53ac0d23c..f06056cfd 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextUnsignedInt32ColumnReader : IColumnReader
 {
-	internal static TextUnsignedInt32ColumnReader Instance { get; } = new TextUnsignedInt32ColumnReader();
+	public static TextUnsignedInt32ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
index 800465646..3692f703f 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextUnsignedInt64ColumnReader : IColumnReader
 {
-	internal static TextUnsignedInt64ColumnReader Instance { get; } = new TextUnsignedInt64ColumnReader();
+	public static TextUnsignedInt64ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
index 0512f6887..920e4fd2b 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TextUnsignedInt8ColumnReader : IColumnReader
 {
-	internal static TextUnsignedInt8ColumnReader Instance { get; } = new TextUnsignedInt8ColumnReader();
+	public static TextUnsignedInt8ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs
index 8b87c6007..c040298de 100644
--- a/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs
@@ -4,7 +4,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal sealed class TimeSwapBinary16ColumnReader : IColumnReader
 {
-	internal static TimeSwapBinary16ColumnReader Instance { get; } = new TimeSwapBinary16ColumnReader();
+	public static TimeSwapBinary16ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{

From e400f960af65cc5e2d604321495cce75e64c22e6 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 13:22:36 -0700
Subject: [PATCH 108/393] Name readers consistent with MySqlGuidFormat enum
 values.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../ColumnReaders/ColumnReaderFactory.cs      | 20 +++++++++----------
 ...nReader.cs => GuidBinary16ColumnReader.cs} |  4 ++--
 ...umnReader.cs => GuidChar32ColumnReader.cs} |  4 ++--
 ...umnReader.cs => GuidChar36ColumnReader.cs} |  4 ++--
 ...> GuidLittleEndianBinary16ColumnReader.cs} |  4 ++--
 ...cs => GuidTimeSwapBinary16ColumnReader.cs} |  4 ++--
 6 files changed, 20 insertions(+), 20 deletions(-)
 rename src/MySqlConnector/ColumnReaders/{Guid16ColumnReader.cs => GuidBinary16ColumnReader.cs} (86%)
 rename src/MySqlConnector/ColumnReaders/{Guid32ColumnReader.cs => GuidChar32ColumnReader.cs} (84%)
 rename src/MySqlConnector/ColumnReaders/{Guid36ColumnReader.cs => GuidChar36ColumnReader.cs} (84%)
 rename src/MySqlConnector/ColumnReaders/{GuidBytesColumnReader.cs => GuidLittleEndianBinary16ColumnReader.cs} (76%)
 rename src/MySqlConnector/ColumnReaders/{TimeSwapBinary16ColumnReader.cs => GuidTimeSwapBinary16ColumnReader.cs} (84%)

diff --git a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
index bbd4eaf27..b7a1babdd 100644
--- a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
+++ b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
@@ -31,10 +31,10 @@ internal static IColumnReader GetReader(bool binary, ColumnDefinitionPayload col
 				case ColumnType.String:
 					if (connection.GuidFormat == MySqlGuidFormat.Char36
 					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
-						return Guid36ColumnReader.Instance;
+						return GuidChar36ColumnReader.Instance;
 					if (connection.GuidFormat == MySqlGuidFormat.Char32
 					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
-						return Guid32ColumnReader.Instance;
+						return GuidChar32ColumnReader.Instance;
 					goto case ColumnType.VarString;
 
 				case ColumnType.VarString:
@@ -54,11 +54,11 @@ internal static IColumnReader GetReader(bool binary, ColumnDefinitionPayload col
 							switch (guidFormat)
 							{
 							case MySqlGuidFormat.Binary16:
-								return Guid16ColumnReader.Instance;
+								return GuidBinary16ColumnReader.Instance;
 							case MySqlGuidFormat.TimeSwapBinary16:
-								return TimeSwapBinary16ColumnReader.Instance;
+								return GuidTimeSwapBinary16ColumnReader.Instance;
 							default:
-								return GuidBytesColumnReader.Instance;
+								return GuidLittleEndianBinary16ColumnReader.Instance;
 							}
 						}
 						return BytesColumnReader.Instance;
@@ -129,10 +129,10 @@ internal static IColumnReader GetReader(bool binary, ColumnDefinitionPayload col
 				case ColumnType.String:
 					if (connection.GuidFormat == MySqlGuidFormat.Char36
 					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
-						return Guid36ColumnReader.Instance;
+						return GuidChar36ColumnReader.Instance;
 					if (connection.GuidFormat == MySqlGuidFormat.Char32
 					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
-						return Guid32ColumnReader.Instance;
+						return GuidChar32ColumnReader.Instance;
 					goto case ColumnType.VarString;
 
 				case ColumnType.VarString:
@@ -152,11 +152,11 @@ internal static IColumnReader GetReader(bool binary, ColumnDefinitionPayload col
 							switch (guidFormat)
 							{
 								case MySqlGuidFormat.Binary16:
-									return Guid16ColumnReader.Instance;
+									return GuidBinary16ColumnReader.Instance;
 								case MySqlGuidFormat.TimeSwapBinary16:
-									return TimeSwapBinary16ColumnReader.Instance;
+									return GuidTimeSwapBinary16ColumnReader.Instance;
 								default:
-									return GuidBytesColumnReader.Instance;
+									return GuidLittleEndianBinary16ColumnReader.Instance;
 							}
 						}
 						return BytesColumnReader.Instance;
diff --git a/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
similarity index 86%
rename from src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
rename to src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
index de01f9152..dbfe41445 100644
--- a/src/MySqlConnector/ColumnReaders/Guid16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
@@ -2,9 +2,9 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class Guid16ColumnReader : IColumnReader
+internal sealed class GuidBinary16ColumnReader : IColumnReader
 {
-	public static Guid16ColumnReader Instance { get; } = new();
+	public static GuidBinary16ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs
similarity index 84%
rename from src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
rename to src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs
index f64dd2cf2..973e980bd 100644
--- a/src/MySqlConnector/ColumnReaders/Guid32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs
@@ -5,9 +5,9 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class Guid32ColumnReader : IColumnReader
+internal sealed class GuidChar32ColumnReader : IColumnReader
 {
-	public static Guid32ColumnReader Instance { get; } = new();
+	public static GuidChar32ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs
similarity index 84%
rename from src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
rename to src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs
index 51b516b4c..fa86c3b30 100644
--- a/src/MySqlConnector/ColumnReaders/Guid36ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs
@@ -5,9 +5,9 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class Guid36ColumnReader : IColumnReader
+internal sealed class GuidChar36ColumnReader : IColumnReader
 {
-	public static Guid36ColumnReader Instance { get; } = new();
+	public static GuidChar36ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
similarity index 76%
rename from src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
rename to src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
index 5de55e139..883b68986 100644
--- a/src/MySqlConnector/ColumnReaders/GuidBytesColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
@@ -2,9 +2,9 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class GuidBytesColumnReader : IColumnReader
+internal sealed class GuidLittleEndianBinary16ColumnReader : IColumnReader
 {
-	public static GuidBytesColumnReader Instance { get; } = new();
+	public static GuidLittleEndianBinary16ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
diff --git a/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
similarity index 84%
rename from src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs
rename to src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
index c040298de..9fdf22779 100644
--- a/src/MySqlConnector/ColumnReaders/TimeSwapBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
@@ -2,9 +2,9 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TimeSwapBinary16ColumnReader : IColumnReader
+internal sealed class GuidTimeSwapBinary16ColumnReader : IColumnReader
 {
-	public static TimeSwapBinary16ColumnReader Instance { get; } = new();
+	public static GuidTimeSwapBinary16ColumnReader Instance { get; } = new();
 
 	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{

From e130a7dc8c28694f23d588c1be40ffca3313c22b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 13:35:52 -0700
Subject: [PATCH 109/393] Make ColumnReader an abstract base class.

This allows common type handling to be shared. If additional specialized GetX methods are added, they can have a virtual implementation in the base class that's overridden when needed.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../ColumnReaders/BinaryBooleanColumnReader.cs      |  6 +++---
 .../ColumnReaders/BinaryDateTimeColumnReader.cs     |  9 ++-------
 .../ColumnReaders/BinaryDoubleColumnReader.cs       |  9 ++-------
 .../ColumnReaders/BinaryFloatColumnReader.cs        |  9 ++-------
 .../ColumnReaders/BinarySignedInt16ColumnReader.cs  |  8 ++++----
 .../ColumnReaders/BinarySignedInt32ColumnReader.cs  |  6 +++---
 .../ColumnReaders/BinarySignedInt64ColumnReader.cs  |  6 +++---
 .../ColumnReaders/BinarySignedInt8ColumnReader.cs   |  6 +++---
 .../ColumnReaders/BinaryTimeColumnReader.cs         |  9 ++-------
 .../BinaryUnsignedInt16ColumnReader.cs              |  8 ++++----
 .../BinaryUnsignedInt32ColumnReader.cs              |  6 +++---
 .../BinaryUnsignedInt64ColumnReader.cs              |  6 +++---
 .../ColumnReaders/BinaryUnsignedInt8ColumnReader.cs | 10 +++++-----
 .../ColumnReaders/BinaryYearColumnReader.cs         |  8 ++++----
 src/MySqlConnector/ColumnReaders/BitColumnReader.cs |  6 +++---
 .../ColumnReaders/BytesColumnReader.cs              |  9 ++-------
 src/MySqlConnector/ColumnReaders/ColumnReader.cs    | 13 +++++++++++++
 .../ColumnReaders/ColumnReaderFactory.cs            |  2 +-
 .../ColumnReaders/DecimalColumnReader.cs            |  6 +++---
 .../ColumnReaders/GuidBinary16ColumnReader.cs       |  9 ++-------
 .../ColumnReaders/GuidChar32ColumnReader.cs         |  9 ++-------
 .../ColumnReaders/GuidChar36ColumnReader.cs         |  9 ++-------
 .../GuidLittleEndianBinary16ColumnReader.cs         |  9 ++-------
 .../GuidTimeSwapBinary16ColumnReader.cs             |  9 ++-------
 src/MySqlConnector/ColumnReaders/IColumnReader.cs   | 10 ----------
 .../ColumnReaders/NullColumnReader.cs               |  9 ++-------
 .../ColumnReaders/StringColumnReader.cs             |  9 ++-------
 .../ColumnReaders/TextBooleanColumnReader.cs        |  6 +++---
 .../ColumnReaders/TextDateTimeColumnReader.cs       |  9 ++-------
 .../ColumnReaders/TextDoubleColumnReader.cs         |  9 ++-------
 .../ColumnReaders/TextFloatColumnReader.cs          |  9 ++-------
 .../ColumnReaders/TextSignedInt16ColumnReader.cs    |  6 +++---
 .../ColumnReaders/TextSignedInt32ColumnReader.cs    |  6 +++---
 .../ColumnReaders/TextSignedInt64ColumnReader.cs    |  6 +++---
 .../ColumnReaders/TextSignedInt8ColumnReader.cs     |  6 +++---
 .../ColumnReaders/TextTimeColumnReader.cs           |  9 ++-------
 .../ColumnReaders/TextUnsignedInt16ColumnReader.cs  |  6 +++---
 .../ColumnReaders/TextUnsignedInt32ColumnReader.cs  |  6 +++---
 .../ColumnReaders/TextUnsignedInt64ColumnReader.cs  |  6 +++---
 .../ColumnReaders/TextUnsignedInt8ColumnReader.cs   |  6 +++---
 src/MySqlConnector/Core/Row.cs                      |  4 ++--
 41 files changed, 116 insertions(+), 193 deletions(-)
 create mode 100644 src/MySqlConnector/ColumnReaders/ColumnReader.cs
 delete mode 100644 src/MySqlConnector/ColumnReaders/IColumnReader.cs

diff --git a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
index fb4278dda..262826658 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
@@ -2,16 +2,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinaryBooleanColumnReader : IColumnReader
+internal sealed class BinaryBooleanColumnReader : ColumnReader
 {
 	public static BinaryBooleanColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return data[0] != 0;
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return data[0] != 0 ? 1 : 0;
 	}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
index 5f14eb514..48b548a39 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
@@ -5,7 +5,7 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinaryDateTimeColumnReader : IColumnReader
+internal sealed class BinaryDateTimeColumnReader : ColumnReader
 {
 	public BinaryDateTimeColumnReader(MySqlConnection connection)
 	{
@@ -14,7 +14,7 @@ public BinaryDateTimeColumnReader(MySqlConnection connection)
 		m_dateTimeKind = connection.DateTimeKind;
 	}
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (data.Length == 0)
 		{
@@ -60,11 +60,6 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 		}
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
-
 	private readonly bool m_allowZeroDateTime;
 	private readonly bool m_convertZeroDateTime;
 	private readonly DateTimeKind m_dateTimeKind;
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
index b6e4d0b8c..2558a092f 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
@@ -3,17 +3,12 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinaryDoubleColumnReader : IColumnReader
+internal sealed class BinaryDoubleColumnReader : ColumnReader
 {
 	public static BinaryDoubleColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return MemoryMarshal.Read<double>(data);
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
index 480504c4f..528ce347c 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
@@ -3,17 +3,12 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinaryFloatColumnReader : IColumnReader
+internal sealed class BinaryFloatColumnReader : ColumnReader
 {
 	public static BinaryFloatColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return MemoryMarshal.Read<float>(data);
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
index ead1c24f8..5b95529dd 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
@@ -3,17 +3,17 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinarySignedInt16ColumnReader : IColumnReader
+internal sealed class BinarySignedInt16ColumnReader : ColumnReader
 {
 	public static BinarySignedInt16ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return MemoryMarshal.Read<short>(data);
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
-		return (int) MemoryMarshal.Read<short>(data);
+		return MemoryMarshal.Read<short>(data);
 	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
index 0408ddf82..2f8171729 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinarySignedInt32ColumnReader : IColumnReader
+internal sealed class BinarySignedInt32ColumnReader : ColumnReader
 {
 	public static BinarySignedInt32ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return MemoryMarshal.Read<int>(data);
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return MemoryMarshal.Read<int>(data);
 	}
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
index 8b97addd9..2bc7571e4 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinarySignedInt64ColumnReader : IColumnReader
+internal sealed class BinarySignedInt64ColumnReader : ColumnReader
 {
 	public static BinarySignedInt64ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return MemoryMarshal.Read<long>(data);
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return checked((int) MemoryMarshal.Read<long>(data));
 	}
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
index 24beb34b6..1bbf09bd2 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
@@ -2,16 +2,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinarySignedInt8ColumnReader : IColumnReader
+internal sealed class BinarySignedInt8ColumnReader : ColumnReader
 {
 	public static BinarySignedInt8ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return (sbyte) data[0];
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return (int) (sbyte) data[0];
 	}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
index bb6c529ad..f8ff90a41 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
@@ -3,11 +3,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinaryTimeColumnReader : IColumnReader
+internal sealed class BinaryTimeColumnReader : ColumnReader
 {
 	public static BinaryTimeColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (data.Length == 0)
 			return TimeSpan.Zero;
@@ -34,9 +34,4 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 		return new TimeSpan(days, hours, minutes, seconds) + TimeSpan.FromTicks(microseconds * 10);
 #endif
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
index 5fed7cf46..97c9ede38 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
@@ -3,17 +3,17 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinaryUnsignedInt16ColumnReader : IColumnReader
+internal sealed class BinaryUnsignedInt16ColumnReader : ColumnReader
 {
 	public static BinaryUnsignedInt16ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return MemoryMarshal.Read<ushort>(data);
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
-		return (int) MemoryMarshal.Read<ushort>(data);
+		return MemoryMarshal.Read<ushort>(data);
 	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
index 451370b4c..ac11fe082 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinaryUnsignedInt32ColumnReader : IColumnReader
+internal sealed class BinaryUnsignedInt32ColumnReader : ColumnReader
 {
 	public static BinaryUnsignedInt32ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return MemoryMarshal.Read<uint>(data);
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return checked((int) MemoryMarshal.Read<uint>(data));
 	}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
index 86dc8d6e3..c233c295d 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinaryUnsignedInt64ColumnReader : IColumnReader
+internal sealed class BinaryUnsignedInt64ColumnReader : ColumnReader
 {
 	public static BinaryUnsignedInt64ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return MemoryMarshal.Read<ulong>(data);
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return checked((int) MemoryMarshal.Read<ulong>(data));
 	}
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
index 1850a41eb..0ae7f6e26 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
@@ -2,17 +2,17 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinaryUnsignedInt8ColumnReader : IColumnReader
+internal sealed class BinaryUnsignedInt8ColumnReader : ColumnReader
 {
 	public static BinaryUnsignedInt8ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
-		return (byte) data[0];
+		return data[0];
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
-		return (int) (byte) data[0];
+		return data[0];
 	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
index 386242594..20438eb37 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
@@ -3,17 +3,17 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BinaryYearColumnReader : IColumnReader
+internal sealed class BinaryYearColumnReader : ColumnReader
 {
 	public static BinaryYearColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return (int) MemoryMarshal.Read<short>(data);
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
-		return (int) MemoryMarshal.Read<short>(data);
+		return MemoryMarshal.Read<short>(data);
 	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
index f8cab81fc..ac45d98a5 100644
--- a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
@@ -4,11 +4,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BitColumnReader : IColumnReader
+internal sealed class BitColumnReader : ColumnReader
 {
 	public static BitColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if ((columnDefinition.ColumnFlags & ColumnFlags.Binary) == 0)
 		{
@@ -31,7 +31,7 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 		}
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if ((columnDefinition.ColumnFlags & ColumnFlags.Binary) == 0)
 		{
diff --git a/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs b/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
index 2c35dd940..e3feaeb25 100644
--- a/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
@@ -2,17 +2,12 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class BytesColumnReader : IColumnReader
+internal sealed class BytesColumnReader : ColumnReader
 {
 	public static BytesColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return data.ToArray();
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/ColumnReader.cs b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
new file mode 100644
index 000000000..090d76667
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
@@ -0,0 +1,13 @@
+using MySqlConnector.Protocol.Payloads;
+
+namespace MySqlConnector.ColumnReaders;
+
+internal abstract class ColumnReader
+{
+	public abstract object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
+
+	public virtual int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	}
+}
diff --git a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
index b7a1babdd..b6f2361b9 100644
--- a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
+++ b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
@@ -6,7 +6,7 @@ namespace MySqlConnector.ColumnReaders;
 
 internal class ColumnReaderFactory
 {
-	internal static IColumnReader GetReader(bool binary, ColumnDefinitionPayload columnDefinition, MySqlConnection connection)
+	internal static ColumnReader GetReader(bool binary, ColumnDefinitionPayload columnDefinition, MySqlConnection connection)
 	{
 		var isUnsigned = (columnDefinition.ColumnFlags & ColumnFlags.Unsigned) != 0;
 		if (binary)
diff --git a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
index 86381fc74..f058ec7e9 100644
--- a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class DecimalColumnReader : IColumnReader
+internal sealed class DecimalColumnReader : ColumnReader
 {
 	public static DecimalColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) && bytesConsumed == data.Length ? decimalValue : throw new FormatException();
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (!Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) || bytesConsumed != data.Length)
 		{
diff --git a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
index dbfe41445..5e65be4f6 100644
--- a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
@@ -2,11 +2,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class GuidBinary16ColumnReader : IColumnReader
+internal sealed class GuidBinary16ColumnReader : ColumnReader
 {
 	public static GuidBinary16ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 		return new Guid(stackalloc byte[16] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
@@ -18,9 +18,4 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 		});
 #endif
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs
index 973e980bd..159252c59 100644
--- a/src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs
@@ -5,11 +5,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class GuidChar32ColumnReader : IColumnReader
+internal sealed class GuidChar32ColumnReader : ColumnReader
 {
 	public static GuidChar32ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return Utf8Parser.TryParse(data, out Guid guid, out int guid32BytesConsumed, 'N') &&
 		       guid32BytesConsumed == 32
@@ -17,9 +17,4 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 			: throw new FormatException(
 				$"Could not parse CHAR(32) value as Guid: {Encoding.UTF8.GetString(data)}");
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs
index fa86c3b30..8ab236324 100644
--- a/src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs
@@ -5,11 +5,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class GuidChar36ColumnReader : IColumnReader
+internal sealed class GuidChar36ColumnReader : ColumnReader
 {
 	public static GuidChar36ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return Utf8Parser.TryParse(data, out Guid guid, out int guid36BytesConsumed, 'D') &&
 		       guid36BytesConsumed == 36
@@ -17,9 +17,4 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 			: throw new FormatException(
 				$"Could not parse CHAR(36) value as Guid: {Encoding.UTF8.GetString(data)}");
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
index 883b68986..3007da795 100644
--- a/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
@@ -2,11 +2,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class GuidLittleEndianBinary16ColumnReader : IColumnReader
+internal sealed class GuidLittleEndianBinary16ColumnReader : ColumnReader
 {
 	public static GuidLittleEndianBinary16ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 		return new Guid(data);
@@ -14,9 +14,4 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 		return new Guid(data.ToArray());
 #endif
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
index 9fdf22779..8937064d9 100644
--- a/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
@@ -2,11 +2,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class GuidTimeSwapBinary16ColumnReader : IColumnReader
+internal sealed class GuidTimeSwapBinary16ColumnReader : ColumnReader
 {
 	public static GuidTimeSwapBinary16ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 		return new Guid(stackalloc byte[16] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
@@ -18,9 +18,4 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 		});
 #endif
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/IColumnReader.cs b/src/MySqlConnector/ColumnReaders/IColumnReader.cs
deleted file mode 100644
index dbb3facd5..000000000
--- a/src/MySqlConnector/ColumnReaders/IColumnReader.cs
+++ /dev/null
@@ -1,10 +0,0 @@
-using MySqlConnector.Protocol.Payloads;
-
-namespace MySqlConnector.ColumnReaders;
-
-internal interface IColumnReader
-{
-	object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
-
-	int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
-}
diff --git a/src/MySqlConnector/ColumnReaders/NullColumnReader.cs b/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
index 27bc1ec84..9d3bd78a8 100644
--- a/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
@@ -2,17 +2,12 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class NullColumnReader : IColumnReader
+internal sealed class NullColumnReader : ColumnReader
 {
 	public static NullColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return DBNull.Value;
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/StringColumnReader.cs b/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
index 16a95810d..6c13015c0 100644
--- a/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
@@ -4,17 +4,12 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class StringColumnReader : IColumnReader
+internal sealed class StringColumnReader : ColumnReader
 {
 	public static StringColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return Encoding.UTF8.GetString(data);
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
index 1a27af530..a3a996621 100644
--- a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
@@ -2,16 +2,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextBooleanColumnReader : IColumnReader
+internal sealed class TextBooleanColumnReader : ColumnReader
 {
 	public static TextBooleanColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return data[0] != (byte) '0';
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return data[0] != (byte) '0' ? 1 : 0;
 	}
diff --git a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
index 2cd01800c..9e1b3404a 100644
--- a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
@@ -5,7 +5,7 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextDateTimeColumnReader : IColumnReader
+internal sealed class TextDateTimeColumnReader : ColumnReader
 {
 	public TextDateTimeColumnReader(MySqlConnection connection)
 	{
@@ -14,7 +14,7 @@ public TextDateTimeColumnReader(MySqlConnection connection)
 		m_dateTimeKind = connection.DateTimeKind;
 	}
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		Exception? exception = null;
 		if (!Utf8Parser.TryParse(data, out int year, out var bytesConsumed) || bytesConsumed != 4)
@@ -94,11 +94,6 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 		throw new FormatException($"Couldn't interpret value as a valid DateTime: {Encoding.UTF8.GetString(data)}", exception);
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
-
 	private readonly bool m_allowZeroDateTime;
 	private readonly bool m_convertZeroDateTime;
 	private readonly DateTimeKind m_dateTimeKind;
diff --git a/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
index fabdadf01..38a4427ab 100644
--- a/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
@@ -5,11 +5,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextDoubleColumnReader : IColumnReader
+internal sealed class TextDoubleColumnReader : ColumnReader
 {
 	public static TextDoubleColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (Utf8Parser.TryParse(data, out double doubleValue, out var doubleBytesConsumed) && doubleBytesConsumed == data.Length)
 			return doubleValue;
@@ -22,9 +22,4 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 			return double.NaN;
 		throw new FormatException($"Couldn't parse value as double: {Encoding.UTF8.GetString(data)}");
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
index 07cb77338..7f583f968 100644
--- a/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
@@ -5,11 +5,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextFloatColumnReader : IColumnReader
+internal sealed class TextFloatColumnReader : ColumnReader
 {
 	public static TextFloatColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (Utf8Parser.TryParse(data, out float floatValue, out var floatBytesConsumed) && floatBytesConsumed == data.Length)
 			return floatValue;
@@ -22,9 +22,4 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 			return float.NaN;
 		throw new FormatException($"Couldn't parse value as float: {Encoding.UTF8.GetString(data)}");
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
index 207a520c7..280ac41c2 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextSignedInt16ColumnReader : IColumnReader
+internal sealed class TextSignedInt16ColumnReader : ColumnReader
 {
 	public static TextSignedInt16ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return !Utf8Parser.TryParse(data, out short value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
 		{
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
index 059203bc8..df1a002eb 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextSignedInt32ColumnReader : IColumnReader
+internal sealed class TextSignedInt32ColumnReader : ColumnReader
 {
 	public static TextSignedInt32ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return !Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return !Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 	}
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
index 494cb9423..e4ee2473c 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextSignedInt64ColumnReader : IColumnReader
+internal sealed class TextSignedInt64ColumnReader : ColumnReader
 {
 	public static TextSignedInt64ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return !Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length)
 		{
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
index 5e4c3435b..db99dc773 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
@@ -3,11 +3,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextSignedInt8ColumnReader : IColumnReader
+internal sealed class TextSignedInt8ColumnReader : ColumnReader
 {
 	public static TextSignedInt8ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
 		{
@@ -16,7 +16,7 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 		return (sbyte) value;
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
 		{
diff --git a/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
index e905663c9..3f24e0692 100644
--- a/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
@@ -3,17 +3,12 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextTimeColumnReader : IColumnReader
+internal sealed class TextTimeColumnReader : ColumnReader
 {
 	public static TextTimeColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return Utility.ParseTimeSpan(data);
 	}
-
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
index eefbb3312..10b67f8e8 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextUnsignedInt16ColumnReader : IColumnReader
+internal sealed class TextUnsignedInt16ColumnReader : ColumnReader
 {
 	public static TextUnsignedInt16ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return !Utf8Parser.TryParse(data, out ushort value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
 		{
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
index f06056cfd..b10690839 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextUnsignedInt32ColumnReader : IColumnReader
+internal sealed class TextUnsignedInt32ColumnReader : ColumnReader
 {
 	public static TextUnsignedInt32ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return !Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (!Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length)
 		{
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
index 3692f703f..93ff6573f 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
@@ -3,16 +3,16 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextUnsignedInt64ColumnReader : IColumnReader
+internal sealed class TextUnsignedInt64ColumnReader : ColumnReader
 {
 	public static TextUnsignedInt64ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		return !Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length)
 		{
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
index 920e4fd2b..cced4a580 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
@@ -3,11 +3,11 @@
 
 namespace MySqlConnector.ColumnReaders;
 
-internal sealed class TextUnsignedInt8ColumnReader : IColumnReader
+internal sealed class TextUnsignedInt8ColumnReader : ColumnReader
 {
 	public static TextUnsignedInt8ColumnReader Instance { get; } = new();
 
-	public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
 		{
@@ -16,7 +16,7 @@ public object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnD
 		return (byte) value;
 	}
 
-	public int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
 		{
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 3a0da6b6b..e34110859 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -376,7 +376,7 @@ protected Row(bool binary, ResultSet resultSet)
 		m_dataOffsets = new int[ResultSet.ColumnDefinitions!.Length];
 		m_dataLengths = new int[ResultSet.ColumnDefinitions.Length];
 		m_columnReaders = Array.ConvertAll(ResultSet.ColumnDefinitions,
-			new Converter<ColumnDefinitionPayload, IColumnReader>(column => ColumnReaderFactory.GetReader(binary, column, resultSet.Connection)));
+			new Converter<ColumnDefinitionPayload, ColumnReader>(column => ColumnReaderFactory.GetReader(binary, column, resultSet.Connection)));
 	}
 
 	protected abstract Row CloneCore();
@@ -517,6 +517,6 @@ private static void CheckBufferArguments<T>(long dataOffset, T[] buffer, int buf
 
 	private readonly int[] m_dataOffsets;
 	private readonly int[] m_dataLengths;
-	private readonly IColumnReader[] m_columnReaders;
+	private readonly ColumnReader[] m_columnReaders;
 	private ReadOnlyMemory<byte> m_data;
 }

From 0772e153c6cbc57bfd418340010e9857a498c0c2 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 14:23:14 -0700
Subject: [PATCH 110/393] Use expression-bodied methods.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../BinaryBooleanColumnReader.cs              | 12 ++++-------
 .../ColumnReaders/BinaryDoubleColumnReader.cs |  6 ++----
 .../ColumnReaders/BinaryFloatColumnReader.cs  |  6 ++----
 .../BinarySignedInt16ColumnReader.cs          | 12 ++++-------
 .../BinarySignedInt32ColumnReader.cs          | 12 ++++-------
 .../BinarySignedInt64ColumnReader.cs          | 12 ++++-------
 .../BinarySignedInt8ColumnReader.cs           | 12 ++++-------
 .../BinaryUnsignedInt16ColumnReader.cs        | 12 ++++-------
 .../BinaryUnsignedInt32ColumnReader.cs        | 12 ++++-------
 .../BinaryUnsignedInt64ColumnReader.cs        | 12 ++++-------
 .../BinaryUnsignedInt8ColumnReader.cs         | 12 ++++-------
 .../ColumnReaders/BinaryYearColumnReader.cs   | 12 ++++-------
 .../ColumnReaders/BytesColumnReader.cs        |  6 ++----
 .../ColumnReaders/ColumnReader.cs             |  4 +---
 .../ColumnReaders/DecimalColumnReader.cs      | 16 ++++-----------
 .../ColumnReaders/GuidBinary16ColumnReader.cs | 12 +++--------
 .../ColumnReaders/GuidChar32ColumnReader.cs   | 11 +++-------
 .../ColumnReaders/GuidChar36ColumnReader.cs   | 11 +++-------
 .../GuidLittleEndianBinary16ColumnReader.cs   |  8 +++-----
 .../GuidTimeSwapBinary16ColumnReader.cs       | 12 +++--------
 .../ColumnReaders/NullColumnReader.cs         |  6 ++----
 .../ColumnReaders/StringColumnReader.cs       |  6 ++----
 .../ColumnReaders/TextBooleanColumnReader.cs  | 12 ++++-------
 .../TextSignedInt16ColumnReader.cs            | 16 ++++-----------
 .../TextSignedInt32ColumnReader.cs            | 12 ++++-------
 .../TextSignedInt64ColumnReader.cs            | 16 ++++-----------
 .../TextSignedInt8ColumnReader.cs             | 20 ++++---------------
 .../ColumnReaders/TextTimeColumnReader.cs     |  6 ++----
 .../TextUnsignedInt16ColumnReader.cs          | 16 ++++-----------
 .../TextUnsignedInt32ColumnReader.cs          | 16 ++++-----------
 .../TextUnsignedInt64ColumnReader.cs          | 16 ++++-----------
 .../TextUnsignedInt8ColumnReader.cs           | 20 ++++---------------
 32 files changed, 108 insertions(+), 266 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
index 262826658..fad523124 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
@@ -6,13 +6,9 @@ internal sealed class BinaryBooleanColumnReader : ColumnReader
 {
 	public static BinaryBooleanColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return data[0] != 0;
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		data[0] != 0;
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return data[0] != 0 ? 1 : 0;
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		data[0] != 0 ? 1 : 0;
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
index 2558a092f..6b8c49456 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryDoubleColumnReader.cs
@@ -7,8 +7,6 @@ internal sealed class BinaryDoubleColumnReader : ColumnReader
 {
 	public static BinaryDoubleColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<double>(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<double>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
index 528ce347c..7ab4d5726 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryFloatColumnReader.cs
@@ -7,8 +7,6 @@ internal sealed class BinaryFloatColumnReader : ColumnReader
 {
 	public static BinaryFloatColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<float>(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<float>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
index 5b95529dd..ee1fc139d 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
@@ -7,13 +7,9 @@ internal sealed class BinarySignedInt16ColumnReader : ColumnReader
 {
 	public static BinarySignedInt16ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<short>(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<short>(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<short>(data);
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<short>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
index 2f8171729..49fa97b10 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
@@ -7,13 +7,9 @@ internal sealed class BinarySignedInt32ColumnReader : ColumnReader
 {
 	public static BinarySignedInt32ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<int>(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<int>(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<int>(data);
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<int>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
index 2bc7571e4..3511b9358 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
@@ -7,13 +7,9 @@ internal sealed class BinarySignedInt64ColumnReader : ColumnReader
 {
 	public static BinarySignedInt64ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<long>(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<long>(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return checked((int) MemoryMarshal.Read<long>(data));
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		checked((int) MemoryMarshal.Read<long>(data));
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
index 1bbf09bd2..62835b925 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
@@ -6,13 +6,9 @@ internal sealed class BinarySignedInt8ColumnReader : ColumnReader
 {
 	public static BinarySignedInt8ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return (sbyte) data[0];
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		(sbyte) data[0];
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return (int) (sbyte) data[0];
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		(sbyte) data[0];
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
index 97c9ede38..ca476f2c6 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
@@ -7,13 +7,9 @@ internal sealed class BinaryUnsignedInt16ColumnReader : ColumnReader
 {
 	public static BinaryUnsignedInt16ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<ushort>(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<ushort>(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<ushort>(data);
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<ushort>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
index ac11fe082..7e3b4fa89 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
@@ -7,13 +7,9 @@ internal sealed class BinaryUnsignedInt32ColumnReader : ColumnReader
 {
 	public static BinaryUnsignedInt32ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<uint>(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<uint>(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return checked((int) MemoryMarshal.Read<uint>(data));
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		checked((int) MemoryMarshal.Read<uint>(data));
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
index c233c295d..b5426e792 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
@@ -7,13 +7,9 @@ internal sealed class BinaryUnsignedInt64ColumnReader : ColumnReader
 {
 	public static BinaryUnsignedInt64ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<ulong>(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<ulong>(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return checked((int) MemoryMarshal.Read<ulong>(data));
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		checked((int) MemoryMarshal.Read<ulong>(data));
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
index 0ae7f6e26..2f5b0022b 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
@@ -6,13 +6,9 @@ internal sealed class BinaryUnsignedInt8ColumnReader : ColumnReader
 {
 	public static BinaryUnsignedInt8ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return data[0];
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		data[0];
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return data[0];
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		data[0];
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
index 20438eb37..4681a4819 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
@@ -7,13 +7,9 @@ internal sealed class BinaryYearColumnReader : ColumnReader
 {
 	public static BinaryYearColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return (int) MemoryMarshal.Read<short>(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		(int) MemoryMarshal.Read<short>(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return MemoryMarshal.Read<short>(data);
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		MemoryMarshal.Read<short>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs b/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
index e3feaeb25..facd2d1cc 100644
--- a/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BytesColumnReader.cs
@@ -6,8 +6,6 @@ internal sealed class BytesColumnReader : ColumnReader
 {
 	public static BytesColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return data.ToArray();
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		data.ToArray();
 }
diff --git a/src/MySqlConnector/ColumnReaders/ColumnReader.cs b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
index 090d76667..fb121e0ce 100644
--- a/src/MySqlConnector/ColumnReaders/ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
@@ -6,8 +6,6 @@ internal abstract class ColumnReader
 {
 	public abstract object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
 
-	public virtual int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
+	public virtual int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
index f058ec7e9..7c5880182 100644
--- a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
@@ -7,17 +7,9 @@ internal sealed class DecimalColumnReader : ColumnReader
 {
 	public static DecimalColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) && bytesConsumed == data.Length ? decimalValue : throw new FormatException();
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : decimalValue;
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if (!Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) || bytesConsumed != data.Length)
-		{
-			throw new FormatException();
-		}
-		return (int) decimalValue;
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : (int) decimalValue;
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
index 5e65be4f6..dbbb0f778 100644
--- a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
@@ -6,16 +6,10 @@ internal sealed class GuidBinary16ColumnReader : ColumnReader
 {
 	public static GuidBinary16ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-		return new Guid(stackalloc byte[16] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
+		new Guid(stackalloc byte[16] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #else
-		return new Guid(new[]
-		{
-			data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9],
-			data[10], data[11], data[12], data[13], data[14], data[15],
-		});
+		new Guid(new[] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #endif
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs
index 159252c59..94a4cd73d 100644
--- a/src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidChar32ColumnReader.cs
@@ -9,12 +9,7 @@ internal sealed class GuidChar32ColumnReader : ColumnReader
 {
 	public static GuidChar32ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return Utf8Parser.TryParse(data, out Guid guid, out int guid32BytesConsumed, 'N') &&
-		       guid32BytesConsumed == 32
-			? guid
-			: throw new FormatException(
-				$"Could not parse CHAR(32) value as Guid: {Encoding.UTF8.GetString(data)}");
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		Utf8Parser.TryParse(data, out Guid guid, out int guid32BytesConsumed, 'N') && guid32BytesConsumed == 32 ?
+			guid : throw new FormatException($"Could not parse CHAR(32) value as Guid: {Encoding.UTF8.GetString(data)}");
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs
index 8ab236324..2dbfa4f62 100644
--- a/src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidChar36ColumnReader.cs
@@ -9,12 +9,7 @@ internal sealed class GuidChar36ColumnReader : ColumnReader
 {
 	public static GuidChar36ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return Utf8Parser.TryParse(data, out Guid guid, out int guid36BytesConsumed, 'D') &&
-		       guid36BytesConsumed == 36
-			? guid
-			: throw new FormatException(
-				$"Could not parse CHAR(36) value as Guid: {Encoding.UTF8.GetString(data)}");
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		Utf8Parser.TryParse(data, out Guid guid, out int guid36BytesConsumed, 'D') && guid36BytesConsumed == 36 ?
+			guid : throw new FormatException($"Could not parse CHAR(36) value as Guid: {Encoding.UTF8.GetString(data)}");
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
index 3007da795..99936f37e 100644
--- a/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
@@ -6,12 +6,10 @@ internal sealed class GuidLittleEndianBinary16ColumnReader : ColumnReader
 {
 	public static GuidLittleEndianBinary16ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-		return new Guid(data);
+		new Guid(data);
 #else
-		return new Guid(data.ToArray());
+		new Guid(data.ToArray());
 #endif
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
index 8937064d9..6781a629d 100644
--- a/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
@@ -6,16 +6,10 @@ internal sealed class GuidTimeSwapBinary16ColumnReader : ColumnReader
 {
 	public static GuidTimeSwapBinary16ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-		return new Guid(stackalloc byte[16] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
+		new Guid(stackalloc byte[16] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #else
-		return new Guid(new[]
-		{
-			data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9],
-			data[10], data[11], data[12], data[13], data[14], data[15],
-		});
+		new Guid(new[] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #endif
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/NullColumnReader.cs b/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
index 9d3bd78a8..49ef0d2da 100644
--- a/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/NullColumnReader.cs
@@ -6,8 +6,6 @@ internal sealed class NullColumnReader : ColumnReader
 {
 	public static NullColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return DBNull.Value;
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		DBNull.Value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/StringColumnReader.cs b/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
index 6c13015c0..bd25bb34e 100644
--- a/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/StringColumnReader.cs
@@ -8,8 +8,6 @@ internal sealed class StringColumnReader : ColumnReader
 {
 	public static StringColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return Encoding.UTF8.GetString(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		Encoding.UTF8.GetString(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
index a3a996621..3310cf0c4 100644
--- a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
@@ -6,13 +6,9 @@ internal sealed class TextBooleanColumnReader : ColumnReader
 {
 	public static TextBooleanColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return data[0] != (byte) '0';
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		data[0] != (byte) '0';
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return data[0] != (byte) '0' ? 1 : 0;
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		data[0] != (byte) '0' ? 1 : 0;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
index 280ac41c2..2c3018542 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
@@ -7,17 +7,9 @@ internal sealed class TextSignedInt16ColumnReader : ColumnReader
 {
 	public static TextSignedInt16ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return !Utf8Parser.TryParse(data, out short value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out short value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
-		{
-			throw new FormatException();
-		}
-		return value;
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
index df1a002eb..ecd36224f 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
@@ -7,13 +7,9 @@ internal sealed class TextSignedInt32ColumnReader : ColumnReader
 {
 	public static TextSignedInt32ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return !Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return !Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
index e4ee2473c..469578a9a 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
@@ -7,17 +7,9 @@ internal sealed class TextSignedInt64ColumnReader : ColumnReader
 {
 	public static TextSignedInt64ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return !Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if (!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length)
-		{
-			throw new FormatException();
-		}
-		return checked((int) value);
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : checked((int) value);
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
index db99dc773..1854d13eb 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
@@ -7,21 +7,9 @@ internal sealed class TextSignedInt8ColumnReader : ColumnReader
 {
 	public static TextSignedInt8ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
-		{
-			throw new FormatException();
-		}
-		return (sbyte) value;
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : (sbyte) value;
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
-		{
-			throw new FormatException();
-		}
-		return value;
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
index 3f24e0692..b4d5d5779 100644
--- a/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextTimeColumnReader.cs
@@ -7,8 +7,6 @@ internal sealed class TextTimeColumnReader : ColumnReader
 {
 	public static TextTimeColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return Utility.ParseTimeSpan(data);
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		Utility.ParseTimeSpan(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
index 10b67f8e8..819303d15 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
@@ -7,17 +7,9 @@ internal sealed class TextUnsignedInt16ColumnReader : ColumnReader
 {
 	public static TextUnsignedInt16ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return !Utf8Parser.TryParse(data, out ushort value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out ushort value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
-		{
-			throw new FormatException();
-		}
-		return value;
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
index b10690839..154cfaee6 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
@@ -7,17 +7,9 @@ internal sealed class TextUnsignedInt32ColumnReader : ColumnReader
 {
 	public static TextUnsignedInt32ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return !Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if (!Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length)
-		{
-			throw new FormatException();
-		}
-		return checked((int) value);
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : checked((int) value);
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
index 93ff6573f..7edf1489f 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
@@ -7,17 +7,9 @@ internal sealed class TextUnsignedInt64ColumnReader : ColumnReader
 {
 	public static TextUnsignedInt64ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		return !Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if (!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length)
-		{
-			throw new FormatException();
-		}
-		return checked((int) value);
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : checked((int) value);
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
index cced4a580..b500ed234 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
@@ -7,21 +7,9 @@ internal sealed class TextUnsignedInt8ColumnReader : ColumnReader
 {
 	public static TextUnsignedInt8ColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
-		{
-			throw new FormatException();
-		}
-		return (byte) value;
-	}
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : (byte) value;
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if (!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length)
-		{
-			throw new FormatException();
-		}
-		return value;
-	}
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }

From 34bf3aa111a71dc57354989ec60a5f8987fd8ab8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 16:07:42 -0700
Subject: [PATCH 111/393] Refactor duplicated logic into shared methods.

Use MethodImplOptions.AggressiveInlining on the implementation method as profiling showed a modest improvement in performance from it.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../BinaryBooleanColumnReader.cs              |  9 ++++-
 .../BinarySignedInt16ColumnReader.cs          |  7 +++-
 .../BinarySignedInt32ColumnReader.cs          |  7 +++-
 .../BinarySignedInt64ColumnReader.cs          |  9 ++++-
 .../BinarySignedInt8ColumnReader.cs           |  7 +++-
 .../BinaryUnsignedInt16ColumnReader.cs        |  7 +++-
 .../BinaryUnsignedInt32ColumnReader.cs        |  9 ++++-
 .../BinaryUnsignedInt64ColumnReader.cs        |  9 ++++-
 .../BinaryUnsignedInt8ColumnReader.cs         |  7 +++-
 .../ColumnReaders/BinaryYearColumnReader.cs   |  7 +++-
 .../ColumnReaders/BitColumnReader.cs          | 37 ++++---------------
 .../ColumnReaders/DecimalColumnReader.cs      |  9 ++++-
 .../ColumnReaders/TextBooleanColumnReader.cs  |  9 ++++-
 .../TextSignedInt16ColumnReader.cs            |  9 ++++-
 .../TextSignedInt32ColumnReader.cs            |  7 +++-
 .../TextSignedInt64ColumnReader.cs            |  9 ++++-
 .../TextSignedInt8ColumnReader.cs             |  9 ++++-
 .../TextUnsignedInt16ColumnReader.cs          |  9 ++++-
 .../TextUnsignedInt32ColumnReader.cs          |  7 +++-
 .../TextUnsignedInt64ColumnReader.cs          |  9 ++++-
 .../TextUnsignedInt8ColumnReader.cs           |  9 ++++-
 21 files changed, 139 insertions(+), 62 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
index fad523124..50ecaf3e8 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -7,8 +8,12 @@ internal sealed class BinaryBooleanColumnReader : ColumnReader
 	public static BinaryBooleanColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		data[0] != 0;
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		data[0] != 0 ? 1 : 0;
+		DoReadValue(data) ? 1 : 0;
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static bool DoReadValue(ReadOnlySpan<byte> data) =>
+		data[0] != 0;
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
index ee1fc139d..dfe7077ba 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
 
@@ -8,8 +9,12 @@ internal sealed class BinarySignedInt16ColumnReader : ColumnReader
 	public static BinarySignedInt16ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		MemoryMarshal.Read<short>(data);
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static short DoReadValue(ReadOnlySpan<byte> data) =>
 		MemoryMarshal.Read<short>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
index 49fa97b10..224a7e6c8 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
 
@@ -8,8 +9,12 @@ internal sealed class BinarySignedInt32ColumnReader : ColumnReader
 	public static BinarySignedInt32ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		MemoryMarshal.Read<int>(data);
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static int DoReadValue(ReadOnlySpan<byte> data) =>
 		MemoryMarshal.Read<int>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
index 3511b9358..872e2eaba 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
 
@@ -8,8 +9,12 @@ internal sealed class BinarySignedInt64ColumnReader : ColumnReader
 	public static BinarySignedInt64ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		MemoryMarshal.Read<long>(data);
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		checked((int) MemoryMarshal.Read<long>(data));
+		checked((int) DoReadValue(data));
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static long DoReadValue(ReadOnlySpan<byte> data) =>
+		MemoryMarshal.Read<long>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
index 62835b925..0886df664 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -7,8 +8,12 @@ internal sealed class BinarySignedInt8ColumnReader : ColumnReader
 	public static BinarySignedInt8ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		(sbyte) data[0];
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static sbyte DoReadValue(ReadOnlySpan<byte> data) =>
 		(sbyte) data[0];
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
index ca476f2c6..4dbceb781 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
 
@@ -8,8 +9,12 @@ internal sealed class BinaryUnsignedInt16ColumnReader : ColumnReader
 	public static BinaryUnsignedInt16ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		MemoryMarshal.Read<ushort>(data);
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static ushort DoReadValue(ReadOnlySpan<byte> data) =>
 		MemoryMarshal.Read<ushort>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
index 7e3b4fa89..7d549b48f 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
 
@@ -8,8 +9,12 @@ internal sealed class BinaryUnsignedInt32ColumnReader : ColumnReader
 	public static BinaryUnsignedInt32ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		MemoryMarshal.Read<uint>(data);
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		checked((int) MemoryMarshal.Read<uint>(data));
+		checked((int) DoReadValue(data));
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static uint DoReadValue(ReadOnlySpan<byte> data) =>
+		MemoryMarshal.Read<uint>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
index b5426e792..2f1e0ed86 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
 
@@ -8,8 +9,12 @@ internal sealed class BinaryUnsignedInt64ColumnReader : ColumnReader
 	public static BinaryUnsignedInt64ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		MemoryMarshal.Read<ulong>(data);
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		checked((int) MemoryMarshal.Read<ulong>(data));
+		checked((int) DoReadValue(data));
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static ulong DoReadValue(ReadOnlySpan<byte> data) =>
+		MemoryMarshal.Read<ulong>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
index 2f5b0022b..49e50b4ff 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -7,8 +8,12 @@ internal sealed class BinaryUnsignedInt8ColumnReader : ColumnReader
 	public static BinaryUnsignedInt8ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		data[0];
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static byte DoReadValue(ReadOnlySpan<byte> data) =>
 		data[0];
 }
diff --git a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
index 4681a4819..615e6b977 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using MySqlConnector.Protocol.Payloads;
 
@@ -8,8 +9,12 @@ internal sealed class BinaryYearColumnReader : ColumnReader
 	public static BinaryYearColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		(int) MemoryMarshal.Read<short>(data);
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static int DoReadValue(ReadOnlySpan<byte> data) =>
 		MemoryMarshal.Read<short>(data);
 }
diff --git a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
index ac45d98a5..46828ccde 100644
--- a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
@@ -8,7 +8,13 @@ internal sealed class BitColumnReader : ColumnReader
 {
 	public static BitColumnReader Instance { get; } = new();
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		DoReadValue(data, columnDefinition);
+
+	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		checked((int) DoReadValue(data, columnDefinition));
+
+	private static ulong DoReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
 	{
 		if ((columnDefinition.ColumnFlags & ColumnFlags.Binary) == 0)
 		{
@@ -22,7 +28,7 @@ public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayloa
 		{
 			// a server bug may return the data as binary even when we expect text: https://github.com/mysql-net/MySqlConnector/issues/713
 			// in this case, the data can't possibly be an ASCII digit, so assume it's the binary serialisation of BIT(n) where n <= 5
-			return (ulong) data[0];
+			return data[0];
 		}
 		else
 		{
@@ -30,31 +36,4 @@ public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayloa
 			return !Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 		}
 	}
-
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
-	{
-		if ((columnDefinition.ColumnFlags & ColumnFlags.Binary) == 0)
-		{
-			// when the Binary flag IS NOT set, the BIT column is transmitted as MSB byte array
-			ulong bitValue = 0;
-			for (int i = 0; i < data.Length; i++)
-				bitValue = bitValue * 256 + data[i];
-			return checked((int) bitValue);
-		}
-		else if (columnDefinition.ColumnLength <= 5 && data.Length == 1 && data[0] < (byte) (1 << (int) columnDefinition.ColumnLength))
-		{
-			// a server bug may return the data as binary even when we expect text: https://github.com/mysql-net/MySqlConnector/issues/713
-			// in this case, the data can't possibly be an ASCII digit, so assume it's the binary serialisation of BIT(n) where n <= 5
-			return checked((int) data[0]);
-		}
-		else
-		{
-			// when the Binary flag IS set, the BIT column is transmitted as text
-			if (!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length)
-			{
-				throw new FormatException();
-			}
-			return checked((int) value);
-		}
-	}
 }
diff --git a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
index 7c5880182..e6127672b 100644
--- a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
@@ -1,4 +1,5 @@
 using System.Buffers.Text;
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -8,8 +9,12 @@ internal sealed class DecimalColumnReader : ColumnReader
 	public static DecimalColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : decimalValue;
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : (int) decimalValue;
+		(int) DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static decimal DoReadValue(ReadOnlySpan<byte> data) =>
+		!Utf8Parser.TryParse(data, out decimal decimalValue, out int bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : decimalValue;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
index 3310cf0c4..7a78ab1e6 100644
--- a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -7,8 +8,12 @@ internal sealed class TextBooleanColumnReader : ColumnReader
 	public static TextBooleanColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		data[0] != (byte) '0';
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		data[0] != (byte) '0' ? 1 : 0;
+		DoReadValue(data) ? 1 : 0;
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static bool DoReadValue(ReadOnlySpan<byte> data) =>
+		data[0] != (byte) '0';
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
index 2c3018542..95e6bcaa1 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
@@ -1,4 +1,5 @@
 using System.Buffers.Text;
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -8,8 +9,12 @@ internal sealed class TextSignedInt16ColumnReader : ColumnReader
 	public static TextSignedInt16ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out short value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static short DoReadValue(ReadOnlySpan<byte> data) =>
+		!Utf8Parser.TryParse(data, out short value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
index ecd36224f..fe7f6e402 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
@@ -1,4 +1,5 @@
 using System.Buffers.Text;
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -8,8 +9,12 @@ internal sealed class TextSignedInt32ColumnReader : ColumnReader
 	public static TextSignedInt32ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static int DoReadValue(ReadOnlySpan<byte> data) =>
 		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
index 469578a9a..12323ea7d 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
@@ -1,4 +1,5 @@
 using System.Buffers.Text;
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -8,8 +9,12 @@ internal sealed class TextSignedInt64ColumnReader : ColumnReader
 	public static TextSignedInt64ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : checked((int) value);
+		checked((int) DoReadValue(data));
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static long DoReadValue(ReadOnlySpan<byte> data) =>
+		!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
index 1854d13eb..a6adfe659 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
@@ -1,4 +1,5 @@
 using System.Buffers.Text;
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -8,8 +9,12 @@ internal sealed class TextSignedInt8ColumnReader : ColumnReader
 	public static TextSignedInt8ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : (sbyte) value;
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static sbyte DoReadValue(ReadOnlySpan<byte> data) =>
+		!Utf8Parser.TryParse(data, out sbyte value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
index 819303d15..11ce899a1 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
@@ -1,4 +1,5 @@
 using System.Buffers.Text;
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -8,8 +9,12 @@ internal sealed class TextUnsignedInt16ColumnReader : ColumnReader
 	public static TextUnsignedInt16ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out ushort value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static ushort DoReadValue(ReadOnlySpan<byte> data) =>
+		!Utf8Parser.TryParse(data, out ushort value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
index 154cfaee6..901aa37fc 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
@@ -8,8 +8,11 @@ internal sealed class TextUnsignedInt32ColumnReader : ColumnReader
 	public static TextUnsignedInt32ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : checked((int) value);
+		checked((int) DoReadValue(data));
+
+	private static uint DoReadValue(ReadOnlySpan<byte> data) =>
+		!Utf8Parser.TryParse(data, out uint value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
index 7edf1489f..f3c319679 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
@@ -1,4 +1,5 @@
 using System.Buffers.Text;
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -8,8 +9,12 @@ internal sealed class TextUnsignedInt64ColumnReader : ColumnReader
 	public static TextUnsignedInt64ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : checked((int) value);
+		checked((int) DoReadValue(data));
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static ulong DoReadValue(ReadOnlySpan<byte> data) =>
+		!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
index b500ed234..2524a70c1 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
@@ -1,4 +1,5 @@
 using System.Buffers.Text;
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -8,8 +9,12 @@ internal sealed class TextUnsignedInt8ColumnReader : ColumnReader
 	public static TextUnsignedInt8ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : (byte) value;
+		DoReadValue(data);
 
 	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		!Utf8Parser.TryParse(data, out int value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
+		DoReadValue(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static byte DoReadValue(ReadOnlySpan<byte> data) =>
+		!Utf8Parser.TryParse(data, out byte value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
 }

From ffa23d57e2c883a99101a33e2fc4b4820cb0ec64 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 17:35:33 -0700
Subject: [PATCH 112/393] Remove duplicated ParseDateTime method.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../ColumnReaders/TextDateTimeColumnReader.cs | 15 ++--
 src/MySqlConnector/Core/Row.cs                | 82 +------------------
 2 files changed, 10 insertions(+), 87 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
index 9e1b3404a..1064fde65 100644
--- a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
@@ -14,7 +14,10 @@ public TextDateTimeColumnReader(MySqlConnection connection)
 		m_dateTimeKind = connection.DateTimeKind;
 	}
 
-	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		ParseDateTime(data, m_convertZeroDateTime, m_allowZeroDateTime, m_dateTimeKind);
+
+	public static object ParseDateTime(ReadOnlySpan<byte> data, bool convertZeroDateTime, bool allowZeroDateTime, DateTimeKind dateTimeKind)
 	{
 		Exception? exception = null;
 		if (!Utf8Parser.TryParse(data, out int year, out var bytesConsumed) || bytesConsumed != 4)
@@ -30,9 +33,9 @@ public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayloa
 
 		if (year == 0 && month == 0 && day == 0)
 		{
-			if (m_convertZeroDateTime)
+			if (convertZeroDateTime)
 				return DateTime.MinValue;
-			if (m_allowZeroDateTime)
+			if (allowZeroDateTime)
 				return default(MySqlDateTime);
 			throw new InvalidCastException("Unable to convert MySQL date/time to System.DateTime, set AllowZeroDateTime=True or ConvertZeroDateTime=True in the connection string. See https://mysqlconnector.net/connection-options/");
 		}
@@ -78,11 +81,11 @@ public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayloa
 
 		try
 		{
-			return m_allowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
+			return allowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
 #if NET7_0_OR_GREATER
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, m_dateTimeKind);
+				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, dateTimeKind);
 #else
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, m_dateTimeKind).AddTicks(microseconds % 1000 * 10);
+				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, dateTimeKind).AddTicks(microseconds % 1000 * 10);
 #endif
 		}
 		catch (Exception ex)
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index e34110859..0c950cd18 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -268,7 +268,7 @@ public DateTime GetDateTime(int ordinal)
 		{
 			// slightly inefficient to roundtrip the bytes through a string, but this is assumed to be an infrequent code path; this could be optimised to reprocess the original bytes
 			if (dateString.Length is >= 10 and <= 26)
-				value = ParseDateTime(Encoding.UTF8.GetBytes(dateString));
+				value = TextDateTimeColumnReader.ParseDateTime(Encoding.UTF8.GetBytes(dateString), Connection.ConvertZeroDateTime, Connection.AllowZeroDateTime, Connection.DateTimeKind);
 			else
 				throw new FormatException($"Couldn't interpret value as a valid DateTime: {value}");
 		}
@@ -386,86 +386,6 @@ protected Row(bool binary, ResultSet resultSet)
 	protected ResultSet ResultSet { get; }
 	protected MySqlConnection Connection => ResultSet.Connection;
 
-	protected object ParseDateTime(ReadOnlySpan<byte> value)
-	{
-		Exception? exception = null;
-		if (!Utf8Parser.TryParse(value, out int year, out var bytesConsumed) || bytesConsumed != 4)
-			goto InvalidDateTime;
-		if (value.Length < 5 || value[4] != 45)
-			goto InvalidDateTime;
-		if (!Utf8Parser.TryParse(value[5..], out int month, out bytesConsumed) || bytesConsumed != 2)
-			goto InvalidDateTime;
-		if (value.Length < 8 || value[7] != 45)
-			goto InvalidDateTime;
-		if (!Utf8Parser.TryParse(value[8..], out int day, out bytesConsumed) || bytesConsumed != 2)
-			goto InvalidDateTime;
-
-		if (year == 0 && month == 0 && day == 0)
-		{
-			if (Connection.ConvertZeroDateTime)
-				return DateTime.MinValue;
-			if (Connection.AllowZeroDateTime)
-				return default(MySqlDateTime);
-			throw new InvalidCastException("Unable to convert MySQL date/time to System.DateTime, set AllowZeroDateTime=True or ConvertZeroDateTime=True in the connection string. See https://mysqlconnector.net/connection-options/");
-		}
-
-		int hour, minute, second, microseconds;
-		if (value.Length == 10)
-		{
-			hour = 0;
-			minute = 0;
-			second = 0;
-			microseconds = 0;
-		}
-		else
-		{
-			if (value[10] != 32)
-				goto InvalidDateTime;
-			if (!Utf8Parser.TryParse(value[11..], out hour, out bytesConsumed) || bytesConsumed != 2)
-				goto InvalidDateTime;
-			if (value.Length < 14 || value[13] != 58)
-				goto InvalidDateTime;
-			if (!Utf8Parser.TryParse(value[14..], out minute, out bytesConsumed) || bytesConsumed != 2)
-				goto InvalidDateTime;
-			if (value.Length < 17 || value[16] != 58)
-				goto InvalidDateTime;
-			if (!Utf8Parser.TryParse(value[17..], out second, out bytesConsumed) || bytesConsumed != 2)
-				goto InvalidDateTime;
-
-			if (value.Length == 19)
-			{
-				microseconds = 0;
-			}
-			else
-			{
-				if (value[19] != 46)
-					goto InvalidDateTime;
-
-				if (!Utf8Parser.TryParse(value[20..], out microseconds, out bytesConsumed) || bytesConsumed != value.Length - 20)
-					goto InvalidDateTime;
-				for (; bytesConsumed < 6; bytesConsumed++)
-					microseconds *= 10;
-			}
-		}
-
-		try
-		{
-			return Connection.AllowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
-#if NET7_0_OR_GREATER
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, Connection.DateTimeKind);
-#else
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, Connection.DateTimeKind).AddTicks(microseconds % 1000 * 10);
-#endif
-		}
-		catch (Exception ex)
-		{
-			exception = ex;
-		}
-
-InvalidDateTime:
-		throw new FormatException($"Couldn't interpret value as a valid DateTime: {Encoding.UTF8.GetString(value)}", exception);
-	}
-
 #if NET5_0_OR_GREATER
 	[SkipLocalsInit]
 #endif

From 88a54a64bf13a872ebf930c1a694aea88e9bdf01 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 22:20:16 -0700
Subject: [PATCH 113/393] Use MySqlDbType in InvalidCastException message.

This restores the content of the exception message to what it was before #1330.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../ColumnReaders/BinaryBooleanColumnReader.cs             | 2 +-
 .../ColumnReaders/BinarySignedInt16ColumnReader.cs         | 2 +-
 .../ColumnReaders/BinarySignedInt32ColumnReader.cs         | 2 +-
 .../ColumnReaders/BinarySignedInt64ColumnReader.cs         | 2 +-
 .../ColumnReaders/BinarySignedInt8ColumnReader.cs          | 2 +-
 .../ColumnReaders/BinaryUnsignedInt16ColumnReader.cs       | 2 +-
 .../ColumnReaders/BinaryUnsignedInt32ColumnReader.cs       | 2 +-
 .../ColumnReaders/BinaryUnsignedInt64ColumnReader.cs       | 2 +-
 .../ColumnReaders/BinaryUnsignedInt8ColumnReader.cs        | 2 +-
 src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs | 2 +-
 src/MySqlConnector/ColumnReaders/BitColumnReader.cs        | 2 +-
 src/MySqlConnector/ColumnReaders/ColumnReader.cs           | 4 ++--
 src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs    | 2 +-
 .../ColumnReaders/TextBooleanColumnReader.cs               | 2 +-
 .../ColumnReaders/TextSignedInt16ColumnReader.cs           | 2 +-
 .../ColumnReaders/TextSignedInt32ColumnReader.cs           | 2 +-
 .../ColumnReaders/TextSignedInt64ColumnReader.cs           | 2 +-
 .../ColumnReaders/TextSignedInt8ColumnReader.cs            | 2 +-
 .../ColumnReaders/TextUnsignedInt16ColumnReader.cs         | 2 +-
 .../ColumnReaders/TextUnsignedInt32ColumnReader.cs         | 2 +-
 .../ColumnReaders/TextUnsignedInt64ColumnReader.cs         | 2 +-
 .../ColumnReaders/TextUnsignedInt8ColumnReader.cs          | 2 +-
 src/MySqlConnector/Core/Row.cs                             | 7 ++++---
 23 files changed, 27 insertions(+), 26 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
index 50ecaf3e8..9f45dedab 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryBooleanColumnReader.cs
@@ -10,7 +10,7 @@ internal sealed class BinaryBooleanColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data) ? 1 : 0;
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
index dfe7077ba..60e6ffa2b 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt16ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class BinarySignedInt16ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
index 224a7e6c8..e35c499ca 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt32ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class BinarySignedInt32ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
index 872e2eaba..38b17e572 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt64ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class BinarySignedInt64ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		checked((int) DoReadValue(data));
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
index 0886df664..6737512e4 100644
--- a/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinarySignedInt8ColumnReader.cs
@@ -10,7 +10,7 @@ internal sealed class BinarySignedInt8ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
index 4dbceb781..7b4a52099 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt16ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class BinaryUnsignedInt16ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
index 7d549b48f..b84065a0d 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt32ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class BinaryUnsignedInt32ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		checked((int) DoReadValue(data));
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
index 2f1e0ed86..a0c795f78 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt64ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class BinaryUnsignedInt64ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		checked((int) DoReadValue(data));
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
index 49e50b4ff..4c7cbc88a 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryUnsignedInt8ColumnReader.cs
@@ -10,7 +10,7 @@ internal sealed class BinaryUnsignedInt8ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
index 615e6b977..d3f2506cd 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryYearColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class BinaryYearColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
index 46828ccde..d4d87ea47 100644
--- a/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BitColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class BitColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data, columnDefinition);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		checked((int) DoReadValue(data, columnDefinition));
 
 	private static ulong DoReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
diff --git a/src/MySqlConnector/ColumnReaders/ColumnReader.cs b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
index fb121e0ce..02b4b4779 100644
--- a/src/MySqlConnector/ColumnReaders/ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
@@ -6,6 +6,6 @@ internal abstract class ColumnReader
 {
 	public abstract object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
 
-	public virtual int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
-		throw new InvalidCastException($"Can't convert {columnDefinition.ColumnType} to Int32");
+	public virtual int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		default;
 }
diff --git a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
index e6127672b..44c12d4f0 100644
--- a/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/DecimalColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class DecimalColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		(int) DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
index 7a78ab1e6..d05f3c518 100644
--- a/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextBooleanColumnReader.cs
@@ -10,7 +10,7 @@ internal sealed class TextBooleanColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data) ? 1 : 0;
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
index 95e6bcaa1..595e5dc6b 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt16ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class TextSignedInt16ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
index fe7f6e402..7b307c89f 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt32ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class TextSignedInt32ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
index 12323ea7d..3115e08d7 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt64ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class TextSignedInt64ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		checked((int) DoReadValue(data));
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
index a6adfe659..9fa1c87b7 100644
--- a/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextSignedInt8ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class TextSignedInt8ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
index 11ce899a1..c0b19800b 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt16ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class TextUnsignedInt16ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
index 901aa37fc..44fdbbfa3 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt32ColumnReader.cs
@@ -10,7 +10,7 @@ internal sealed class TextUnsignedInt32ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		checked((int) DoReadValue(data));
 
 	private static uint DoReadValue(ReadOnlySpan<byte> data) =>
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
index f3c319679..60d31f581 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt64ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class TextUnsignedInt64ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		checked((int) DoReadValue(data));
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
index 2524a70c1..02e64ac65 100644
--- a/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextUnsignedInt8ColumnReader.cs
@@ -11,7 +11,7 @@ internal sealed class TextUnsignedInt8ColumnReader : ColumnReader
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
-	public override int ReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+	public override int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
 		DoReadValue(data);
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 0c950cd18..6ac4869ba 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -175,13 +175,14 @@ public int GetInt32(int ordinal)
 	{
 		if (ordinal < 0 || ordinal >= ResultSet.ColumnDefinitions!.Length)
 			throw new ArgumentOutOfRangeException(nameof(ordinal), $"value must be between 0 and {ResultSet.ColumnDefinitions!.Length - 1}");
-
 		if (m_dataOffsets[ordinal] == -1)
-			throw new InvalidCastException("Couldn't interpret null value as int");
+			throw new InvalidCastException("Can't convert NULL to Int32");
 
 		var data = m_data.Slice(m_dataOffsets[ordinal], m_dataLengths[ordinal]).Span;
 		var columnDefinition = ResultSet.ColumnDefinitions[ordinal];
-		return m_columnReaders[ordinal].ReadInt32(data, columnDefinition);
+		if (m_columnReaders[ordinal].TryReadInt32(data, columnDefinition) is { } value)
+			return value;
+		throw new InvalidCastException($"Can't convert {ResultSet.GetColumnType(ordinal)} to Int32");
 	}
 
 	public long GetInt64(int ordinal)

From 808f91fc42400b4bd88f75e3b7df6a9e590721d9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 22:38:38 -0700
Subject: [PATCH 114/393] Eliminate repeated logic in ColumnReaderFactory.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../ColumnReaders/ColumnReaderFactory.cs      | 293 +++++++-----------
 1 file changed, 104 insertions(+), 189 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
index b6f2361b9..03c988f96 100644
--- a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
+++ b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
@@ -6,200 +6,115 @@ namespace MySqlConnector.ColumnReaders;
 
 internal class ColumnReaderFactory
 {
-	internal static ColumnReader GetReader(bool binary, ColumnDefinitionPayload columnDefinition, MySqlConnection connection)
+	internal static ColumnReader GetReader(bool isBinary, ColumnDefinitionPayload columnDefinition, MySqlConnection connection)
 	{
 		var isUnsigned = (columnDefinition.ColumnFlags & ColumnFlags.Unsigned) != 0;
-		if (binary)
+		switch (columnDefinition.ColumnType)
 		{
-			switch (columnDefinition.ColumnType)
-			{
-				case ColumnType.Tiny:
-					if (connection.TreatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned)
-						return BinaryBooleanColumnReader.Instance;
-					return isUnsigned ? BinaryUnsignedInt8ColumnReader.Instance : BinarySignedInt8ColumnReader.Instance;
-
-				case ColumnType.Int24:
-				case ColumnType.Long:
-					return isUnsigned ? BinaryUnsignedInt32ColumnReader.Instance : BinarySignedInt32ColumnReader.Instance;
-
-				case ColumnType.Longlong:
-					return isUnsigned ? BinaryUnsignedInt64ColumnReader.Instance : BinarySignedInt64ColumnReader.Instance;
-
-				case ColumnType.Bit:
-					return BitColumnReader.Instance;
-
-				case ColumnType.String:
-					if (connection.GuidFormat == MySqlGuidFormat.Char36
-					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
-						return GuidChar36ColumnReader.Instance;
-					if (connection.GuidFormat == MySqlGuidFormat.Char32
-					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
-						return GuidChar32ColumnReader.Instance;
-					goto case ColumnType.VarString;
-
-				case ColumnType.VarString:
-				case ColumnType.VarChar:
-				case ColumnType.TinyBlob:
-				case ColumnType.Blob:
-				case ColumnType.MediumBlob:
-				case ColumnType.LongBlob:
-				case ColumnType.Enum:
-				case ColumnType.Set:
-					if (columnDefinition.CharacterSet == CharacterSet.Binary)
-					{
-						var guidFormat = connection.GuidFormat;
-						if ((guidFormat is MySqlGuidFormat.Binary16 or MySqlGuidFormat.TimeSwapBinary16
-							    or MySqlGuidFormat.LittleEndianBinary16) && columnDefinition.ColumnLength == 16)
-						{
-							switch (guidFormat)
-							{
-							case MySqlGuidFormat.Binary16:
-								return GuidBinary16ColumnReader.Instance;
-							case MySqlGuidFormat.TimeSwapBinary16:
-								return GuidTimeSwapBinary16ColumnReader.Instance;
-							default:
-								return GuidLittleEndianBinary16ColumnReader.Instance;
-							}
-						}
-						return BytesColumnReader.Instance;
-					}
-					return StringColumnReader.Instance;
-
-				case ColumnType.Json:
+			case ColumnType.Tiny:
+				if (connection.TreatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned)
+					return isBinary ? BinaryBooleanColumnReader.Instance : TextBooleanColumnReader.Instance;
+				return (isBinary, isUnsigned) switch
+				{
+					(false, false) => TextSignedInt8ColumnReader.Instance,
+					(false, true) => TextUnsignedInt8ColumnReader.Instance,
+					(true, false) => BinarySignedInt8ColumnReader.Instance,
+					(true, true) => BinaryUnsignedInt8ColumnReader.Instance,
+				};
+
+			case ColumnType.Int24:
+			case ColumnType.Long:
+				return (isBinary, isUnsigned) switch
+				{
+					(false, false) => TextSignedInt32ColumnReader.Instance,
+					(false, true) => TextUnsignedInt32ColumnReader.Instance,
+					(true, false) => BinarySignedInt32ColumnReader.Instance,
+					(true, true) => BinaryUnsignedInt32ColumnReader.Instance,
+				};
+
+			case ColumnType.Longlong:
+				return (isBinary, isUnsigned) switch
+				{
+					(false, false) => TextSignedInt64ColumnReader.Instance,
+					(false, true) => TextUnsignedInt64ColumnReader.Instance,
+					(true, false) => BinarySignedInt64ColumnReader.Instance,
+					(true, true) => BinaryUnsignedInt64ColumnReader.Instance,
+				};
+
+			case ColumnType.Bit:
+				return BitColumnReader.Instance;
+
+			case ColumnType.String:
+				if (connection.GuidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
+					return GuidChar36ColumnReader.Instance;
+				if (connection.GuidFormat == MySqlGuidFormat.Char32 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
+					return GuidChar32ColumnReader.Instance;
+				goto case ColumnType.VarString;
+
+			case ColumnType.VarString:
+			case ColumnType.VarChar:
+			case ColumnType.TinyBlob:
+			case ColumnType.Blob:
+			case ColumnType.MediumBlob:
+			case ColumnType.LongBlob:
+			case ColumnType.Enum:
+			case ColumnType.Set:
+				if (columnDefinition.CharacterSet != CharacterSet.Binary)
 					return StringColumnReader.Instance;
-
-				case ColumnType.Short:
-					return isUnsigned ?
-						BinaryUnsignedInt16ColumnReader.Instance :
-						BinarySignedInt16ColumnReader.Instance;
-
-				case ColumnType.Date:
-				case ColumnType.DateTime:
-				case ColumnType.NewDate:
-				case ColumnType.Timestamp:
-					return new BinaryDateTimeColumnReader(connection);
-
-				case ColumnType.Time:
-					return BinaryTimeColumnReader.Instance;
-
-				case ColumnType.Year:
-					return BinaryYearColumnReader.Instance;
-
-				case ColumnType.Float:
-					return BinaryFloatColumnReader.Instance;
-
-				case ColumnType.Double:
-					return BinaryDoubleColumnReader.Instance;
-
-				case ColumnType.Decimal:
-				case ColumnType.NewDecimal:
-					return DecimalColumnReader.Instance;
-
-				case ColumnType.Geometry:
-					return BytesColumnReader.Instance;
-
-				case ColumnType.Null:
-					return NullColumnReader.Instance;
-
-				default:
-					throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
-			}
-		}
-		else
-		{
-			switch (columnDefinition.ColumnType)
-			{
-				case ColumnType.Tiny:
-					if (connection.TreatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned)
-						return TextBooleanColumnReader.Instance;
-					return isUnsigned ? TextUnsignedInt8ColumnReader.Instance : TextSignedInt8ColumnReader.Instance;
-
-				case ColumnType.Int24:
-				case ColumnType.Long:
-					return isUnsigned ? TextUnsignedInt32ColumnReader.Instance : TextSignedInt32ColumnReader.Instance;
-
-				case ColumnType.Longlong:
-					return isUnsigned ?
-						TextUnsignedInt64ColumnReader.Instance :
-						TextSignedInt64ColumnReader.Instance;
-
-				case ColumnType.Bit:
-					return BitColumnReader.Instance;
-
-				case ColumnType.String:
-					if (connection.GuidFormat == MySqlGuidFormat.Char36
-					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
-						return GuidChar36ColumnReader.Instance;
-					if (connection.GuidFormat == MySqlGuidFormat.Char32
-					    && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
-						return GuidChar32ColumnReader.Instance;
-					goto case ColumnType.VarString;
-
-				case ColumnType.VarString:
-				case ColumnType.VarChar:
-				case ColumnType.TinyBlob:
-				case ColumnType.Blob:
-				case ColumnType.MediumBlob:
-				case ColumnType.LongBlob:
-				case ColumnType.Enum:
-				case ColumnType.Set:
-					if (columnDefinition.CharacterSet == CharacterSet.Binary)
+				if (columnDefinition.ColumnLength == 16)
+				{
+					return connection.GuidFormat switch
 					{
-						var guidFormat = connection.GuidFormat;
-						if ((guidFormat is MySqlGuidFormat.Binary16 or MySqlGuidFormat.TimeSwapBinary16
-							    or MySqlGuidFormat.LittleEndianBinary16) && columnDefinition.ColumnLength == 16)
-						{
-							switch (guidFormat)
-							{
-								case MySqlGuidFormat.Binary16:
-									return GuidBinary16ColumnReader.Instance;
-								case MySqlGuidFormat.TimeSwapBinary16:
-									return GuidTimeSwapBinary16ColumnReader.Instance;
-								default:
-									return GuidLittleEndianBinary16ColumnReader.Instance;
-							}
-						}
-						return BytesColumnReader.Instance;
-					}
-					return StringColumnReader.Instance;
-
-				case ColumnType.Json:
-					return StringColumnReader.Instance;
-
-				case ColumnType.Short:
-					return isUnsigned ? TextUnsignedInt16ColumnReader.Instance : TextSignedInt16ColumnReader.Instance;
-
-				case ColumnType.Date:
-				case ColumnType.DateTime:
-				case ColumnType.NewDate:
-				case ColumnType.Timestamp:
-					return new TextDateTimeColumnReader(connection);
-
-				case ColumnType.Time:
-					return TextTimeColumnReader.Instance;
-
-				case ColumnType.Float:
-					return TextFloatColumnReader.Instance;
-
-				case ColumnType.Double:
-					return TextDoubleColumnReader.Instance;
-
-				case ColumnType.Decimal:
-				case ColumnType.NewDecimal:
-					return DecimalColumnReader.Instance;
-
-				case ColumnType.Year:
-					return TextSignedInt32ColumnReader.Instance;
-
-				case ColumnType.Geometry:
-					return BytesColumnReader.Instance;
-
-				case ColumnType.Null:
-					return NullColumnReader.Instance;
-
-				default:
-					throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
-			}
+						MySqlGuidFormat.Binary16 => GuidBinary16ColumnReader.Instance,
+						MySqlGuidFormat.TimeSwapBinary16 => GuidTimeSwapBinary16ColumnReader.Instance,
+						MySqlGuidFormat.LittleEndianBinary16 => GuidLittleEndianBinary16ColumnReader.Instance,
+						_ => BytesColumnReader.Instance,
+					};
+				}
+				return BytesColumnReader.Instance;
+
+			case ColumnType.Json:
+				return StringColumnReader.Instance;
+
+			case ColumnType.Short:
+				return (isBinary, isUnsigned) switch
+				{
+					(false, false) => TextSignedInt16ColumnReader.Instance,
+					(false, true) => TextUnsignedInt16ColumnReader.Instance,
+					(true, false) => BinarySignedInt16ColumnReader.Instance,
+					(true, true) => BinaryUnsignedInt16ColumnReader.Instance,
+				};
+
+			case ColumnType.Date:
+			case ColumnType.DateTime:
+			case ColumnType.NewDate:
+			case ColumnType.Timestamp:
+				return isBinary ? new BinaryDateTimeColumnReader(connection) : new TextDateTimeColumnReader(connection);
+
+			case ColumnType.Time:
+				return isBinary ? BinaryTimeColumnReader.Instance : TextTimeColumnReader.Instance;
+
+			case ColumnType.Year:
+				return isBinary ? BinaryYearColumnReader.Instance : TextSignedInt32ColumnReader.Instance;
+
+			case ColumnType.Float:
+				return isBinary ? BinaryFloatColumnReader.Instance : TextFloatColumnReader.Instance;
+
+			case ColumnType.Double:
+				return isBinary ? BinaryDoubleColumnReader.Instance : TextDoubleColumnReader.Instance;
+
+			case ColumnType.Decimal:
+			case ColumnType.NewDecimal:
+				return DecimalColumnReader.Instance;
+
+			case ColumnType.Geometry:
+				return BytesColumnReader.Instance;
+
+			case ColumnType.Null:
+				return NullColumnReader.Instance;
+
+			default:
+				throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
 		}
 	}
 }

From d5d2ef755d45748437b35e9243f385b8aa7768a7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Jun 2023 22:40:21 -0700
Subject: [PATCH 115/393] Move ColumnReader factory method into ColumnReader
 class.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../ColumnReaders/ColumnReader.cs             | 114 +++++++++++++++++
 .../ColumnReaders/ColumnReaderFactory.cs      | 120 ------------------
 src/MySqlConnector/Core/Row.cs                |   2 +-
 3 files changed, 115 insertions(+), 121 deletions(-)
 delete mode 100644 src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs

diff --git a/src/MySqlConnector/ColumnReaders/ColumnReader.cs b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
index 02b4b4779..86ba9145f 100644
--- a/src/MySqlConnector/ColumnReaders/ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
@@ -1,9 +1,123 @@
+using MySqlConnector.Protocol;
 using MySqlConnector.Protocol.Payloads;
+using MySqlConnector.Protocol.Serialization;
 
 namespace MySqlConnector.ColumnReaders;
 
 internal abstract class ColumnReader
 {
+	public static ColumnReader Create(bool isBinary, ColumnDefinitionPayload columnDefinition, MySqlConnection connection)
+	{
+		var isUnsigned = (columnDefinition.ColumnFlags & ColumnFlags.Unsigned) != 0;
+		switch (columnDefinition.ColumnType)
+		{
+			case ColumnType.Tiny:
+				if (connection.TreatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned)
+					return isBinary ? BinaryBooleanColumnReader.Instance : TextBooleanColumnReader.Instance;
+				return (isBinary, isUnsigned) switch
+				{
+					(false, false) => TextSignedInt8ColumnReader.Instance,
+					(false, true) => TextUnsignedInt8ColumnReader.Instance,
+					(true, false) => BinarySignedInt8ColumnReader.Instance,
+					(true, true) => BinaryUnsignedInt8ColumnReader.Instance,
+				};
+
+			case ColumnType.Int24:
+			case ColumnType.Long:
+				return (isBinary, isUnsigned) switch
+				{
+					(false, false) => TextSignedInt32ColumnReader.Instance,
+					(false, true) => TextUnsignedInt32ColumnReader.Instance,
+					(true, false) => BinarySignedInt32ColumnReader.Instance,
+					(true, true) => BinaryUnsignedInt32ColumnReader.Instance,
+				};
+
+			case ColumnType.Longlong:
+				return (isBinary, isUnsigned) switch
+				{
+					(false, false) => TextSignedInt64ColumnReader.Instance,
+					(false, true) => TextUnsignedInt64ColumnReader.Instance,
+					(true, false) => BinarySignedInt64ColumnReader.Instance,
+					(true, true) => BinaryUnsignedInt64ColumnReader.Instance,
+				};
+
+			case ColumnType.Bit:
+				return BitColumnReader.Instance;
+
+			case ColumnType.String:
+				if (connection.GuidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
+					return GuidChar36ColumnReader.Instance;
+				if (connection.GuidFormat == MySqlGuidFormat.Char32 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
+					return GuidChar32ColumnReader.Instance;
+				goto case ColumnType.VarString;
+
+			case ColumnType.VarString:
+			case ColumnType.VarChar:
+			case ColumnType.TinyBlob:
+			case ColumnType.Blob:
+			case ColumnType.MediumBlob:
+			case ColumnType.LongBlob:
+			case ColumnType.Enum:
+			case ColumnType.Set:
+				if (columnDefinition.CharacterSet != CharacterSet.Binary)
+					return StringColumnReader.Instance;
+				if (columnDefinition.ColumnLength == 16)
+				{
+					return connection.GuidFormat switch
+					{
+						MySqlGuidFormat.Binary16 => GuidBinary16ColumnReader.Instance,
+						MySqlGuidFormat.TimeSwapBinary16 => GuidTimeSwapBinary16ColumnReader.Instance,
+						MySqlGuidFormat.LittleEndianBinary16 => GuidLittleEndianBinary16ColumnReader.Instance,
+						_ => BytesColumnReader.Instance,
+					};
+				}
+				return BytesColumnReader.Instance;
+
+			case ColumnType.Json:
+				return StringColumnReader.Instance;
+
+			case ColumnType.Short:
+				return (isBinary, isUnsigned) switch
+				{
+					(false, false) => TextSignedInt16ColumnReader.Instance,
+					(false, true) => TextUnsignedInt16ColumnReader.Instance,
+					(true, false) => BinarySignedInt16ColumnReader.Instance,
+					(true, true) => BinaryUnsignedInt16ColumnReader.Instance,
+				};
+
+			case ColumnType.Date:
+			case ColumnType.DateTime:
+			case ColumnType.NewDate:
+			case ColumnType.Timestamp:
+				return isBinary ? new BinaryDateTimeColumnReader(connection) : new TextDateTimeColumnReader(connection);
+
+			case ColumnType.Time:
+				return isBinary ? BinaryTimeColumnReader.Instance : TextTimeColumnReader.Instance;
+
+			case ColumnType.Year:
+				return isBinary ? BinaryYearColumnReader.Instance : TextSignedInt32ColumnReader.Instance;
+
+			case ColumnType.Float:
+				return isBinary ? BinaryFloatColumnReader.Instance : TextFloatColumnReader.Instance;
+
+			case ColumnType.Double:
+				return isBinary ? BinaryDoubleColumnReader.Instance : TextDoubleColumnReader.Instance;
+
+			case ColumnType.Decimal:
+			case ColumnType.NewDecimal:
+				return DecimalColumnReader.Instance;
+
+			case ColumnType.Geometry:
+				return BytesColumnReader.Instance;
+
+			case ColumnType.Null:
+				return NullColumnReader.Instance;
+
+			default:
+				throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
+		}
+	}
+
 	public abstract object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition);
 
 	public virtual int? TryReadInt32(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
diff --git a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs b/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
deleted file mode 100644
index 03c988f96..000000000
--- a/src/MySqlConnector/ColumnReaders/ColumnReaderFactory.cs
+++ /dev/null
@@ -1,120 +0,0 @@
-using MySqlConnector.Protocol;
-using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Protocol.Serialization;
-
-namespace MySqlConnector.ColumnReaders;
-
-internal class ColumnReaderFactory
-{
-	internal static ColumnReader GetReader(bool isBinary, ColumnDefinitionPayload columnDefinition, MySqlConnection connection)
-	{
-		var isUnsigned = (columnDefinition.ColumnFlags & ColumnFlags.Unsigned) != 0;
-		switch (columnDefinition.ColumnType)
-		{
-			case ColumnType.Tiny:
-				if (connection.TreatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned)
-					return isBinary ? BinaryBooleanColumnReader.Instance : TextBooleanColumnReader.Instance;
-				return (isBinary, isUnsigned) switch
-				{
-					(false, false) => TextSignedInt8ColumnReader.Instance,
-					(false, true) => TextUnsignedInt8ColumnReader.Instance,
-					(true, false) => BinarySignedInt8ColumnReader.Instance,
-					(true, true) => BinaryUnsignedInt8ColumnReader.Instance,
-				};
-
-			case ColumnType.Int24:
-			case ColumnType.Long:
-				return (isBinary, isUnsigned) switch
-				{
-					(false, false) => TextSignedInt32ColumnReader.Instance,
-					(false, true) => TextUnsignedInt32ColumnReader.Instance,
-					(true, false) => BinarySignedInt32ColumnReader.Instance,
-					(true, true) => BinaryUnsignedInt32ColumnReader.Instance,
-				};
-
-			case ColumnType.Longlong:
-				return (isBinary, isUnsigned) switch
-				{
-					(false, false) => TextSignedInt64ColumnReader.Instance,
-					(false, true) => TextUnsignedInt64ColumnReader.Instance,
-					(true, false) => BinarySignedInt64ColumnReader.Instance,
-					(true, true) => BinaryUnsignedInt64ColumnReader.Instance,
-				};
-
-			case ColumnType.Bit:
-				return BitColumnReader.Instance;
-
-			case ColumnType.String:
-				if (connection.GuidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
-					return GuidChar36ColumnReader.Instance;
-				if (connection.GuidFormat == MySqlGuidFormat.Char32 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
-					return GuidChar32ColumnReader.Instance;
-				goto case ColumnType.VarString;
-
-			case ColumnType.VarString:
-			case ColumnType.VarChar:
-			case ColumnType.TinyBlob:
-			case ColumnType.Blob:
-			case ColumnType.MediumBlob:
-			case ColumnType.LongBlob:
-			case ColumnType.Enum:
-			case ColumnType.Set:
-				if (columnDefinition.CharacterSet != CharacterSet.Binary)
-					return StringColumnReader.Instance;
-				if (columnDefinition.ColumnLength == 16)
-				{
-					return connection.GuidFormat switch
-					{
-						MySqlGuidFormat.Binary16 => GuidBinary16ColumnReader.Instance,
-						MySqlGuidFormat.TimeSwapBinary16 => GuidTimeSwapBinary16ColumnReader.Instance,
-						MySqlGuidFormat.LittleEndianBinary16 => GuidLittleEndianBinary16ColumnReader.Instance,
-						_ => BytesColumnReader.Instance,
-					};
-				}
-				return BytesColumnReader.Instance;
-
-			case ColumnType.Json:
-				return StringColumnReader.Instance;
-
-			case ColumnType.Short:
-				return (isBinary, isUnsigned) switch
-				{
-					(false, false) => TextSignedInt16ColumnReader.Instance,
-					(false, true) => TextUnsignedInt16ColumnReader.Instance,
-					(true, false) => BinarySignedInt16ColumnReader.Instance,
-					(true, true) => BinaryUnsignedInt16ColumnReader.Instance,
-				};
-
-			case ColumnType.Date:
-			case ColumnType.DateTime:
-			case ColumnType.NewDate:
-			case ColumnType.Timestamp:
-				return isBinary ? new BinaryDateTimeColumnReader(connection) : new TextDateTimeColumnReader(connection);
-
-			case ColumnType.Time:
-				return isBinary ? BinaryTimeColumnReader.Instance : TextTimeColumnReader.Instance;
-
-			case ColumnType.Year:
-				return isBinary ? BinaryYearColumnReader.Instance : TextSignedInt32ColumnReader.Instance;
-
-			case ColumnType.Float:
-				return isBinary ? BinaryFloatColumnReader.Instance : TextFloatColumnReader.Instance;
-
-			case ColumnType.Double:
-				return isBinary ? BinaryDoubleColumnReader.Instance : TextDoubleColumnReader.Instance;
-
-			case ColumnType.Decimal:
-			case ColumnType.NewDecimal:
-				return DecimalColumnReader.Instance;
-
-			case ColumnType.Geometry:
-				return BytesColumnReader.Instance;
-
-			case ColumnType.Null:
-				return NullColumnReader.Instance;
-
-			default:
-				throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
-		}
-	}
-}
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 6ac4869ba..81b71062c 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -377,7 +377,7 @@ protected Row(bool binary, ResultSet resultSet)
 		m_dataOffsets = new int[ResultSet.ColumnDefinitions!.Length];
 		m_dataLengths = new int[ResultSet.ColumnDefinitions.Length];
 		m_columnReaders = Array.ConvertAll(ResultSet.ColumnDefinitions,
-			new Converter<ColumnDefinitionPayload, ColumnReader>(column => ColumnReaderFactory.GetReader(binary, column, resultSet.Connection)));
+			new Converter<ColumnDefinitionPayload, ColumnReader>(column => ColumnReader.Create(binary, column, resultSet.Connection)));
 	}
 
 	protected abstract Row CloneCore();

From 3e48b93214d105b06e13b88f1531e471dc50ba10 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 17 Jun 2023 01:07:13 -0700
Subject: [PATCH 116/393] Remove use of Array.ConvertAll.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/Row.cs | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 81b71062c..506016ae8 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -1,10 +1,8 @@
-using System.Buffers.Text;
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text;
 using MySqlConnector.ColumnReaders;
 using MySqlConnector.Protocol;
-using MySqlConnector.Protocol.Payloads;
 using MySqlConnector.Utilities;
 
 namespace MySqlConnector.Core;
@@ -375,9 +373,10 @@ protected Row(bool binary, ResultSet resultSet)
 	{
 		ResultSet = resultSet;
 		m_dataOffsets = new int[ResultSet.ColumnDefinitions!.Length];
-		m_dataLengths = new int[ResultSet.ColumnDefinitions.Length];
-		m_columnReaders = Array.ConvertAll(ResultSet.ColumnDefinitions,
-			new Converter<ColumnDefinitionPayload, ColumnReader>(column => ColumnReader.Create(binary, column, resultSet.Connection)));
+		m_dataLengths = new int[m_dataOffsets.Length];
+		m_columnReaders = new ColumnReader[m_dataOffsets.Length];
+		for (var i = 0; i < m_columnReaders.Length; i++)
+			m_columnReaders[i] = ColumnReader.Create(binary, ResultSet.ColumnDefinitions[i], resultSet.Connection);
 	}
 
 	protected abstract Row CloneCore();

From acd9b9475bf6841c637b1b7777ddfadd37df7ca5 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 17 Jun 2023 01:23:30 -0700
Subject: [PATCH 117/393] Remove TextRow and BinaryRow classes.

There's so little logic left in these classes that the one remaining method can be moved into the base class.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/BinaryRow.cs | 58 ----------------
 src/MySqlConnector/Core/ResultSet.cs |  2 +-
 src/MySqlConnector/Core/Row.cs       | 99 ++++++++++++++++++++--------
 src/MySqlConnector/Core/TextRow.cs   | 25 -------
 4 files changed, 73 insertions(+), 111 deletions(-)
 delete mode 100644 src/MySqlConnector/Core/BinaryRow.cs
 delete mode 100644 src/MySqlConnector/Core/TextRow.cs

diff --git a/src/MySqlConnector/Core/BinaryRow.cs b/src/MySqlConnector/Core/BinaryRow.cs
deleted file mode 100644
index fdcd6a637..000000000
--- a/src/MySqlConnector/Core/BinaryRow.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-using MySqlConnector.Protocol;
-using MySqlConnector.Protocol.Serialization;
-
-namespace MySqlConnector.Core;
-
-internal sealed class BinaryRow : Row
-{
-	public BinaryRow(ResultSet resultSet)
-		: base(true, resultSet)
-	{
-	}
-
-	protected override Row CloneCore() => new BinaryRow(ResultSet);
-
-	protected override void GetDataOffsets(ReadOnlySpan<byte> data, int[] dataOffsets, int[] dataLengths)
-	{
-		Array.Clear(dataOffsets, 0, dataOffsets.Length);
-		for (var column = 0; column < dataOffsets.Length; column++)
-		{
-			if ((data[(column + 2) / 8 + 1] & (1 << ((column + 2) % 8))) != 0)
-			{
-				// column is NULL
-				dataOffsets[column] = -1;
-			}
-		}
-
-		var reader = new ByteArrayReader(data);
-
-		// skip packet header (1 byte) and NULL bitmap (formula for length at https://dev.mysql.com/doc/internals/en/null-bitmap.html)
-		reader.Offset += 1 + (dataOffsets.Length + 7 + 2) / 8;
-		for (var column = 0; column < dataOffsets.Length; column++)
-		{
-			if (dataOffsets[column] == -1)
-			{
-				dataLengths[column] = 0;
-			}
-			else
-			{
-				var columnDefinition = ResultSet.ColumnDefinitions![column];
-				var length = columnDefinition.ColumnType switch
-				{
-					ColumnType.Longlong or ColumnType.Double => 8,
-					ColumnType.Long or ColumnType.Int24 or ColumnType.Float => 4,
-					ColumnType.Short or ColumnType.Year => 2,
-					ColumnType.Tiny => 1,
-					ColumnType.Date or ColumnType.DateTime or ColumnType.NewDate or ColumnType.Timestamp or ColumnType.Time => reader.ReadByte(),
-					ColumnType.DateTime2 or ColumnType.Timestamp2 => throw new NotSupportedException($"ColumnType {columnDefinition.ColumnType} is not supported"),
-					_ => checked((int) reader.ReadLengthEncodedInteger()),
-				};
-
-				dataLengths[column] = length;
-				dataOffsets[column] = reader.Offset;
-			}
-
-			reader.Offset += dataLengths[column];
-		}
-	}
-}
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 2e466b463..6ba23f599 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -275,7 +275,7 @@ public async Task<bool> ReadAsync(IOBehavior ioBehavior, CancellationToken cance
 				}
 			}
 
-			row ??= resultSet.Command.TryGetPreparedStatements() is null ? new TextRow(resultSet) : new BinaryRow(resultSet);
+			row ??= new Row(resultSet.Command.TryGetPreparedStatements() is not null, resultSet);
 			row.SetData(payload.Memory);
 			resultSet.m_hasRows = true;
 			resultSet.BufferState = ResultSetState.ReadingRows;
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 506016ae8..8ff14eb02 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -3,25 +3,83 @@
 using System.Text;
 using MySqlConnector.ColumnReaders;
 using MySqlConnector.Protocol;
+using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
 namespace MySqlConnector.Core;
 
-internal abstract class Row
+internal sealed class Row
 {
-	public void SetData(ReadOnlyMemory<byte> data)
+	public Row(bool isBinary, ResultSet resultSet)
 	{
-		m_data = data;
-		GetDataOffsets(m_data.Span, m_dataOffsets, m_dataLengths!);
+		m_isBinary = isBinary;
+		ResultSet = resultSet;
+
+		var columnDefinitions = ResultSet.ColumnDefinitions!;
+		m_dataOffsets = new int[columnDefinitions.Length];
+		m_dataLengths = new int[columnDefinitions.Length];
+		m_columnReaders = new ColumnReader[columnDefinitions.Length];
+		for (var column = 0; column < m_columnReaders.Length; column++)
+			m_columnReaders[column] = ColumnReader.Create(isBinary, columnDefinitions[column], resultSet.Connection);
 	}
 
-	public Row Clone()
+	public void SetData(ReadOnlyMemory<byte> data)
 	{
-		var clonedRow = CloneCore();
-		var clonedData = new byte[m_data.Length];
-		m_data.CopyTo(clonedData);
-		clonedRow.SetData(clonedData);
-		return clonedRow;
+		m_data = data;
+		if (m_isBinary)
+		{
+			Array.Clear(m_dataOffsets, 0, m_dataOffsets.Length);
+			for (var column = 0; column < m_dataOffsets.Length; column++)
+			{
+				if ((data.Span[(column + 2) / 8 + 1] & (1 << ((column + 2) % 8))) != 0)
+				{
+					// column is NULL
+					m_dataOffsets[column] = -1;
+				}
+			}
+
+			var reader = new ByteArrayReader(data.Span);
+
+			// skip packet header (1 byte) and NULL bitmap (formula for length at https://dev.mysql.com/doc/internals/en/null-bitmap.html)
+			reader.Offset += 1 + (m_dataOffsets.Length + 7 + 2) / 8;
+			for (var column = 0; column < m_dataOffsets.Length; column++)
+			{
+				if (m_dataOffsets[column] == -1)
+				{
+					m_dataLengths[column] = 0;
+				}
+				else
+				{
+					var columnDefinition = ResultSet.ColumnDefinitions![column];
+					var length = columnDefinition.ColumnType switch
+					{
+						ColumnType.Longlong or ColumnType.Double => 8,
+						ColumnType.Long or ColumnType.Int24 or ColumnType.Float => 4,
+						ColumnType.Short or ColumnType.Year => 2,
+						ColumnType.Tiny => 1,
+						ColumnType.Date or ColumnType.DateTime or ColumnType.NewDate or ColumnType.Timestamp or ColumnType.Time => reader.ReadByte(),
+						ColumnType.DateTime2 or ColumnType.Timestamp2 => throw new NotSupportedException($"ColumnType {columnDefinition.ColumnType} is not supported"),
+						_ => checked((int)reader.ReadLengthEncodedInteger()),
+					};
+
+					m_dataLengths[column] = length;
+					m_dataOffsets[column] = reader.Offset;
+				}
+
+				reader.Offset += m_dataLengths[column];
+			}
+		}
+		else
+		{
+			var reader = new ByteArrayReader(data.Span);
+			for (var column = 0; column < m_dataOffsets.Length; column++)
+			{
+				var length = reader.ReadLengthEncodedIntegerOrNull();
+				m_dataLengths[column] = length == -1 ? 0 : length;
+				m_dataOffsets[column] = length == -1 ? -1 : reader.Offset;
+				reader.Offset += m_dataLengths[column];
+			}
+		}
 	}
 
 	public object GetValue(int ordinal)
@@ -369,27 +427,13 @@ public int GetValues(object[] values)
 
 	public object this[string name] => GetValue(ResultSet.GetOrdinal(name));
 
-	protected Row(bool binary, ResultSet resultSet)
-	{
-		ResultSet = resultSet;
-		m_dataOffsets = new int[ResultSet.ColumnDefinitions!.Length];
-		m_dataLengths = new int[m_dataOffsets.Length];
-		m_columnReaders = new ColumnReader[m_dataOffsets.Length];
-		for (var i = 0; i < m_columnReaders.Length; i++)
-			m_columnReaders[i] = ColumnReader.Create(binary, ResultSet.ColumnDefinitions[i], resultSet.Connection);
-	}
-
-	protected abstract Row CloneCore();
-
-	protected abstract void GetDataOffsets(ReadOnlySpan<byte> data, int[] dataOffsets, int[] dataLengths);
-
-	protected ResultSet ResultSet { get; }
-	protected MySqlConnection Connection => ResultSet.Connection;
+	private ResultSet ResultSet { get; }
+	private MySqlConnection Connection => ResultSet.Connection;
 
 #if NET5_0_OR_GREATER
 	[SkipLocalsInit]
 #endif
-	protected static unsafe Guid CreateGuidFromBytes(MySqlGuidFormat guidFormat, ReadOnlySpan<byte> bytes) =>
+	private static unsafe Guid CreateGuidFromBytes(MySqlGuidFormat guidFormat, ReadOnlySpan<byte> bytes) =>
 		guidFormat switch
 		{
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
@@ -435,6 +479,7 @@ private static void CheckBufferArguments<T>(long dataOffset, T[] buffer, int buf
 			throw new ArgumentException(nameof(bufferOffset) + " + " + nameof(length) + " cannot exceed " + nameof(buffer) + "." + nameof(buffer.Length), nameof(length));
 	}
 
+	private readonly bool m_isBinary;
 	private readonly int[] m_dataOffsets;
 	private readonly int[] m_dataLengths;
 	private readonly ColumnReader[] m_columnReaders;
diff --git a/src/MySqlConnector/Core/TextRow.cs b/src/MySqlConnector/Core/TextRow.cs
deleted file mode 100644
index ee5794c72..000000000
--- a/src/MySqlConnector/Core/TextRow.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using MySqlConnector.Protocol.Serialization;
-
-namespace MySqlConnector.Core;
-
-internal sealed class TextRow : Row
-{
-	public TextRow(ResultSet resultSet)
-		: base(false, resultSet)
-	{
-	}
-
-	protected override Row CloneCore() => new TextRow(ResultSet);
-
-	protected override void GetDataOffsets(ReadOnlySpan<byte> data, int[] dataOffsets, int[] dataLengths)
-	{
-		var reader = new ByteArrayReader(data);
-		for (var column = 0; column < dataOffsets.Length; column++)
-		{
-			var length = reader.ReadLengthEncodedIntegerOrNull();
-			dataLengths[column] = length == -1 ? 0 : length;
-			dataOffsets[column] = length == -1 ? -1 : reader.Offset;
-			reader.Offset += dataLengths[column];
-		}
-	}
-}

From cf779cd83e582495c5a191e9ef373863a97733bf Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 17 Jun 2023 10:19:10 -0700
Subject: [PATCH 118/393] Reuse GuidColumnReader code.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../ColumnReaders/GuidBinary16ColumnReader.cs | 12 +++++++--
 .../GuidLittleEndianBinary16ColumnReader.cs   |  5 ++++
 .../GuidTimeSwapBinary16ColumnReader.cs       | 12 +++++++--
 src/MySqlConnector/Core/Row.cs                | 26 ++++++-------------
 4 files changed, 33 insertions(+), 22 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
index dbbb0f778..8cc2dd94f 100644
--- a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -7,9 +8,16 @@ internal sealed class GuidBinary16ColumnReader : ColumnReader
 	public static GuidBinary16ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		ReadGuid(data);
+
+#if NET5_0_OR_GREATER
+	[SkipLocalsInit]
+#endif
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	public static Guid ReadGuid(ReadOnlySpan<byte> data) =>
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-		new Guid(stackalloc byte[16] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
+		new(stackalloc byte[16] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #else
-		new Guid(new[] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
+		new(new[] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #endif
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
index 99936f37e..0bd7c2996 100644
--- a/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -7,6 +8,10 @@ internal sealed class GuidLittleEndianBinary16ColumnReader : ColumnReader
 	public static GuidLittleEndianBinary16ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		ReadGuid(data);
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	public static Guid ReadGuid(ReadOnlySpan<byte> data) =>
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 		new Guid(data);
 #else
diff --git a/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
index 6781a629d..8a642708e 100644
--- a/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
@@ -1,3 +1,4 @@
+using System.Runtime.CompilerServices;
 using MySqlConnector.Protocol.Payloads;
 
 namespace MySqlConnector.ColumnReaders;
@@ -7,9 +8,16 @@ internal sealed class GuidTimeSwapBinary16ColumnReader : ColumnReader
 	public static GuidTimeSwapBinary16ColumnReader Instance { get; } = new();
 
 	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition) =>
+		ReadGuid(data);
+
+#if NET5_0_OR_GREATER
+	[SkipLocalsInit]
+#endif
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	public static Guid ReadGuid(ReadOnlySpan<byte> data) =>
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-		new Guid(stackalloc byte[16] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
+		new(stackalloc byte[16] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #else
-		new Guid(new[] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
+		new(new[] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #endif
 }
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 8ff14eb02..4d1961470 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -203,7 +203,14 @@ public Guid GetGuid(int ordinal)
 			return guid;
 
 		if (value is byte[] { Length: 16 } bytes)
-			return CreateGuidFromBytes(Connection.GuidFormat, bytes);
+		{
+			return Connection.GuidFormat switch
+			{
+				MySqlGuidFormat.Binary16 => GuidBinary16ColumnReader.ReadGuid(bytes),
+				MySqlGuidFormat.TimeSwapBinary16 => GuidTimeSwapBinary16ColumnReader.ReadGuid(bytes),
+				_ => GuidLittleEndianBinary16ColumnReader.ReadGuid(bytes),
+			};
+		}
 
 		return (Guid) value;
 	}
@@ -430,23 +437,6 @@ public int GetValues(object[] values)
 	private ResultSet ResultSet { get; }
 	private MySqlConnection Connection => ResultSet.Connection;
 
-#if NET5_0_OR_GREATER
-	[SkipLocalsInit]
-#endif
-	private static unsafe Guid CreateGuidFromBytes(MySqlGuidFormat guidFormat, ReadOnlySpan<byte> bytes) =>
-		guidFormat switch
-		{
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-			MySqlGuidFormat.Binary16 => new Guid(stackalloc byte[16] { bytes[3], bytes[2], bytes[1], bytes[0], bytes[5], bytes[4], bytes[7], bytes[6], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15] }),
-			MySqlGuidFormat.TimeSwapBinary16 => new Guid(stackalloc byte[16] { bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15] }),
-			_ => new Guid(bytes),
-#else
-			MySqlGuidFormat.Binary16 => new Guid(new[] { bytes[3], bytes[2], bytes[1], bytes[0], bytes[5], bytes[4], bytes[7], bytes[6], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15] }),
-			MySqlGuidFormat.TimeSwapBinary16 => new Guid(new[] { bytes[7], bytes[6], bytes[5], bytes[4], bytes[3], bytes[2], bytes[1], bytes[0], bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15] }),
-			_ => new Guid(bytes.ToArray()),
-#endif
-		};
-
 	private void CheckBinaryColumn(int ordinal)
 	{
 		if (m_dataOffsets[ordinal] == -1)

From 675b43d19fbe3f6a3d429d60f2d575a5158e8807 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 1 Jun 2023 23:05:00 -0700
Subject: [PATCH 119/393] Inline temporary local variables.

(cherry picked from commit 4bc694a55be6379829251974ab8d706626daff25)

Conflicts:
	src/MySqlConnector/Core/TextRow.cs
---
 src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs | 5 ++---
 src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs  | 5 ++---
 src/MySqlConnector/Core/ServerSession.cs                   | 3 +--
 src/MySqlConnector/MySqlParameter.cs                       | 7 ++-----
 4 files changed, 7 insertions(+), 13 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
index 38a4427ab..270d1464d 100644
--- a/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextDoubleColumnReader.cs
@@ -13,10 +13,9 @@ public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayloa
 	{
 		if (Utf8Parser.TryParse(data, out double doubleValue, out var doubleBytesConsumed) && doubleBytesConsumed == data.Length)
 			return doubleValue;
-		ReadOnlySpan<byte> doubleInfinity = "-inf"u8;
-		if (data.SequenceEqual(doubleInfinity))
+		if (data.SequenceEqual("-inf"u8))
 			return double.NegativeInfinity;
-		if (data.SequenceEqual(doubleInfinity.Slice(1)))
+		if (data.SequenceEqual("inf"u8))
 			return double.PositiveInfinity;
 		if (data.SequenceEqual("nan"u8))
 			return double.NaN;
diff --git a/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
index 7f583f968..f32594390 100644
--- a/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextFloatColumnReader.cs
@@ -13,10 +13,9 @@ public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayloa
 	{
 		if (Utf8Parser.TryParse(data, out float floatValue, out var floatBytesConsumed) && floatBytesConsumed == data.Length)
 			return floatValue;
-		ReadOnlySpan<byte> floatInfinity = "-inf"u8;
-		if (data.SequenceEqual(floatInfinity))
+		if (data.SequenceEqual("-inf"u8))
 			return float.NegativeInfinity;
-		if (data.SequenceEqual(floatInfinity.Slice(1)))
+		if (data.SequenceEqual("inf"u8))
 			return float.PositiveInfinity;
 		if (data.SequenceEqual("nan"u8))
 			return float.NaN;
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 6f2f81402..29b5df4a2 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1406,7 +1406,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 				// find the index of each individual certificate in the file (assuming there may be multiple certificates concatenated together)
 				for (var index = 0; index != -1;)
 				{
-					var nextIndex = Utility.FindNextIndex(certificateBytes, index + 1, BeginCertificateBytes);
+					var nextIndex = Utility.FindNextIndex(certificateBytes, index + 1, "-----BEGIN CERTIFICATE-----"u8);
 					try
 					{
 						// load the certificate at this index; note that 'new X509Certificate' stops at the end of the first certificate it loads
@@ -1953,7 +1953,6 @@ protected override void OnStatementBegin(int index)
 	[LoggerMessage(EventIds.ExpectedSessionState6, LogLevel.Error, "Session {SessionId} should have state {ExpectedState1} or {ExpectedState2} or {ExpectedState3} or {ExpectedState4} or {ExpectedState5} or {ExpectedState6} but was {SessionState}")]
 	private static partial void ExpectedSessionState6(ILogger logger, string sessionId, State expectedState1, State expectedState2, State expectedState3, State expectedState4, State expectedState5, State expectedState6, State sessionState);
 
-	private static ReadOnlySpan<byte> BeginCertificateBytes => "-----BEGIN CERTIFICATE-----"u8;
 	private static readonly PayloadData s_setNamesUtf8NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8;"u8);
 	private static readonly PayloadData s_setNamesUtf8mb4NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8mb4;"u8);
 	private static readonly PayloadData s_setNamesUtf8WithAttributesPayload = QueryPayload.Create(true, "SET NAMES utf8;"u8);
diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 597bc376f..94f8e32b0 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -193,8 +193,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 
 		if (Value is null || Value == DBNull.Value)
 		{
-			ReadOnlySpan<byte> nullBytes = "NULL"u8;
-			writer.Write(nullBytes);
+			writer.Write("NULL"u8);
 		}
 		else if (Value is string stringValue)
 		{
@@ -313,9 +312,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		}
 		else if (Value is bool boolValue)
 		{
-			ReadOnlySpan<byte> trueBytes = "true"u8;
-			ReadOnlySpan<byte> falseBytes = "false"u8;
-			writer.Write(boolValue ? trueBytes : falseBytes);
+			writer.Write(boolValue ? "true"u8 : "false"u8);
 		}
 		else if (Value is float floatValue)
 		{

From 9b57d3992cff0a6bc476d707068d827bd7381076 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Piotr=20Kie=C5=82kowicz?= <pkiekowicz@splunk.com>
Date: Tue, 20 Jun 2023 10:03:42 +0200
Subject: [PATCH 120/393] Activities - update handling status
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Piotr Kiełkowicz <pkiekowicz@splunk.com>
---
 src/MySqlConnector/MySqlConnection.cs              |  2 --
 src/MySqlConnector/MySqlDataReader.cs              |  5 -----
 src/MySqlConnector/MySqlTransaction.cs             |  2 --
 .../Utilities/ActivitySourceHelper.cs              |  9 ---------
 tests/IntegrationTests/ActivityTests.cs            | 14 ++------------
 5 files changed, 2 insertions(+), 30 deletions(-)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 0d59b2270..c65a5be9f 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -447,8 +447,6 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 
 			if (m_connectionSettings.AutoEnlist && System.Transactions.Transaction.Current is not null)
 				EnlistTransaction(System.Transactions.Transaction.Current);
-
-			activity?.SetSuccess();
 		}
 		catch (Exception ex) when (activity is { IsAllDataRequested: true })
 		{
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index b4091b832..1ec0e8106 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -468,7 +468,6 @@ internal async Task InitAsync(CommandListPosition commandListPosition, ICommandP
 			throw new InvalidOperationException("Expected ColumnDefinitions to be null");
 		m_closed = false;
 		m_hasWarnings = false;
-		m_initializationFailed = false;
 		RealRecordsAffected = null;
 
 		// initialize for new command
@@ -503,7 +502,6 @@ internal async Task InitAsync(CommandListPosition commandListPosition, ICommandP
 				activity.SetException(ex);
 				activity.Stop();
 			}
-			m_initializationFailed = true;
 			Dispose();
 			throw;
 		}
@@ -638,8 +636,6 @@ internal async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancel
 			Command.CancellableCommand.SetTimeout(Constants.InfiniteTimeout);
 			connection.FinishQuerying(m_hasWarnings);
 
-			if (!m_initializationFailed)
-				Activity?.SetSuccess();
 			Activity?.Stop();
 			Activity = null;
 
@@ -709,6 +705,5 @@ private ResultSet GetResultSet()
 	private bool m_closed;
 	private bool m_hasWarnings;
 	private bool m_hasMoreResults;
-	private bool m_initializationFailed;
 	private DataTable? m_schemaTable;
 }
diff --git a/src/MySqlConnector/MySqlTransaction.cs b/src/MySqlConnector/MySqlTransaction.cs
index ded7bfa59..fffd938c6 100644
--- a/src/MySqlConnector/MySqlTransaction.cs
+++ b/src/MySqlConnector/MySqlTransaction.cs
@@ -36,7 +36,6 @@ private async Task CommitAsync(IOBehavior ioBehavior, CancellationToken cancella
 				await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 			Connection!.CurrentTransaction = null;
 			Connection = null;
-			activity?.SetSuccess();
 		}
 		catch (Exception ex) when (activity is { IsAllDataRequested: true })
 		{
@@ -260,7 +259,6 @@ private async Task DoRollback(IOBehavior ioBehavior, CancellationToken cancellat
 		{
 			using var cmd = new MySqlCommand("rollback", Connection, this) { NoActivity = true };
 			await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			activity?.SetSuccess();
 		}
 		catch (Exception ex) when (activity is { IsAllDataRequested: true })
 		{
diff --git a/src/MySqlConnector/Utilities/ActivitySourceHelper.cs b/src/MySqlConnector/Utilities/ActivitySourceHelper.cs
index 55ba7a24d..a96dbe78e 100644
--- a/src/MySqlConnector/Utilities/ActivitySourceHelper.cs
+++ b/src/MySqlConnector/Utilities/ActivitySourceHelper.cs
@@ -16,7 +16,6 @@ internal static class ActivitySourceHelper
 	public const string NetPeerNameTagName = "net.peer.name";
 	public const string NetPeerPortTagName = "net.peer.port";
 	public const string NetTransportTagName = "net.transport";
-	public const string StatusCodeTagName = "otel.status_code";
 	public const string ThreadIdTagName = "thread.id";
 
 	public const string DatabaseSystemValue = "mysql";
@@ -35,18 +34,10 @@ internal static class ActivitySourceHelper
 		return activity;
 	}
 
-	public static void SetSuccess(this Activity activity)
-	{
-		activity.SetStatus(ActivityStatusCode.Ok);
-		activity.SetTag(StatusCodeTagName, "OK");
-	}
-
 	public static void SetException(this Activity activity, Exception exception)
 	{
 		var description = exception is MySqlException mySqlException ? mySqlException.ErrorCode.ToString() : exception.Message;
 		activity.SetStatus(ActivityStatusCode.Error, description);
-		activity.SetTag(StatusCodeTagName, "ERROR");
-		activity.SetTag("otel.status_description", description);
 		activity.AddEvent(new ActivityEvent("exception", tags: new ActivityTagsCollection
 		{
 			{ "exception.type", exception.GetType().FullName },
diff --git a/tests/IntegrationTests/ActivityTests.cs b/tests/IntegrationTests/ActivityTests.cs
index 65d8e22fc..7875f99e2 100644
--- a/tests/IntegrationTests/ActivityTests.cs
+++ b/tests/IntegrationTests/ActivityTests.cs
@@ -38,12 +38,9 @@ public void OpenTags()
 		Assert.NotNull(activity);
 		Assert.Equal(ActivityKind.Client, activity.Kind);
 		Assert.Equal("Open", activity.OperationName);
-#if NET6_0_OR_GREATER
-		Assert.Equal(ActivityStatusCode.Ok, activity.Status);
-#endif
+		Assert.Equal(ActivityStatusCode.Unset, activity.Status);
 
 		AssertTags(activity.Tags, csb);
-		AssertTag(activity.Tags, "otel.status_code", "OK");
 	}
 
 	[Fact]
@@ -74,7 +71,6 @@ public void OpenFromPoolTags()
 				connection.Open();
 
 			Assert.NotNull(activities[i]);
-			AssertTag(activities[i].Tags, "otel.status_code", "OK");
 		}
 
 		// activities should have the same connection ID
@@ -104,12 +100,9 @@ public void OpenFailedTags()
 		Assert.NotNull(activity);
 		Assert.Equal(ActivityKind.Client, activity.Kind);
 		Assert.Equal("Open", activity.OperationName);
-#if NET6_0_OR_GREATER
 		Assert.Equal(ActivityStatusCode.Error, activity.Status);
-#endif
 
 		AssertTags(activity.Tags, csb);
-		AssertTag(activity.Tags, "otel.status_code", "ERROR");
 	}
 
 	[Fact]
@@ -140,14 +133,11 @@ public void SelectTags()
 		Assert.NotNull(activity);
 		Assert.Equal(ActivityKind.Client, activity.Kind);
 		Assert.Equal("Execute", activity.OperationName);
-#if NET6_0_OR_GREATER
-		Assert.Equal(ActivityStatusCode.Ok, activity.Status);
-#endif
+		Assert.Equal(ActivityStatusCode.Unset, activity.Status);
 
 		AssertTags(activity.Tags, csb);
 		AssertTag(activity.Tags, "db.connection_id", connection.ServerThread.ToString(CultureInfo.InvariantCulture));
 		AssertTag(activity.Tags, "db.statement", "SELECT 1;");
-		AssertTag(activity.Tags, "otel.status_code", "OK");
 	}
 
 	private void AssertTags(IEnumerable<KeyValuePair<string, string>> tags, MySqlConnectionStringBuilder csb)

From 24ce84893ba3e67838c00f5a45822d3ef15d9878 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 24 Jun 2023 18:27:30 -0700
Subject: [PATCH 121/393] Return concrete type from RegisterCancel.

This eliminates the overhead of boxing.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ICancellableCommand.cs | 2 +-
 src/MySqlConnector/MySqlBatch.cs               | 4 ++--
 src/MySqlConnector/MySqlCommand.cs             | 4 ++--
 3 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/MySqlConnector/Core/ICancellableCommand.cs b/src/MySqlConnector/Core/ICancellableCommand.cs
index 2eb99a367..bb4892381 100644
--- a/src/MySqlConnector/Core/ICancellableCommand.cs
+++ b/src/MySqlConnector/Core/ICancellableCommand.cs
@@ -11,7 +11,7 @@ internal interface ICancellableCommand
 	int CommandTimeout { get; }
 	int CancelAttemptCount { get; set; }
 	MySqlConnection? Connection { get; }
-	IDisposable? RegisterCancel(CancellationToken cancellationToken);
+	CancellationTokenRegistration RegisterCancel(CancellationToken cancellationToken);
 	void SetTimeout(int milliseconds);
 	bool IsTimedOut { get; }
 }
diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index a762e1bad..dccfc1ab4 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -249,10 +249,10 @@ public void Dispose()
 	int ICancellableCommand.CommandTimeout => Timeout;
 	int ICancellableCommand.CancelAttemptCount { get; set; }
 
-	IDisposable? ICancellableCommand.RegisterCancel(CancellationToken cancellationToken)
+	CancellationTokenRegistration ICancellableCommand.RegisterCancel(CancellationToken cancellationToken)
 	{
 		if (!cancellationToken.CanBeCanceled)
-			return null;
+			return default;
 
 		m_cancelAction ??= Cancel;
 		return cancellationToken.Register(m_cancelAction);
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index 9bdfa4b19..f2aa3757c 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -385,10 +385,10 @@ public Task DisposeAsync()
 	/// <returns>An object that must be disposed to revoke the cancellation registration.</returns>
 	/// <remarks>This method is more efficient than calling <code>token.Register(Command.Cancel)</code> because it avoids
 	/// unnecessary allocations.</remarks>
-	IDisposable? ICancellableCommand.RegisterCancel(CancellationToken cancellationToken)
+	CancellationTokenRegistration ICancellableCommand.RegisterCancel(CancellationToken cancellationToken)
 	{
 		if (!cancellationToken.CanBeCanceled)
-			return null;
+			return default;
 
 		m_cancelAction ??= Cancel;
 		return cancellationToken.Register(m_cancelAction);

From 3f3bb0b86df270fc5614f76435b7424f7ddafd4f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 24 Jun 2023 18:32:01 -0700
Subject: [PATCH 122/393] Remove duplicate call to RegisterCancel.

This was identified as unnecessary in https://github.com/mysql-net/MySqlConnector/pull/1337.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ResultSet.cs | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 6ba23f599..944356edd 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -231,7 +231,6 @@ public async Task<bool> ReadAsync(IOBehavior ioBehavior, CancellationToken cance
 		if (BufferState is ResultSetState.HasMoreData or ResultSetState.NoMoreData or ResultSetState.None)
 			return new ValueTask<Row?>(default(Row?));
 
-		using var registration = Command.CancellableCommand.RegisterCancel(cancellationToken);
 		var payloadValueTask = Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None);
 		return payloadValueTask.IsCompletedSuccessfully
 			? new ValueTask<Row?>(ScanRowAsyncRemainder(this, payloadValueTask.Result, row))

From 4cd2fe8b83e6c7aedeeebcc835c6af4f0172107f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 24 Jun 2023 18:56:53 -0700
Subject: [PATCH 123/393] Reduce overhead of ResetCommandTimeout.

Specifically, avoid calling 'session.SetTimeout(Constants.InfiniteTimeout)' if CommandTimeout has been set to zero (which disables a lot of expensive timeout-tracking work).

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Core/ICancellableCommand.cs               | 58 ++++++++++++-------
 src/MySqlConnector/MySqlBatch.cs              | 24 +++++---
 src/MySqlConnector/MySqlCommand.cs            |  9 ++-
 3 files changed, 61 insertions(+), 30 deletions(-)

diff --git a/src/MySqlConnector/Core/ICancellableCommand.cs b/src/MySqlConnector/Core/ICancellableCommand.cs
index bb4892381..0379005d9 100644
--- a/src/MySqlConnector/Core/ICancellableCommand.cs
+++ b/src/MySqlConnector/Core/ICancellableCommand.cs
@@ -9,6 +9,7 @@ internal interface ICancellableCommand
 {
 	int CommandId { get; }
 	int CommandTimeout { get; }
+	int? EffectiveCommandTimeout { get; set; }
 	int CancelAttemptCount { get; set; }
 	MySqlConnection? Connection { get; }
 	CancellationTokenRegistration RegisterCancel(CancellationToken cancellationToken);
@@ -27,7 +28,7 @@ internal static class ICancellableCommandExtensions
 	/// Returns the time (in seconds) until a command should be canceled, clamping it to the maximum time
 	/// allowed including CancellationTimeout.
 	/// </summary>
-	public static int GetCommandTimeUntilCanceled(this ICancellableCommand command)
+	private static int GetCommandTimeUntilCanceled(ICancellableCommand command)
 	{
 		var commandTimeout = command.CommandTimeout;
 		var session = command.Connection?.Session;
@@ -36,7 +37,7 @@ public static int GetCommandTimeUntilCanceled(this ICancellableCommand command)
 
 		// the total cancellation period (graphically) is [===CommandTimeout===][=CancellationTimeout=], which can't
 		// exceed int.MaxValue/1000 because it has to be multiplied by 1000 to be converted to milliseconds
-		return Math.Min(commandTimeout, Math.Max(1, (int.MaxValue / 1000) - session.CancellationTimeout));
+		return Math.Min(commandTimeout, Math.Max(1, (int.MaxValue / 1000) - Math.Max(0, session.CancellationTimeout)));
 	}
 
 	/// <summary>
@@ -53,28 +54,41 @@ public static int GetCommandTimeUntilCanceled(this ICancellableCommand command)
 	/// method call.</remarks>
 	public static void ResetCommandTimeout(this ICancellableCommand command)
 	{
+		// read value cached on the command
+		var effectiveCommandTimeout = command.EffectiveCommandTimeout;
+
+		// early out if there is no timeout
+		if (effectiveCommandTimeout == Constants.InfiniteTimeout)
+			return;
+
 		var session = command.Connection?.Session;
-		if (session is not null)
+		if (session is null)
+			return;
+
+		// determine the effective command timeout if not already cached
+		if (effectiveCommandTimeout is null)
+		{
+			effectiveCommandTimeout = command.CommandTimeout == 0 || session.CancellationTimeout == 0 ?
+				Constants.InfiniteTimeout :
+				GetCommandTimeUntilCanceled(command);
+			command.EffectiveCommandTimeout = effectiveCommandTimeout;
+		}
+
+		if (effectiveCommandTimeout == Constants.InfiniteTimeout)
+		{
+			// for no timeout, we set an infinite timeout once (then early out above)
+			session.SetTimeout(Constants.InfiniteTimeout);
+		}
+		else if (session.CancellationTimeout > 0)
+		{
+			// try to cancel first, then close socket
+			command.SetTimeout(effectiveCommandTimeout.Value * 1000);
+			session.SetTimeout((effectiveCommandTimeout.Value + session.CancellationTimeout) * 1000);
+		}
+		else
 		{
-			if (command.CommandTimeout == 0 || session.CancellationTimeout == 0)
-			{
-				session.SetTimeout(Constants.InfiniteTimeout);
-			}
-			else
-			{
-				var commandTimeUntilCanceled = command.GetCommandTimeUntilCanceled() * 1000;
-				if (session.CancellationTimeout > 0)
-				{
-					// try to cancel first, then close socket
-					command.SetTimeout(commandTimeUntilCanceled);
-					session.SetTimeout(commandTimeUntilCanceled + session.CancellationTimeout * 1000);
-				}
-				else
-				{
-					// close socket once the timeout is reached
-					session.SetTimeout(commandTimeUntilCanceled);
-				}
-			}
+			// close socket once the timeout is reached
+			session.SetTimeout(effectiveCommandTimeout.Value * 1000);
 		}
 	}
 
diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index dccfc1ab4..9735492f1 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -133,7 +133,7 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
 	private DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
 #endif
 	{
-		((ICancellableCommand) this).ResetCommandTimeout();
+		this.ResetCommandTimeout();
 		return ExecuteReaderAsync(behavior, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 	}
 
@@ -143,7 +143,7 @@ protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBeha
 	private async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
 #endif
 	{
-		((ICancellableCommand) this).ResetCommandTimeout();
+		this.ResetCommandTimeout();
 		using var registration = ((ICancellableCommand) this).RegisterCancel(cancellationToken);
 		return await ExecuteReaderAsync(behavior, AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
 	}
@@ -191,11 +191,19 @@ public Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken = defa
 #endif
 		ExecuteScalarAsync(AsyncIOBehavior, cancellationToken);
 
+	public
 #if NET6_0_OR_GREATER
-	public override int Timeout { get; set; }
-#else
-	public int Timeout { get; set; }
+		override
 #endif
+		int Timeout
+	{
+		get => m_timeout;
+		set
+		{
+			m_timeout = value;
+			((ICancellableCommand) this).EffectiveCommandTimeout = null;
+		}
+	}
 
 #if NET6_0_OR_GREATER
 	public override void Prepare()
@@ -247,6 +255,7 @@ public void Dispose()
 
 	int ICancellableCommand.CommandId => m_commandId;
 	int ICancellableCommand.CommandTimeout => Timeout;
+	int? ICancellableCommand.EffectiveCommandTimeout { get; set; }
 	int ICancellableCommand.CancelAttemptCount { get; set; }
 
 	CancellationTokenRegistration ICancellableCommand.RegisterCancel(CancellationToken cancellationToken)
@@ -282,7 +291,7 @@ private void CancelCommandForTimeout()
 
 	private async Task<int> ExecuteNonQueryAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		((ICancellableCommand) this).ResetCommandTimeout();
+		this.ResetCommandTimeout();
 		using var registration = ((ICancellableCommand) this).RegisterCancel(cancellationToken);
 		using var reader = await ExecuteReaderAsync(CommandBehavior.Default, ioBehavior, cancellationToken).ConfigureAwait(false);
 		do
@@ -296,7 +305,7 @@ private async Task<int> ExecuteNonQueryAsync(IOBehavior ioBehavior, Cancellation
 
 	private async Task<object?> ExecuteScalarAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		((ICancellableCommand) this).ResetCommandTimeout();
+		this.ResetCommandTimeout();
 		using var registration = ((ICancellableCommand) this).RegisterCancel(cancellationToken);
 		var hasSetResult = false;
 		object? result = null;
@@ -402,6 +411,7 @@ private bool IsPrepared
 
 	private readonly int m_commandId;
 	private bool m_isDisposed;
+	private int m_timeout;
 	private Action? m_cancelAction;
 	private Action? m_cancelForCommandTimeoutAction;
 	private uint m_cancelTimerId;
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index f2aa3757c..a933a8935 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -70,6 +70,7 @@ private MySqlCommand(MySqlCommand other)
 	{
 		GC.SuppressFinalize(this);
 		m_commandTimeout = other.m_commandTimeout;
+		((ICancellableCommand) this).EffectiveCommandTimeout = null;
 		m_commandType = other.m_commandType;
 		DesignTimeVisible = other.DesignTimeVisible;
 		UpdatedRowSource = other.UpdatedRowSource;
@@ -226,7 +227,11 @@ public override string CommandText
 	public override int CommandTimeout
 	{
 		get => Math.Min(m_commandTimeout ?? Connection?.DefaultCommandTimeout ?? 0, int.MaxValue / 1000);
-		set => m_commandTimeout = value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(value), "CommandTimeout must be greater than or equal to zero.");
+		set
+		{
+			m_commandTimeout = value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(value), "CommandTimeout must be greater than or equal to zero.");
+			((ICancellableCommand) this).EffectiveCommandTimeout = null;
+		}
 	}
 
 	/// <inheritdoc/>
@@ -410,6 +415,8 @@ void ICancellableCommand.SetTimeout(int milliseconds)
 
 	int ICancellableCommand.CommandId => m_commandId;
 
+	int? ICancellableCommand.EffectiveCommandTimeout { get; set; }
+
 	int ICancellableCommand.CancelAttemptCount { get; set; }
 
 	ICancellableCommand IMySqlCommand.CancellableCommand => this;

From 8db156dc998817c8254bd192c2cca2a3677af1eb Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 25 Jun 2023 08:06:09 -0700
Subject: [PATCH 124/393] Inline GetCommandTimeUntilCanceled.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Core/ICancellableCommand.cs               | 40 +++++++++----------
 1 file changed, 18 insertions(+), 22 deletions(-)

diff --git a/src/MySqlConnector/Core/ICancellableCommand.cs b/src/MySqlConnector/Core/ICancellableCommand.cs
index 0379005d9..04a713fcc 100644
--- a/src/MySqlConnector/Core/ICancellableCommand.cs
+++ b/src/MySqlConnector/Core/ICancellableCommand.cs
@@ -24,22 +24,6 @@ internal static class ICancellableCommandExtensions
 	/// </summary>
 	public static int GetNextId() => Interlocked.Increment(ref s_id);
 
-	/// <summary>
-	/// Returns the time (in seconds) until a command should be canceled, clamping it to the maximum time
-	/// allowed including CancellationTimeout.
-	/// </summary>
-	private static int GetCommandTimeUntilCanceled(ICancellableCommand command)
-	{
-		var commandTimeout = command.CommandTimeout;
-		var session = command.Connection?.Session;
-		if (commandTimeout == 0 || session is null)
-			return 0;
-
-		// the total cancellation period (graphically) is [===CommandTimeout===][=CancellationTimeout=], which can't
-		// exceed int.MaxValue/1000 because it has to be multiplied by 1000 to be converted to milliseconds
-		return Math.Min(commandTimeout, Math.Max(1, (int.MaxValue / 1000) - Math.Max(0, session.CancellationTimeout)));
-	}
-
 	/// <summary>
 	/// Causes the effective command timeout to be reset back to the value specified by <see cref="ICancellableCommand.CommandTimeout"/>
 	/// plus <see cref="MySqlConnectionStringBuilder.CancellationTimeout"/>. This allows for the command to time out, a cancellation to attempt
@@ -68,9 +52,21 @@ public static void ResetCommandTimeout(this ICancellableCommand command)
 		// determine the effective command timeout if not already cached
 		if (effectiveCommandTimeout is null)
 		{
-			effectiveCommandTimeout = command.CommandTimeout == 0 || session.CancellationTimeout == 0 ?
-				Constants.InfiniteTimeout :
-				GetCommandTimeUntilCanceled(command);
+			var commandTimeout = command.CommandTimeout;
+			var cancellationTimeout = session.CancellationTimeout;
+
+			if (commandTimeout == 0 || cancellationTimeout == 0)
+			{
+				// if commandTimeout is zero, then cancellation doesn't occur
+				effectiveCommandTimeout = Constants.InfiniteTimeout;
+			}
+			else
+			{
+				// the total cancellation period (graphically) is [===CommandTimeout===][=CancellationTimeout=], which can't
+				// exceed int.MaxValue/1000 because it has to be multiplied by 1000 to be converted to milliseconds
+				effectiveCommandTimeout = Math.Min(commandTimeout, Math.Max(1, (int.MaxValue / 1000) - Math.Max(0, session.CancellationTimeout))) * 1000;
+			}
+
 			command.EffectiveCommandTimeout = effectiveCommandTimeout;
 		}
 
@@ -82,13 +78,13 @@ public static void ResetCommandTimeout(this ICancellableCommand command)
 		else if (session.CancellationTimeout > 0)
 		{
 			// try to cancel first, then close socket
-			command.SetTimeout(effectiveCommandTimeout.Value * 1000);
-			session.SetTimeout((effectiveCommandTimeout.Value + session.CancellationTimeout) * 1000);
+			command.SetTimeout(effectiveCommandTimeout.Value);
+			session.SetTimeout(effectiveCommandTimeout.Value + (session.CancellationTimeout * 1000));
 		}
 		else
 		{
 			// close socket once the timeout is reached
-			session.SetTimeout(effectiveCommandTimeout.Value * 1000);
+			session.SetTimeout(effectiveCommandTimeout.Value);
 		}
 	}
 

From d9fffcb8be5e2e56073e85375f6ca291738eabfd Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 25 Jun 2023 12:32:08 -0700
Subject: [PATCH 125/393] Enable CancelSleepSuccessfully tests.

If 'DO SLEEP' returns an error when it's cancelled, test that this still happens within the desired time period, instead of skipping the test.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 tests/IntegrationTests/CancelTests.cs         | 12 ++---
 tests/IntegrationTests/CommandTimeoutTests.cs | 41 ++++-----------
 tests/IntegrationTests/TestUtilities.cs       | 50 +++++++++++++++++++
 3 files changed, 66 insertions(+), 37 deletions(-)

diff --git a/tests/IntegrationTests/CancelTests.cs b/tests/IntegrationTests/CancelTests.cs
index 8c0a2b295..e7fcdfc0a 100644
--- a/tests/IntegrationTests/CancelTests.cs
+++ b/tests/IntegrationTests/CancelTests.cs
@@ -21,7 +21,7 @@ public void NoCancel()
 		Assert.InRange(stopwatch.ElapsedMilliseconds, 100, 1000);
 	}
 
-	[SkippableFact(ServerFeatures.CancelSleepSuccessfully)]
+	[Fact]
 	public void CancelCommand()
 	{
 		using var cmd = new MySqlCommand("SELECT SLEEP(5)", m_database.Connection);
@@ -32,14 +32,14 @@ public void CancelCommand()
 		});
 
 		var stopwatch = Stopwatch.StartNew();
-		TestUtilities.AssertIsOne(cmd.ExecuteScalar());
+		TestUtilities.AssertExecuteScalarReturnsOneOrIsCanceled(cmd);
 		Assert.InRange(stopwatch.ElapsedMilliseconds, 250, 2500);
 
 		task.Wait(); // shouldn't throw
 	}
 
 #if !MYSQL_DATA
-	[SkippableFact(ServerFeatures.CancelSleepSuccessfully | ServerFeatures.Timeout)]
+	[SkippableFact(ServerFeatures.Timeout)]
 	public async Task CancelCommandWithPasswordCallback()
 	{
 		var csb = AppConfig.CreateConnectionStringBuilder();
@@ -58,13 +58,13 @@ public async Task CancelCommandWithPasswordCallback()
 		});
 
 		var stopwatch = Stopwatch.StartNew();
-		TestUtilities.AssertIsOne(await command.ExecuteScalarAsync());
+		await TestUtilities.AssertExecuteScalarReturnsOneOrIsCanceledAsync(command);
 		Assert.InRange(stopwatch.ElapsedMilliseconds, 250, 2500);
 
 		task.Wait(); // shouldn't throw
 	}
 
-	[SkippableFact(ServerFeatures.CancelSleepSuccessfully | ServerFeatures.Timeout)]
+	[SkippableFact(ServerFeatures.Timeout)]
 	public async Task CancelCommandCancellationTokenWithPasswordCallback()
 	{
 		var csb = AppConfig.CreateConnectionStringBuilder();
@@ -78,7 +78,7 @@ public async Task CancelCommandCancellationTokenWithPasswordCallback()
 		using var command = new MySqlCommand("SELECT SLEEP(5)", connection);
 		using var cts = new CancellationTokenSource(TimeSpan.FromMilliseconds(500));
 		var stopwatch = Stopwatch.StartNew();
-		TestUtilities.AssertIsOne(await command.ExecuteScalarAsync(cts.Token));
+		await TestUtilities.AssertExecuteScalarReturnsOneOrIsCanceledAsync(command, cts.Token);
 		Assert.InRange(stopwatch.ElapsedMilliseconds, 250, 2500);
 	}
 #endif
diff --git a/tests/IntegrationTests/CommandTimeoutTests.cs b/tests/IntegrationTests/CommandTimeoutTests.cs
index 4c6c9eadc..d3e9f1224 100644
--- a/tests/IntegrationTests/CommandTimeoutTests.cs
+++ b/tests/IntegrationTests/CommandTimeoutTests.cs
@@ -46,7 +46,7 @@ public void LargeCommandTimeoutIsCoerced()
 		Assert.Equal(2_147_483, command.CommandTimeout);
 	}
 
-	[SkippableFact(ServerFeatures.CancelSleepSuccessfully)]
+	[Fact]
 	public void CommandTimeoutWithSleepSync()
 	{
 		var connectionState = m_connection.State;
@@ -59,11 +59,7 @@ public void CommandTimeoutWithSleepSync()
 			Assert.Contains("fatal error", ex.Message, StringComparison.OrdinalIgnoreCase);
 			connectionState = ConnectionState.Closed;
 #else
-			using (var reader = cmd.ExecuteReader())
-			{
-				Assert.True(reader.Read());
-				Assert.Equal(1, reader.GetInt32(0));
-			}
+			TestUtilities.AssertExecuteScalarReturnsOneOrTimesOut(cmd);
 #endif
 			sw.Stop();
 			TestUtilities.AssertDuration(sw, cmd.CommandTimeout * 1000 - 100, 500);
@@ -72,7 +68,7 @@ public void CommandTimeoutWithSleepSync()
 		Assert.Equal(connectionState, m_connection.State);
 	}
 
-	[SkippableFact(ServerFeatures.CancelSleepSuccessfully | ServerFeatures.Timeout, MySqlData = "https://bugs.mysql.com/bug.php?id=110790")]
+	[SkippableFact(ServerFeatures.Timeout, MySqlData = "https://bugs.mysql.com/bug.php?id=110790")]
 	public async Task CommandTimeoutWithSleepAsync()
 	{
 		var connectionState = m_connection.State;
@@ -85,11 +81,7 @@ public async Task CommandTimeoutWithSleepAsync()
 			Assert.Contains("fatal error", exception.Message, StringComparison.OrdinalIgnoreCase);
 			connectionState = ConnectionState.Closed;
 #else
-			using (var reader = await cmd.ExecuteReaderAsync())
-			{
-				Assert.True(await reader.ReadAsync());
-				Assert.Equal(1, reader.GetInt32(0));
-			}
+			await TestUtilities.AssertExecuteScalarReturnsOneOrTimesOutAsync(cmd);
 #endif
 			sw.Stop();
 			TestUtilities.AssertDuration(sw, cmd.CommandTimeout * 1000 - 100, 700);
@@ -98,7 +90,7 @@ public async Task CommandTimeoutWithSleepAsync()
 		Assert.Equal(connectionState, m_connection.State);
 	}
 
-	[SkippableTheory(ServerFeatures.CancelSleepSuccessfully)]
+	[Theory]
 	[InlineData(true)]
 	[InlineData(false)]
 	public void CommandTimeoutWithStoredProcedureSleepSync(bool pooling)
@@ -126,11 +118,7 @@ create procedure sleep_sproc(IN seconds INT)
 		var ex = Assert.Throws<MySqlException>(cmd.ExecuteReader);
 		Assert.Contains("fatal error", ex.Message, StringComparison.OrdinalIgnoreCase);
 #else
-		using (var reader = cmd.ExecuteReader())
-		{
-			Assert.True(reader.Read());
-			Assert.Equal(1, reader.GetInt32(0));
-		}
+		TestUtilities.AssertExecuteScalarReturnsOneOrTimesOut(cmd);
 #endif
 		sw.Stop();
 		TestUtilities.AssertDuration(sw, cmd.CommandTimeout * 1000 - 100, 500);
@@ -245,8 +233,7 @@ public async Task CommandTimeoutResetsOnReadAsync()
 		Assert.Equal(ConnectionState.Open, m_connection.State);
 	}
 
-
-	[SkippableFact(ServerFeatures.CancelSleepSuccessfully)]
+	[Fact]
 	public void TransactionCommandTimeoutWithSleepSync()
 	{
 		var connectionState = m_connection.State;
@@ -260,11 +247,7 @@ public void TransactionCommandTimeoutWithSleepSync()
 			Assert.Contains("fatal error", ex.Message, StringComparison.OrdinalIgnoreCase);
 			connectionState = ConnectionState.Closed;
 #else
-			using (var reader = cmd.ExecuteReader())
-			{
-				Assert.True(reader.Read());
-				Assert.Equal(1, reader.GetInt32(0));
-			}
+			TestUtilities.AssertExecuteScalarReturnsOneOrTimesOut(cmd);
 #endif
 			sw.Stop();
 			TestUtilities.AssertDuration(sw, cmd.CommandTimeout * 1000 - 100, 500);
@@ -273,7 +256,7 @@ public void TransactionCommandTimeoutWithSleepSync()
 		Assert.Equal(connectionState, m_connection.State);
 	}
 
-	[SkippableFact(ServerFeatures.CancelSleepSuccessfully, MySqlData = "https://bugs.mysql.com/bug.php?id=110790")]
+	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=110790")]
 	public async Task TransactionCommandTimeoutWithSleepAsync()
 	{
 		var connectionState = m_connection.State;
@@ -287,11 +270,7 @@ public async Task TransactionCommandTimeoutWithSleepAsync()
 			Assert.Contains("fatal error", ex.Message, StringComparison.OrdinalIgnoreCase);
 			connectionState = ConnectionState.Closed;
 #else
-			using (var reader = await cmd.ExecuteReaderAsync())
-			{
-				Assert.True(await reader.ReadAsync());
-				Assert.Equal(1, reader.GetInt32(0));
-			}
+			await TestUtilities.AssertExecuteScalarReturnsOneOrTimesOutAsync(cmd);
 #endif
 			sw.Stop();
 		}
diff --git a/tests/IntegrationTests/TestUtilities.cs b/tests/IntegrationTests/TestUtilities.cs
index 3593dc35b..eddceeabe 100644
--- a/tests/IntegrationTests/TestUtilities.cs
+++ b/tests/IntegrationTests/TestUtilities.cs
@@ -38,6 +38,56 @@ public static void AssertIsOne(object value)
 		}
 	}
 
+	public static void AssertExecuteScalarReturnsOneOrIsCanceled(MySqlCommand command) =>
+		AssertExecuteScalarReturnsOneOrThrowsException(command, MySqlErrorCode.QueryInterrupted);
+
+#if !MYSQL_DATA
+	public static void AssertExecuteScalarReturnsOneOrTimesOut(MySqlCommand command) =>
+		AssertExecuteScalarReturnsOneOrThrowsException(command, MySqlErrorCode.CommandTimeoutExpired);
+#endif
+
+	private static void AssertExecuteScalarReturnsOneOrThrowsException(MySqlCommand command, MySqlErrorCode expectedCode)
+	{
+		if (AppConfig.SupportedFeatures.HasFlag(ServerFeatures.CancelSleepSuccessfully))
+		{
+			AssertIsOne(command.ExecuteScalar());
+		}
+		else
+		{
+			var ex = Assert.Throws<MySqlException>(command.ExecuteScalar);
+#if MYSQL_DATA
+			Assert.Equal((int) expectedCode, ex.Number);
+#else
+			Assert.Equal(expectedCode, ex.ErrorCode);
+#endif
+		}
+	}
+
+	public static async Task AssertExecuteScalarReturnsOneOrIsCanceledAsync(MySqlCommand command, CancellationToken token = default) =>
+		await AssertExecuteScalarReturnsOneOrThrowsExceptionAsync(command, MySqlErrorCode.QueryInterrupted, token);
+
+#if !MYSQL_DATA
+	public static async Task AssertExecuteScalarReturnsOneOrTimesOutAsync(MySqlCommand command, CancellationToken token = default) =>
+		await AssertExecuteScalarReturnsOneOrThrowsExceptionAsync(command, MySqlErrorCode.CommandTimeoutExpired, token);
+#endif
+
+	private static async Task AssertExecuteScalarReturnsOneOrThrowsExceptionAsync(MySqlCommand command, MySqlErrorCode expectedCode, CancellationToken token)
+	{
+		if (AppConfig.SupportedFeatures.HasFlag(ServerFeatures.CancelSleepSuccessfully))
+		{
+			AssertIsOne(await command.ExecuteScalarAsync(token));
+		}
+		else
+		{
+			var ex = await Assert.ThrowsAsync<MySqlException>(async () => await command.ExecuteScalarAsync(token));
+#if MYSQL_DATA
+			Assert.Equal((int) expectedCode, ex.Number);
+#else
+			Assert.Equal(expectedCode, ex.ErrorCode);
+#endif
+		}
+	}
+
 	/// <summary>
 	/// Asserts that <paramref name="stopwatch"/> is in the range [minimumMilliseconds, minimumMilliseconds + lengthMilliseconds].
 	/// </summary>

From 58af81126f451fbc823aef496aa7f6eba0cd78cf Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 25 Jun 2023 13:58:24 -0700
Subject: [PATCH 126/393] Skip tests that require DB user to have a password.

---
 tests/IntegrationTests/ConfigSettings.cs | 3 ++-
 tests/IntegrationTests/ConnectAsync.cs   | 2 +-
 tests/IntegrationTests/ConnectSync.cs    | 2 +-
 tests/IntegrationTests/TestUtilities.cs  | 3 +++
 4 files changed, 7 insertions(+), 3 deletions(-)

diff --git a/tests/IntegrationTests/ConfigSettings.cs b/tests/IntegrationTests/ConfigSettings.cs
index fea01b55b..7215e534b 100644
--- a/tests/IntegrationTests/ConfigSettings.cs
+++ b/tests/IntegrationTests/ConfigSettings.cs
@@ -16,5 +16,6 @@ public enum ConfigSettings
 	SecondaryDatabase = 0x400,
 	KnownClientCertificate = 0x800,
 	GSSAPIUser = 0x1000,
-	HasKerberos = 0x2000
+	HasKerberos = 0x2000,
+	UserHasPassword = 0x4000,
 }
diff --git a/tests/IntegrationTests/ConnectAsync.cs b/tests/IntegrationTests/ConnectAsync.cs
index 1f339cc04..27e8d428d 100644
--- a/tests/IntegrationTests/ConnectAsync.cs
+++ b/tests/IntegrationTests/ConnectAsync.cs
@@ -181,7 +181,7 @@ public async Task UsePasswordProvider()
 		Assert.True(wasCalled);
 	}
 
-	[Fact]
+	[SkippableFact(ConfigSettings.UserHasPassword)]
 	public async Task UsePasswordProviderPasswordTakesPrecedence()
 	{
 		var csb = AppConfig.CreateConnectionStringBuilder();
diff --git a/tests/IntegrationTests/ConnectSync.cs b/tests/IntegrationTests/ConnectSync.cs
index bed47320a..502548416 100644
--- a/tests/IntegrationTests/ConnectSync.cs
+++ b/tests/IntegrationTests/ConnectSync.cs
@@ -255,7 +255,7 @@ public void UsePasswordProvider()
 		Assert.True(wasCalled);
 	}
 
-	[Fact]
+	[SkippableFact(ConfigSettings.UserHasPassword)]
 	public void UsePasswordProviderPasswordTakesPrecedence()
 	{
 		var csb = AppConfig.CreateConnectionStringBuilder();
diff --git a/tests/IntegrationTests/TestUtilities.cs b/tests/IntegrationTests/TestUtilities.cs
index eddceeabe..91bf89c9b 100644
--- a/tests/IntegrationTests/TestUtilities.cs
+++ b/tests/IntegrationTests/TestUtilities.cs
@@ -139,6 +139,9 @@ public static string GetSkipReason(ServerFeatures serverFeatures, ConfigSettings
 		if (configSettings.HasFlag(ConfigSettings.PasswordlessUser) && string.IsNullOrWhiteSpace(AppConfig.PasswordlessUser))
 			return "Requires PasswordlessUser in config.json";
 
+		if (configSettings.HasFlag(ConfigSettings.UserHasPassword) && csb.Password.Length == 0)
+			return "Requires password in connection string";
+
 		if (configSettings.HasFlag(ConfigSettings.GSSAPIUser) && string.IsNullOrWhiteSpace(AppConfig.GSSAPIUser))
 			return "Requires GSSAPIUser in config.json";
 

From af4dc5a9e6fa4bee0bac29a20221782d3dcaac17 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 25 Jun 2023 14:05:38 -0700
Subject: [PATCH 127/393] Test MariaDB 11.0.

MariaDB 11.0 replaces 10.10 as the current STS version; it was released on 6 June 2023.
---
 .ci/docker-run.sh    | 20 ++++++++++++--------
 azure-pipelines.yml  |  8 ++++----
 docs/content/home.md |  2 +-
 3 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index f698394bf..0d2425a00 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -23,6 +23,7 @@ IMAGE=$1
 PORT=$2
 OMIT_FEATURES=$3
 MYSQL_EXTRA=
+MYSQL=mysql
 
 if [ "$IMAGE" == "mysql:8.0" ]; then
   MYSQL_EXTRA='--default-authentication-plugin=mysql_native_password'
@@ -30,6 +31,9 @@ fi
 if [[ "$IMAGE" == mariadb* ]]; then
   MYSQL_EXTRA='--in-predicate-conversion-threshold=100000'
 fi
+if [ "$IMAGE" == "mariadb:11.0" ]; then
+  MYSQL='mariadb'
+fi
 
 sudo mkdir -p run/mysql
 sudo chmod 777 run/mysql
@@ -52,40 +56,40 @@ for i in `seq 1 120`; do
 	# wait for mysql to come up
 	sleep 1
 	echo "Testing if container is responding"
-	docker exec mysql mysql -uroot -ptest -e "SELECT 1" >/dev/null 2>&1
+	docker exec mysql $MYSQL -uroot -ptest -e "SELECT 1" >/dev/null 2>&1
 	if [ $? -ne 0 ]; then continue; fi
 
 	# try running the init script
 	echo "Creating mysqltest user"
-	docker exec mysql bash -c 'mysql -uroot -ptest < /etc/mysql/conf.d/init.sql'
+	docker exec mysql bash -c "$MYSQL -uroot -ptest < /etc/mysql/conf.d/init.sql"
 	if [ $? -ne 0 ]; then continue; fi
 
 	if [[ $OMIT_FEATURES != *"Sha256Password"* ]]; then
 		echo "Creating sha256_password user"
-	 	docker exec mysql bash -c 'mysql -uroot -ptest < /etc/mysql/conf.d/init_sha256.sql'
+	 	docker exec mysql bash -c "$MYSQL -uroot -ptest < /etc/mysql/conf.d/init_sha256.sql"
 		if [ $? -ne 0 ]; then exit $?; fi
 	fi
 
 	if [[ $OMIT_FEATURES != *"CachingSha2Password"* ]]; then
 		echo "Creating caching_sha2_password user"
-		docker exec mysql bash -c 'mysql -uroot -ptest < /etc/mysql/conf.d/init_caching_sha2.sql'
+		docker exec mysql bash -c "$MYSQL -uroot -ptest < /etc/mysql/conf.d/init_caching_sha2.sql"
 		if [ $? -ne 0 ]; then exit $?; fi
 	fi
 
 	if [[ $OMIT_FEATURES != *"Ed25519"* ]]; then
 		echo "Installing auth_ed25519 component"
-		docker exec mysql bash -c 'mysql -uroot -ptest < /etc/mysql/conf.d/init_ed25519.sql'
+		docker exec mysql bash -c "$MYSQL -uroot -ptest < /etc/mysql/conf.d/init_ed25519.sql"
 		if [ $? -ne 0 ]; then exit $?; fi
 	fi
 
 	if [[ $OMIT_FEATURES != *"QueryAttributes"* ]]; then
 		echo "Installing query_attributes component"
-		docker exec mysql mysql -uroot -ptest -e "INSTALL COMPONENT 'file://component_query_attributes';"
+		docker exec mysql $MYSQL -uroot -ptest -e "INSTALL COMPONENT 'file://component_query_attributes';"
 		if [ $? -ne 0 ]; then exit $?; fi
 	fi
 
 	# exit if successful
-	docker exec mysql mysql -ussltest -ptest \
+	docker exec mysql $MYSQL -ussltest -ptest \
 		--ssl-mode=REQUIRED \
 		--ssl-ca=/etc/mysql/conf.d/certs/ssl-ca-cert.pem \
 		--ssl-cert=/etc/mysql/conf.d/certs/ssl-client-cert.pem \
@@ -93,7 +97,7 @@ for i in `seq 1 120`; do
 		-e "SELECT 1"
 	if [ $? -ne 0 ]; then
 		# mariadb uses --ssl=TRUE instead of --ssl-mode=REQUIRED
-		docker exec mysql mysql -ussltest -ptest \
+		docker exec mysql $MYSQL -ussltest -ptest \
 			--ssl=TRUE \
 			--ssl-ca=/etc/mysql/conf.d/certs/ssl-ca-cert.pem \
 			--ssl-cert=/etc/mysql/conf.d/certs/ssl-client-cert.pem \
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index a98e16645..dc81df8b3 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -197,14 +197,14 @@ jobs:
         image: 'mariadb:10.6'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
-      'MariaDB 10.10':
-        image: 'mariadb:10.10'
-        connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
       'MariaDB 10.11':
         image: 'mariadb:10.11'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
+      'MariaDB 11.0':
+        image: 'mariadb:11.0'
+        connectionStringExtra: ''
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
   steps:
   - template: '.ci/integration-tests-steps.yml'
     parameters:
diff --git a/docs/content/home.md b/docs/content/home.md
index 50481a17a..05d86eb67 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -64,7 +64,7 @@ Server  | Versions | Notes
 Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://mysqlconnector.net/troubleshooting/aurora-freeze/)
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
-MariaDB | 10.x, 11.x (**10.6**, **10.10**, **10.11**) |
+MariaDB | 10.x, 11.x (**10.6**, **10.11**, **11.0**) |
 MySQL | 5.5, **5.6**, **5.7**, **8.0** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 is EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)

From 22a78dd344f27ccd393f54fa9f77e4bd8e1ad4d5 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 25 Jun 2023 17:18:53 -0700
Subject: [PATCH 128/393] Fix spacing/indentation style issues.

Set the default style to indent case labels.
---
 .editorconfig                                 |   2 +-
 src/MySqlConnector/Core/CachedProcedure.cs    |   6 +-
 src/MySqlConnector/Core/ConnectionSettings.cs |  26 +-
 src/MySqlConnector/Core/ResultSet.cs          |  44 +-
 src/MySqlConnector/Core/Row.cs                |   2 +-
 src/MySqlConnector/Core/ServerSession.cs      | 116 +--
 .../Core/SingleCommandPayloadCreator.cs       |  38 +-
 src/MySqlConnector/Core/TypeMapper.cs         | 164 ++--
 src/MySqlConnector/MySqlBatch.cs              |   2 +-
 src/MySqlConnector/MySqlConnection.cs         |   4 +-
 .../MySqlConnectionStringBuilder.cs           |   4 +-
 src/MySqlConnector/MySqlParameter.cs          |  24 +-
 .../Payloads/HandshakeResponse41Payload.cs    |  42 +-
 .../Protocol/Payloads/OkPayload.cs            |  12 +-
 .../Protocol/Serialization/AuthGSSAPI.cs      |  32 +-
 .../Serialization/ByteBufferWriter.cs         |  34 +-
 .../Protocol/Serialization/ProtocolUtility.cs | 798 +++++++++---------
 .../Utilities/UnixDomainSocketEndPoint.cs     |   4 +-
 src/MySqlConnector/Utilities/Utility.cs       |   2 +-
 19 files changed, 678 insertions(+), 678 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index ee0f4b83f..6d1ccd5a7 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -20,7 +20,7 @@ csharp_style_expression_bodied_methods = true:suggestion
 csharp_style_expression_bodied_constructors = true:suggestion
 csharp_style_expression_bodied_operators = true:suggestion
 csharp_prefer_braces = false:none
-csharp_indent_switch_labels = false
+csharp_indent_switch_labels = true
 csharp_space_after_cast = true
 csharp_preserve_single_line_statements = false
 
diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index c3bad0846..26a45264c 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -12,9 +12,9 @@ internal sealed class CachedProcedure
 	public static async Task<CachedProcedure?> FillAsync(IOBehavior ioBehavior, MySqlConnection connection, string schema, string component, ILogger logger, CancellationToken cancellationToken)
 	{
 		// try to use mysql.proc first, as it is much faster
-		if (!connection.Session.ServerVersion.IsMariaDb
-		    && connection.Session.ServerVersion.Version < ServerVersions.RemovesMySqlProcTable
-		    && !connection.Session.ProcAccessDenied)
+		if (!connection.Session.ServerVersion.IsMariaDb &&
+			connection.Session.ServerVersion.Version < ServerVersions.RemovesMySqlProcTable &&
+			!connection.Session.ProcAccessDenied)
 		{
 			try
 			{
diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index dada980d9..768357de6 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -154,19 +154,19 @@ private static MySqlGuidFormat GetEffectiveGuidFormat(MySqlGuidFormat guidFormat
 	{
 		switch (guidFormat)
 		{
-		case MySqlGuidFormat.Default:
-			return oldGuids ? MySqlGuidFormat.LittleEndianBinary16 : MySqlGuidFormat.Char36;
-		case MySqlGuidFormat.None:
-		case MySqlGuidFormat.Char36:
-		case MySqlGuidFormat.Char32:
-		case MySqlGuidFormat.Binary16:
-		case MySqlGuidFormat.TimeSwapBinary16:
-		case MySqlGuidFormat.LittleEndianBinary16:
-			if (oldGuids)
-				throw new MySqlException("OldGuids cannot be used with GuidFormat");
-			return guidFormat;
-		default:
-			throw new MySqlException("Unknown GuidFormat");
+			case MySqlGuidFormat.Default:
+				return oldGuids ? MySqlGuidFormat.LittleEndianBinary16 : MySqlGuidFormat.Char36;
+			case MySqlGuidFormat.None:
+			case MySqlGuidFormat.Char36:
+			case MySqlGuidFormat.Char32:
+			case MySqlGuidFormat.Binary16:
+			case MySqlGuidFormat.TimeSwapBinary16:
+			case MySqlGuidFormat.LittleEndianBinary16:
+				if (oldGuids)
+					throw new MySqlException("OldGuids cannot be used with GuidFormat");
+				return guidFormat;
+			default:
+				throw new MySqlException("Unknown GuidFormat");
 		}
 	}
 
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 944356edd..5bd0072ac 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -78,30 +78,30 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 							File.OpenRead(localInfile.FileName);
 						switch (source)
 						{
-						case Stream stream:
-							var buffer = ArrayPool<byte>.Shared.Rent(1048576);
-							try
-							{
-								int byteCount;
-								while ((byteCount = await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false)) > 0)
+							case Stream stream:
+								var buffer = ArrayPool<byte>.Shared.Rent(1048576);
+								try
 								{
-									payload = new(new ArraySegment<byte>(buffer, 0, byteCount));
-									await Session.SendReplyAsync(payload, ioBehavior, CancellationToken.None).ConfigureAwait(false);
+									int byteCount;
+									while ((byteCount = await stream.ReadAsync(buffer, 0, buffer.Length, CancellationToken.None).ConfigureAwait(false)) > 0)
+									{
+										payload = new(new ArraySegment<byte>(buffer, 0, byteCount));
+										await Session.SendReplyAsync(payload, ioBehavior, CancellationToken.None).ConfigureAwait(false);
+									}
 								}
-							}
-							finally
-							{
-								ArrayPool<byte>.Shared.Return(buffer);
-								stream.Dispose();
-							}
-							break;
-
-						case MySqlBulkCopy bulkCopy:
-							await bulkCopy.SendDataReaderAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
-							break;
-
-						default:
-							throw new InvalidOperationException($"Unsupported Source type: {source.GetType().Name}");
+								finally
+								{
+									ArrayPool<byte>.Shared.Return(buffer);
+									stream.Dispose();
+								}
+								break;
+
+							case MySqlBulkCopy bulkCopy:
+								await bulkCopy.SendDataReaderAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
+								break;
+
+							default:
+								throw new InvalidOperationException($"Unsupported Source type: {source.GetType().Name}");
 						}
 					}
 					catch (Exception ex)
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 4d1961470..2d8a0d966 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -59,7 +59,7 @@ public void SetData(ReadOnlyMemory<byte> data)
 						ColumnType.Tiny => 1,
 						ColumnType.Date or ColumnType.DateTime or ColumnType.NewDate or ColumnType.Timestamp or ColumnType.Time => reader.ReadByte(),
 						ColumnType.DateTime2 or ColumnType.Timestamp2 => throw new NotSupportedException($"ColumnType {columnDefinition.ColumnType} is not supported"),
-						_ => checked((int)reader.ReadLengthEncodedInteger()),
+						_ => checked((int) reader.ReadLengthEncodedInteger()),
 					};
 
 					m_dataLengths[column] = length;
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 29b5df4a2..7e081ade0 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -611,9 +611,9 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 			ClearPreparedStatements();
 
 			PayloadData payload;
-			if (DatabaseOverride is null
-			    && ((!ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0)
-			    || (ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.MariaDbSupportsResetConnection) >= 0)))
+			if (DatabaseOverride is null &&
+				((!ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) ||
+				(ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.MariaDbSupportsResetConnection) >= 0)))
 			{
 				if (m_supportsPipelining)
 				{
@@ -698,71 +698,71 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 		Log.SwitchingToAuthenticationMethod(m_logger, Id, switchRequest.Name);
 		switch (switchRequest.Name)
 		{
-		case "mysql_native_password":
-			AuthPluginData = switchRequest.Data;
-			var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, password);
-			payload = new(hashedPassword);
-			await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
-			return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			case "mysql_native_password":
+				AuthPluginData = switchRequest.Data;
+				var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, password);
+				payload = new(hashedPassword);
+				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
+				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
-		case "mysql_clear_password":
-			if (!m_isSecureConnection)
-			{
-				Log.NeedsSecureConnection(m_logger, Id, switchRequest.Name);
-				throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, $"Authentication method '{switchRequest.Name}' requires a secure connection.");
-			}
+			case "mysql_clear_password":
+				if (!m_isSecureConnection)
+				{
+					Log.NeedsSecureConnection(m_logger, Id, switchRequest.Name);
+					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, $"Authentication method '{switchRequest.Name}' requires a secure connection.");
+				}
 
-			// send the password as a NULL-terminated UTF-8 string
-			var passwordBytes = Encoding.UTF8.GetBytes(password);
-			Array.Resize(ref passwordBytes, passwordBytes.Length + 1);
-			payload = new(passwordBytes);
-			await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
-			return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-
-		case "caching_sha2_password":
-			var scrambleBytes = AuthenticationUtility.CreateScrambleResponse(Utility.TrimZeroByte(switchRequest.Data.AsSpan()), password);
-			payload = new(scrambleBytes);
-			await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
-			payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+				// send the password as a NULL-terminated UTF-8 string
+				var passwordBytes = Encoding.UTF8.GetBytes(password);
+				Array.Resize(ref passwordBytes, passwordBytes.Length + 1);
+				payload = new(passwordBytes);
+				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
+				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
-			// OK payload can be sent immediately (e.g., if password is empty( (short-circuiting the )
-			if (OkPayload.IsOk(payload.Span, SupportsDeprecateEof))
-				return payload;
+			case "caching_sha2_password":
+				var scrambleBytes = AuthenticationUtility.CreateScrambleResponse(Utility.TrimZeroByte(switchRequest.Data.AsSpan()), password);
+				payload = new(scrambleBytes);
+				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
+				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
-			var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload.Span);
-			if (cachingSha2ServerResponsePayload.Succeeded)
-				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+				// OK payload can be sent immediately (e.g., if password is empty( (short-circuiting the )
+				if (OkPayload.IsOk(payload.Span, SupportsDeprecateEof))
+					return payload;
 
-			goto case "sha256_password";
+				var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload.Span);
+				if (cachingSha2ServerResponsePayload.Succeeded)
+					return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
-		case "sha256_password":
-			if (!m_isSecureConnection && password.Length != 0)
-			{
-				var publicKey = await GetRsaPublicKeyAsync(switchRequest.Name, cs, ioBehavior, cancellationToken).ConfigureAwait(false);
-				return await SendEncryptedPasswordAsync(switchRequest.Data, publicKey, password, ioBehavior, cancellationToken).ConfigureAwait(false);
-			}
-			else
-			{
-				return await SendClearPasswordAsync(password, ioBehavior, cancellationToken).ConfigureAwait(false);
-			}
+				goto case "sha256_password";
 
-		case "auth_gssapi_client":
-			return await AuthGSSAPI.AuthenticateAsync(cs, switchRequest.Data, this, ioBehavior, cancellationToken).ConfigureAwait(false);
+			case "sha256_password":
+				if (!m_isSecureConnection && password.Length != 0)
+				{
+					var publicKey = await GetRsaPublicKeyAsync(switchRequest.Name, cs, ioBehavior, cancellationToken).ConfigureAwait(false);
+					return await SendEncryptedPasswordAsync(switchRequest.Data, publicKey, password, ioBehavior, cancellationToken).ConfigureAwait(false);
+				}
+				else
+				{
+					return await SendClearPasswordAsync(password, ioBehavior, cancellationToken).ConfigureAwait(false);
+				}
 
-		case "mysql_old_password":
-			Log.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
-			throw new NotSupportedException("'MySQL Server is requesting the insecure pre-4.1 auth mechanism (mysql_old_password). The user password must be upgraded; see https://dev.mysql.com/doc/refman/5.7/en/account-upgrades.html.");
+			case "auth_gssapi_client":
+				return await AuthGSSAPI.AuthenticateAsync(cs, switchRequest.Data, this, ioBehavior, cancellationToken).ConfigureAwait(false);
 
-		case "client_ed25519":
-			if (!AuthenticationPlugins.TryGetPlugin(switchRequest.Name, out var ed25519Plugin))
-				throw new NotSupportedException("You must install the MySqlConnector.Authentication.Ed25519 package and call Ed25519AuthenticationPlugin.Install to use client_ed25519 authentication.");
-			payload = new(ed25519Plugin.CreateResponse(password, switchRequest.Data));
-			await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
-			return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			case "mysql_old_password":
+				Log.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
+				throw new NotSupportedException("'MySQL Server is requesting the insecure pre-4.1 auth mechanism (mysql_old_password). The user password must be upgraded; see https://dev.mysql.com/doc/refman/5.7/en/account-upgrades.html.");
+
+			case "client_ed25519":
+				if (!AuthenticationPlugins.TryGetPlugin(switchRequest.Name, out var ed25519Plugin))
+					throw new NotSupportedException("You must install the MySqlConnector.Authentication.Ed25519 package and call Ed25519AuthenticationPlugin.Install to use client_ed25519 authentication.");
+				payload = new(ed25519Plugin.CreateResponse(password, switchRequest.Data));
+				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
+				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
-		default:
-			Log.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
-			throw new NotSupportedException($"Authentication method '{switchRequest.Name}' is not supported.");
+			default:
+				Log.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
+				throw new NotSupportedException($"Authentication method '{switchRequest.Name}' is not supported.");
 		}
 	}
 
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index 9c51ca5a3..b6e18ab95 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -201,25 +201,25 @@ private static bool WriteStoredProcedure(IMySqlCommand command, IDictionary<stri
 			var outName = "@outParam" + i;
 			switch (param.Direction)
 			{
-			case ParameterDirection.Input:
-			case ParameterDirection.InputOutput:
-				var inParam = param.WithParameterName(inName);
-				inParameters.Add(inParam);
-				if (param.Direction == ParameterDirection.InputOutput)
-				{
-					inOutSetParameters += $"SET {outName}={inName}; ";
-					goto case ParameterDirection.Output;
-				}
-				argParameterNames.Add(inName);
-				break;
-			case ParameterDirection.Output:
-				outParameters.Add(param);
-				outParameterNames.Add(outName);
-				argParameterNames.Add(outName);
-				break;
-			case ParameterDirection.ReturnValue:
-				returnParameter = param;
-				break;
+				case ParameterDirection.Input:
+				case ParameterDirection.InputOutput:
+					var inParam = param.WithParameterName(inName);
+					inParameters.Add(inParam);
+					if (param.Direction == ParameterDirection.InputOutput)
+					{
+						inOutSetParameters += $"SET {outName}={inName}; ";
+						goto case ParameterDirection.Output;
+					}
+					argParameterNames.Add(inName);
+					break;
+				case ParameterDirection.Output:
+					outParameters.Add(param);
+					outParameterNames.Add(outName);
+					argParameterNames.Add(outName);
+					break;
+				case ParameterDirection.ReturnValue:
+					returnParameter = param;
+					break;
 			}
 		}
 
diff --git a/src/MySqlConnector/Core/TypeMapper.cs b/src/MySqlConnector/Core/TypeMapper.cs
index 9df3dd64c..2888ce05e 100644
--- a/src/MySqlConnector/Core/TypeMapper.cs
+++ b/src/MySqlConnector/Core/TypeMapper.cs
@@ -193,113 +193,113 @@ public static MySqlDbType ConvertToMySqlDbType(ColumnDefinitionPayload columnDef
 		var isUnsigned = (columnDefinition.ColumnFlags & ColumnFlags.Unsigned) != 0;
 		switch (columnDefinition.ColumnType)
 		{
-		case ColumnType.Tiny:
-			return treatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned ? MySqlDbType.Bool :
-				isUnsigned ? MySqlDbType.UByte : MySqlDbType.Byte;
+			case ColumnType.Tiny:
+				return treatTinyAsBoolean && columnDefinition.ColumnLength == 1 && !isUnsigned ? MySqlDbType.Bool :
+					isUnsigned ? MySqlDbType.UByte : MySqlDbType.Byte;
 
-		case ColumnType.Int24:
-			return isUnsigned ? MySqlDbType.UInt24 : MySqlDbType.Int24;
+			case ColumnType.Int24:
+				return isUnsigned ? MySqlDbType.UInt24 : MySqlDbType.Int24;
 
-		case ColumnType.Long:
-			return isUnsigned ? MySqlDbType.UInt32 : MySqlDbType.Int32;
+			case ColumnType.Long:
+				return isUnsigned ? MySqlDbType.UInt32 : MySqlDbType.Int32;
 
-		case ColumnType.Longlong:
-			return isUnsigned ? MySqlDbType.UInt64 : MySqlDbType.Int64;
+			case ColumnType.Longlong:
+				return isUnsigned ? MySqlDbType.UInt64 : MySqlDbType.Int64;
 
-		case ColumnType.Bit:
-			return MySqlDbType.Bit;
+			case ColumnType.Bit:
+				return MySqlDbType.Bit;
 
-		case ColumnType.String:
-			if (guidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
-				return MySqlDbType.Guid;
-			if (guidFormat == MySqlGuidFormat.Char32 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
-				return MySqlDbType.Guid;
-			if ((columnDefinition.ColumnFlags & ColumnFlags.Enum) != 0)
-				return MySqlDbType.Enum;
-			if ((columnDefinition.ColumnFlags & ColumnFlags.Set) != 0)
-				return MySqlDbType.Set;
-			goto case ColumnType.VarString;
-
-		case ColumnType.VarChar:
-		case ColumnType.VarString:
-		case ColumnType.TinyBlob:
-		case ColumnType.Blob:
-		case ColumnType.MediumBlob:
-		case ColumnType.LongBlob:
-			var type = columnDefinition.ColumnType;
-			if (columnDefinition.CharacterSet == CharacterSet.Binary)
-			{
-				if ((guidFormat is MySqlGuidFormat.Binary16 or MySqlGuidFormat.TimeSwapBinary16 or MySqlGuidFormat.LittleEndianBinary16) && columnDefinition.ColumnLength == 16)
+			case ColumnType.String:
+				if (guidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
 					return MySqlDbType.Guid;
-
+				if (guidFormat == MySqlGuidFormat.Char32 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
+					return MySqlDbType.Guid;
+				if ((columnDefinition.ColumnFlags & ColumnFlags.Enum) != 0)
+					return MySqlDbType.Enum;
+				if ((columnDefinition.ColumnFlags & ColumnFlags.Set) != 0)
+					return MySqlDbType.Set;
+				goto case ColumnType.VarString;
+
+			case ColumnType.VarChar:
+			case ColumnType.VarString:
+			case ColumnType.TinyBlob:
+			case ColumnType.Blob:
+			case ColumnType.MediumBlob:
+			case ColumnType.LongBlob:
+				var type = columnDefinition.ColumnType;
+				if (columnDefinition.CharacterSet == CharacterSet.Binary)
+				{
+					if ((guidFormat is MySqlGuidFormat.Binary16 or MySqlGuidFormat.TimeSwapBinary16 or MySqlGuidFormat.LittleEndianBinary16) && columnDefinition.ColumnLength == 16)
+						return MySqlDbType.Guid;
+
+					return type switch
+					{
+						ColumnType.String => MySqlDbType.Binary,
+						ColumnType.VarString => MySqlDbType.VarBinary,
+						ColumnType.TinyBlob => MySqlDbType.TinyBlob,
+						ColumnType.Blob => MySqlDbType.Blob,
+						ColumnType.MediumBlob => MySqlDbType.MediumBlob,
+						_ => MySqlDbType.LongBlob,
+					};
+				}
 				return type switch
 				{
-					ColumnType.String => MySqlDbType.Binary,
-					ColumnType.VarString => MySqlDbType.VarBinary,
-					ColumnType.TinyBlob => MySqlDbType.TinyBlob,
-					ColumnType.Blob => MySqlDbType.Blob,
-					ColumnType.MediumBlob => MySqlDbType.MediumBlob,
-					_ => MySqlDbType.LongBlob,
+					ColumnType.String => MySqlDbType.String,
+					ColumnType.VarString => MySqlDbType.VarChar,
+					ColumnType.TinyBlob => MySqlDbType.TinyText,
+					ColumnType.Blob => MySqlDbType.Text,
+					ColumnType.MediumBlob => MySqlDbType.MediumText,
+					_ => MySqlDbType.LongText,
 				};
-			}
-			return type switch
-			{
-				ColumnType.String => MySqlDbType.String,
-				ColumnType.VarString => MySqlDbType.VarChar,
-				ColumnType.TinyBlob => MySqlDbType.TinyText,
-				ColumnType.Blob => MySqlDbType.Text,
-				ColumnType.MediumBlob => MySqlDbType.MediumText,
-				_ => MySqlDbType.LongText,
-			};
 
-		case ColumnType.Json:
-			return MySqlDbType.JSON;
+			case ColumnType.Json:
+				return MySqlDbType.JSON;
 
-		case ColumnType.Short:
-			return isUnsigned ? MySqlDbType.UInt16 : MySqlDbType.Int16;
+			case ColumnType.Short:
+				return isUnsigned ? MySqlDbType.UInt16 : MySqlDbType.Int16;
 
-		case ColumnType.Date:
-		case ColumnType.NewDate:
-			return MySqlDbType.Date;
+			case ColumnType.Date:
+			case ColumnType.NewDate:
+				return MySqlDbType.Date;
 
-		case ColumnType.DateTime:
-			return MySqlDbType.DateTime;
+			case ColumnType.DateTime:
+				return MySqlDbType.DateTime;
 
-		case ColumnType.Timestamp:
-			return MySqlDbType.Timestamp;
+			case ColumnType.Timestamp:
+				return MySqlDbType.Timestamp;
 
-		case ColumnType.Time:
-			return MySqlDbType.Time;
+			case ColumnType.Time:
+				return MySqlDbType.Time;
 
-		case ColumnType.Year:
-			return MySqlDbType.Year;
+			case ColumnType.Year:
+				return MySqlDbType.Year;
 
-		case ColumnType.Float:
-			return MySqlDbType.Float;
+			case ColumnType.Float:
+				return MySqlDbType.Float;
 
-		case ColumnType.Double:
-			return MySqlDbType.Double;
+			case ColumnType.Double:
+				return MySqlDbType.Double;
 
-		case ColumnType.Decimal:
-			return MySqlDbType.Decimal;
+			case ColumnType.Decimal:
+				return MySqlDbType.Decimal;
 
-		case ColumnType.NewDecimal:
-			return MySqlDbType.NewDecimal;
+			case ColumnType.NewDecimal:
+				return MySqlDbType.NewDecimal;
 
-		case ColumnType.Geometry:
-			return MySqlDbType.Geometry;
+			case ColumnType.Geometry:
+				return MySqlDbType.Geometry;
 
-		case ColumnType.Null:
-			return MySqlDbType.Null;
+			case ColumnType.Null:
+				return MySqlDbType.Null;
 
-		case ColumnType.Enum:
-			return MySqlDbType.Enum;
+			case ColumnType.Enum:
+				return MySqlDbType.Enum;
 
-		case ColumnType.Set:
-			return MySqlDbType.Set;
+			case ColumnType.Set:
+				return MySqlDbType.Set;
 
-		default:
-			throw new NotImplementedException($"ConvertToMySqlDbType for {columnDefinition.ColumnType} is not implemented");
+			default:
+				throw new NotImplementedException($"ConvertToMySqlDbType for {columnDefinition.ColumnType} is not implemented");
 		}
 	}
 
diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index 9735492f1..f7a0257bc 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -151,7 +151,7 @@ private async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavi
 	private Task<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		if (!IsValid(out var exception))
-		 	return Task.FromException<MySqlDataReader>(exception);
+			return Task.FromException<MySqlDataReader>(exception);
 
 		CurrentCommandBehavior = behavior;
 		foreach (MySqlBatchCommand batchCommand in BatchCommands)
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index c65a5be9f..2827e5f68 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -216,7 +216,7 @@ public override void EnlistTransaction(System.Transactions.Transaction? transact
 			else
 			{
 				m_enlistedTransaction = GetInitializedConnectionSettings().UseXaTransactions ?
-					(EnlistedTransactionBase)new XaEnlistedTransaction(transaction, this) :
+					(EnlistedTransactionBase) new XaEnlistedTransaction(transaction, this) :
 					new StandardEnlistedTransaction(transaction, this);
 				m_enlistedTransaction.Start();
 
@@ -360,7 +360,7 @@ private async Task ChangeDatabaseAsync(IOBehavior ioBehavior, string databaseNam
 		m_session.DatabaseOverride = databaseName;
 	}
 
-	public new MySqlCommand CreateCommand() => (MySqlCommand)base.CreateCommand();
+	public new MySqlCommand CreateCommand() => (MySqlCommand) base.CreateCommand();
 
 #pragma warning disable CA2012 // Safe because method completes synchronously
 	public bool Ping() => PingAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index df62505ad..b99bad560 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -705,8 +705,8 @@ public bool Pipelining
 	}
 
 	/// <summary>
-		/// Whether to use server redirection.
-		/// </summary>
+	/// Whether to use server redirection.
+	/// </summary>
 	[Category("Connection")]
 	[DefaultValue(MySqlServerRedirectionMode.Disabled)]
 	[Description("Whether to use server redirection.")]
diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 94f8e32b0..dfda400f5 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -212,21 +212,21 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 			writer.Write((byte) '\'');
 			switch (charValue)
 			{
-			case '\0' when !noBackslashEscapes:
-				writer.Write((ushort) 0x305C); // \0
-				break;
+				case '\0' when !noBackslashEscapes:
+					writer.Write((ushort) 0x305C); // \0
+					break;
 
-			case '\'':
-				writer.Write((ushort) 0x2727); // ''
-				break;
+				case '\'':
+					writer.Write((ushort) 0x2727); // ''
+					break;
 
-			case '\\' when !noBackslashEscapes:
-				writer.Write((ushort) 0x5C5C); // \\
-				break;
+				case '\\' when !noBackslashEscapes:
+					writer.Write((ushort) 0x5C5C); // \\
+					break;
 
-			default:
-				writer.Write(charValue.ToString());
-				break;
+				default:
+					writer.Write(charValue.ToString());
+					break;
 			}
 			writer.Write((byte) '\'');
 		}
diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
index 7707d5476..72c23b59e 100644
--- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
@@ -10,27 +10,27 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 		var writer = new ByteBufferWriter();
 
 		var clientCapabilities = (ProtocolCapabilities.Protocol41 |
-		                          (cs.InteractiveSession ? ProtocolCapabilities.Interactive : 0) |
-		                          ProtocolCapabilities.LongPassword |
-		                          ProtocolCapabilities.Transactions |
-		                          ProtocolCapabilities.SecureConnection |
-		                          ProtocolCapabilities.PluginAuth |
-		                          ProtocolCapabilities.PluginAuthLengthEncodedClientData |
-		                          ProtocolCapabilities.MultiStatements |
-		                          ProtocolCapabilities.MultiResults |
-		                          (cs.AllowLoadLocalInfile ? ProtocolCapabilities.LocalFiles : 0) |
-		                          (string.IsNullOrWhiteSpace(cs.Database)
-			                          ? 0
-			                          : ProtocolCapabilities.ConnectWithDatabase) |
-		                          (cs.UseAffectedRows ? 0 : ProtocolCapabilities.FoundRows) |
-		                          (useCompression ? ProtocolCapabilities.Compress : ProtocolCapabilities.None) |
-		                          ProtocolCapabilities.ConnectionAttributes |
-		                          ProtocolCapabilities.SessionTrack |
-		                          ProtocolCapabilities.DeprecateEof |
-		                          ProtocolCapabilities.QueryAttributes |
-		                          ProtocolCapabilities.MariaDbComMulti |
-		                          ProtocolCapabilities.MariaDbCacheMetadata |
-		                          additionalCapabilities) & serverCapabilities;
+			(cs.InteractiveSession ? ProtocolCapabilities.Interactive : 0) |
+			ProtocolCapabilities.LongPassword |
+			ProtocolCapabilities.Transactions |
+			ProtocolCapabilities.SecureConnection |
+			ProtocolCapabilities.PluginAuth |
+			ProtocolCapabilities.PluginAuthLengthEncodedClientData |
+			ProtocolCapabilities.MultiStatements |
+			ProtocolCapabilities.MultiResults |
+			(cs.AllowLoadLocalInfile ? ProtocolCapabilities.LocalFiles : 0) |
+			(string.IsNullOrWhiteSpace(cs.Database)
+				? 0
+				: ProtocolCapabilities.ConnectWithDatabase) |
+			(cs.UseAffectedRows ? 0 : ProtocolCapabilities.FoundRows) |
+			(useCompression ? ProtocolCapabilities.Compress : ProtocolCapabilities.None) |
+			ProtocolCapabilities.ConnectionAttributes |
+			ProtocolCapabilities.SessionTrack |
+			ProtocolCapabilities.DeprecateEof |
+			ProtocolCapabilities.QueryAttributes |
+			ProtocolCapabilities.MariaDbComMulti |
+			ProtocolCapabilities.MariaDbCacheMetadata |
+			additionalCapabilities) & serverCapabilities;
 
 		writer.Write((int) clientCapabilities);
 		writer.Write(0x4000_0000);
diff --git a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
index 431557666..8cd03b5f7 100644
--- a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
@@ -55,13 +55,13 @@ public static OkPayload Create(ReadOnlySpan<byte> span, bool deprecateEof, bool
 						var dataLength = (int) reader.ReadLengthEncodedInteger();
 						switch (kind)
 						{
-						case SessionTrackKind.Schema:
-							newSchema = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
-							break;
+							case SessionTrackKind.Schema:
+								newSchema = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
+								break;
 
-						default:
-							reader.Offset += dataLength;
-							break;
+							default:
+								reader.Offset += dataLength;
+								break;
 						}
 					}
 				}
diff --git a/src/MySqlConnector/Protocol/Serialization/AuthGSSAPI.cs b/src/MySqlConnector/Protocol/Serialization/AuthGSSAPI.cs
index 812b2b0dd..39baf77b9 100644
--- a/src/MySqlConnector/Protocol/Serialization/AuthGSSAPI.cs
+++ b/src/MySqlConnector/Protocol/Serialization/AuthGSSAPI.cs
@@ -56,10 +56,10 @@ public NegotiateToMySqlConverterStream(ServerSession serverSession, IOBehavior i
 	private static void CreateNegotiateStreamMessageHeader(byte[] buffer, int offset, byte messageId, long payloadLength)
 	{
 		buffer[offset] = messageId;
-		buffer[offset+1] = NegotiateStreamConstants.MajorVersion;
-		buffer[offset+2] = NegotiateStreamConstants.MinorVersion;
-		buffer[offset+3] = (byte) (payloadLength >> 8);
-		buffer[offset+4] = (byte) (payloadLength & 0xff);
+		buffer[offset + 1] = NegotiateStreamConstants.MajorVersion;
+		buffer[offset + 2] = NegotiateStreamConstants.MinorVersion;
+		buffer[offset + 3] = (byte) (payloadLength >> 8);
+		buffer[offset + 4] = (byte) (payloadLength & 0xff);
 	}
 	public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
 	{
@@ -90,13 +90,13 @@ public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
 			// during pluggable authentication packet exchanges.
 			switch (payloadMemory.Span[0])
 			{
-			case 0x0:
-				MySQLProtocolPayload = payload;
-				CreateNegotiateStreamMessageHeader(buffer, offset, NegotiateStreamConstants.HandshakeDone, 0);
-				return NegotiateStreamConstants.HeaderLength;
-			case 0x1:
-				payloadMemory = payloadMemory[1..];
-				break;
+				case 0x0:
+					MySQLProtocolPayload = payload;
+					CreateNegotiateStreamMessageHeader(buffer, offset, NegotiateStreamConstants.HandshakeDone, 0);
+					return NegotiateStreamConstants.HeaderLength;
+				case 0x1:
+					payloadMemory = payloadMemory[1..];
+					break;
 			}
 
 			m_readBuffer = new(payloadMemory.ToArray());
@@ -125,11 +125,11 @@ public override async Task WriteAsync(byte[] buffer, int offset, int count, Canc
 				throw new InvalidDataException("Cannot parse NegotiateStream handshake message header");
 
 			// Parse NegotiateStream handshake header
-			var messageId = buffer[offset+0];
-			var majorProtocolVersion = buffer[offset+1];
-			var minorProtocolVersion = buffer[offset+2];
-			var payloadSizeLow = buffer[offset+4];
-			var payloadSizeHigh = buffer[offset+3];
+			var messageId = buffer[offset + 0];
+			var majorProtocolVersion = buffer[offset + 1];
+			var minorProtocolVersion = buffer[offset + 2];
+			var payloadSizeLow = buffer[offset + 4];
+			var payloadSizeHigh = buffer[offset + 3];
 
 			if (majorProtocolVersion != NegotiateStreamConstants.MajorVersion ||
 				minorProtocolVersion != NegotiateStreamConstants.MinorVersion)
diff --git a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
index 0e95365df..5819aa2ab 100644
--- a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
@@ -247,23 +247,23 @@ public static void WriteLengthEncodedInteger(this ByteBufferWriter writer, ulong
 	{
 		switch (value)
 		{
-		case < 251:
-			writer.Write((byte) value);
-			break;
-
-		case < 65536:
-			writer.Write((byte) 0xfc);
-			writer.Write((ushort) value);
-			break;
-
-		case < 16777216:
-			writer.Write((uint) ((value << 8) | 0xfd));
-			break;
-
-		default:
-			writer.Write((byte) 0xfe);
-			writer.Write(value);
-			break;
+			case < 251:
+				writer.Write((byte) value);
+				break;
+
+			case < 65536:
+				writer.Write((byte) 0xfc);
+				writer.Write((ushort) value);
+				break;
+
+			case < 16777216:
+				writer.Write((uint) ((value << 8) | 0xfd));
+				break;
+
+			default:
+				writer.Write((byte) 0xfe);
+				writer.Write(value);
+				break;
 		}
 	}
 
diff --git a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
index 8d819291b..c8ee11029 100644
--- a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
@@ -10,405 +10,405 @@ public static int GetBytesPerCharacter(CharacterSet characterSet)
 	{
 		switch (characterSet)
 		{
-		case CharacterSet.Latin2CzechCaseSensitive:
-		case CharacterSet.Dec8SwedishCaseInsensitive:
-		case CharacterSet.Cp850GeneralCaseInsensitive:
-		case CharacterSet.Latin1German1CaseInsensitive:
-		case CharacterSet.Hp8EnglishCaseInsensitive:
-		case CharacterSet.Koi8rGeneralCaseInsensitive:
-		case CharacterSet.Latin1SwedishCaseInsensitive:
-		case CharacterSet.Latin2GeneralCaseInsensitive:
-		case CharacterSet.Swe7SwedishCaseInsensitive:
-		case CharacterSet.AsciiGeneralCaseInsensitive:
-		case CharacterSet.Cp1251BulgarianCaseInsensitive:
-		case CharacterSet.Latin1DanishCaseInsensitive:
-		case CharacterSet.HebrewGeneralCaseInsensitive:
-		case CharacterSet.Tis620ThaiCaseInsensitive:
-		case CharacterSet.Latin7EstonianCaseSensitive:
-		case CharacterSet.Latin2HungarianCaseInsensitive:
-		case CharacterSet.Koi8uGeneralCaseInsensitive:
-		case CharacterSet.Cp1251UkrainianCaseInsensitive:
-		case CharacterSet.GreekGeneralCaseInsensitive:
-		case CharacterSet.Cp1250GeneralCaseInsensitive:
-		case CharacterSet.Latin2CroatianCaseInsensitive:
-		case CharacterSet.Cp1257LithuanianCaseInsensitive:
-		case CharacterSet.Latin5TurkishCaseInsensitive:
-		case CharacterSet.Latin1German2CaseInsensitive:
-		case CharacterSet.Armscii8GeneralCaseInsensitive:
-		case CharacterSet.Cp1250CzechCaseSensitive:
-		case CharacterSet.Cp866GeneralCaseInsensitive:
-		case CharacterSet.Keybcs2GeneralCaseInsensitive:
-		case CharacterSet.MacceGeneralCaseInsensitive:
-		case CharacterSet.MacromanGeneralCaseInsensitive:
-		case CharacterSet.Cp852GeneralCaseInsensitive:
-		case CharacterSet.Latin7GeneralCaseInsensitive:
-		case CharacterSet.Latin7GeneralCaseSensitive:
-		case CharacterSet.MacceBinary:
-		case CharacterSet.Cp1250CroatianCaseInsensitive:
-		case CharacterSet.Latin1Binary:
-		case CharacterSet.Latin1GeneralCaseInsensitive:
-		case CharacterSet.Latin1GeneralCaseSensitive:
-		case CharacterSet.Cp1251Binary:
-		case CharacterSet.Cp1251GeneralCaseInsensitive:
-		case CharacterSet.Cp1251GeneralCaseSensitive:
-		case CharacterSet.MacromanBinary:
-		case CharacterSet.Cp1256GeneralCaseInsensitive:
-		case CharacterSet.Cp1257Binary:
-		case CharacterSet.Cp1257GeneralCaseInsensitive:
-		case CharacterSet.Binary:
-		case CharacterSet.Armscii8Binary:
-		case CharacterSet.AsciiBinary:
-		case CharacterSet.Cp1250Binary:
-		case CharacterSet.Cp1256Binary:
-		case CharacterSet.Cp866Binary:
-		case CharacterSet.Dec8Binary:
-		case CharacterSet.GreekBinary:
-		case CharacterSet.HebrewBinary:
-		case CharacterSet.Hp8Binary:
-		case CharacterSet.Keybcs2Binary:
-		case CharacterSet.Koi8rBinary:
-		case CharacterSet.Koi8uBinary:
-		case CharacterSet.Latin2Binary:
-		case CharacterSet.Latin5Binary:
-		case CharacterSet.Latin7Binary:
-		case CharacterSet.Cp850Binary:
-		case CharacterSet.Cp852Binary:
-		case CharacterSet.Swe7Binary:
-		case CharacterSet.Tis620Binary:
-		case CharacterSet.Geostd8GeneralCaseInsensitive:
-		case CharacterSet.Geostd8Binary:
-		case CharacterSet.Latin1SpanishCaseInsensitive:
-		case CharacterSet.Cp1250PolishCaseInsensitive:
-		case CharacterSet.Dec8SwedishNoPadCaseInsensitive:
-		case CharacterSet.Cp850GeneralNoPadCaseInsensitive:
-		case CharacterSet.Hp8EnglishNoPadCaseInsensitive:
-		case CharacterSet.Koi8rGeneralNoPadCaseInsensitive:
-		case CharacterSet.Latin1SwedishNoPadCaseInsensitive:
-		case CharacterSet.Latin2GeneralNoPadCaseInsensitive:
-		case CharacterSet.Swe7SwedishNoPadCaseInsensitive:
-		case CharacterSet.AsciiGeneralNoPadCaseInsensitive:
-		case CharacterSet.HebrewGeneralNoPadCaseInsensitive:
-		case CharacterSet.Tis620ThaiNoPadCaseInsensitive:
-		case CharacterSet.Koi8uGeneralNoPadCaseInsensitive:
-		case CharacterSet.GreekGeneralNoPadCaseInsensitive:
-		case CharacterSet.Cp1250GeneralNoPadCaseInsensitive:
-		case CharacterSet.Latin5TurkishNoPadCaseInsensitive:
-		case CharacterSet.Armscii8GeneralNoPadCaseInsensitive:
-		case CharacterSet.Cp866GeneralNoPadCaseInsensitive:
-		case CharacterSet.Keybcs2GeneralNoPadCaseInsensitive:
-		case CharacterSet.MacCentralEuropeanGeneralNoPadCaseInsensitive:
-		case CharacterSet.MacRomanGeneralNoPadCaseInsensitive:
-		case CharacterSet.Cp852GeneralNoPadCaseInsensitive:
-		case CharacterSet.Latin7GeneralNoPadCaseInsensitive:
-		case CharacterSet.MacCentralEuropeanNoPadBinary:
-		case CharacterSet.Latin1NoPadBinary:
-		case CharacterSet.Cp1251NoPadBinary:
-		case CharacterSet.Cp1251GeneralNoPadCaseInsensitive:
-		case CharacterSet.MacRomanNoPadBinary:
-		case CharacterSet.Cp1256GeneralNoPadCaseInsensitive:
-		case CharacterSet.Cp1257NoPadBinary:
-		case CharacterSet.Cp1257GeneralNoPadCaseInsensitive:
-		case CharacterSet.Armscii8NoPadBinary:
-		case CharacterSet.AsciiNoPadBinary:
-		case CharacterSet.Cp1250NoPadBinary:
-		case CharacterSet.Cp1256NoPadBinary:
-		case CharacterSet.Cp866NoPadBinary:
-		case CharacterSet.Dec8NoPadBinary:
-		case CharacterSet.GreekNoPadBinary:
-		case CharacterSet.HebrewNoPadBinary:
-		case CharacterSet.Hp8NoPadBinary:
-		case CharacterSet.Keybcs2NoPadBinary:
-		case CharacterSet.Koi8rNoPadBinary:
-		case CharacterSet.Koi8uNoPadBinary:
-		case CharacterSet.Latin2NoPadBinary:
-		case CharacterSet.Latin5NoPadBinary:
-		case CharacterSet.Latin7NoPadBinary:
-		case CharacterSet.Cp850NoPadBinary:
-		case CharacterSet.Cp852NoPadBinary:
-		case CharacterSet.Swe7NoPadBinary:
-		case CharacterSet.Tis620NoPadBinary:
-		case CharacterSet.Geostd8GeneralNoPadCaseInsensitive:
-		case CharacterSet.Geostd8NoPadBinary:
-			return 1;
-
-		case CharacterSet.Big5ChineseCaseInsensitive:
-		case CharacterSet.SjisJapaneseCaseInsensitive:
-		case CharacterSet.EuckrKoreanCaseInsensitive:
-		case CharacterSet.Gb2312ChineseCaseInsensitive:
-		case CharacterSet.GbkChineseCaseInsensitive:
-		case CharacterSet.Ucs2GeneralCaseInsensitive:
-		case CharacterSet.Big5Binary:
-		case CharacterSet.EuckrBinary:
-		case CharacterSet.Gb2312Binary:
-		case CharacterSet.GbkBinary:
-		case CharacterSet.SjisBinary:
-		case CharacterSet.Ucs2Binary:
-		case CharacterSet.Cp932JapaneseCaseInsensitive:
-		case CharacterSet.Cp932Binary:
-		case CharacterSet.Ucs2UnicodeCaseInsensitive:
-		case CharacterSet.Ucs2IcelandicCaseInsensitive:
-		case CharacterSet.Ucs2LatvianCaseInsensitive:
-		case CharacterSet.Ucs2RomanianCaseInsensitive:
-		case CharacterSet.Ucs2SlovenianCaseInsensitive:
-		case CharacterSet.Ucs2PolishCaseInsensitive:
-		case CharacterSet.Ucs2EstonianCaseInsensitive:
-		case CharacterSet.Ucs2SpanishCaseInsensitive:
-		case CharacterSet.Ucs2SwedishCaseInsensitive:
-		case CharacterSet.Ucs2TurkishCaseInsensitive:
-		case CharacterSet.Ucs2CzechCaseInsensitive:
-		case CharacterSet.Ucs2DanishCaseInsensitive:
-		case CharacterSet.Ucs2LithuanianCaseInsensitive:
-		case CharacterSet.Ucs2SlovakCaseInsensitive:
-		case CharacterSet.Ucs2Spanish2CaseInsensitive:
-		case CharacterSet.Ucs2RomanCaseInsensitive:
-		case CharacterSet.Ucs2PersianCaseInsensitive:
-		case CharacterSet.Ucs2EsperantoCaseInsensitive:
-		case CharacterSet.Ucs2HungarianCaseInsensitive:
-		case CharacterSet.Ucs2SinhalaCaseInsensitive:
-		case CharacterSet.Ucs2German2CaseInsensitive:
-		case CharacterSet.Ucs2CroatianCaseInsensitive:
-		case CharacterSet.Ucs2Unicode520CaseInsensitive:
-		case CharacterSet.Ucs2VietnameseCaseInsensitive:
-		case CharacterSet.Ucs2GeneralMySql500CaseInsensitive:
-		case CharacterSet.Ucs2CroatianCaseInsensitiveMariaDb:
-		case CharacterSet.Ucs2MyanmarCaseInsensitive:
-		case CharacterSet.Ucs2ThaiUnicode520Weight2:
-		case CharacterSet.Big5ChineseNoPadCaseInsensitive:
-		case CharacterSet.SjisJapaneseNoPadCaseInsensitive:
-		case CharacterSet.EuckrKoreanNoPadCaseInsensitive:
-		case CharacterSet.Gb2312ChineseNoPadCaseInsensitive:
-		case CharacterSet.GbkChineseNoPadCaseInsensitive:
-		case CharacterSet.Ucs2GeneralNoPadCaseInsensitive:
-		case CharacterSet.Big5NoPadBinary:
-		case CharacterSet.EuckrNoPadBinary:
-		case CharacterSet.Gb2312NoPadBinary:
-		case CharacterSet.GbkNoPadBinary:
-		case CharacterSet.SjisNoPadBinary:
-		case CharacterSet.Ucs2NoPadBinary:
-		case CharacterSet.Cp932JapaneseNoPadCaseInsensitive:
-		case CharacterSet.Cp932NoPadBinary:
-		case CharacterSet.Ucs2UnicodeNoPadCaseInsensitive:
-		case CharacterSet.Ucs2Unicode520NoPadCaseInsensitive:
-			return 2;
-
-		case CharacterSet.UjisJapaneseCaseInsensitive:
-		case CharacterSet.Utf8Mb3GeneralCaseInsensitive:
-		case CharacterSet.Utf8Mb3ToLowerCaseInsensitive:
-		case CharacterSet.Utf8Mb3Binary:
-		case CharacterSet.UjisBinary:
-		case CharacterSet.EucjpmsJapaneseCaseInsensitive:
-		case CharacterSet.EucjpmsBinary:
-		case CharacterSet.Utf8Mb3UnicodeCaseInsensitive:
-		case CharacterSet.Utf8Mb3IcelandicCaseInsensitive:
-		case CharacterSet.Utf8Mb3LatvianCaseInsensitive:
-		case CharacterSet.Utf8Mb3RomanianCaseInsensitive:
-		case CharacterSet.Utf8Mb3SlovenianCaseInsensitive:
-		case CharacterSet.Utf8Mb3PolishCaseInsensitive:
-		case CharacterSet.Utf8Mb3EstonianCaseInsensitive:
-		case CharacterSet.Utf8Mb3SpanishCaseInsensitive:
-		case CharacterSet.Utf8Mb3SwedishCaseInsensitive:
-		case CharacterSet.Utf8Mb3TurkishCaseInsensitive:
-		case CharacterSet.Utf8Mb3CzechCaseInsensitive:
-		case CharacterSet.Utf8Mb3DanishCaseInsensitive:
-		case CharacterSet.Utf8Mb3LithuanianCaseInsensitive:
-		case CharacterSet.Utf8Mb3SlovakCaseInsensitive:
-		case CharacterSet.Utf8Mb3Spanish2CaseInsensitive:
-		case CharacterSet.Utf8Mb3RomanCaseInsensitive:
-		case CharacterSet.Utf8Mb3PersianCaseInsensitive:
-		case CharacterSet.Utf8Mb3EsperantoCaseInsensitive:
-		case CharacterSet.Utf8Mb3HungarianCaseInsensitive:
-		case CharacterSet.Utf8Mb3SinhalaCaseInsensitive:
-		case CharacterSet.Utf8Mb3German2CaseInsensitive:
-		case CharacterSet.Utf8Mb3CroatianCaseInsensitive:
-		case CharacterSet.Utf8Mb3Unicode520CaseInsensitive:
-		case CharacterSet.Utf8Mb3VietnameseCaseInsensitive:
-		case CharacterSet.Utf8Mb3GeneralMySql500CaseInsensitive:
-		case CharacterSet.Utf8Mb3CroatianCaseInsensitiveMariaDb:
-		case CharacterSet.Utf8Mb3MyanmarCaseInsensitive:
-		case CharacterSet.Utf8Mb3ThaiUnicode520Weight2:
-		case CharacterSet.UjisJapaneseNoPadCaseInsensitive:
-		case CharacterSet.Utf8Mb3GeneralNoPadCaseInsensitive:
-		case CharacterSet.Utf8Mb3NoPadBinary:
-		case CharacterSet.UjisNoPadBinary:
-		case CharacterSet.EucjpmsJapaneseNoPadCaseInsensitive:
-		case CharacterSet.EucjpmsNoPadBinary:
-		case CharacterSet.Utf8Mb3UnicodeNoPadCaseInsensitive:
-		case CharacterSet.Utf8Mb3Unicode520NoPadCaseInsensitive:
-			return 3;
-
-		case CharacterSet.Utf8Mb4GeneralCaseInsensitive:
-		case CharacterSet.Utf8Mb4Binary:
-		case CharacterSet.Utf16GeneralCaseInsensitive:
-		case CharacterSet.Utf16Binary:
-		case CharacterSet.Utf16leGeneralCaseInsensitive:
-		case CharacterSet.Utf32GeneralCaseInsensitive:
-		case CharacterSet.Utf32Binary:
-		case CharacterSet.Utf16leBinary:
-		case CharacterSet.Utf16UnicodeCaseInsensitive:
-		case CharacterSet.Utf16IcelandicCaseInsensitive:
-		case CharacterSet.Utf16LatvianCaseInsensitive:
-		case CharacterSet.Utf16RomanianCaseInsensitive:
-		case CharacterSet.Utf16SlovenianCaseInsensitive:
-		case CharacterSet.Utf16PolishCaseInsensitive:
-		case CharacterSet.Utf16EstonianCaseInsensitive:
-		case CharacterSet.Utf16SpanishCaseInsensitive:
-		case CharacterSet.Utf16SwedishCaseInsensitive:
-		case CharacterSet.Utf16TurkishCaseInsensitive:
-		case CharacterSet.Utf16CzechCaseInsensitive:
-		case CharacterSet.Utf16DanishCaseInsensitive:
-		case CharacterSet.Utf16LithuanianCaseInsensitive:
-		case CharacterSet.Utf16SlovakCaseInsensitive:
-		case CharacterSet.Utf16Spanish2CaseInsensitive:
-		case CharacterSet.Utf16RomanCaseInsensitive:
-		case CharacterSet.Utf16PersianCaseInsensitive:
-		case CharacterSet.Utf16EsperantoCaseInsensitive:
-		case CharacterSet.Utf16HungarianCaseInsensitive:
-		case CharacterSet.Utf16SinhalaCaseInsensitive:
-		case CharacterSet.Utf16German2CaseInsensitive:
-		case CharacterSet.Utf16CroatianCaseInsensitive:
-		case CharacterSet.Utf16Unicode520CaseInsensitive:
-		case CharacterSet.Utf16VietnameseCaseInsensitive:
-		case CharacterSet.Utf32UnicodeCaseInsensitive:
-		case CharacterSet.Utf32IcelandicCaseInsensitive:
-		case CharacterSet.Utf32LatvianCaseInsensitive:
-		case CharacterSet.Utf32RomanianCaseInsensitive:
-		case CharacterSet.Utf32SlovenianCaseInsensitive:
-		case CharacterSet.Utf32PolishCaseInsensitive:
-		case CharacterSet.Utf32EstonianCaseInsensitive:
-		case CharacterSet.Utf32SpanishCaseInsensitive:
-		case CharacterSet.Utf32SwedishCaseInsensitive:
-		case CharacterSet.Utf32TurkishCaseInsensitive:
-		case CharacterSet.Utf32CzechCaseInsensitive:
-		case CharacterSet.Utf32DanishCaseInsensitive:
-		case CharacterSet.Utf32LithuanianCaseInsensitive:
-		case CharacterSet.Utf32SlovakCaseInsensitive:
-		case CharacterSet.Utf32Spanish2CaseInsensitive:
-		case CharacterSet.Utf32RomanCaseInsensitive:
-		case CharacterSet.Utf32PersianCaseInsensitive:
-		case CharacterSet.Utf32EsperantoCaseInsensitive:
-		case CharacterSet.Utf32HungarianCaseInsensitive:
-		case CharacterSet.Utf32SinhalaCaseInsensitive:
-		case CharacterSet.Utf32German2CaseInsensitive:
-		case CharacterSet.Utf32CroatianCaseInsensitive:
-		case CharacterSet.Utf32Unicode520CaseInsensitive:
-		case CharacterSet.Utf32VietnameseCaseInsensitive:
-		case CharacterSet.Utf8Mb4UnicodeCaseInsensitive:
-		case CharacterSet.Utf8Mb4IcelandicCaseInsensitive:
-		case CharacterSet.Utf8Mb4LatvianCaseInsensitive:
-		case CharacterSet.Utf8Mb4RomanianCaseInsensitive:
-		case CharacterSet.Utf8Mb4SlovenianCaseInsensitive:
-		case CharacterSet.Utf8Mb4PolishCaseInsensitive:
-		case CharacterSet.Utf8Mb4EstonianCaseInsensitive:
-		case CharacterSet.Utf8Mb4SpanishCaseInsensitive:
-		case CharacterSet.Utf8Mb4SwedishCaseInsensitive:
-		case CharacterSet.Utf8Mb4TurkishCaseInsensitive:
-		case CharacterSet.Utf8Mb4CzechCaseInsensitive:
-		case CharacterSet.Utf8Mb4DanishCaseInsensitive:
-		case CharacterSet.Utf8Mb4LithuanianCaseInsensitive:
-		case CharacterSet.Utf8Mb4SlovakCaseInsensitive:
-		case CharacterSet.Utf8Mb4Spanish2CaseInsensitive:
-		case CharacterSet.Utf8Mb4RomanCaseInsensitive:
-		case CharacterSet.Utf8Mb4PersianCaseInsensitive:
-		case CharacterSet.Utf8Mb4EsperantoCaseInsensitive:
-		case CharacterSet.Utf8Mb4HungarianCaseInsensitive:
-		case CharacterSet.Utf8Mb4SinhalaCaseInsensitive:
-		case CharacterSet.Utf8Mb4German2CaseInsensitive:
-		case CharacterSet.Utf8Mb4CroatianCaseInsensitive:
-		case CharacterSet.Utf8Mb4Unicode520CaseInsensitive:
-		case CharacterSet.Utf8Mb4VietnameseCaseInsensitive:
-		case CharacterSet.Gb18030ChineseCaseInsensitive:
-		case CharacterSet.Gb18030Binary:
-		case CharacterSet.Gb18030Unicode520CaseInsensitive:
-		case CharacterSet.Utf8Mb4Uca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4GermanPhonebookUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4IcelandicUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4LatvianUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4RomanianUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4SlovenianUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4PolishUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4EstonianUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4SpanishUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4SwedishUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4TurkishUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4CaseSensitiveUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4DanishUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4LithuanianUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4SlovakUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4TraditionalSpanishUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4LatinUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4EsperantoUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4HungarianUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4CroatianUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4VietnameseUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4Uca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4GermanPhonebookUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4IcelandicUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4LatvianUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4RomanianUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4SlovenianUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4PolishUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4EstonianUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4SpanishUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4SwedishUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4TurkishUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4CaseSensitiveUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4DanishUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4LithuanianUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4SlovakUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4TraditionalSpanishUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4LatinUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4EsperantoUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4HungarianUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4CroatianUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4VietnameseUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4JapaneseUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4JapaneseUca900AccentSensitiveCaseSensitiveKanaSensitive:
-		case CharacterSet.Utf8Mb4Uca900AccentSensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4RussianUca900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4RussianUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4ChineseUca900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4Uca900Binary:
-		case CharacterSet.Utf8Mb4NorwegianBokmal0900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4NorwegianBokmal0900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4NorwegianNynorsk0900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4NorwegianNynorsk0900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4SerbianLatin0900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4SerbianLatin0900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4Bosnian0900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4Bosnian0900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4Bulgarian0900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4Bulgarian0900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4Galician0900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4Galician0900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4MongolianCyrillic0900AccentInsensitiveCaseInsensitive:
-		case CharacterSet.Utf8Mb4MongolianCyrillic0900AccentSensitiveCaseSensitive:
-		case CharacterSet.Utf8Mb4CroatianCaseInsensitiveMariaDb:
-		case CharacterSet.Utf8Mb4MyanmarCaseInsensitive:
-		case CharacterSet.Utf8Mb4ThaiUnicode520Weight2:
-		case CharacterSet.Utf16CroatianCaseInsensitiveMariaDb:
-		case CharacterSet.Utf16MyanmarCaseInsensitive:
-		case CharacterSet.Utf16ThaiUnicode520Weight2:
-		case CharacterSet.Utf32CroatianCaseInsensitiveMariaDb:
-		case CharacterSet.Utf32MyanmarCaseInsensitive:
-		case CharacterSet.Utf32ThaiUnicode520Weight2:
-		case CharacterSet.Utf8Mb4GeneralNoPadCaseInsensitive:
-		case CharacterSet.Utf8Mb4NoPadBinary:
-		case CharacterSet.Utf16GeneralNoPadCaseInsensitive:
-		case CharacterSet.Utf16NoPadBinary:
-		case CharacterSet.Utf16leGeneralNoPadCaseInsensitive:
-		case CharacterSet.Utf32GeneralNoPadCaseInsensitive:
-		case CharacterSet.Utf32NoPadBinary:
-		case CharacterSet.Utf16leNoPadBinary:
-		case CharacterSet.Utf16UnicodeNoPadCaseInsensitive:
-		case CharacterSet.Utf16Unicode520NoPadCaseInsensitive:
-		case CharacterSet.Utf32UnicodeNoPadCaseInsensitive:
-		case CharacterSet.Utf32Unicode520NoPadCaseInsensitive:
-		case CharacterSet.Utf8Mb4UnicodeNoPadCaseInsensitive:
-		case CharacterSet.Utf8Mb4Unicode520NoPadCaseInsensitive:
-			return 4;
-
-		default:
-			throw new NotSupportedException($"Maximum byte length of character set {characterSet} is unknown.");
+			case CharacterSet.Latin2CzechCaseSensitive:
+			case CharacterSet.Dec8SwedishCaseInsensitive:
+			case CharacterSet.Cp850GeneralCaseInsensitive:
+			case CharacterSet.Latin1German1CaseInsensitive:
+			case CharacterSet.Hp8EnglishCaseInsensitive:
+			case CharacterSet.Koi8rGeneralCaseInsensitive:
+			case CharacterSet.Latin1SwedishCaseInsensitive:
+			case CharacterSet.Latin2GeneralCaseInsensitive:
+			case CharacterSet.Swe7SwedishCaseInsensitive:
+			case CharacterSet.AsciiGeneralCaseInsensitive:
+			case CharacterSet.Cp1251BulgarianCaseInsensitive:
+			case CharacterSet.Latin1DanishCaseInsensitive:
+			case CharacterSet.HebrewGeneralCaseInsensitive:
+			case CharacterSet.Tis620ThaiCaseInsensitive:
+			case CharacterSet.Latin7EstonianCaseSensitive:
+			case CharacterSet.Latin2HungarianCaseInsensitive:
+			case CharacterSet.Koi8uGeneralCaseInsensitive:
+			case CharacterSet.Cp1251UkrainianCaseInsensitive:
+			case CharacterSet.GreekGeneralCaseInsensitive:
+			case CharacterSet.Cp1250GeneralCaseInsensitive:
+			case CharacterSet.Latin2CroatianCaseInsensitive:
+			case CharacterSet.Cp1257LithuanianCaseInsensitive:
+			case CharacterSet.Latin5TurkishCaseInsensitive:
+			case CharacterSet.Latin1German2CaseInsensitive:
+			case CharacterSet.Armscii8GeneralCaseInsensitive:
+			case CharacterSet.Cp1250CzechCaseSensitive:
+			case CharacterSet.Cp866GeneralCaseInsensitive:
+			case CharacterSet.Keybcs2GeneralCaseInsensitive:
+			case CharacterSet.MacceGeneralCaseInsensitive:
+			case CharacterSet.MacromanGeneralCaseInsensitive:
+			case CharacterSet.Cp852GeneralCaseInsensitive:
+			case CharacterSet.Latin7GeneralCaseInsensitive:
+			case CharacterSet.Latin7GeneralCaseSensitive:
+			case CharacterSet.MacceBinary:
+			case CharacterSet.Cp1250CroatianCaseInsensitive:
+			case CharacterSet.Latin1Binary:
+			case CharacterSet.Latin1GeneralCaseInsensitive:
+			case CharacterSet.Latin1GeneralCaseSensitive:
+			case CharacterSet.Cp1251Binary:
+			case CharacterSet.Cp1251GeneralCaseInsensitive:
+			case CharacterSet.Cp1251GeneralCaseSensitive:
+			case CharacterSet.MacromanBinary:
+			case CharacterSet.Cp1256GeneralCaseInsensitive:
+			case CharacterSet.Cp1257Binary:
+			case CharacterSet.Cp1257GeneralCaseInsensitive:
+			case CharacterSet.Binary:
+			case CharacterSet.Armscii8Binary:
+			case CharacterSet.AsciiBinary:
+			case CharacterSet.Cp1250Binary:
+			case CharacterSet.Cp1256Binary:
+			case CharacterSet.Cp866Binary:
+			case CharacterSet.Dec8Binary:
+			case CharacterSet.GreekBinary:
+			case CharacterSet.HebrewBinary:
+			case CharacterSet.Hp8Binary:
+			case CharacterSet.Keybcs2Binary:
+			case CharacterSet.Koi8rBinary:
+			case CharacterSet.Koi8uBinary:
+			case CharacterSet.Latin2Binary:
+			case CharacterSet.Latin5Binary:
+			case CharacterSet.Latin7Binary:
+			case CharacterSet.Cp850Binary:
+			case CharacterSet.Cp852Binary:
+			case CharacterSet.Swe7Binary:
+			case CharacterSet.Tis620Binary:
+			case CharacterSet.Geostd8GeneralCaseInsensitive:
+			case CharacterSet.Geostd8Binary:
+			case CharacterSet.Latin1SpanishCaseInsensitive:
+			case CharacterSet.Cp1250PolishCaseInsensitive:
+			case CharacterSet.Dec8SwedishNoPadCaseInsensitive:
+			case CharacterSet.Cp850GeneralNoPadCaseInsensitive:
+			case CharacterSet.Hp8EnglishNoPadCaseInsensitive:
+			case CharacterSet.Koi8rGeneralNoPadCaseInsensitive:
+			case CharacterSet.Latin1SwedishNoPadCaseInsensitive:
+			case CharacterSet.Latin2GeneralNoPadCaseInsensitive:
+			case CharacterSet.Swe7SwedishNoPadCaseInsensitive:
+			case CharacterSet.AsciiGeneralNoPadCaseInsensitive:
+			case CharacterSet.HebrewGeneralNoPadCaseInsensitive:
+			case CharacterSet.Tis620ThaiNoPadCaseInsensitive:
+			case CharacterSet.Koi8uGeneralNoPadCaseInsensitive:
+			case CharacterSet.GreekGeneralNoPadCaseInsensitive:
+			case CharacterSet.Cp1250GeneralNoPadCaseInsensitive:
+			case CharacterSet.Latin5TurkishNoPadCaseInsensitive:
+			case CharacterSet.Armscii8GeneralNoPadCaseInsensitive:
+			case CharacterSet.Cp866GeneralNoPadCaseInsensitive:
+			case CharacterSet.Keybcs2GeneralNoPadCaseInsensitive:
+			case CharacterSet.MacCentralEuropeanGeneralNoPadCaseInsensitive:
+			case CharacterSet.MacRomanGeneralNoPadCaseInsensitive:
+			case CharacterSet.Cp852GeneralNoPadCaseInsensitive:
+			case CharacterSet.Latin7GeneralNoPadCaseInsensitive:
+			case CharacterSet.MacCentralEuropeanNoPadBinary:
+			case CharacterSet.Latin1NoPadBinary:
+			case CharacterSet.Cp1251NoPadBinary:
+			case CharacterSet.Cp1251GeneralNoPadCaseInsensitive:
+			case CharacterSet.MacRomanNoPadBinary:
+			case CharacterSet.Cp1256GeneralNoPadCaseInsensitive:
+			case CharacterSet.Cp1257NoPadBinary:
+			case CharacterSet.Cp1257GeneralNoPadCaseInsensitive:
+			case CharacterSet.Armscii8NoPadBinary:
+			case CharacterSet.AsciiNoPadBinary:
+			case CharacterSet.Cp1250NoPadBinary:
+			case CharacterSet.Cp1256NoPadBinary:
+			case CharacterSet.Cp866NoPadBinary:
+			case CharacterSet.Dec8NoPadBinary:
+			case CharacterSet.GreekNoPadBinary:
+			case CharacterSet.HebrewNoPadBinary:
+			case CharacterSet.Hp8NoPadBinary:
+			case CharacterSet.Keybcs2NoPadBinary:
+			case CharacterSet.Koi8rNoPadBinary:
+			case CharacterSet.Koi8uNoPadBinary:
+			case CharacterSet.Latin2NoPadBinary:
+			case CharacterSet.Latin5NoPadBinary:
+			case CharacterSet.Latin7NoPadBinary:
+			case CharacterSet.Cp850NoPadBinary:
+			case CharacterSet.Cp852NoPadBinary:
+			case CharacterSet.Swe7NoPadBinary:
+			case CharacterSet.Tis620NoPadBinary:
+			case CharacterSet.Geostd8GeneralNoPadCaseInsensitive:
+			case CharacterSet.Geostd8NoPadBinary:
+				return 1;
+
+			case CharacterSet.Big5ChineseCaseInsensitive:
+			case CharacterSet.SjisJapaneseCaseInsensitive:
+			case CharacterSet.EuckrKoreanCaseInsensitive:
+			case CharacterSet.Gb2312ChineseCaseInsensitive:
+			case CharacterSet.GbkChineseCaseInsensitive:
+			case CharacterSet.Ucs2GeneralCaseInsensitive:
+			case CharacterSet.Big5Binary:
+			case CharacterSet.EuckrBinary:
+			case CharacterSet.Gb2312Binary:
+			case CharacterSet.GbkBinary:
+			case CharacterSet.SjisBinary:
+			case CharacterSet.Ucs2Binary:
+			case CharacterSet.Cp932JapaneseCaseInsensitive:
+			case CharacterSet.Cp932Binary:
+			case CharacterSet.Ucs2UnicodeCaseInsensitive:
+			case CharacterSet.Ucs2IcelandicCaseInsensitive:
+			case CharacterSet.Ucs2LatvianCaseInsensitive:
+			case CharacterSet.Ucs2RomanianCaseInsensitive:
+			case CharacterSet.Ucs2SlovenianCaseInsensitive:
+			case CharacterSet.Ucs2PolishCaseInsensitive:
+			case CharacterSet.Ucs2EstonianCaseInsensitive:
+			case CharacterSet.Ucs2SpanishCaseInsensitive:
+			case CharacterSet.Ucs2SwedishCaseInsensitive:
+			case CharacterSet.Ucs2TurkishCaseInsensitive:
+			case CharacterSet.Ucs2CzechCaseInsensitive:
+			case CharacterSet.Ucs2DanishCaseInsensitive:
+			case CharacterSet.Ucs2LithuanianCaseInsensitive:
+			case CharacterSet.Ucs2SlovakCaseInsensitive:
+			case CharacterSet.Ucs2Spanish2CaseInsensitive:
+			case CharacterSet.Ucs2RomanCaseInsensitive:
+			case CharacterSet.Ucs2PersianCaseInsensitive:
+			case CharacterSet.Ucs2EsperantoCaseInsensitive:
+			case CharacterSet.Ucs2HungarianCaseInsensitive:
+			case CharacterSet.Ucs2SinhalaCaseInsensitive:
+			case CharacterSet.Ucs2German2CaseInsensitive:
+			case CharacterSet.Ucs2CroatianCaseInsensitive:
+			case CharacterSet.Ucs2Unicode520CaseInsensitive:
+			case CharacterSet.Ucs2VietnameseCaseInsensitive:
+			case CharacterSet.Ucs2GeneralMySql500CaseInsensitive:
+			case CharacterSet.Ucs2CroatianCaseInsensitiveMariaDb:
+			case CharacterSet.Ucs2MyanmarCaseInsensitive:
+			case CharacterSet.Ucs2ThaiUnicode520Weight2:
+			case CharacterSet.Big5ChineseNoPadCaseInsensitive:
+			case CharacterSet.SjisJapaneseNoPadCaseInsensitive:
+			case CharacterSet.EuckrKoreanNoPadCaseInsensitive:
+			case CharacterSet.Gb2312ChineseNoPadCaseInsensitive:
+			case CharacterSet.GbkChineseNoPadCaseInsensitive:
+			case CharacterSet.Ucs2GeneralNoPadCaseInsensitive:
+			case CharacterSet.Big5NoPadBinary:
+			case CharacterSet.EuckrNoPadBinary:
+			case CharacterSet.Gb2312NoPadBinary:
+			case CharacterSet.GbkNoPadBinary:
+			case CharacterSet.SjisNoPadBinary:
+			case CharacterSet.Ucs2NoPadBinary:
+			case CharacterSet.Cp932JapaneseNoPadCaseInsensitive:
+			case CharacterSet.Cp932NoPadBinary:
+			case CharacterSet.Ucs2UnicodeNoPadCaseInsensitive:
+			case CharacterSet.Ucs2Unicode520NoPadCaseInsensitive:
+				return 2;
+
+			case CharacterSet.UjisJapaneseCaseInsensitive:
+			case CharacterSet.Utf8Mb3GeneralCaseInsensitive:
+			case CharacterSet.Utf8Mb3ToLowerCaseInsensitive:
+			case CharacterSet.Utf8Mb3Binary:
+			case CharacterSet.UjisBinary:
+			case CharacterSet.EucjpmsJapaneseCaseInsensitive:
+			case CharacterSet.EucjpmsBinary:
+			case CharacterSet.Utf8Mb3UnicodeCaseInsensitive:
+			case CharacterSet.Utf8Mb3IcelandicCaseInsensitive:
+			case CharacterSet.Utf8Mb3LatvianCaseInsensitive:
+			case CharacterSet.Utf8Mb3RomanianCaseInsensitive:
+			case CharacterSet.Utf8Mb3SlovenianCaseInsensitive:
+			case CharacterSet.Utf8Mb3PolishCaseInsensitive:
+			case CharacterSet.Utf8Mb3EstonianCaseInsensitive:
+			case CharacterSet.Utf8Mb3SpanishCaseInsensitive:
+			case CharacterSet.Utf8Mb3SwedishCaseInsensitive:
+			case CharacterSet.Utf8Mb3TurkishCaseInsensitive:
+			case CharacterSet.Utf8Mb3CzechCaseInsensitive:
+			case CharacterSet.Utf8Mb3DanishCaseInsensitive:
+			case CharacterSet.Utf8Mb3LithuanianCaseInsensitive:
+			case CharacterSet.Utf8Mb3SlovakCaseInsensitive:
+			case CharacterSet.Utf8Mb3Spanish2CaseInsensitive:
+			case CharacterSet.Utf8Mb3RomanCaseInsensitive:
+			case CharacterSet.Utf8Mb3PersianCaseInsensitive:
+			case CharacterSet.Utf8Mb3EsperantoCaseInsensitive:
+			case CharacterSet.Utf8Mb3HungarianCaseInsensitive:
+			case CharacterSet.Utf8Mb3SinhalaCaseInsensitive:
+			case CharacterSet.Utf8Mb3German2CaseInsensitive:
+			case CharacterSet.Utf8Mb3CroatianCaseInsensitive:
+			case CharacterSet.Utf8Mb3Unicode520CaseInsensitive:
+			case CharacterSet.Utf8Mb3VietnameseCaseInsensitive:
+			case CharacterSet.Utf8Mb3GeneralMySql500CaseInsensitive:
+			case CharacterSet.Utf8Mb3CroatianCaseInsensitiveMariaDb:
+			case CharacterSet.Utf8Mb3MyanmarCaseInsensitive:
+			case CharacterSet.Utf8Mb3ThaiUnicode520Weight2:
+			case CharacterSet.UjisJapaneseNoPadCaseInsensitive:
+			case CharacterSet.Utf8Mb3GeneralNoPadCaseInsensitive:
+			case CharacterSet.Utf8Mb3NoPadBinary:
+			case CharacterSet.UjisNoPadBinary:
+			case CharacterSet.EucjpmsJapaneseNoPadCaseInsensitive:
+			case CharacterSet.EucjpmsNoPadBinary:
+			case CharacterSet.Utf8Mb3UnicodeNoPadCaseInsensitive:
+			case CharacterSet.Utf8Mb3Unicode520NoPadCaseInsensitive:
+				return 3;
+
+			case CharacterSet.Utf8Mb4GeneralCaseInsensitive:
+			case CharacterSet.Utf8Mb4Binary:
+			case CharacterSet.Utf16GeneralCaseInsensitive:
+			case CharacterSet.Utf16Binary:
+			case CharacterSet.Utf16leGeneralCaseInsensitive:
+			case CharacterSet.Utf32GeneralCaseInsensitive:
+			case CharacterSet.Utf32Binary:
+			case CharacterSet.Utf16leBinary:
+			case CharacterSet.Utf16UnicodeCaseInsensitive:
+			case CharacterSet.Utf16IcelandicCaseInsensitive:
+			case CharacterSet.Utf16LatvianCaseInsensitive:
+			case CharacterSet.Utf16RomanianCaseInsensitive:
+			case CharacterSet.Utf16SlovenianCaseInsensitive:
+			case CharacterSet.Utf16PolishCaseInsensitive:
+			case CharacterSet.Utf16EstonianCaseInsensitive:
+			case CharacterSet.Utf16SpanishCaseInsensitive:
+			case CharacterSet.Utf16SwedishCaseInsensitive:
+			case CharacterSet.Utf16TurkishCaseInsensitive:
+			case CharacterSet.Utf16CzechCaseInsensitive:
+			case CharacterSet.Utf16DanishCaseInsensitive:
+			case CharacterSet.Utf16LithuanianCaseInsensitive:
+			case CharacterSet.Utf16SlovakCaseInsensitive:
+			case CharacterSet.Utf16Spanish2CaseInsensitive:
+			case CharacterSet.Utf16RomanCaseInsensitive:
+			case CharacterSet.Utf16PersianCaseInsensitive:
+			case CharacterSet.Utf16EsperantoCaseInsensitive:
+			case CharacterSet.Utf16HungarianCaseInsensitive:
+			case CharacterSet.Utf16SinhalaCaseInsensitive:
+			case CharacterSet.Utf16German2CaseInsensitive:
+			case CharacterSet.Utf16CroatianCaseInsensitive:
+			case CharacterSet.Utf16Unicode520CaseInsensitive:
+			case CharacterSet.Utf16VietnameseCaseInsensitive:
+			case CharacterSet.Utf32UnicodeCaseInsensitive:
+			case CharacterSet.Utf32IcelandicCaseInsensitive:
+			case CharacterSet.Utf32LatvianCaseInsensitive:
+			case CharacterSet.Utf32RomanianCaseInsensitive:
+			case CharacterSet.Utf32SlovenianCaseInsensitive:
+			case CharacterSet.Utf32PolishCaseInsensitive:
+			case CharacterSet.Utf32EstonianCaseInsensitive:
+			case CharacterSet.Utf32SpanishCaseInsensitive:
+			case CharacterSet.Utf32SwedishCaseInsensitive:
+			case CharacterSet.Utf32TurkishCaseInsensitive:
+			case CharacterSet.Utf32CzechCaseInsensitive:
+			case CharacterSet.Utf32DanishCaseInsensitive:
+			case CharacterSet.Utf32LithuanianCaseInsensitive:
+			case CharacterSet.Utf32SlovakCaseInsensitive:
+			case CharacterSet.Utf32Spanish2CaseInsensitive:
+			case CharacterSet.Utf32RomanCaseInsensitive:
+			case CharacterSet.Utf32PersianCaseInsensitive:
+			case CharacterSet.Utf32EsperantoCaseInsensitive:
+			case CharacterSet.Utf32HungarianCaseInsensitive:
+			case CharacterSet.Utf32SinhalaCaseInsensitive:
+			case CharacterSet.Utf32German2CaseInsensitive:
+			case CharacterSet.Utf32CroatianCaseInsensitive:
+			case CharacterSet.Utf32Unicode520CaseInsensitive:
+			case CharacterSet.Utf32VietnameseCaseInsensitive:
+			case CharacterSet.Utf8Mb4UnicodeCaseInsensitive:
+			case CharacterSet.Utf8Mb4IcelandicCaseInsensitive:
+			case CharacterSet.Utf8Mb4LatvianCaseInsensitive:
+			case CharacterSet.Utf8Mb4RomanianCaseInsensitive:
+			case CharacterSet.Utf8Mb4SlovenianCaseInsensitive:
+			case CharacterSet.Utf8Mb4PolishCaseInsensitive:
+			case CharacterSet.Utf8Mb4EstonianCaseInsensitive:
+			case CharacterSet.Utf8Mb4SpanishCaseInsensitive:
+			case CharacterSet.Utf8Mb4SwedishCaseInsensitive:
+			case CharacterSet.Utf8Mb4TurkishCaseInsensitive:
+			case CharacterSet.Utf8Mb4CzechCaseInsensitive:
+			case CharacterSet.Utf8Mb4DanishCaseInsensitive:
+			case CharacterSet.Utf8Mb4LithuanianCaseInsensitive:
+			case CharacterSet.Utf8Mb4SlovakCaseInsensitive:
+			case CharacterSet.Utf8Mb4Spanish2CaseInsensitive:
+			case CharacterSet.Utf8Mb4RomanCaseInsensitive:
+			case CharacterSet.Utf8Mb4PersianCaseInsensitive:
+			case CharacterSet.Utf8Mb4EsperantoCaseInsensitive:
+			case CharacterSet.Utf8Mb4HungarianCaseInsensitive:
+			case CharacterSet.Utf8Mb4SinhalaCaseInsensitive:
+			case CharacterSet.Utf8Mb4German2CaseInsensitive:
+			case CharacterSet.Utf8Mb4CroatianCaseInsensitive:
+			case CharacterSet.Utf8Mb4Unicode520CaseInsensitive:
+			case CharacterSet.Utf8Mb4VietnameseCaseInsensitive:
+			case CharacterSet.Gb18030ChineseCaseInsensitive:
+			case CharacterSet.Gb18030Binary:
+			case CharacterSet.Gb18030Unicode520CaseInsensitive:
+			case CharacterSet.Utf8Mb4Uca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4GermanPhonebookUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4IcelandicUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4LatvianUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4RomanianUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4SlovenianUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4PolishUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4EstonianUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4SpanishUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4SwedishUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4TurkishUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4CaseSensitiveUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4DanishUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4LithuanianUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4SlovakUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4TraditionalSpanishUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4LatinUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4EsperantoUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4HungarianUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4CroatianUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4VietnameseUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4Uca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4GermanPhonebookUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4IcelandicUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4LatvianUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4RomanianUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4SlovenianUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4PolishUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4EstonianUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4SpanishUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4SwedishUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4TurkishUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4CaseSensitiveUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4DanishUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4LithuanianUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4SlovakUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4TraditionalSpanishUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4LatinUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4EsperantoUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4HungarianUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4CroatianUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4VietnameseUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4JapaneseUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4JapaneseUca900AccentSensitiveCaseSensitiveKanaSensitive:
+			case CharacterSet.Utf8Mb4Uca900AccentSensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4RussianUca900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4RussianUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4ChineseUca900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4Uca900Binary:
+			case CharacterSet.Utf8Mb4NorwegianBokmal0900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4NorwegianBokmal0900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4NorwegianNynorsk0900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4NorwegianNynorsk0900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4SerbianLatin0900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4SerbianLatin0900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4Bosnian0900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4Bosnian0900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4Bulgarian0900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4Bulgarian0900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4Galician0900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4Galician0900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4MongolianCyrillic0900AccentInsensitiveCaseInsensitive:
+			case CharacterSet.Utf8Mb4MongolianCyrillic0900AccentSensitiveCaseSensitive:
+			case CharacterSet.Utf8Mb4CroatianCaseInsensitiveMariaDb:
+			case CharacterSet.Utf8Mb4MyanmarCaseInsensitive:
+			case CharacterSet.Utf8Mb4ThaiUnicode520Weight2:
+			case CharacterSet.Utf16CroatianCaseInsensitiveMariaDb:
+			case CharacterSet.Utf16MyanmarCaseInsensitive:
+			case CharacterSet.Utf16ThaiUnicode520Weight2:
+			case CharacterSet.Utf32CroatianCaseInsensitiveMariaDb:
+			case CharacterSet.Utf32MyanmarCaseInsensitive:
+			case CharacterSet.Utf32ThaiUnicode520Weight2:
+			case CharacterSet.Utf8Mb4GeneralNoPadCaseInsensitive:
+			case CharacterSet.Utf8Mb4NoPadBinary:
+			case CharacterSet.Utf16GeneralNoPadCaseInsensitive:
+			case CharacterSet.Utf16NoPadBinary:
+			case CharacterSet.Utf16leGeneralNoPadCaseInsensitive:
+			case CharacterSet.Utf32GeneralNoPadCaseInsensitive:
+			case CharacterSet.Utf32NoPadBinary:
+			case CharacterSet.Utf16leNoPadBinary:
+			case CharacterSet.Utf16UnicodeNoPadCaseInsensitive:
+			case CharacterSet.Utf16Unicode520NoPadCaseInsensitive:
+			case CharacterSet.Utf32UnicodeNoPadCaseInsensitive:
+			case CharacterSet.Utf32Unicode520NoPadCaseInsensitive:
+			case CharacterSet.Utf8Mb4UnicodeNoPadCaseInsensitive:
+			case CharacterSet.Utf8Mb4Unicode520NoPadCaseInsensitive:
+				return 4;
+
+			default:
+				throw new NotSupportedException($"Maximum byte length of character set {characterSet} is unknown.");
 		}
 	}
 
diff --git a/src/MySqlConnector/Utilities/UnixDomainSocketEndPoint.cs b/src/MySqlConnector/Utilities/UnixDomainSocketEndPoint.cs
index 0e87bfa89..eb5f5fa09 100644
--- a/src/MySqlConnector/Utilities/UnixDomainSocketEndPoint.cs
+++ b/src/MySqlConnector/Utilities/UnixDomainSocketEndPoint.cs
@@ -45,9 +45,9 @@ internal sealed class UnixDomainSocketEndPoint : EndPoint
 	public UnixDomainSocketEndPoint(string filename)
 	{
 		if (filename is null)
-			throw new ArgumentNullException (nameof(filename));
+			throw new ArgumentNullException(nameof(filename));
 		if (filename.Length == 0)
-			throw new ArgumentException ("Cannot be empty.", nameof(filename));
+			throw new ArgumentException("Cannot be empty.", nameof(filename));
 		Filename = filename;
 	}
 
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 2f026ffc1..8db3cf75b 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -138,7 +138,7 @@ public static RSAParameters GetRsaParameters(string key)
 		if (bufferLength > 1024)
 			bufferBytes = buffer = ArrayPool<byte>.Shared.Rent(bufferLength);
 		else
-			 bufferBytes = stackalloc byte[bufferLength];
+			bufferBytes = stackalloc byte[bufferLength];
 		try
 		{
 			if (!System.Convert.TryFromBase64Chars(keyChars, bufferBytes, out var bytesWritten))

From 1b7595fb32a32c2cc55c6bee58b587d1995f406a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 7 Jul 2023 16:09:19 -0700
Subject: [PATCH 129/393] Fix Serilog example code error.

---
 docs/content/overview/logging.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/content/overview/logging.md b/docs/content/overview/logging.md
index 500f36f24..a08f622ae 100644
--- a/docs/content/overview/logging.md
+++ b/docs/content/overview/logging.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-01-21
+lastmod: 2023-07-07
 date: 2018-01-20
 menu:
   main:
@@ -71,5 +71,5 @@ Install [MySqlConnector.Logging.Serilog](https://www.nuget.org/packages/MySqlCon
 Add the following line of code to your application startup routine:
 
 ```csharp
-MySqlConnectorLogManager.Provider = new SerilogLoggerProvider(loggerFactory);
+MySqlConnectorLogManager.Provider = new SerilogLoggerProvider();
 ```

From 009b899fdee0cbedc59a7b889abf852bb605a998 Mon Sep 17 00:00:00 2001
From: Long Chen <chensir_@hotmail.com>
Date: Fri, 28 Jul 2023 16:42:44 +0800
Subject: [PATCH 130/393] Add Bug1348 unit test

Signed-off-by: Long Chen <chensir_@hotmail.com>
---
 .../IntegrationTests/TransactionScopeTests.cs | 45 +++++++++++++++++++
 1 file changed, 45 insertions(+)

diff --git a/tests/IntegrationTests/TransactionScopeTests.cs b/tests/IntegrationTests/TransactionScopeTests.cs
index 3bf1ea086..dbf5f1d37 100644
--- a/tests/IntegrationTests/TransactionScopeTests.cs
+++ b/tests/IntegrationTests/TransactionScopeTests.cs
@@ -1,3 +1,4 @@
+using System.Security.Cryptography;
 using System.Transactions;
 
 namespace IntegrationTests;
@@ -840,6 +841,50 @@ public void CannotMixNonXaAndXaTransactions()
 			Assert.Throws<NotSupportedException>(() => conn2.Open());
 		}
 	}
+
+	[Fact]
+	public void Bug1348()
+	{
+		var xid = string.Empty;
+		// TransactionAbortedException、MySqlException
+		Assert.ThrowsAny<Exception>(() =>
+		{
+			using (TransactionScope scope = new())
+			{
+				xid = System.Transactions.Transaction.Current.TransactionInformation.LocalIdentifier;
+				using var conn1 = new MySqlConnection(AppConfig.ConnectionString);
+				conn1.Open();
+
+				using var conn2 = new MySqlConnection(AppConfig.ConnectionString);
+				conn2.Open();
+				// Rolling back the second branch transaction early so that it has an exception in the preparation phase
+				var command2 = conn2.CreateCommand();
+				command2.CommandText = $"XA END '{xid}','2';XA ROLLBACK '{xid}','2'";
+				command2.ExecuteNonQuery();
+
+				scope.Complete();
+			}
+		});
+
+		// Asserts whether the first branch transaction is rolled back
+		using var conn = new MySqlConnection(AppConfig.ConnectionString);
+		conn.Open();
+		var command = conn.CreateCommand();
+		command.CommandText = $"XA RECOVER;";
+		using var reader = command.ExecuteReader();
+		var rollbacked = true;
+		while (reader.Read())
+		{
+			var branchID = reader.GetString(3);
+			if (branchID == $"{xid}1")
+			{
+				rollbacked = false;
+				break;
+			}
+		}
+
+		Assert.True(rollbacked, $"First branch transaction '{xid}1' not rolled back");
+	}
 #endif
 
 	DatabaseFixture m_database;

From e53d40c8c50b9b7f64bbfbf9d3fa769eb439a993 Mon Sep 17 00:00:00 2001
From: Long Chen <chensir_@hotmail.com>
Date: Fri, 28 Jul 2023 16:51:40 +0800
Subject: [PATCH 131/393] Add xa branching transaction preparation exception
 handling. Fixes #1348

Signed-off-by: Long Chen <chensir_@hotmail.com>
---
 .../Core/EnlistedTransactionBase.cs               | 15 +++++++++++++--
 src/MySqlConnector/Core/XaEnlistedTransaction.cs  |  4 +++-
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/src/MySqlConnector/Core/EnlistedTransactionBase.cs b/src/MySqlConnector/Core/EnlistedTransactionBase.cs
index 73a156846..e8506ee14 100644
--- a/src/MySqlConnector/Core/EnlistedTransactionBase.cs
+++ b/src/MySqlConnector/Core/EnlistedTransactionBase.cs
@@ -10,6 +10,9 @@ internal abstract class EnlistedTransactionBase : IEnlistmentNotification
 	// Whether the connection is idle, i.e., a client has closed it and is no longer using it
 	public bool IsIdle { get; set; }
 
+	// Whether to enter the PREPARED state, The rollback operation needs to determine if the Xa transaction has entered the PREPARED state.
+	public bool IsPrepared { get; set; }
+
 	public Transaction Transaction { get; private set; }
 
 	public void Start()
@@ -20,8 +23,16 @@ public void Start()
 
 	void IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)
 	{
-		OnPrepare(preparingEnlistment);
-		preparingEnlistment.Prepared();
+		try
+		{
+			OnPrepare(preparingEnlistment);
+			IsPrepared = true;
+			preparingEnlistment.Prepared();
+		}
+		catch (Exception ex)
+		{
+			preparingEnlistment.ForceRollback(ex);
+		}
 	}
 
 	void IEnlistmentNotification.Commit(Enlistment enlistment)
diff --git a/src/MySqlConnector/Core/XaEnlistedTransaction.cs b/src/MySqlConnector/Core/XaEnlistedTransaction.cs
index 3937d420d..69ef49255 100644
--- a/src/MySqlConnector/Core/XaEnlistedTransaction.cs
+++ b/src/MySqlConnector/Core/XaEnlistedTransaction.cs
@@ -37,7 +37,9 @@ protected override void OnRollback(Enlistment enlistment)
 	{
 		try
 		{
-			ExecuteXaCommand("END");
+			if (!IsPrepared)
+				ExecuteXaCommand("END");
+
 			ExecuteXaCommand("ROLLBACK");
 		}
 		catch (MySqlException ex) when (ex.ErrorCode is MySqlErrorCode.XARBDeadlock)

From 6d83970f56e1746e385c34e47073489a518d30c5 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 30 Jul 2023 07:07:17 -0700
Subject: [PATCH 132/393] Change IsPrepared to have a private setter.

---
 src/MySqlConnector/Core/EnlistedTransactionBase.cs | 4 ++--
 tests/IntegrationTests/TransactionScopeTests.cs    | 1 -
 2 files changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/MySqlConnector/Core/EnlistedTransactionBase.cs b/src/MySqlConnector/Core/EnlistedTransactionBase.cs
index e8506ee14..44260e309 100644
--- a/src/MySqlConnector/Core/EnlistedTransactionBase.cs
+++ b/src/MySqlConnector/Core/EnlistedTransactionBase.cs
@@ -10,8 +10,8 @@ internal abstract class EnlistedTransactionBase : IEnlistmentNotification
 	// Whether the connection is idle, i.e., a client has closed it and is no longer using it
 	public bool IsIdle { get; set; }
 
-	// Whether to enter the PREPARED state, The rollback operation needs to determine if the Xa transaction has entered the PREPARED state.
-	public bool IsPrepared { get; set; }
+	// Whether the distributed transaction was prepared successfully
+	public bool IsPrepared { get; private set; }
 
 	public Transaction Transaction { get; private set; }
 
diff --git a/tests/IntegrationTests/TransactionScopeTests.cs b/tests/IntegrationTests/TransactionScopeTests.cs
index dbf5f1d37..dee270ccb 100644
--- a/tests/IntegrationTests/TransactionScopeTests.cs
+++ b/tests/IntegrationTests/TransactionScopeTests.cs
@@ -1,4 +1,3 @@
-using System.Security.Cryptography;
 using System.Transactions;
 
 namespace IntegrationTests;

From 7ae44e38318eb474e76d0e855d7ebd1d377b25f9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 30 Jul 2023 07:28:45 -0700
Subject: [PATCH 133/393] Release 2.3.0 Beta 3.

---
 docs/content/overview/version-history.md | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 21072019b..cda9516cd 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-07-21
+lastmod: 2023-07-30
 date: 2017-03-27
 menu:
   main:
@@ -11,6 +11,18 @@ weight: 30
 
 # Version History
 
+### 2.3.0 Beta 3
+
+* Fix version parsing for MariaDB 11.0 and later: [#1311](https://github.com/mysql-net/MySqlConnector/pull/1311).
+* Support per-query variables for `CommandBehavior.SchemaOnly` and `SingleRow`: [#1312](https://github.com/mysql-net/MySqlConnector/pull/1312).
+* Perform XA rollback when preparing a transaction fails: [#1348](https://github.com/mysql-net/MySqlConnector/issues/1348).
+* Implement faster parsing for result sets with multiple rows: [#1330](https://github.com/mysql-net/MySqlConnector/pull/1330).
+* Add `MySqlDataSource.Name` and `MySqlDataSourceBuilder.UseName`.
+* Fix potential error in reallocating an internal buffer when writing ASCII text.
+* Update handling of `ActivityStatus` to latest conventions: [#1334](https://github.com/mysql-net/MySqlConnector/pull/1334).
+* Reduce overhead of `CommandTimeout`: [#1338](https://github.com/mysql-net/MySqlConnector/pull/1338)
+* Thanks to [Diego Dupin](https://github.com/rusher), [JackBOBO](https://github.com/JackBOBO), and [Piotr Kiełkowicz](https://github.com/Kielek) for contributions to this release.
+
 ### 2.3.0 Beta 2
 
 * Support skipping metadata for prepared statements with MariaDB 10.6 and later: [#1301](https://github.com/mysql-net/MySqlConnector/pull/1301).

From 23198432ebc96daf78e4a61c3cefb143d9edfa91 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 30 Jul 2023 07:31:49 -0700
Subject: [PATCH 134/393] Add missing period to release notes.

---
 docs/content/overview/version-history.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index cda9516cd..30e29e489 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -20,7 +20,7 @@ weight: 30
 * Add `MySqlDataSource.Name` and `MySqlDataSourceBuilder.UseName`.
 * Fix potential error in reallocating an internal buffer when writing ASCII text.
 * Update handling of `ActivityStatus` to latest conventions: [#1334](https://github.com/mysql-net/MySqlConnector/pull/1334).
-* Reduce overhead of `CommandTimeout`: [#1338](https://github.com/mysql-net/MySqlConnector/pull/1338)
+* Reduce overhead of `CommandTimeout`: [#1338](https://github.com/mysql-net/MySqlConnector/pull/1338).
 * Thanks to [Diego Dupin](https://github.com/rusher), [JackBOBO](https://github.com/JackBOBO), and [Piotr Kiełkowicz](https://github.com/Kielek) for contributions to this release.
 
 ### 2.3.0 Beta 2

From 8d45815b0b66cd5293513ecb97b8162a7a709b15 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 31 Jul 2023 19:47:48 -0700
Subject: [PATCH 135/393] Enable Visual Studio build acceleration.

---
 src/Directory.Build.props | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 53a9d1697..9410142da 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -18,6 +18,7 @@
     <EnableStrictModeForCompatibleFrameworksInPackageValidation>true</EnableStrictModeForCompatibleFrameworksInPackageValidation>
     <EnableStrictModeForCompatibleTfms>true</EnableStrictModeForCompatibleTfms>
     <LangVersion>11.0</LangVersion>
+    <AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
     <AnalysisLevel>latest-all</AnalysisLevel>
     <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
     <RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>

From 9d2a96937a20dc44dcce7585c9a00db7a70e06ea Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 7 Aug 2023 06:40:17 -0700
Subject: [PATCH 136/393] Update Microsoft.NET.Test.Sdk to 17.7.0.

---
 tests/Conformance.Tests/Conformance.Tests.csproj       | 2 +-
 tests/IntegrationTests/IntegrationTests.csproj         | 2 +-
 tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/Conformance.Tests/Conformance.Tests.csproj b/tests/Conformance.Tests/Conformance.Tests.csproj
index bb3f7db21..e04c1dc8e 100644
--- a/tests/Conformance.Tests/Conformance.Tests.csproj
+++ b/tests/Conformance.Tests/Conformance.Tests.csproj
@@ -12,7 +12,7 @@
 
  <ItemGroup>
     <PackageReference Include="AdoNet.Specification.Tests" Version="2.0.0-beta.1" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
     <PackageReference Include="xunit" Version="2.4.2" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index 6ec889108..a052a6915 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -28,7 +28,7 @@
 
   <ItemGroup>
     <!--testing packages-->
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
     <PackageReference Include="xunit" Version="2.4.2" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 70688abdb..ea52b4080 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -24,7 +24,7 @@
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>

From 8175db1813a64466a7eb5a1d11e80b223cbf85f5 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 9 Aug 2023 06:13:19 -0700
Subject: [PATCH 137/393] Remove optimizeForReading argument.

This is a breaking change in .NET 8.0 Preview 7: https://github.com/dotnet/runtime/pull/87988.
---
 src/MySqlConnector/MySqlConnectionStringBuilder.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index 7f8f0184f..a1ac6c084 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -1262,7 +1262,7 @@ static MySqlConnectionStringOption()
 #pragma warning restore SA1118 // Parameter should not span multiple lines
 
 #if NET8_0_OR_GREATER
-		s_options = options.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase, optimizeForReading: true);
+		s_options = options.ToFrozenDictionary(StringComparer.OrdinalIgnoreCase);
 #else
 		s_options = options;
 #endif

From 8f799fd9bd5d66852291e8770093bc436e99b7b9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 9 Aug 2023 06:45:37 -0700
Subject: [PATCH 138/393] Update package validation suppression file for .NET
 8.0 Preview 7.

---
 .../CompatibilitySuppressions.xml             |  1 +
 .../CompatibilitySuppressions.xml             |  1 +
 .../CompatibilitySuppressions.xml             |  1 +
 .../CompatibilitySuppressions.xml             |  1 +
 .../CompatibilitySuppressions.xml             | 55 +++++++++++++------
 5 files changed, 43 insertions(+), 16 deletions(-)

diff --git a/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml b/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml
index a95918371..738dbc799 100644
--- a/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml
@@ -1,4 +1,5 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
+<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>
diff --git a/src/MySqlConnector.Logging.NLog/CompatibilitySuppressions.xml b/src/MySqlConnector.Logging.NLog/CompatibilitySuppressions.xml
index a95918371..738dbc799 100644
--- a/src/MySqlConnector.Logging.NLog/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector.Logging.NLog/CompatibilitySuppressions.xml
@@ -1,4 +1,5 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
+<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>
diff --git a/src/MySqlConnector.Logging.Serilog/CompatibilitySuppressions.xml b/src/MySqlConnector.Logging.Serilog/CompatibilitySuppressions.xml
index a95918371..738dbc799 100644
--- a/src/MySqlConnector.Logging.Serilog/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector.Logging.Serilog/CompatibilitySuppressions.xml
@@ -1,4 +1,5 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
+<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>
diff --git a/src/MySqlConnector.Logging.log4net/CompatibilitySuppressions.xml b/src/MySqlConnector.Logging.log4net/CompatibilitySuppressions.xml
index a95918371..738dbc799 100644
--- a/src/MySqlConnector.Logging.log4net/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector.Logging.log4net/CompatibilitySuppressions.xml
@@ -1,4 +1,5 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
+<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>
diff --git a/src/MySqlConnector/CompatibilitySuppressions.xml b/src/MySqlConnector/CompatibilitySuppressions.xml
index 1bb44ab01..35851e7b5 100644
--- a/src/MySqlConnector/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector/CompatibilitySuppressions.xml
@@ -1,11 +1,31 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
+<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:System.Data.Common.DbColumn</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:System.Data.Common.DbColumn</Target>
+    <Left>lib/net462/MySqlConnector.dll</Left>
+    <Right>lib/net471/MySqlConnector.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:System.Data.Common.DbDataSource</Target>
     <Left>lib/net6.0/MySqlConnector.dll</Left>
     <Right>lib/net7.0/MySqlConnector.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:MySqlConnector.MySqlDataReader</Target>
+    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
+    <Right>lib/net462/MySqlConnector.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0008</DiagnosticId>
     <Target>T:MySqlConnector.MySqlBatchCommandCollection</Target>
@@ -13,29 +33,32 @@
     <Right>lib/net6.0/MySqlConnector.dll</Right>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:System.Data.Common.DbColumn</Target>
-    <Left>lib/net461/MySqlConnector.dll</Left>
-    <Right>lib/net471/MySqlConnector.dll</Right>
+    <DiagnosticId>CP1002</DiagnosticId>
+    <Target>System.Data.Common, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
+    <Left>lib/net5.0/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:System.Data.Common.DbColumn</Target>
-    <Left>lib/net461/MySqlConnector.dll</Left>
-    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <DiagnosticId>CP1002</DiagnosticId>
+    <Target>System.Runtime, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
+    <Left>lib/net5.0/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
     <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:System.Data.Common.DbColumn</Target>
-    <Left>lib/net462/MySqlConnector.dll</Left>
-    <Right>lib/net471/MySqlConnector.dll</Right>
+    <DiagnosticId>CP1002</DiagnosticId>
+    <Target>System.Data.Common, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
+    <Left>lib/netcoreapp3.1/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP0008</DiagnosticId>
-    <Target>T:MySqlConnector.MySqlDataReader</Target>
-    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
-    <Right>lib/net462/MySqlConnector.dll</Right>
+    <DiagnosticId>CP1002</DiagnosticId>
+    <Target>System.Runtime, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
+    <Left>lib/netcoreapp3.1/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>

From 0622c025e99992fa1408c14a5a9d604b4a86a49f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 9 Aug 2023 07:19:47 -0700
Subject: [PATCH 139/393] Update NuGet packages.

---
 src/Directory.Build.props                                 | 2 +-
 ...lConnector.Logging.Microsoft.Extensions.Logging.csproj | 2 +-
 src/MySqlConnector/MySqlConnector.csproj                  | 4 ++--
 tests/IntegrationTests/ActivityTests.cs                   | 2 +-
 tests/IntegrationTests/IntegrationTests.csproj            | 8 ++++----
 tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj    | 4 ++--
 .../SchemaCollectionGenerator.csproj                      | 2 +-
 7 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 9410142da..e73c5bfa3 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -45,7 +45,7 @@
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
-    <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.435">
+    <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
index e0fac0dd0..5233621fb 100644
--- a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
+++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
@@ -12,7 +12,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index a0251e51e..4ae3c8e46 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -16,7 +16,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
   </ItemGroup>
 
   <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' ">
@@ -32,7 +32,7 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(TargetFrameworkIdentifier)' != '.NETCoreApp' ">
-    <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="7.0.0" />
+    <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/tests/IntegrationTests/ActivityTests.cs b/tests/IntegrationTests/ActivityTests.cs
index 7875f99e2..60ada3b6f 100644
--- a/tests/IntegrationTests/ActivityTests.cs
+++ b/tests/IntegrationTests/ActivityTests.cs
@@ -155,7 +155,7 @@ private void AssertTag(IEnumerable<KeyValuePair<string, string>> tags, string ex
 	{
 		var tag = tags.SingleOrDefault(x => x.Key == expectedTag);
 		if (tag.Key is null)
-			Assert.True(false, $"tags did not contain '{expectedTag}'");
+			Assert.Fail($"tags did not contain '{expectedTag}'");
 		Assert.Equal(expectedValue, tag.Value);
 	}
 }
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index a052a6915..fdcb24726 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -29,14 +29,14 @@
   <ItemGroup>
     <!--testing packages-->
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
-    <PackageReference Include="xunit" Version="2.4.2" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+    <PackageReference Include="xunit" Version="2.5.0" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
     <!--app packages-->
-    <PackageReference Include="Dapper.StrongName" Version="2.0.123" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.1" />
+    <PackageReference Include="Dapper.StrongName" Version="2.0.143" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
     <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
     <Using Include="System" />
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index ea52b4080..9800da918 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -19,8 +19,8 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="xunit" Version="2.4.2" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+    <PackageReference Include="xunit" Version="2.5.0" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
index 5334b4ac3..300f65cc5 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
+++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
@@ -12,7 +12,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="YamlDotNet" Version="12.3.1" />
+    <PackageReference Include="YamlDotNet" Version="13.1.1" />
   </ItemGroup>
 
 </Project>

From e8d587862d149beaeaa4d2c52ea77e51aa64a300 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 10 Aug 2023 11:11:08 -0700
Subject: [PATCH 140/393] Add Component Detection workflow.

---
 .github/workflows/ci.yml | 32 ++++++++++++++++++++++++++++++++
 1 file changed, 32 insertions(+)
 create mode 100644 .github/workflows/ci.yml

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 000000000..f7bc72786
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,32 @@
+name: "Continuous Integration"
+
+on:
+  push:
+    branches: [ master, test ]
+  pull_request:
+    branches: [ master ]
+
+jobs:
+  build:
+    name: Build
+    runs-on: ubuntu-latest
+    permissions:
+      contents: write
+
+    steps:
+    - name: Checkout repository
+      uses: actions/checkout@v3
+
+    - name: Set up .NET 7.0
+      uses: actions/setup-dotnet@v3
+      with:
+        dotnet-version: 7.x
+
+    - name: Restore
+      run: dotnet restore
+
+    - name: Build
+      run: dotnet build --configuration Release --no-restore
+
+    - name: Component detection
+      uses: advanced-security/component-detection-dependency-submission-action@v0.0.2

From 640f9554ebebe4db63bb8c784d46ebd69262d3ce Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 12 Aug 2023 20:01:51 -0700
Subject: [PATCH 141/393] Avoid allocating an unused OkPayload.

---
 src/MySqlConnector/Core/ServerSession.cs      | 18 +++----
 src/MySqlConnector/MySqlConnection.cs         |  4 +-
 .../Protocol/Payloads/OkPayload.cs            | 48 +++++++++++++++----
 3 files changed, 50 insertions(+), 20 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index a3e1a1ce4..f20992f27 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -316,7 +316,7 @@ public void FinishQuerying()
 			SendAsync(payload, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 			payload = ReceiveReplyAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 #pragma warning restore CA2012
-			OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 		}
 
 		lock (m_lock)
@@ -582,7 +582,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			// set 'collation_connection' to the server default
 			await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 
 			if (ShouldGetRealServerDetails(cs))
 				await GetRealServerDetailsAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
@@ -627,11 +627,11 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 					// read two OK replies
 					m_payloadHandler.SetNextSequenceNumber(1);
 					payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-					OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+					OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 
 					m_payloadHandler.SetNextSequenceNumber(1);
 					payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-					OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+					OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 
 					return true;
 				}
@@ -639,7 +639,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 				Log.SendingResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
 				await SendAsync(ResetConnectionPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-				OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+				OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 			}
 			else
 			{
@@ -663,13 +663,13 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 					Log.OptimisticReauthenticationFailed(m_logger, Id);
 					payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				}
-				OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+				OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 			}
 
 			// set 'collation_connection' to the server default
 			await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 
 			return true;
 		}
@@ -870,7 +870,7 @@ public async ValueTask<bool> TryPingAsync(bool logInfo, IOBehavior ioBehavior, C
 			Log.PingingServer(m_logger, Id);
 			await SendAsync(PingPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
 			var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 			Log.SuccessfullyPingedServer(m_logger, logInfo ? LogLevel.Information : LogLevel.Trace, Id);
 			return true;
 		}
@@ -1710,7 +1710,7 @@ static void ReadRow(ReadOnlySpan<byte> span, out int? connectionId, out ServerVe
 			// OK/EOF payload
 			payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 			if (OkPayload.IsOk(payload.Span, SupportsDeprecateEof))
-				OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+				OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 			else
 				EofPayload.Create(payload.Span);
 
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index c954725f4..c8ec05b5b 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -356,7 +356,7 @@ private async Task ChangeDatabaseAsync(IOBehavior ioBehavior, string databaseNam
 		using (var initDatabasePayload = InitDatabasePayload.Create(databaseName))
 			await m_session!.SendAsync(initDatabasePayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 		var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-		OkPayload.Create(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+		OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
 		m_session.DatabaseOverride = databaseName;
 	}
 
@@ -472,7 +472,7 @@ public async ValueTask ResetConnectionAsync(CancellationToken cancellationToken
 		Log.ResettingConnection(m_logger, session.Id);
 		await session.SendAsync(ResetConnectionPayload.Instance, AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
 		var payload = await session.ReceiveReplyAsync(AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
-		OkPayload.Create(payload.Span, session.SupportsDeprecateEof, session.SupportsSessionTrack);
+		OkPayload.Verify(payload.Span, session.SupportsDeprecateEof, session.SupportsSessionTrack);
 	}
 
 	[AllowNull]
diff --git a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
index 8cd03b5f7..d5d4a1aa2 100644
--- a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
@@ -25,7 +25,30 @@ public static bool IsOk(ReadOnlySpan<byte> span, bool deprecateEof) =>
 			(span.Length > 6 && span[0] == Signature ||
 			 deprecateEof && span.Length < 0xFF_FFFF && span[0] == EofPayload.Signature);
 
-	public static OkPayload Create(ReadOnlySpan<byte> span, bool deprecateEof, bool clientSessionTrack)
+	/// <summary>
+	/// Creates an <see cref="OkPayload"/> from the given <paramref name="span"/>, or throws <see cref="FormatException"/>
+	/// if the bytes do not represent a valid <see cref="OkPayload"/>.
+	/// </summary>
+	/// <param name="span">The bytes from which to read an OK packet.</param>
+	/// <param name="deprecateEof">Whether the <see cref="ProtocolCapabilities.DeprecateEof"/> flag was set on the connection.</param>
+	/// <param name="clientSessionTrack">Whether <see cref="ProtocolCapabilities.SessionTrack"/> flag was set on the connection.</param>
+	/// <returns>A <see cref="OkPayload"/> with the contents of the OK packet.</returns>
+	/// <exception cref="FormatException">Thrown when the bytes are not a valid OK packet.</exception>
+	public static OkPayload Create(ReadOnlySpan<byte> span, bool deprecateEof, bool clientSessionTrack) =>
+		Read(span, deprecateEof, clientSessionTrack, true)!;
+
+	/// <summary>
+	/// Verifies that the bytes in the given <paramref name="span"/> form a valid <see cref="OkPayload"/>, or throws
+	/// <see cref="FormatException"/> if they do not.
+	/// </summary>
+	/// <param name="span">The bytes from which to read an OK packet.</param>
+	/// <param name="deprecateEof">Whether the <see cref="ProtocolCapabilities.DeprecateEof"/> flag was set on the connection.</param>
+	/// <param name="clientSessionTrack">Whether <see cref="ProtocolCapabilities.SessionTrack"/> flag was set on the connection.</param>
+	/// <exception cref="FormatException">Thrown when the bytes are not a valid OK packet.</exception>
+	public static void Verify(ReadOnlySpan<byte> span, bool deprecateEof, bool clientSessionTrack) =>
+		Read(span, deprecateEof, clientSessionTrack, createPayload: false);
+
+	private static OkPayload? Read(ReadOnlySpan<byte> span, bool deprecateEof, bool clientSessionTrack, bool createPayload)
 	{
 		var reader = new ByteArrayReader(span);
 		var signature = reader.ReadByte();
@@ -82,17 +105,24 @@ public static OkPayload Create(ReadOnlySpan<byte> span, bool deprecateEof, bool
 				statusBytes = statusBytes[1..];
 		}
 
-		var statusInfo = statusBytes.Length == 0 ? null : Encoding.UTF8.GetString(statusBytes);
+		if (createPayload)
+		{
+			var statusInfo = statusBytes.Length == 0 ? null : Encoding.UTF8.GetString(statusBytes);
 
-		if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && statusInfo is null && newSchema is null)
+			if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && statusInfo is null && newSchema is null)
+			{
+				if (serverStatus == ServerStatus.AutoCommit)
+					return s_autoCommitOk;
+				if (serverStatus == (ServerStatus.AutoCommit | ServerStatus.SessionStateChanged))
+					return s_autoCommitSessionStateChangedOk;
+			}
+
+			return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, statusInfo, newSchema);
+		}
+		else
 		{
-			if (serverStatus == ServerStatus.AutoCommit)
-				return s_autoCommitOk;
-			if (serverStatus == (ServerStatus.AutoCommit | ServerStatus.SessionStateChanged))
-				return s_autoCommitSessionStateChangedOk;
+			return null;
 		}
-
-		return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, statusInfo, newSchema);
 	}
 
 	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, string? statusInfo, string? newSchema)

From 3f5b2869ba82ce0ee89a58c1577c762cf015400c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 12 Aug 2023 20:02:21 -0700
Subject: [PATCH 142/393] Fix small typos in comment.

---
 src/MySqlConnector/Core/SchemaProvider.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/MySqlConnector/Core/SchemaProvider.cs b/src/MySqlConnector/Core/SchemaProvider.cs
index 2617d63e6..3c87b0de1 100644
--- a/src/MySqlConnector/Core/SchemaProvider.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.cs
@@ -103,12 +103,12 @@ private static void DoFillReservedWords(DataTable dataTable)
 	{
 		// Note:
 		// For MySQL 8.0, the INFORMATION_SCHEMA.KEYWORDS table could be used to load the list at runtime,
-		// unfortunately this bug https://bugs.mysql.com/bug.php?id=90160 makes it impratical to do it
+		// unfortunately this bug https://bugs.mysql.com/bug.php?id=90160 makes it impractical to do it
 		// (the bug is marked as fixed in MySQL 8.0.13, not published yet at the time of writing this note).
 		//
 		// Note:
 		// Once the previously mentioned bug will be fixed, for versions >= 8.0.13 reserved words could be
-		// loaded at runtime form INFORMATION_SCHEMA.KEYWORDS, and for other versions the hard coded list
+		// loaded at runtime from INFORMATION_SCHEMA.KEYWORDS, and for other versions the hard coded list
 		// could be used (notice the list could change with the release, adopting the 8.0.12 list is a
 		// suboptimal one-size-fits-it-all solution.
 		// To get the current MySQL version at runtime one could query SELECT VERSION(); which returns a

From 016657a13a7d5859ab3c9d60d1fc3b7d204af1d7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 12 Aug 2023 20:41:21 -0700
Subject: [PATCH 143/393] Avoid Task<MySqlDataReader> allocation on sync path.

---
 src/MySqlConnector/Core/CommandExecutor.cs |  2 +-
 src/MySqlConnector/MySqlBatch.cs           |  8 +++++---
 src/MySqlConnector/MySqlCommand.cs         | 22 +++++++++++++++-------
 3 files changed, 21 insertions(+), 11 deletions(-)

diff --git a/src/MySqlConnector/Core/CommandExecutor.cs b/src/MySqlConnector/Core/CommandExecutor.cs
index 752329a4b..0434a8314 100644
--- a/src/MySqlConnector/Core/CommandExecutor.cs
+++ b/src/MySqlConnector/Core/CommandExecutor.cs
@@ -8,7 +8,7 @@ namespace MySqlConnector.Core;
 
 internal static class CommandExecutor
 {
-	public static async Task<MySqlDataReader> ExecuteReaderAsync(IReadOnlyList<IMySqlCommand> commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	public static async ValueTask<MySqlDataReader> ExecuteReaderAsync(IReadOnlyList<IMySqlCommand> commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		try
 		{
diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index f7a0257bc..1f1ab42f6 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -134,7 +134,9 @@ private DbDataReader ExecuteDbDataReader(CommandBehavior behavior)
 #endif
 	{
 		this.ResetCommandTimeout();
-		return ExecuteReaderAsync(behavior, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
+#pragma warning disable CA2012 // OK to read .Result because the ValueTask is completed
+		return ExecuteReaderAsync(behavior, IOBehavior.Synchronous, CancellationToken.None).Result;
+#pragma warning restore CA2012
 	}
 
 #if NET6_0_OR_GREATER
@@ -148,10 +150,10 @@ private async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavi
 		return await ExecuteReaderAsync(behavior, AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
 	}
 
-	private Task<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	private ValueTask<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		if (!IsValid(out var exception))
-			return Task.FromException<MySqlDataReader>(exception);
+			return new ValueTask<MySqlDataReader>(Task.FromException<MySqlDataReader>(exception));
 
 		CurrentCommandBehavior = behavior;
 		foreach (MySqlBatchCommand batchCommand in BatchCommands)
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index a933a8935..aa3b7db88 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -109,9 +109,11 @@ private MySqlCommand(MySqlCommand other)
 	/// <inheritdoc/>
 	public override object? ExecuteScalar() => ExecuteScalarAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 
-	public new MySqlDataReader ExecuteReader() => ExecuteReaderAsync(default, IOBehavior.Synchronous, default).GetAwaiter().GetResult();
+#pragma warning disable CA2012 // OK to read .Result because the ValueTask is completed
+	public new MySqlDataReader ExecuteReader() => ExecuteReaderAsync(default, IOBehavior.Synchronous, default).Result;
 
 	public new MySqlDataReader ExecuteReader(CommandBehavior commandBehavior) => ExecuteReaderAsync(commandBehavior, IOBehavior.Synchronous, default).GetAwaiter().GetResult();
+#pragma warning restore CA2012
 
 	/// <inheritdoc/>
 	public override void Prepare()
@@ -278,8 +280,10 @@ protected override DbTransaction? DbTransaction
 
 	protected override DbParameter CreateDbParameter() => new MySqlParameter();
 
+#pragma warning disable CA2012 // OK to read .Result because the ValueTask is completed
 	protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) =>
-		ExecuteReaderAsync(behavior, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
+		ExecuteReaderAsync(behavior, IOBehavior.Synchronous, CancellationToken.None).Result;
+#pragma warning restore CA2012
 
 	/// <summary>
 	/// Executes this command asynchronously on the associated <see cref="MySqlConnection"/>.
@@ -332,15 +336,15 @@ internal async Task<int> ExecuteNonQueryAsync(IOBehavior ioBehavior, Cancellatio
 	}
 
 	public new Task<MySqlDataReader> ExecuteReaderAsync(CancellationToken cancellationToken = default) =>
-		ExecuteReaderAsync(default, AsyncIOBehavior, cancellationToken);
+		ExecuteReaderAsync(default, AsyncIOBehavior, cancellationToken).AsTask();
 
 	public new Task<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken = default) =>
-		ExecuteReaderAsync(behavior, AsyncIOBehavior, cancellationToken);
+		ExecuteReaderAsync(behavior, AsyncIOBehavior, cancellationToken).AsTask();
 
 	protected override async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken) =>
 		await ExecuteReaderAsync(behavior, AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
 
-	internal async Task<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	internal async ValueTask<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		Volatile.Write(ref m_commandTimedOut, false);
 		this.ResetCommandTimeout();
@@ -348,10 +352,14 @@ internal async Task<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior
 		return await ExecuteReaderNoResetTimeoutAsync(behavior, ioBehavior, cancellationToken).ConfigureAwait(false);
 	}
 
-	internal Task<MySqlDataReader> ExecuteReaderNoResetTimeoutAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	internal ValueTask<MySqlDataReader> ExecuteReaderNoResetTimeoutAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		if (!IsValid(out var exception))
-			return Task.FromException<MySqlDataReader>(exception);
+#if NET5_0_OR_GREATER
+			return ValueTask.FromException<MySqlDataReader>(exception);
+#else
+			return new ValueTask<MySqlDataReader>(Task.FromException<MySqlDataReader>(exception));
+#endif
 
 		var activity = NoActivity ? null : Connection!.Session.StartActivity(ActivitySourceHelper.ExecuteActivityName,
 			ActivitySourceHelper.DatabaseStatementTagName, CommandText);

From 7c4f7270f1af0e8ca5bb2d39585c303823c6d5c9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 12 Aug 2023 22:34:42 -0700
Subject: [PATCH 144/393] Replace two array allocations with just one.

This probably also improves data locality.
---
 src/MySqlConnector/Core/Row.cs | 81 ++++++++++++++++++++--------------
 1 file changed, 48 insertions(+), 33 deletions(-)

diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 2d8a0d966..e5b2c06f0 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -16,8 +16,7 @@ public Row(bool isBinary, ResultSet resultSet)
 		ResultSet = resultSet;
 
 		var columnDefinitions = ResultSet.ColumnDefinitions!;
-		m_dataOffsets = new int[columnDefinitions.Length];
-		m_dataLengths = new int[columnDefinitions.Length];
+		m_dataOffsetLengths = new OffsetLength[columnDefinitions.Length];
 		m_columnReaders = new ColumnReader[columnDefinitions.Length];
 		for (var column = 0; column < m_columnReaders.Length; column++)
 			m_columnReaders[column] = ColumnReader.Create(isBinary, columnDefinitions[column], resultSet.Connection);
@@ -28,27 +27,23 @@ public void SetData(ReadOnlyMemory<byte> data)
 		m_data = data;
 		if (m_isBinary)
 		{
-			Array.Clear(m_dataOffsets, 0, m_dataOffsets.Length);
-			for (var column = 0; column < m_dataOffsets.Length; column++)
+			Array.Clear(m_dataOffsetLengths, 0, m_dataOffsetLengths.Length);
+			for (var column = 0; column < m_dataOffsetLengths.Length; column++)
 			{
 				if ((data.Span[(column + 2) / 8 + 1] & (1 << ((column + 2) % 8))) != 0)
 				{
 					// column is NULL
-					m_dataOffsets[column] = -1;
+					m_dataOffsetLengths[column] = (-1, 0);
 				}
 			}
 
 			var reader = new ByteArrayReader(data.Span);
 
 			// skip packet header (1 byte) and NULL bitmap (formula for length at https://dev.mysql.com/doc/internals/en/null-bitmap.html)
-			reader.Offset += 1 + (m_dataOffsets.Length + 7 + 2) / 8;
-			for (var column = 0; column < m_dataOffsets.Length; column++)
+			reader.Offset += 1 + (m_dataOffsetLengths.Length + 7 + 2) / 8;
+			for (var column = 0; column < m_dataOffsetLengths.Length; column++)
 			{
-				if (m_dataOffsets[column] == -1)
-				{
-					m_dataLengths[column] = 0;
-				}
-				else
+				if (m_dataOffsetLengths[column].Offset != -1)
 				{
 					var columnDefinition = ResultSet.ColumnDefinitions![column];
 					var length = columnDefinition.ColumnType switch
@@ -62,22 +57,19 @@ public void SetData(ReadOnlyMemory<byte> data)
 						_ => checked((int) reader.ReadLengthEncodedInteger()),
 					};
 
-					m_dataLengths[column] = length;
-					m_dataOffsets[column] = reader.Offset;
+					m_dataOffsetLengths[column] = (reader.Offset, length);
+					reader.Offset += length;
 				}
-
-				reader.Offset += m_dataLengths[column];
 			}
 		}
 		else
 		{
 			var reader = new ByteArrayReader(data.Span);
-			for (var column = 0; column < m_dataOffsets.Length; column++)
+			for (var column = 0; column < m_dataOffsetLengths.Length; column++)
 			{
 				var length = reader.ReadLengthEncodedIntegerOrNull();
-				m_dataLengths[column] = length == -1 ? 0 : length;
-				m_dataOffsets[column] = length == -1 ? -1 : reader.Offset;
-				reader.Offset += m_dataLengths[column];
+				m_dataOffsetLengths[column] = length == -1 ? (-1, 0) : (reader.Offset, length);
+				reader.Offset += m_dataOffsetLengths[column].Length;
 			}
 		}
 	}
@@ -87,10 +79,11 @@ public object GetValue(int ordinal)
 		if (ordinal < 0 || ordinal >= ResultSet.ColumnDefinitions!.Length)
 			throw new ArgumentOutOfRangeException(nameof(ordinal), $"value must be between 0 and {ResultSet.ColumnDefinitions!.Length - 1}");
 
-		if (m_dataOffsets[ordinal] == -1)
+		if (m_dataOffsetLengths[ordinal].Offset == -1)
 			return DBNull.Value;
 
-		var data = m_data.Slice(m_dataOffsets[ordinal], m_dataLengths[ordinal]).Span;
+		var (offset, length) = m_dataOffsetLengths[ordinal];
+		var data = m_data.Slice(offset, length).Span;
 		var columnDefinition = ResultSet.ColumnDefinitions[ordinal];
 		return m_columnReaders[ordinal].ReadValue(data, columnDefinition);
 	}
@@ -160,15 +153,15 @@ public long GetBytes(int ordinal, long dataOffset, byte[]? buffer, int bufferOff
 		{
 			// this isn't required by the DbDataReader.GetBytes API documentation, but is what mysql-connector-net does
 			// (as does SqlDataReader: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.getbytes.aspx)
-			return m_dataLengths[ordinal];
+			return m_dataOffsetLengths[ordinal].Length;
 		}
 
 		CheckBufferArguments(dataOffset, buffer, bufferOffset, length);
 
 		var offset = (int) dataOffset;
-		var lengthToCopy = Math.Max(0, Math.Min(m_dataLengths[ordinal] - offset, length));
+		var lengthToCopy = Math.Max(0, Math.Min(m_dataOffsetLengths[ordinal].Length - offset, length));
 		if (lengthToCopy > 0)
-			m_data.Slice(m_dataOffsets[ordinal] + offset, lengthToCopy).Span.CopyTo(buffer.AsSpan(bufferOffset));
+			m_data.Slice(m_dataOffsetLengths[ordinal].Offset + offset, lengthToCopy).Span.CopyTo(buffer.AsSpan(bufferOffset));
 		return lengthToCopy;
 	}
 
@@ -238,10 +231,11 @@ public int GetInt32(int ordinal)
 	{
 		if (ordinal < 0 || ordinal >= ResultSet.ColumnDefinitions!.Length)
 			throw new ArgumentOutOfRangeException(nameof(ordinal), $"value must be between 0 and {ResultSet.ColumnDefinitions!.Length - 1}");
-		if (m_dataOffsets[ordinal] == -1)
+		if (m_dataOffsetLengths[ordinal].Offset == -1)
 			throw new InvalidCastException("Can't convert NULL to Int32");
 
-		var data = m_data.Slice(m_dataOffsets[ordinal], m_dataLengths[ordinal]).Span;
+		var (offset, length) = m_dataOffsetLengths[ordinal];
+		var data = m_data.Slice(offset, length).Span;
 		var columnDefinition = ResultSet.ColumnDefinitions[ordinal];
 		if (m_columnReaders[ordinal].TryReadInt32(data, columnDefinition) is { } value)
 			return value;
@@ -347,8 +341,9 @@ public DateTime GetDateTime(int ordinal)
 	public Stream GetStream(int ordinal)
 	{
 		CheckBinaryColumn(ordinal);
+		var (offset, length) = m_dataOffsetLengths[ordinal];
 		return MemoryMarshal.TryGetArray(m_data, out var arraySegment) ?
-			new MemoryStream(arraySegment.Array!, arraySegment.Offset + m_dataOffsets[ordinal], m_dataLengths[ordinal], writable: false) :
+			new MemoryStream(arraySegment.Array!, arraySegment.Offset + offset, length, writable: false) :
 			throw new InvalidOperationException("Can't get underlying array.");
 	}
 
@@ -413,7 +408,8 @@ public MySqlDecimal GetMySqlDecimal(int ordinal)
 	{
 		if (IsDBNull(ordinal))
 			return (MySqlDecimal) GetValue(ordinal);
-		var data = m_data.Slice(m_dataOffsets[ordinal], m_dataLengths[ordinal]).Span;
+		var (offset, length) = m_dataOffsetLengths[ordinal];
+		var data = m_data.Slice(offset, length).Span;
 		var columnType = ResultSet.ColumnDefinitions![ordinal].ColumnType;
 		if (columnType is ColumnType.NewDecimal or ColumnType.Decimal)
 			return new MySqlDecimal(Encoding.UTF8.GetString(data));
@@ -428,7 +424,7 @@ public int GetValues(object[] values)
 		return count;
 	}
 
-	public bool IsDBNull(int ordinal) => m_dataOffsets[ordinal] == -1;
+	public bool IsDBNull(int ordinal) => m_dataOffsetLengths[ordinal].Offset == -1;
 
 	public object this[int ordinal] => GetValue(ordinal);
 
@@ -439,7 +435,7 @@ public int GetValues(object[] values)
 
 	private void CheckBinaryColumn(int ordinal)
 	{
-		if (m_dataOffsets[ordinal] == -1)
+		if (m_dataOffsetLengths[ordinal].Offset == -1)
 			throw new InvalidCastException("Column is NULL.");
 
 		var column = ResultSet.ColumnDefinitions![ordinal];
@@ -469,9 +465,28 @@ private static void CheckBufferArguments<T>(long dataOffset, T[] buffer, int buf
 			throw new ArgumentException(nameof(bufferOffset) + " + " + nameof(length) + " cannot exceed " + nameof(buffer) + "." + nameof(buffer.Length), nameof(length));
 	}
 
+	private readonly struct OffsetLength
+	{
+		public OffsetLength(int offset, int length)
+		{
+			Offset = offset;
+			Length = length;
+		}
+
+		public static implicit operator OffsetLength((int Offset, int Length) x) => new(x.Offset, x.Length);
+
+		public void Deconstruct(out int offset, out int length)
+		{
+			offset = Offset;
+			length = Length;
+		}
+
+		public int Offset { get; }
+		public int Length { get; }
+	}
+
 	private readonly bool m_isBinary;
-	private readonly int[] m_dataOffsets;
-	private readonly int[] m_dataLengths;
+	private readonly OffsetLength[] m_dataOffsetLengths;
 	private readonly ColumnReader[] m_columnReaders;
 	private ReadOnlyMemory<byte> m_data;
 }

From 845461cd08e4328fb6e30a3492c700809d47b704 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 13 Aug 2023 07:25:51 -0700
Subject: [PATCH 145/393] Avoid Task allocation on sync path.

---
 src/MySqlConnector/MySqlCommand.cs | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index aa3b7db88..c0d158543 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -97,6 +97,7 @@ private MySqlCommand(MySqlCommand other)
 	/// <inheritdoc/>
 	public override void Cancel() => Connection?.Cancel(this, m_commandId, true);
 
+#pragma warning disable CA2012 // OK to read .Result because the ValueTask is completed
 	/// <summary>
 	/// Executes this command on the associated <see cref="MySqlConnection"/>.
 	/// </summary>
@@ -104,16 +105,14 @@ private MySqlCommand(MySqlCommand other)
 	/// <remarks>For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command.
 	/// For stored procedures, the return value is the number of rows affected by the last statement in the stored procedure,
 	/// or zero if the last statement is a SELECT. For all other types of statements, the return value is -1.</remarks>
-	public override int ExecuteNonQuery() => ExecuteNonQueryAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
+	public override int ExecuteNonQuery() => ExecuteNonQueryAsync(IOBehavior.Synchronous, CancellationToken.None).Result;
 
-	/// <inheritdoc/>
-	public override object? ExecuteScalar() => ExecuteScalarAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
+	public override object? ExecuteScalar() => ExecuteScalarAsync(IOBehavior.Synchronous, CancellationToken.None).Result;
 
-#pragma warning disable CA2012 // OK to read .Result because the ValueTask is completed
 	public new MySqlDataReader ExecuteReader() => ExecuteReaderAsync(default, IOBehavior.Synchronous, default).Result;
 
 	public new MySqlDataReader ExecuteReader(CommandBehavior commandBehavior) => ExecuteReaderAsync(commandBehavior, IOBehavior.Synchronous, default).GetAwaiter().GetResult();
-#pragma warning restore CA2012
+#pragma warning restore CA2012 // OK to read .Result because the ValueTask is completed
 
 	/// <inheritdoc/>
 	public override void Prepare()
@@ -294,9 +293,9 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) =>
 	/// For stored procedures, the return value is the number of rows affected by the last statement in the stored procedure,
 	/// or zero if the last statement is a SELECT. For all other types of statements, the return value is -1.</remarks>
 	public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken) =>
-		ExecuteNonQueryAsync(AsyncIOBehavior, cancellationToken);
+		ExecuteNonQueryAsync(AsyncIOBehavior, cancellationToken).AsTask();
 
-	internal async Task<int> ExecuteNonQueryAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
+	internal async ValueTask<int> ExecuteNonQueryAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		Volatile.Write(ref m_commandTimedOut, false);
 		this.ResetCommandTimeout();
@@ -312,9 +311,9 @@ internal async Task<int> ExecuteNonQueryAsync(IOBehavior ioBehavior, Cancellatio
 	}
 
 	public override Task<object?> ExecuteScalarAsync(CancellationToken cancellationToken) =>
-		ExecuteScalarAsync(AsyncIOBehavior, cancellationToken);
+		ExecuteScalarAsync(AsyncIOBehavior, cancellationToken).AsTask();
 
-	internal async Task<object?> ExecuteScalarAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
+	internal async ValueTask<object?> ExecuteScalarAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		Volatile.Write(ref m_commandTimedOut, false);
 		this.ResetCommandTimeout();

From 461def29b6cf11419cf81490fc124803d21de74f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 13 Aug 2023 07:45:22 -0700
Subject: [PATCH 146/393] Use one-shot UTF-8 encoding when possible.

This avoids allocating a EncoderNLS object and making multiple passes over the source data when the entire string could be converted at once. Additionally, UTF8Encoding.GetBytes is more likely to be highly optimized in the framework.
---
 src/MySqlConnector/MySqlParameter.cs          | 57 +++++++++++++++----
 .../Serialization/ByteBufferWriter.cs         | 17 +++++-
 2 files changed, 59 insertions(+), 15 deletions(-)

diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index dfda400f5..fd85c721a 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -197,15 +197,15 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		}
 		else if (Value is string stringValue)
 		{
-			WriteString(writer, noBackslashEscapes, writeDelimiters: true, stringValue.AsSpan());
+			WriteString(writer, noBackslashEscapes, stringValue.AsSpan());
 		}
 		else if (Value is ReadOnlyMemory<char> readOnlyMemoryChar)
 		{
-			WriteString(writer, noBackslashEscapes, writeDelimiters: true, readOnlyMemoryChar.Span);
+			WriteString(writer, noBackslashEscapes, readOnlyMemoryChar.Span);
 		}
 		else if (Value is Memory<char> memoryChar)
 		{
-			WriteString(writer, noBackslashEscapes, writeDelimiters: true, memoryChar.Span);
+			WriteString(writer, noBackslashEscapes, memoryChar.Span);
 		}
 		else if (Value is char charValue)
 		{
@@ -447,12 +447,12 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 #if NETCOREAPP3_1_OR_GREATER
 			writer.Write((byte) '\'');
 			foreach (var chunk in stringBuilder.GetChunks())
-				WriteString(writer, noBackslashEscapes, writeDelimiters: false, chunk.Span);
+				WriteStringChunk(writer, noBackslashEscapes, chunk.Span);
 			if (stringBuilder.Length != 0)
 				writer.Write("".AsSpan(), flush: true);
 			writer.Write((byte) '\'');
 #else
-			WriteString(writer, noBackslashEscapes, writeDelimiters: true, stringBuilder.ToString().AsSpan());
+			WriteString(writer, noBackslashEscapes, stringBuilder.ToString().AsSpan());
 #endif
 		}
 		else if (MySqlDbType == MySqlDbType.Int16)
@@ -494,10 +494,9 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 			throw new NotSupportedException($"Parameter type {Value.GetType().Name} is not supported; see https://fl.vu/mysql-param-type. Value: {Value}");
 		}
 
-		static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, bool writeDelimiters, ReadOnlySpan<char> value)
+		static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, ReadOnlySpan<char> value)
 		{
-			if (writeDelimiters)
-				writer.Write((byte) '\'');
+			writer.Write((byte) '\'');
 
 			var charsWritten = 0;
 			while (charsWritten < value.Length)
@@ -507,13 +506,13 @@ static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, bool w
 				if (nextDelimiterIndex == -1)
 				{
 					// write the rest of the string
-					writer.Write(remainingValue, flush: writeDelimiters);
+					writer.Write(remainingValue);
 					charsWritten += remainingValue.Length;
 				}
 				else
 				{
 					// write up to (and including) the delimiter, then double it
-					writer.Write(remainingValue[..nextDelimiterIndex], flush: true);
+					writer.Write(remainingValue[..nextDelimiterIndex]);
 					if (remainingValue[nextDelimiterIndex] == '\\' && !noBackslashEscapes)
 						writer.Write((ushort) 0x5C5C); // \\
 					else if (remainingValue[nextDelimiterIndex] == '\\' && noBackslashEscapes)
@@ -528,9 +527,43 @@ static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, bool w
 				}
 			}
 
-			if (writeDelimiters)
-				writer.Write((byte) '\'');
+			writer.Write((byte) '\'');
 		}
+
+#if NETCOREAPP3_1_OR_GREATER
+		// Writes a partial chunk of a string (that may end with half of a surrogate pair), escaping any delimiter characters.
+		static void WriteStringChunk(ByteBufferWriter writer, bool noBackslashEscapes, ReadOnlySpan<char> value)
+		{
+			var charsWritten = 0;
+			while (charsWritten < value.Length)
+			{
+				var remainingValue = value[charsWritten..];
+				var nextDelimiterIndex = remainingValue.IndexOfAny('\0', '\'', '\\');
+				if (nextDelimiterIndex == -1)
+				{
+					// write the rest of the string
+					writer.Write(remainingValue, flush: false);
+					charsWritten += remainingValue.Length;
+				}
+				else
+				{
+					// write up to (and including) the delimiter, then double it
+					writer.Write(remainingValue[..nextDelimiterIndex], flush: true);
+					if (remainingValue[nextDelimiterIndex] == '\\' && !noBackslashEscapes)
+						writer.Write((ushort) 0x5C5C); // \\
+					else if (remainingValue[nextDelimiterIndex] == '\\' && noBackslashEscapes)
+						writer.Write((byte) 0x5C); // \
+					else if (remainingValue[nextDelimiterIndex] == '\'')
+						writer.Write((ushort) 0x2727); // ''
+					else if (remainingValue[nextDelimiterIndex] == '\0' && !noBackslashEscapes)
+						writer.Write((ushort) 0x305C); // \0
+					else if (remainingValue[nextDelimiterIndex] == '\0' && noBackslashEscapes)
+						writer.Write((byte) 0x00); // (nul)
+					charsWritten += nextDelimiterIndex + 1;
+				}
+			}
+		}
+#endif
 	}
 
 	internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions options)
diff --git a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
index 5819aa2ab..aabcae82a 100644
--- a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
@@ -99,11 +99,22 @@ public void Write(ReadOnlySpan<byte> span)
 		m_output = m_output[span.Length..];
 	}
 
-	public void Write(string value) => Write(value.AsSpan(), flush: true);
+	public void Write(string value) => Write(value.AsSpan());
 
 	public void WriteAscii(string value) => WriteAscii(value.AsSpan());
 
-	public void Write(string value, int offset, int length) => Write(value.AsSpan(offset, length), flush: true);
+	public void Write(string value, int offset, int length) => Write(value.AsSpan(offset, length));
+
+	public void Write(ReadOnlySpan<char> chars)
+	{
+		if (m_output.Length < chars.Length * 3)
+		{
+			var neededBytes = Encoding.UTF8.GetByteCount(chars);
+			if (m_output.Length < neededBytes)
+				Reallocate(neededBytes);
+		}
+		m_output = m_output[Encoding.UTF8.GetBytes(chars, m_output.Span)..];
+	}
 
 	public void Write(ReadOnlySpan<char> chars, bool flush)
 	{
@@ -273,7 +284,7 @@ public static void WriteLengthEncodedString(this ByteBufferWriter writer, ReadOn
 	{
 		var byteCount = Encoding.UTF8.GetByteCount(value);
 		writer.WriteLengthEncodedInteger((ulong) byteCount);
-		writer.Write(value, flush: true);
+		writer.Write(value);
 	}
 
 	public static void WriteLengthEncodedAsciiString(this ByteBufferWriter writer, string value)

From 3fd4d043bc6afdf5dd357f3ea482ae4a7c82f41b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 13 Aug 2023 08:22:44 -0700
Subject: [PATCH 147/393] Eliminate allocations when serializing binary GUIDs.

---
 src/MySqlConnector/MySqlParameter.cs    | 10 ++++++++++
 src/MySqlConnector/Utilities/Utility.cs |  2 +-
 2 files changed, 11 insertions(+), 1 deletion(-)

diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index fd85c721a..148f3edcb 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -404,7 +404,12 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 			StatementPreparerOptions guidOptions = options & StatementPreparerOptions.GuidFormatMask;
 			if (guidOptions is StatementPreparerOptions.GuidFormatBinary16 or StatementPreparerOptions.GuidFormatTimeSwapBinary16 or StatementPreparerOptions.GuidFormatLittleEndianBinary16)
 			{
+#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
+				Span<byte> bytes = stackalloc byte[16];
+				guidValue.TryWriteBytes(bytes);
+#else
 				var bytes = guidValue.ToByteArray();
+#endif
 				if (guidOptions != StatementPreparerOptions.GuidFormatLittleEndianBinary16)
 				{
 					Utility.SwapBytes(bytes, 0, 3);
@@ -710,7 +715,12 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
 			StatementPreparerOptions guidOptions = options & StatementPreparerOptions.GuidFormatMask;
 			if (guidOptions is StatementPreparerOptions.GuidFormatBinary16 or StatementPreparerOptions.GuidFormatTimeSwapBinary16 or StatementPreparerOptions.GuidFormatLittleEndianBinary16)
 			{
+#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
+				Span<byte> bytes = stackalloc byte[16];
+				guidValue.TryWriteBytes(bytes);
+#else
 				var bytes = guidValue.ToByteArray();
+#endif
 				if (guidOptions != StatementPreparerOptions.GuidFormatLittleEndianBinary16)
 				{
 					Utility.SwapBytes(bytes, 0, 3);
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 8db3cf75b..6c4e53b9e 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -510,7 +510,7 @@ public static Task WriteAsync(this Stream stream, ReadOnlyMemory<byte> data)
 	public static void Write(this Stream stream, ReadOnlyMemory<byte> data) => stream.Write(data.Span);
 #endif
 
-	public static void SwapBytes(byte[] bytes, int offset1, int offset2)
+	public static void SwapBytes(Span<byte> bytes, int offset1, int offset2)
 	{
 		byte swap = bytes[offset1];
 		bytes[offset1] = bytes[offset2];

From ecbfccf1c90ebe2e813cef2181792533cf8030f0 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 13 Aug 2023 08:23:16 -0700
Subject: [PATCH 148/393] Optimize the implementation of SwapBytes.

---
 src/MySqlConnector/Utilities/Utility.cs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 6c4e53b9e..c4b9b30d1 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -3,6 +3,7 @@
 using System.Diagnostics.CodeAnalysis;
 using System.Net;
 using System.Reflection;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Security.Authentication;
 using System.Security.Cryptography;
@@ -512,9 +513,8 @@ public static Task WriteAsync(this Stream stream, ReadOnlyMemory<byte> data)
 
 	public static void SwapBytes(Span<byte> bytes, int offset1, int offset2)
 	{
-		byte swap = bytes[offset1];
-		bytes[offset1] = bytes[offset2];
-		bytes[offset2] = swap;
+		ref var first = ref Unsafe.AsRef(bytes[0]);
+		(Unsafe.Add(ref first, offset2), Unsafe.Add(ref first, offset1)) = (Unsafe.Add(ref first, offset1), Unsafe.Add(ref first, offset2));
 	}
 
 #if NET462

From beebf68574c560cddf420b8b05a4c1e662b00367 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 13 Aug 2023 12:01:34 -0700
Subject: [PATCH 149/393] Use ValueTaskExtensions to create ValueTask for
 exception.

The non-net5.0 methods aren't implemented any differently from the framework implementations, but this moves in the direction of eventually being able to deprecate the custom methods.
---
 src/MySqlConnector/MySqlBatch.cs               |  2 +-
 src/MySqlConnector/MySqlCommand.cs             |  6 +-----
 .../Utilities/ValueTaskExtensions.cs           | 18 ++++++++++++++++--
 3 files changed, 18 insertions(+), 8 deletions(-)

diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index 1f1ab42f6..5ea46fd11 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -153,7 +153,7 @@ private async Task<DbDataReader> ExecuteDbDataReaderAsync(CommandBehavior behavi
 	private ValueTask<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		if (!IsValid(out var exception))
-			return new ValueTask<MySqlDataReader>(Task.FromException<MySqlDataReader>(exception));
+			return ValueTaskExtensions.FromException<MySqlDataReader>(exception);
 
 		CurrentCommandBehavior = behavior;
 		foreach (MySqlBatchCommand batchCommand in BatchCommands)
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index c0d158543..7de4a27d6 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -354,11 +354,7 @@ internal async ValueTask<MySqlDataReader> ExecuteReaderAsync(CommandBehavior beh
 	internal ValueTask<MySqlDataReader> ExecuteReaderNoResetTimeoutAsync(CommandBehavior behavior, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		if (!IsValid(out var exception))
-#if NET5_0_OR_GREATER
-			return ValueTask.FromException<MySqlDataReader>(exception);
-#else
-			return new ValueTask<MySqlDataReader>(Task.FromException<MySqlDataReader>(exception));
-#endif
+			return ValueTaskExtensions.FromException<MySqlDataReader>(exception);
 
 		var activity = NoActivity ? null : Connection!.Session.StartActivity(ActivitySourceHelper.ExecuteActivityName,
 			ActivitySourceHelper.DatabaseStatementTagName, CommandText);
diff --git a/src/MySqlConnector/Utilities/ValueTaskExtensions.cs b/src/MySqlConnector/Utilities/ValueTaskExtensions.cs
index 7baf4e620..77c10c876 100644
--- a/src/MySqlConnector/Utilities/ValueTaskExtensions.cs
+++ b/src/MySqlConnector/Utilities/ValueTaskExtensions.cs
@@ -1,8 +1,22 @@
+using System.Runtime.CompilerServices;
+
 namespace MySqlConnector.Utilities;
 
 internal static class ValueTaskExtensions
 {
-	public static ValueTask FromException(Exception exception) => new(Task.FromException(exception));
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	public static ValueTask FromException(Exception exception) =>
+#if NET5_0_OR_GREATER
+		ValueTask.FromException(exception);
+#else
+		new(Task.FromException(exception));
+#endif
 
-	public static ValueTask<T> FromException<T>(Exception exception) => new(Task.FromException<T>(exception));
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	public static ValueTask<T> FromException<T>(Exception exception) =>
+#if NET5_0_OR_GREATER
+		ValueTask.FromException<T>(exception);
+#else
+		new(Task.FromException<T>(exception));
+#endif
 }

From f4e10acfcc1146f782498a843e5e0ed6253054bf Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 13 Aug 2023 13:24:48 -0700
Subject: [PATCH 150/393] Remove AddContinuation methods.

Instead of splitting the implementation across eight methods, including two static local methods (to try to create an optimal implementation), rely on the compiler creating an optimized async state machine. This reduces the number of async methods from two to one, reducing the number of async state machines that are allocated.
---
 .../Protocol/Serialization/ProtocolUtility.cs | 144 ++++++------------
 1 file changed, 46 insertions(+), 98 deletions(-)

diff --git a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
index c8ee11029..9736004dc 100644
--- a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
@@ -412,117 +412,65 @@ public static int GetBytesPerCharacter(CharacterSet characterSet)
 		}
 	}
 
-	private static ValueTask<Packet> ReadPacketAsync(BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func<int> getNextSequenceNumber, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior)
+	public static async ValueTask<ArraySegment<byte>> ReadPayloadAsync(BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func<int> getNextSequenceNumber, ArraySegmentHolder<byte> previousPayloads, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior)
 	{
-		var headerBytesTask = bufferedByteReader.ReadBytesAsync(byteHandler, 4, ioBehavior);
-		if (headerBytesTask.IsCompletedSuccessfully)
-			return ReadPacketAfterHeader(headerBytesTask.Result, bufferedByteReader, byteHandler, getNextSequenceNumber, protocolErrorBehavior, ioBehavior);
-		return AddContinuation(headerBytesTask, bufferedByteReader, byteHandler, getNextSequenceNumber, protocolErrorBehavior, ioBehavior);
-
-		static async ValueTask<Packet> AddContinuation(ValueTask<ArraySegment<byte>> headerBytes, BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func<int> getNextSequenceNumber, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior) =>
-			await ReadPacketAfterHeader(await headerBytes.ConfigureAwait(false), bufferedByteReader, byteHandler, getNextSequenceNumber, protocolErrorBehavior, ioBehavior).ConfigureAwait(false);
-	}
-
-	private static ValueTask<Packet> ReadPacketAfterHeader(ReadOnlySpan<byte> headerBytes, BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func<int> getNextSequenceNumber, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior)
-	{
-		if (headerBytes.Length < 4)
+		previousPayloads.Clear();
+		while (true)
 		{
-			return protocolErrorBehavior == ProtocolErrorBehavior.Ignore ? default :
-				ValueTaskExtensions.FromException<Packet>(new EndOfStreamException($"Expected to read 4 header bytes but only received {headerBytes.Length:d}."));
-		}
-
-		var payloadLength = (int) SerializationUtility.ReadUInt32(headerBytes[..3]);
-		int packetSequenceNumber = headerBytes[3];
-
-		Exception? packetOutOfOrderException = null;
-		var expectedSequenceNumber = getNextSequenceNumber() % 256;
-		if (expectedSequenceNumber != -1 && packetSequenceNumber != expectedSequenceNumber)
-			packetOutOfOrderException = MySqlProtocolException.CreateForPacketOutOfOrder(expectedSequenceNumber, packetSequenceNumber);
-
-		var payloadBytesTask = bufferedByteReader.ReadBytesAsync(byteHandler, payloadLength, ioBehavior);
-		if (payloadBytesTask.IsCompletedSuccessfully)
-			return CreatePacketFromPayload(payloadBytesTask.Result, payloadLength, protocolErrorBehavior, packetOutOfOrderException);
-		return AddContinuation(payloadBytesTask, payloadLength, protocolErrorBehavior, packetOutOfOrderException);
+			// read the packet header
+			var headerBytes = await bufferedByteReader.ReadBytesAsync(byteHandler, 4, ioBehavior).ConfigureAwait(false);
+			if (headerBytes.Count < 4)
+			{
+				return protocolErrorBehavior == ProtocolErrorBehavior.Ignore ? default :
+					throw new EndOfStreamException($"Expected to read 4 header bytes but only received {headerBytes.Count:d}.");
+			}
 
-		static async ValueTask<Packet> AddContinuation(ValueTask<ArraySegment<byte>> payloadBytesTask, int payloadLength, ProtocolErrorBehavior protocolErrorBehavior, Exception? packetOutOfOrderException) =>
-			await CreatePacketFromPayload(await payloadBytesTask.ConfigureAwait(false), payloadLength, protocolErrorBehavior, packetOutOfOrderException).ConfigureAwait(false);
-	}
+			// read values from the header before the memory is potentially overwritten by ReadBytesAsync
+			var payloadLength = (int) SerializationUtility.ReadUInt32(headerBytes.AsSpan()[..3]);
+			int packetSequenceNumber = headerBytes.AsSpan()[3];
+			var expectedSequenceNumber = getNextSequenceNumber() % 256;
 
-	private static ValueTask<Packet> CreatePacketFromPayload(ArraySegment<byte> payloadBytes, int payloadLength, ProtocolErrorBehavior protocolErrorBehavior, Exception? exception)
-	{
-		if (exception is not null)
-		{
-			if (protocolErrorBehavior == ProtocolErrorBehavior.Ignore)
-				return default;
+			// read the packet payload
+			var payloadBytes = await bufferedByteReader.ReadBytesAsync(byteHandler, payloadLength, ioBehavior).ConfigureAwait(false);
 
+			Packet packet;
+			if (expectedSequenceNumber != -1 && packetSequenceNumber != expectedSequenceNumber)
+			{
+				if (protocolErrorBehavior == ProtocolErrorBehavior.Ignore)
+					packet = default;
 #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
-			if (payloadBytes is [ ErrorPayload.Signature, .. ])
+				else if (payloadBytes is [ ErrorPayload.Signature, .. ])
 #else
-			if (payloadBytes.Count > 0 && payloadBytes.AsSpan()[0] == ErrorPayload.Signature)
+				else if (payloadBytes.Count > 0 && payloadBytes.AsSpan()[0] == ErrorPayload.Signature)
 #endif
-				return new ValueTask<Packet>(new Packet(payloadBytes));
-
-			return ValueTaskExtensions.FromException<Packet>(exception);
-		}
-
-		return payloadBytes.Count >= payloadLength ? new ValueTask<Packet>(new Packet(payloadBytes)) :
-			protocolErrorBehavior == ProtocolErrorBehavior.Throw ? ValueTaskExtensions.FromException<Packet>(new EndOfStreamException($"Expected to read {payloadLength:d} payload bytes but only received {payloadBytes.Count:d}.")) :
-			default;
-	}
-
-	public static ValueTask<ArraySegment<byte>> ReadPayloadAsync(BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func<int> getNextSequenceNumber, ArraySegmentHolder<byte> cache, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior)
-	{
-		cache.Clear();
-		return DoReadPayloadAsync(bufferedByteReader, byteHandler, getNextSequenceNumber, cache, protocolErrorBehavior, ioBehavior);
-	}
-
-	private static ValueTask<ArraySegment<byte>> DoReadPayloadAsync(BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func<int> getNextSequenceNumber, ArraySegmentHolder<byte> previousPayloads, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior)
-	{
-		var readPacketTask = ReadPacketAsync(bufferedByteReader, byteHandler, getNextSequenceNumber, protocolErrorBehavior, ioBehavior);
-		while (readPacketTask.IsCompletedSuccessfully)
-		{
-			if (HasReadPayload(previousPayloads, readPacketTask.Result, out var result))
-				return result;
-
-			readPacketTask = ReadPacketAsync(bufferedByteReader, byteHandler, getNextSequenceNumber, protocolErrorBehavior, ioBehavior);
-		}
-
-		return AddContinuation(readPacketTask, bufferedByteReader, byteHandler, getNextSequenceNumber, previousPayloads, protocolErrorBehavior, ioBehavior);
-
-		static async ValueTask<ArraySegment<byte>> AddContinuation(ValueTask<Packet> readPacketTask, BufferedByteReader bufferedByteReader, IByteHandler byteHandler, Func<int> getNextSequenceNumber, ArraySegmentHolder<byte> previousPayloads, ProtocolErrorBehavior protocolErrorBehavior, IOBehavior ioBehavior)
-		{
-			var packet = await readPacketTask.ConfigureAwait(false);
-			var resultTask = HasReadPayload(previousPayloads, packet, out var result) ? result :
-				DoReadPayloadAsync(bufferedByteReader, byteHandler, getNextSequenceNumber, previousPayloads, protocolErrorBehavior, ioBehavior);
-			return await resultTask.ConfigureAwait(false);
-		}
-	}
+					packet = new(payloadBytes);
+				else
+					throw MySqlProtocolException.CreateForPacketOutOfOrder(expectedSequenceNumber, packetSequenceNumber);
+			}
+			else
+			{
+				packet = payloadBytes.Count >= payloadLength ? new(payloadBytes) :
+					protocolErrorBehavior == ProtocolErrorBehavior.Throw ? throw new EndOfStreamException($"Expected to read {payloadLength:d} payload bytes but only received {payloadBytes.Count:d}.") :
+					default;
+			}
 
-	private static bool HasReadPayload(ArraySegmentHolder<byte> previousPayloads, Packet packet, out ValueTask<ArraySegment<byte>> result)
-	{
-		if (previousPayloads.Count == 0 && packet.Contents.Count < MaxPacketSize)
-		{
-			result = new(packet.Contents);
-			return true;
-		}
+			// if this is a complete packet, return it
+			if (previousPayloads.Count == 0 && packet.Contents.Count < MaxPacketSize)
+				return packet.Contents;
 
-		var previousPayloadsArray = previousPayloads.Array;
-		if (previousPayloadsArray is null)
-			previousPayloadsArray = new byte[ProtocolUtility.MaxPacketSize + 1];
-		else if (previousPayloads.Offset + previousPayloads.Count + packet.Contents.Count > previousPayloadsArray.Length)
-			Array.Resize(ref previousPayloadsArray, previousPayloadsArray.Length * 2);
+			// resize the buffer of previous payloads if necessary, then append this payload to it
+			var previousPayloadsArray = previousPayloads.Array;
+			if (previousPayloadsArray is null)
+				previousPayloadsArray = new byte[ProtocolUtility.MaxPacketSize + 1];
+			else if (previousPayloads.Offset + previousPayloads.Count + packet.Contents.Count > previousPayloadsArray.Length)
+				Array.Resize(ref previousPayloadsArray, previousPayloadsArray.Length * 2);
 
-		Buffer.BlockCopy(packet.Contents.Array!, packet.Contents.Offset, previousPayloadsArray, previousPayloads.Offset + previousPayloads.Count, packet.Contents.Count);
-		previousPayloads.ArraySegment = new(previousPayloadsArray, previousPayloads.Offset, previousPayloads.Count + packet.Contents.Count);
+			packet.Contents.AsSpan().CopyTo(previousPayloadsArray.AsSpan(previousPayloads.Offset + previousPayloads.Count));
+			previousPayloads.ArraySegment = new(previousPayloadsArray, previousPayloads.Offset, previousPayloads.Count + packet.Contents.Count);
 
-		if (packet.Contents.Count < ProtocolUtility.MaxPacketSize)
-		{
-			result = new(previousPayloads.ArraySegment);
-			return true;
+			if (packet.Contents.Count < ProtocolUtility.MaxPacketSize)
+				return previousPayloads.ArraySegment;
 		}
-
-		result = default;
-		return false;
 	}
 
 	public static ValueTask WritePayloadAsync(IByteHandler byteHandler, Func<int> getNextSequenceNumber, ReadOnlyMemory<byte> payload, IOBehavior ioBehavior)

From f09893e7cc5d32fbb72ed5e12824bbefc370cc5f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 15 Aug 2023 08:18:38 -0700
Subject: [PATCH 151/393] Test a new MySql.Data bug.

---
 .../tutorials/migrating-from-connector-net.md       |  3 ++-
 .../MySqlConnectionStringBuilderTests.cs            | 13 +++++++++++++
 2 files changed, 15 insertions(+), 1 deletion(-)

diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 0df2bdb6a..46581e98f 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -324,7 +324,8 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * [#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.
 * ~~[#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.~~
 * ~~[#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'~~
-* [110717](https://bugs.mysql.com/bug.php?id=110717): Opening two MySqlConnections simultaneously can crash
+* [110717](https://bugs.mysql.com/bug.php?id=110717), [#111759](https://bugs.mysql.com/bug.php?id=111759): MySql.Data is not thread-safe
 * [110789](https://bugs.mysql.com/bug.php?id=110789): `OpenAsync` throws unhandled exception from thread pool
 * [110790](https://bugs.mysql.com/bug.php?id=110790): `ExecuteReaderAsync` hangs instead of cancelling query after `CommandTimeout`
 * [110791](https://bugs.mysql.com/bug.php?id=110791): `OpenAsync(CancellationToken)` doesn't throw for cancelled token
+* [110797](https://bugs.mysql.com/bug.php?id=110797): Certain sequence of special characters can break connection string validation.
diff --git a/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs b/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
index 708e9e417..9fbeab2c8 100644
--- a/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
@@ -583,4 +583,17 @@ public void NamedProperty(string propertyName, object value)
 			Assert.Equal(value, csb[propertyName]);
 		}
 	}
+
+	[Fact]
+	public void SpecialCharactersInPassword()
+	{
+		var builder = new MySqlConnectionStringBuilder
+		{
+			Password = "foo;=bar,baz",
+		};
+		Assert.Equal("Password=\"foo;=bar,baz\"", builder.ConnectionString, StringComparer.OrdinalIgnoreCase);
+#if !MYSQL_DATA // https://bugs.mysql.com/bug.php?id=111797
+		using var connection = new MySqlConnection(builder.ConnectionString);
+#endif
+	}
 }

From e4519e367fd8dcfffffa3684529eb9b74be41045 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 15 Aug 2023 10:22:16 -0700
Subject: [PATCH 152/393] Combine async methods that were split into two parts.

These were originally separated so that a Task wasn't allocated for the case of synchronous completion. Now with ValueTask (and much better code generation of async methods by the C# compiler), this code level optimisation is no longer necessary to avoid unnecessary work and allocations.

Simplify code by removing the HandleTimeout method that was doing the same thing as SetFailed.

Note that Benchmark.NET reports (for a simplified version of ReceiveReplyAsync with a do-nothing ReadPayloadAsync method) that the previous code took 40ns and the new code takes 60ns. So there is still overhead of going through a compiler-generated async state machine instead of writing it out by hand. However, this may be insignificant compared to the time taken for async network I/O, and this change makes the code easier to maintain.
---
 src/MySqlConnector/Core/ResultSet.cs          | 73 ++++++++----------
 src/MySqlConnector/Core/ServerSession.cs      | 74 ++++---------------
 src/MySqlConnector/MySqlDataReader.cs         | 11 +--
 .../Protocol/Serialization/ProtocolUtility.cs | 41 ++++------
 4 files changed, 64 insertions(+), 135 deletions(-)

diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 5bd0072ac..8cdedb2d4 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -225,61 +225,48 @@ public async Task<bool> ReadAsync(IOBehavior ioBehavior, CancellationToken cance
 		return row;
 	}
 
-	private ValueTask<Row?> ScanRowAsync(IOBehavior ioBehavior, Row? row, CancellationToken cancellationToken)
+	private async ValueTask<Row?> ScanRowAsync(IOBehavior ioBehavior, Row? row, CancellationToken cancellationToken)
 	{
 		// if we've already read past the end of this resultset, Read returns false
 		if (BufferState is ResultSetState.HasMoreData or ResultSetState.NoMoreData or ResultSetState.None)
-			return new ValueTask<Row?>(default(Row?));
+			return null;
 
-		var payloadValueTask = Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None);
-		return payloadValueTask.IsCompletedSuccessfully
-			? new ValueTask<Row?>(ScanRowAsyncRemainder(this, payloadValueTask.Result, row))
-			: new ValueTask<Row?>(ScanRowAsyncAwaited(this, payloadValueTask.AsTask(), row, cancellationToken));
+		PayloadData payload;
+		try
+		{
+			payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
+		}
+		catch (MySqlException ex)
+		{
+			BufferState = State = ResultSetState.NoMoreData;
+			if (ex.ErrorCode == MySqlErrorCode.QueryInterrupted && cancellationToken.IsCancellationRequested)
+				throw new OperationCanceledException(ex.Message, ex, cancellationToken);
+			if (ex.ErrorCode == MySqlErrorCode.QueryInterrupted && Command.CancellableCommand.IsTimedOut)
+				throw MySqlException.CreateForTimeout(ex);
+			throw;
+		}
 
-		static async Task<Row?> ScanRowAsyncAwaited(ResultSet resultSet, Task<PayloadData> payloadTask, Row? row, CancellationToken token)
+		if (payload.HeaderByte == EofPayload.Signature)
 		{
-			PayloadData payloadData;
-			try
+			if (Session.SupportsDeprecateEof && OkPayload.IsOk(payload.Span, Session.SupportsDeprecateEof))
 			{
-				payloadData = await payloadTask.ConfigureAwait(false);
+				var ok = OkPayload.Create(payload.Span, Session.SupportsDeprecateEof, Session.SupportsSessionTrack);
+				BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
+				return null;
 			}
-			catch (MySqlException ex)
+			if (!Session.SupportsDeprecateEof && EofPayload.IsEof(payload))
 			{
-				resultSet.BufferState = resultSet.State = ResultSetState.NoMoreData;
-				if (ex.ErrorCode == MySqlErrorCode.QueryInterrupted && token.IsCancellationRequested)
-					throw new OperationCanceledException(ex.Message, ex, token);
-				if (ex.ErrorCode == MySqlErrorCode.QueryInterrupted && resultSet.Command.CancellableCommand.IsTimedOut)
-					throw MySqlException.CreateForTimeout(ex);
-				throw;
+				var eof = EofPayload.Create(payload.Span);
+				BufferState = (eof.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
+				return null;
 			}
-			return ScanRowAsyncRemainder(resultSet, payloadData, row);
 		}
 
-		static Row? ScanRowAsyncRemainder(ResultSet resultSet, PayloadData payload, Row? row)
-		{
-			if (payload.HeaderByte == EofPayload.Signature)
-			{
-				var span = payload.Span;
-				if (resultSet.Session.SupportsDeprecateEof && OkPayload.IsOk(span, resultSet.Session.SupportsDeprecateEof))
-				{
-					var ok = OkPayload.Create(span, resultSet.Session.SupportsDeprecateEof, resultSet.Session.SupportsSessionTrack);
-					resultSet.BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
-					return null;
-				}
-				if (!resultSet.Session.SupportsDeprecateEof && EofPayload.IsEof(payload))
-				{
-					var eof = EofPayload.Create(span);
-					resultSet.BufferState = (eof.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
-					return null;
-				}
-			}
-
-			row ??= new Row(resultSet.Command.TryGetPreparedStatements() is not null, resultSet);
-			row.SetData(payload.Memory);
-			resultSet.m_hasRows = true;
-			resultSet.BufferState = ResultSetState.ReadingRows;
-			return row;
-		}
+		row ??= new Row(Command.TryGetPreparedStatements() is not null, this);
+		row.SetData(payload.Memory);
+		m_hasRows = true;
+		BufferState = ResultSetState.ReadingRows;
+		return row;
 	}
 
 #pragma warning disable CA1822 // Mark members as static
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index f20992f27..10a92607c 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -906,81 +906,44 @@ public ValueTask<PayloadData> ReceiveAsync(IOBehavior ioBehavior, CancellationTo
 	}
 
 	// Continues a conversation with the server by receiving a response to a packet sent with 'Send' or 'SendReply'.
-	public ValueTask<PayloadData> ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
+	public async ValueTask<PayloadData> ReceiveReplyAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		ValueTask<ArraySegment<byte>> task;
-		try
-		{
-			VerifyConnected();
-			task = m_payloadHandler!.ReadPayloadAsync(m_payloadCache, ProtocolErrorBehavior.Throw, ioBehavior);
-		}
-		catch (Exception ex)
+		if (CreateExceptionForInvalidState() is { } exception)
 		{
-			Log.FailedInReceiveReplyAsync(m_logger, ex, Id);
-			if ((ex as MySqlException)?.ErrorCode == MySqlErrorCode.CommandTimeoutExpired)
-				HandleTimeout();
-			task = ValueTaskExtensions.FromException<ArraySegment<byte>>(ex);
+			Log.FailedInReceiveReplyAsync(m_logger, exception, Id);
+			throw exception;
 		}
 
-		if (task.IsCompletedSuccessfully)
-		{
-			var payload = new PayloadData(task.Result);
-			if (payload.HeaderByte != ErrorPayload.Signature)
-				return new ValueTask<PayloadData>(payload);
-
-			var exception = CreateExceptionForErrorPayload(payload.Span);
-			return ValueTaskExtensions.FromException<PayloadData>(exception);
-		}
-
-		return ReceiveReplyAsyncAwaited(task);
-	}
-
-	private async ValueTask<PayloadData> ReceiveReplyAsyncAwaited(ValueTask<ArraySegment<byte>> task)
-	{
 		ArraySegment<byte> bytes;
 		try
 		{
-			bytes = await task.ConfigureAwait(false);
+			bytes = await m_payloadHandler!.ReadPayloadAsync(m_payloadCache, ProtocolErrorBehavior.Throw, ioBehavior).ConfigureAwait(false);
 		}
 		catch (Exception ex)
 		{
 			SetFailed(ex);
-			if (ex is MySqlException { ErrorCode: MySqlErrorCode.CommandTimeoutExpired })
-				HandleTimeout();
 			throw;
 		}
+
 		var payload = new PayloadData(bytes);
 		if (payload.HeaderByte == ErrorPayload.Signature)
 			throw CreateExceptionForErrorPayload(payload.Span);
+
 		return payload;
 	}
 
 	// Continues a conversation with the server by sending a reply to a packet received with 'Receive' or 'ReceiveReply'.
-	public ValueTask SendReplyAsync(PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	public async ValueTask SendReplyAsync(PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		ValueTask task;
-		try
-		{
-			VerifyConnected();
-			task = m_payloadHandler!.WritePayloadAsync(payload.Memory, ioBehavior);
-		}
-		catch (Exception ex)
+		if (CreateExceptionForInvalidState() is { } exception)
 		{
-			Log.FailedInSendReplyAsync(m_logger, ex, Id);
-			task = ValueTaskExtensions.FromException(ex);
+			Log.FailedInSendReplyAsync(m_logger, exception, Id);
+			throw exception;
 		}
 
-		if (task.IsCompletedSuccessfully)
-			return task;
-
-		return SendReplyAsyncAwaited(task);
-	}
-
-	private async ValueTask SendReplyAsyncAwaited(ValueTask task)
-	{
 		try
 		{
-			await task.ConfigureAwait(false);
+			await m_payloadHandler!.WritePayloadAsync(payload.Memory, ioBehavior).ConfigureAwait(false);
 		}
 		catch (Exception ex)
 		{
@@ -1001,20 +964,15 @@ public static void ThrowIfStatementContainsDelimiter(MySqlException exception, I
 		}
 	}
 
-	internal void HandleTimeout()
-	{
-		if (OwningConnection is not null && OwningConnection.TryGetTarget(out var connection))
-			connection.SetState(ConnectionState.Closed);
-	}
-
-	private void VerifyConnected()
+	private Exception? CreateExceptionForInvalidState()
 	{
 		lock (m_lock)
 		{
 			if (m_state == State.Closed)
-				throw new ObjectDisposedException(nameof(ServerSession));
+				return new ObjectDisposedException(nameof(ServerSession));
 			if (m_state != State.Connected && m_state != State.Querying && m_state != State.CancelingQuery && m_state != State.ClearingPendingCancellation && m_state != State.Closing)
-				throw new InvalidOperationException("ServerSession is not connected.");
+				return new InvalidOperationException("ServerSession is not connected.");
+			return null;
 		}
 	}
 
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 1ec0e8106..24337a1d7 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -136,25 +136,20 @@ private void ActivateResultSet(CancellationToken cancellationToken)
 		m_hasWarnings = m_resultSet.WarningCount != 0;
 	}
 
-	private ValueTask ScanResultSetAsync(IOBehavior ioBehavior, ResultSet resultSet, CancellationToken cancellationToken)
+	private async ValueTask ScanResultSetAsync(IOBehavior ioBehavior, ResultSet resultSet, CancellationToken cancellationToken)
 	{
 		if (!m_hasMoreResults)
-			return default;
+			return;
 
 		if (resultSet.BufferState is ResultSetState.NoMoreData or ResultSetState.None)
 		{
 			m_hasMoreResults = false;
-			return default;
+			return;
 		}
 
 		if (resultSet.BufferState != ResultSetState.HasMoreData)
 			throw new InvalidOperationException($"Invalid state: {resultSet.BufferState}");
 
-		return new ValueTask(ScanResultSetAsyncAwaited(ioBehavior, resultSet, cancellationToken));
-	}
-
-	private async Task ScanResultSetAsyncAwaited(IOBehavior ioBehavior, ResultSet resultSet, CancellationToken cancellationToken)
-	{
 		using (Command!.CancellableCommand.RegisterCancel(cancellationToken))
 		{
 			try
diff --git a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
index 9736004dc..4c3096001 100644
--- a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
@@ -473,39 +473,28 @@ public static async ValueTask<ArraySegment<byte>> ReadPayloadAsync(BufferedByteR
 		}
 	}
 
-	public static ValueTask WritePayloadAsync(IByteHandler byteHandler, Func<int> getNextSequenceNumber, ReadOnlyMemory<byte> payload, IOBehavior ioBehavior)
+	public static async ValueTask WritePayloadAsync(IByteHandler byteHandler, Func<int> getNextSequenceNumber, ReadOnlyMemory<byte> payload, IOBehavior ioBehavior)
 	{
-		return payload.Length <= MaxPacketSize ? WritePacketAsync(byteHandler, getNextSequenceNumber(), payload, ioBehavior) :
-			WritePayloadAsyncAwaited(byteHandler, getNextSequenceNumber, payload, ioBehavior);
-
-		static async ValueTask WritePayloadAsyncAwaited(IByteHandler byteHandler, Func<int> getNextSequenceNumber, ReadOnlyMemory<byte> payload, IOBehavior ioBehavior)
+		var buffer = ArrayPool<byte>.Shared.Rent(Math.Min(MaxPacketSize, payload.Length) + 4);
+		try
 		{
-			for (var bytesSent = 0; bytesSent < payload.Length; bytesSent += MaxPacketSize)
+			var bytesSent = 0;
+			do
 			{
 				var contents = payload.Slice(bytesSent, Math.Min(MaxPacketSize, payload.Length - bytesSent));
-				await WritePacketAsync(byteHandler, getNextSequenceNumber(), contents, ioBehavior).ConfigureAwait(false);
-			}
-		}
-	}
+				var bufferLength = contents.Length + 4;
 
-	private static ValueTask WritePacketAsync(IByteHandler byteHandler, int sequenceNumber, ReadOnlyMemory<byte> contents, IOBehavior ioBehavior)
-	{
-		var bufferLength = contents.Length + 4;
-		var buffer = ArrayPool<byte>.Shared.Rent(bufferLength);
-		SerializationUtility.WriteUInt32((uint) contents.Length, buffer, 0, 3);
-		buffer[3] = (byte) sequenceNumber;
-		contents.CopyTo(buffer.AsMemory()[4..]);
-		var task = byteHandler.WriteBytesAsync(new ArraySegment<byte>(buffer, 0, bufferLength), ioBehavior);
-		if (task.IsCompletedSuccessfully)
-		{
-			ArrayPool<byte>.Shared.Return(buffer);
-			return default;
-		}
-		return WritePacketAsyncAwaited(task, buffer);
+				SerializationUtility.WriteUInt32((uint) contents.Length, buffer, 0, 3);
+				buffer[3] = (byte) getNextSequenceNumber();
+				contents.CopyTo(buffer.AsMemory(4));
 
-		static async ValueTask WritePacketAsyncAwaited(ValueTask task, byte[] buffer)
+				await byteHandler.WriteBytesAsync(new ArraySegment<byte>(buffer, 0, bufferLength), ioBehavior).ConfigureAwait(false);
+				bytesSent += contents.Length;
+			}
+			while (bytesSent < payload.Length);
+		}
+		finally
 		{
-			await task.ConfigureAwait(false);
 			ArrayPool<byte>.Shared.Return(buffer);
 		}
 	}

From 7c543970004b5fd16898b7748d3d26760cdd206b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 15 Aug 2023 15:40:49 -0700
Subject: [PATCH 153/393] Eliminate temporary single-element IMySqlCommand
 array allocation.

Use CommandListPosition directly to store the potential list of commands. This optimizes the common case of executing a single MySqlCommand.
---
 src/MySqlConnector/Core/CommandExecutor.cs    | 10 +++----
 .../Core/CommandListPosition.cs               | 27 ++++++++++++++++---
 .../Core/ConcatenatedCommandPayloadCreator.cs | 12 ++++-----
 .../Core/SingleCommandPayloadCreator.cs       |  4 +--
 src/MySqlConnector/MySqlBatch.cs              |  2 +-
 src/MySqlConnector/MySqlCommand.cs            |  2 +-
 src/MySqlConnector/MySqlDataReader.cs         |  6 ++---
 7 files changed, 41 insertions(+), 22 deletions(-)

diff --git a/src/MySqlConnector/Core/CommandExecutor.cs b/src/MySqlConnector/Core/CommandExecutor.cs
index 0434a8314..74b2b4e6a 100644
--- a/src/MySqlConnector/Core/CommandExecutor.cs
+++ b/src/MySqlConnector/Core/CommandExecutor.cs
@@ -8,22 +8,22 @@ namespace MySqlConnector.Core;
 
 internal static class CommandExecutor
 {
-	public static async ValueTask<MySqlDataReader> ExecuteReaderAsync(IReadOnlyList<IMySqlCommand> commands, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	public static async ValueTask<MySqlDataReader> ExecuteReaderAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, CommandBehavior behavior, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		try
 		{
 			cancellationToken.ThrowIfCancellationRequested();
-			var commandListPosition = new CommandListPosition(commands);
-			var command = commands[0];
+			var command = commandListPosition.CommandAt(0);
 
 			// pre-requisite: Connection is non-null must be checked before calling this method
 			var connection = command.Connection!;
 
-			Log.CommandExecutorExecuteReader(command.Logger, connection.Session.Id, ioBehavior, commands.Count);
+			Log.CommandExecutorExecuteReader(command.Logger, connection.Session.Id, ioBehavior, commandListPosition.CommandCount);
 
 			Dictionary<string, CachedProcedure?>? cachedProcedures = null;
-			foreach (var command2 in commands)
+			for (var commandIndex = 0; commandIndex < commandListPosition.CommandCount; commandIndex++)
 			{
+				var command2 = commandListPosition.CommandAt(commandIndex);
 				if (command2.CommandType == CommandType.StoredProcedure)
 				{
 					cachedProcedures ??= new();
diff --git a/src/MySqlConnector/Core/CommandListPosition.cs b/src/MySqlConnector/Core/CommandListPosition.cs
index 24d9c0153..a54cb3766 100644
--- a/src/MySqlConnector/Core/CommandListPosition.cs
+++ b/src/MySqlConnector/Core/CommandListPosition.cs
@@ -5,18 +5,37 @@ namespace MySqlConnector.Core;
 /// </summary>
 internal struct CommandListPosition
 {
-	public CommandListPosition(IReadOnlyList<IMySqlCommand> commands)
+	public CommandListPosition(object commands)
 	{
-		Commands = commands;
+		m_commands = commands;
+		CommandCount = commands switch
+		{
+			MySqlCommand _ => 1,
+			IReadOnlyList<MySqlBatchCommand> list => list.Count,
+			_ => 0,
+		};
 		PreparedStatements = null;
 		CommandIndex = 0;
 		PreparedStatementIndex = 0;
 	}
 
+	public readonly IMySqlCommand CommandAt(int index) =>
+		m_commands switch
+		{
+			MySqlCommand command when index is 0 => command,
+			IReadOnlyList<MySqlBatchCommand> list => list[index],
+			_ => throw new ArgumentOutOfRangeException(nameof(index)),
+		};
+
+	/// <summary>
+	/// The commands in this list; either a singular <see cref="MySqlCommand"/> or a <see cref="IReadOnlyList{MySqlBatchCommand}"/>.
+	/// </summary>
+	private readonly object m_commands;
+
 	/// <summary>
-	/// The commands in the list.
+	/// The number of commands in the list.
 	/// </summary>
-	public IReadOnlyList<IMySqlCommand> Commands { get; }
+	public readonly int CommandCount;
 
 	/// <summary>
 	/// Associated prepared statements of commands
diff --git a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
index 6cf0443be..20e0717f6 100644
--- a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
@@ -10,14 +10,14 @@ internal sealed class ConcatenatedCommandPayloadCreator : ICommandPayloadCreator
 
 	public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDictionary<string, CachedProcedure?> cachedProcedures, ByteBufferWriter writer, bool appendSemicolon)
 	{
-		if (commandListPosition.CommandIndex == commandListPosition.Commands.Count)
+		if (commandListPosition.CommandIndex == commandListPosition.CommandCount)
 			return false;
 
 		writer.Write((byte) CommandKind.Query);
 
 		// ConcatenatedCommandPayloadCreator is only used by MySqlBatch, and MySqlBatchCommand doesn't expose attributes,
 		// so just write an empty attribute set if the server needs it.
-		if (commandListPosition.Commands[commandListPosition.CommandIndex].Connection!.Session.SupportsQueryAttributes)
+		if (commandListPosition.CommandAt(commandListPosition.CommandIndex).Connection!.Session.SupportsQueryAttributes)
 		{
 			// attribute count
 			writer.WriteLengthEncodedInteger(0);
@@ -29,15 +29,15 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 		bool isComplete;
 		do
 		{
-			var command = commandListPosition.Commands[commandListPosition.CommandIndex];
+			var command = commandListPosition.CommandAt(commandListPosition.CommandIndex);
 			Log.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 
 			isComplete = SingleCommandPayloadCreator.WriteQueryPayload(command, cachedProcedures, writer,
-				commandListPosition.CommandIndex < commandListPosition.Commands.Count - 1 || appendSemicolon,
+				commandListPosition.CommandIndex < commandListPosition.CommandCount - 1 || appendSemicolon,
 				commandListPosition.CommandIndex == 0,
-				commandListPosition.CommandIndex == commandListPosition.Commands.Count - 1);
+				commandListPosition.CommandIndex == commandListPosition.CommandCount - 1);
 			commandListPosition.CommandIndex++;
-		} while (commandListPosition.CommandIndex < commandListPosition.Commands.Count && isComplete);
+		} while (commandListPosition.CommandIndex < commandListPosition.CommandCount && isComplete);
 
 		return true;
 	}
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index b6e18ab95..0cd528877 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -14,10 +14,10 @@ internal sealed class SingleCommandPayloadCreator : ICommandPayloadCreator
 
 	public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDictionary<string, CachedProcedure?> cachedProcedures, ByteBufferWriter writer, bool appendSemicolon)
 	{
-		if (commandListPosition.CommandIndex == commandListPosition.Commands.Count)
+		if (commandListPosition.CommandIndex == commandListPosition.CommandCount)
 			return false;
 
-		var command = commandListPosition.Commands[commandListPosition.CommandIndex];
+		var command = commandListPosition.CommandAt(commandListPosition.CommandIndex);
 		commandListPosition.PreparedStatements = command.TryGetPreparedStatements();
 		if (commandListPosition.PreparedStatements is null)
 		{
diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index 5ea46fd11..913eefdff 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -162,7 +162,7 @@ private ValueTask<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior,
 		var payloadCreator = Connection!.Session.SupportsComMulti ? BatchedCommandPayloadCreator.Instance :
 			IsPrepared ? SingleCommandPayloadCreator.Instance :
 			ConcatenatedCommandPayloadCreator.Instance;
-		return CommandExecutor.ExecuteReaderAsync(BatchCommands!.Commands, payloadCreator, behavior, default, ioBehavior, cancellationToken);
+		return CommandExecutor.ExecuteReaderAsync(new(BatchCommands!.Commands), payloadCreator, behavior, default, ioBehavior, cancellationToken);
 	}
 
 #if NET6_0_OR_GREATER
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index 7de4a27d6..5cdf1082a 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -359,7 +359,7 @@ internal ValueTask<MySqlDataReader> ExecuteReaderNoResetTimeoutAsync(CommandBeha
 		var activity = NoActivity ? null : Connection!.Session.StartActivity(ActivitySourceHelper.ExecuteActivityName,
 			ActivitySourceHelper.DatabaseStatementTagName, CommandText);
 		m_commandBehavior = behavior;
-		return CommandExecutor.ExecuteReaderAsync(new IMySqlCommand[] { this }, SingleCommandPayloadCreator.Instance, behavior, activity, ioBehavior, cancellationToken);
+		return CommandExecutor.ExecuteReaderAsync(new(this), SingleCommandPayloadCreator.Instance, behavior, activity, ioBehavior, cancellationToken);
 	}
 
 	public MySqlCommand Clone() => new(this);
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 24337a1d7..e05db849e 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -68,9 +68,9 @@ internal async Task<bool> NextResultAsync(IOBehavior ioBehavior, CancellationTok
 
 				if (!m_hasMoreResults)
 				{
-					if (m_commandListPosition.CommandIndex < m_commandListPosition.Commands.Count)
+					if (m_commandListPosition.CommandIndex < m_commandListPosition.CommandCount)
 					{
-						Command = m_commandListPosition.Commands[m_commandListPosition.CommandIndex];
+						Command = m_commandListPosition.CommandAt(m_commandListPosition.CommandIndex);
 						using (Command.CancellableCommand.RegisterCancel(cancellationToken))
 						{
 							var writer = new ByteBufferWriter();
@@ -485,7 +485,7 @@ internal async Task InitAsync(CommandListPosition commandListPosition, ICommandP
 				await ReadOutParametersAsync(command, m_resultSet, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			// if the command list has multiple commands, keep reading until a result set is found
-			while (m_resultSet.State == ResultSetState.NoMoreData && commandListPosition.CommandIndex < commandListPosition.Commands.Count)
+			while (m_resultSet.State == ResultSetState.NoMoreData && commandListPosition.CommandIndex < commandListPosition.CommandCount)
 			{
 				await NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 			}

From ca63a912e0e2c99113ad8e919e8db65ad13f0d9a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 16 Aug 2023 08:58:54 -0700
Subject: [PATCH 154/393] Add more tests for MySqlDataReader boundary
 conditions.

---
 tests/IntegrationTests/QueryTests.cs | 98 +++++++++++++++++++++++++++-
 1 file changed, 97 insertions(+), 1 deletion(-)

diff --git a/tests/IntegrationTests/QueryTests.cs b/tests/IntegrationTests/QueryTests.cs
index ce43b8851..fc531a90e 100644
--- a/tests/IntegrationTests/QueryTests.cs
+++ b/tests/IntegrationTests/QueryTests.cs
@@ -122,7 +122,7 @@ public void GetOrdinalAfterNextResult()
 	public void GetOrdinalBeforeAndAfterRead()
 	{
 		using var cmd = m_database.Connection.CreateCommand();
-		cmd.CommandText = "select 0 as zero, 1 as one;";
+		cmd.CommandText = "select 0 as zero, 1 as one; select 2 as two;";
 
 		using var reader = cmd.ExecuteReader();
 		Assert.Equal(1, reader.GetOrdinal("one"));
@@ -130,6 +130,13 @@ public void GetOrdinalBeforeAndAfterRead()
 		Assert.Equal(1, reader.GetOrdinal("one"));
 		Assert.False(reader.Read());
 		Assert.Equal(1, reader.GetOrdinal("one"));
+
+		Assert.True(reader.NextResult());
+		Assert.Equal(0, reader.GetOrdinal("two"));
+		Assert.True(reader.Read());
+		Assert.Equal(0, reader.GetOrdinal("two"));
+		Assert.False(reader.Read());
+		Assert.Equal(0, reader.GetOrdinal("two"));
 	}
 
 	[Fact]
@@ -727,6 +734,44 @@ public void UseReaderWithoutDisposing()
 			throw ex;
 	}
 
+	[Fact]
+	public void GetNameAfterNextResult()
+	{
+		using var cmd = m_database.Connection.CreateCommand();
+		cmd.CommandText = "select 1 as a;";
+		using var reader = cmd.ExecuteReader();
+		Assert.False(reader.NextResult());
+#if MYSQL_DATA
+		Assert.Throws<IndexOutOfRangeException>(() => reader.GetName(0));
+#else
+		Assert.Throws<InvalidOperationException>(() => reader.GetName(0));
+#endif
+	}
+
+	[Fact]
+	public void GetNameBeforeAndAfterRead()
+	{
+		using var cmd = m_database.Connection.CreateCommand();
+		cmd.CommandText = "select '1' as a; select 1.0 as b;";
+		using var reader = cmd.ExecuteReader();
+		Assert.Equal("a", reader.GetName(0));
+
+		Assert.True(reader.Read());
+		Assert.Equal("a", reader.GetName(0));
+
+		Assert.False(reader.Read());
+		Assert.Equal("a", reader.GetName(0));
+
+		Assert.True(reader.NextResult());
+		Assert.Equal("b", reader.GetName(0));
+
+		Assert.True(reader.Read());
+		Assert.Equal("b", reader.GetName(0));
+
+		Assert.False(reader.Read());
+		Assert.Equal("b", reader.GetName(0));
+	}
+
 	[Theory]
 #if MYSQL_DATA
 	[InlineData("null", typeof(string))]
@@ -778,6 +823,30 @@ public void GetFieldTypeAfterNextResult()
 #endif
 	}
 
+	[Fact]
+	public void GetFieldTypeBeforeAndAfterRead()
+	{
+		using var cmd = m_database.Connection.CreateCommand();
+		cmd.CommandText = "select '1' as a; select 1.0 as b;";
+		using var reader = cmd.ExecuteReader();
+		Assert.Equal(typeof(string), reader.GetFieldType(0));
+
+		Assert.True(reader.Read());
+		Assert.Equal(typeof(string), reader.GetFieldType(0));
+
+		Assert.False(reader.Read());
+		Assert.Equal(typeof(string), reader.GetFieldType(0));
+
+		Assert.True(reader.NextResult());
+		Assert.Equal(typeof(decimal), reader.GetFieldType(0));
+
+		Assert.True(reader.Read());
+		Assert.Equal(typeof(decimal), reader.GetFieldType(0));
+
+		Assert.False(reader.Read());
+		Assert.Equal(typeof(decimal), reader.GetFieldType(0));
+	}
+
 	[Theory]
 #if MYSQL_DATA
 	[InlineData("null", "VARCHAR")]
@@ -828,6 +897,30 @@ public void GetDataTypeNameAfterNextResult()
 #endif
 	}
 
+	[Fact]
+	public void GetDataTypeNameBeforeAndAfterRead()
+	{
+		using var cmd = m_database.Connection.CreateCommand();
+		cmd.CommandText = "select '1' as a; select 1.0 as b;";
+		using var reader = cmd.ExecuteReader();
+		Assert.Equal("VARCHAR", reader.GetDataTypeName(0));
+
+		Assert.True(reader.Read());
+		Assert.Equal("VARCHAR", reader.GetDataTypeName(0));
+
+		Assert.False(reader.Read());
+		Assert.Equal("VARCHAR", reader.GetDataTypeName(0));
+
+		Assert.True(reader.NextResult());
+		Assert.Equal("DECIMAL", reader.GetDataTypeName(0));
+
+		Assert.True(reader.Read());
+		Assert.Equal("DECIMAL", reader.GetDataTypeName(0));
+
+		Assert.False(reader.Read());
+		Assert.Equal("DECIMAL", reader.GetDataTypeName(0));
+	}
+
 #if !MYSQL_DATA
 	[Fact]
 	public void GetColumnSchemaAfterNextResult()
@@ -1078,6 +1171,9 @@ public void HasRowsRepeated()
 		Assert.True(reader.HasRows);
 		Assert.True(reader.HasRows);
 		Assert.True(reader.HasRows);
+
+		Assert.False(reader.NextResult());
+		Assert.False(reader.HasRows);
 	}
 
 	[Fact]

From 5ade1ae3662dc69d0646479c48f2ba4e01adf152 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 16 Aug 2023 09:14:18 -0700
Subject: [PATCH 155/393] Upgrade integration tests to MySql.Data 8.1.0.

---
 .../tutorials/migrating-from-connector-net.md | 19 ++++++++++---------
 .../IntegrationTests/IntegrationTests.csproj  |  2 +-
 .../IntegrationTests/PreparedCommandTests.cs  |  5 -----
 .../IntegrationTests/StoredProcedureTests.cs  | 10 ++++++----
 tests/IntegrationTests/app.config             |  2 +-
 .../MySqlConnector.Tests.csproj               |  2 +-
 .../MySqlParameterTests.cs                    |  4 ----
 7 files changed, 19 insertions(+), 25 deletions(-)

diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 46581e98f..4182b43bd 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -41,7 +41,7 @@ In a .NET Framework application, make the following `app.config` change to regis
     <!-- add name="MySQL Data Provider"
       invariant="MySql.Data.MySqlClient"
       description=".Net Framework Data Provider for MySQL"
-      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.33.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.1.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
     <add name="MySqlConnector"
@@ -314,18 +314,19 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * ~~[#106244](https://bugs.mysql.com/bug.php?id=106244): `MySqlDataReader.GetFieldValue<Stream>` throws `InvalidCastException`~~
 * ~~[#106247](https://bugs.mysql.com/bug.php?id=106247): Can't use ~~`MySqlDbType.Enum` or~~ `MySqlDbType.Set` with prepared command~~
 * [#107259](https://bugs.mysql.com/bug.php?id=107259): Character Set 'utf8mb3' is not supported by .NET Framework
-* [#108756](https://bugs.mysql.com/bug.php?id=108756): Can't insert negative number using prepared statement with `MySqlDbType.Int24`
+* ~~[#108756](https://bugs.mysql.com/bug.php?id=108756): Can't insert negative number using prepared statement with `MySqlDbType.Int24`~~
 * [#108970](https://bugs.mysql.com/bug.php?id=108970): `MySqlConnectionStringBuilder.ContainsKey` method gives wrong result
 * [#109141](https://bugs.mysql.com/bug.php?id=109141): Insert of data into a table results in `System.ArgumentException`
 * [#109331](https://bugs.mysql.com/bug.php?id=109331): MySQL Connector/NET is incompatible with MariaDB 10.10 and later
 * [#109390](https://bugs.mysql.com/bug.php?id=109390): Transaction lock held after connection timeout exception
 * [#109476](https://bugs.mysql.com/bug.php?id=109476): `TransactionScope.Dispose` throws "Connection must be valid and open to rollback"
-* ~[#109670](https://bugs.mysql.com/bug.php?id=109670), [#109752](https://bugs.mysql.com/bug.php?id=109752): Many MSB3246 errors when referencing MySql.Data 8.0.32~
-* [#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.
+* ~~[#109670](https://bugs.mysql.com/bug.php?id=109670), [#109752](https://bugs.mysql.com/bug.php?id=109752): Many MSB3246 errors when referencing MySql.Data 8.0.32~~
+* ~~[#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.~~
 * ~~[#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.~~
 * ~~[#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'~~
-* [110717](https://bugs.mysql.com/bug.php?id=110717), [#111759](https://bugs.mysql.com/bug.php?id=111759): MySql.Data is not thread-safe
-* [110789](https://bugs.mysql.com/bug.php?id=110789): `OpenAsync` throws unhandled exception from thread pool
-* [110790](https://bugs.mysql.com/bug.php?id=110790): `ExecuteReaderAsync` hangs instead of cancelling query after `CommandTimeout`
-* [110791](https://bugs.mysql.com/bug.php?id=110791): `OpenAsync(CancellationToken)` doesn't throw for cancelled token
-* [110797](https://bugs.mysql.com/bug.php?id=110797): Certain sequence of special characters can break connection string validation.
+* [#110717](https://bugs.mysql.com/bug.php?id=110717), [#111759](https://bugs.mysql.com/bug.php?id=111759): MySql.Data is not thread-safe
+* [#110789](https://bugs.mysql.com/bug.php?id=110789): `OpenAsync` throws unhandled exception from thread pool
+* [#110790](https://bugs.mysql.com/bug.php?id=110790): `ExecuteReaderAsync` hangs instead of cancelling query after `CommandTimeout`
+* [#110791](https://bugs.mysql.com/bug.php?id=110791): `OpenAsync(CancellationToken)` doesn't throw for cancelled token
+* [#110797](https://bugs.mysql.com/bug.php?id=110797): Certain sequence of special characters can break connection string validation.
+* [#112088](https://bugs.mysql.com/bug.php?id=112088): `BIT(n)` parameter size incorrectly returns 0
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index fdcb24726..8de202d5d 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -55,7 +55,7 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <PackageReference Include="MySql.Data" Version="8.0.33" />
+    <PackageReference Include="MySql.Data" Version="8.1.0" />
     <Using Include="MySql.Data.MySqlClient" />
   </ItemGroup>
 
diff --git a/tests/IntegrationTests/PreparedCommandTests.cs b/tests/IntegrationTests/PreparedCommandTests.cs
index 3a976cf56..b17ca26e4 100644
--- a/tests/IntegrationTests/PreparedCommandTests.cs
+++ b/tests/IntegrationTests/PreparedCommandTests.cs
@@ -373,12 +373,7 @@ public static IEnumerable<object[]> GetInsertAndQueryData()
 			yield return new object[] { isPrepared, "TINYINT UNSIGNED", (byte) 123, MySqlDbType.UByte };
 			yield return new object[] { isPrepared, "SMALLINT", (short) -12345, MySqlDbType.Int16 };
 			yield return new object[] { isPrepared, "SMALLINT UNSIGNED", (ushort) 12345, MySqlDbType.UInt16 };
-#if !MYSQL_DATA
 			yield return new object[] { isPrepared, "MEDIUMINT", -1234567, MySqlDbType.Int24 };
-#else
-			// https://bugs.mysql.com/bug.php?id=108756
-			yield return new object[] { isPrepared, "MEDIUMINT", -1234567, MySqlDbType.Int32 };
-#endif
 			yield return new object[] { isPrepared, "MEDIUMINT UNSIGNED", 1234567u, MySqlDbType.UInt24 };
 			yield return new object[] { isPrepared, "INT", -123456789, MySqlDbType.Int32 };
 			yield return new object[] { isPrepared, "INT UNSIGNED", 123456789u, MySqlDbType.UInt32 };
diff --git a/tests/IntegrationTests/StoredProcedureTests.cs b/tests/IntegrationTests/StoredProcedureTests.cs
index f3fa66341..e74e1defb 100644
--- a/tests/IntegrationTests/StoredProcedureTests.cs
+++ b/tests/IntegrationTests/StoredProcedureTests.cs
@@ -664,15 +664,17 @@ OUT param2 VARCHAR(100))
 	}
 
 	[Theory]
-	[InlineData("bit(1)", 1)]
-	[InlineData("bit(10)", 10)]
 #if !MYSQL_DATA
 	[InlineData("bool", 1)]
 	[InlineData("tinyint(1)", 1)]
 	[InlineData("decimal(10)", 10)]
-#endif
-    [InlineData("char(30)", 30)]
+
+	// https://bugs.mysql.com/bug.php?id=112088
+	[InlineData("bit(1)", 1)]
+	[InlineData("bit(10)", 10)]
 	[InlineData("mediumtext", 0)]
+#endif
+	[InlineData("char(30)", 30)]
 	[InlineData("varchar(50)", 50)]
 	// These return nonzero sizes for some versions of MySQL Server 8.0
 	// [InlineData("bit", 0)]
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index 4e4e23d52..59296b86a 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -3,7 +3,7 @@
   <system.data>
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.0.33.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.1.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 9800da918..5a0c3b042 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -46,7 +46,7 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <PackageReference Include="MySql.Data" Version="8.0.33" />
+    <PackageReference Include="MySql.Data" Version="8.1.0" />
     <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
     <Using Include="MySql.Data.MySqlClient" />
     <Using Include="MySql.Data.Types" />
diff --git a/tests/MySqlConnector.Tests/MySqlParameterTests.cs b/tests/MySqlConnector.Tests/MySqlParameterTests.cs
index f0a556f40..3d2309e6b 100644
--- a/tests/MySqlConnector.Tests/MySqlParameterTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterTests.cs
@@ -213,11 +213,7 @@ public void CloneDbType()
 	[Fact]
 	public void CloneMySqlDbType()
 	{
-#if MYSQL_DATA // https://bugs.mysql.com/bug.php?id=109682
-		var parameter = new MySqlParameter { MySqlDbType = MySqlDbType.Int64 };
-#else
 		var parameter = new MySqlParameter { MySqlDbType = MySqlDbType.MediumText };
-#endif
 		var clone = parameter.Clone();
 		Assert.Equal(parameter.MySqlDbType, clone.MySqlDbType);
 	}

From fec9506f0641b7335b66476ca9b71e7a8327a2b8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 16 Aug 2023 14:12:48 -0700
Subject: [PATCH 156/393] Cache and reuse ColumnDefinitionPayload objects.

Also reuse the array of payloads created by ResultSet. ResultSet was already caching the byte array from which the column definitions were deserialized; it now caches the deserialized objects, too. This memory is reused when the MySqlDataReader/Connection/session is returned to the pool then retrieved to execute a second query.

If a result set retrieves a huge number of columns, a large amount of memory may be kept alive in the pool until the session is expired. This should hopefully be a rare occurrence but is also offset by the benefit of reusing that memory if very wide queries are being executed frequently.
---
 src/MySqlConnector/Core/ResultSet.cs          | 65 ++++++++++++-------
 src/MySqlConnector/Core/ServerSession.cs      |  4 +-
 src/MySqlConnector/MySqlDataReader.cs         | 21 +++---
 .../Payloads/ColumnDefinitionPayload.cs       | 55 +++++++++-------
 4 files changed, 85 insertions(+), 60 deletions(-)

diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 8cdedb2d4..9511722b3 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -20,7 +20,7 @@ public void Reset()
 	{
 		// ResultSet can be re-used, so initialize everything
 		BufferState = ResultSetState.None;
-		ColumnDefinitions = null;
+		m_columnDefinitions = default;
 		WarningCount = 0;
 		State = ResultSetState.None;
 		ContainsCommandParameters = false;
@@ -55,7 +55,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 					WarningCount = ok.WarningCount;
 					if (ok.NewSchema is not null)
 						Connection.Session.DatabaseOverride = ok.NewSchema;
-					ColumnDefinitions = null;
+					m_columnDefinitions = default;
 					State = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0
 						? ResultSetState.NoMoreData
 						: ResultSetState.HasMoreData;
@@ -115,38 +115,50 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 				else
 				{
 					var columnCountPacket = ColumnCountPayload.Create(payload.Span, Session.SupportsCachedPreparedMetadata);
+					var columnCount = columnCountPacket.ColumnCount;
 					if (!columnCountPacket.MetadataFollows)
 					{
 						// reuse previous metadata
-						ColumnDefinitions = DataReader.LastUsedPreparedStatement!.Columns!;
-						if (ColumnDefinitions.Length != columnCountPacket.ColumnCount)
-							throw new InvalidOperationException($"Expected result set to have {ColumnDefinitions.Length} columns, but it contains {columnCountPacket.ColumnCount} columns");
+						m_columnDefinitions = DataReader.LastUsedPreparedStatement!.Columns!;
+						if (m_columnDefinitions.Length != columnCount)
+							throw new InvalidOperationException($"Expected result set to have {m_columnDefinitions.Length} columns, but it contains {columnCount} columns");
 					}
 					else
 					{
 						// parse columns
 						// reserve adequate space to hold a copy of all column definitions (but note that this can be resized below if we guess too small)
-						Utility.Resize(ref m_columnDefinitionPayloads, columnCountPacket.ColumnCount * 96);
+						Utility.Resize(ref m_columnDefinitionPayloadBytes, columnCount * 96);
 
-						ColumnDefinitions = new ColumnDefinitionPayload[columnCountPacket.ColumnCount];
-						for (var column = 0; column < ColumnDefinitions.Length; column++)
+						// increase the cache size to be large enough to hold all the column definitions
+						if (m_columnDefinitionPayloadCache is null)
+							m_columnDefinitionPayloadCache = new ColumnDefinitionPayload[columnCount];
+						else if (m_columnDefinitionPayloadCache.Length < columnCount)
+							Array.Resize(ref m_columnDefinitionPayloadCache, Math.Max(columnCount, m_columnDefinitionPayloadCache.Length * 2));
+						m_columnDefinitions = m_columnDefinitionPayloadCache.AsMemory(0, columnCount);
+
+						// if the server supports metadata caching but has re-sent it, something has changed since last prepare/execution and we need to update the columns
+						var preparedColumns = Session.SupportsCachedPreparedMetadata ? DataReader.LastUsedPreparedStatement?.Columns : null;
+
+						for (var column = 0; column < columnCount; column++)
 						{
 							payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 							var payloadLength = payload.Span.Length;
 
 							// 'Session.ReceiveReplyAsync' reuses a shared buffer; make a copy so that the column definitions can always be safely read at any future point
-							if (m_columnDefinitionPayloadUsedBytes + payloadLength > m_columnDefinitionPayloads.Count)
-								Utility.Resize(ref m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes + payloadLength);
-							payload.Span.CopyTo(m_columnDefinitionPayloads.AsSpan(m_columnDefinitionPayloadUsedBytes));
+							if (m_columnDefinitionPayloadUsedBytes + payloadLength > m_columnDefinitionPayloadBytes.Count)
+								Utility.Resize(ref m_columnDefinitionPayloadBytes, m_columnDefinitionPayloadUsedBytes + payloadLength);
+							payload.Span.CopyTo(m_columnDefinitionPayloadBytes.AsSpan(m_columnDefinitionPayloadUsedBytes));
+
+							// create/update the column definition in our cache
+							var payloadBytesSegment = new ResizableArraySegment<byte>(m_columnDefinitionPayloadBytes, m_columnDefinitionPayloadUsedBytes, payloadLength);
+							ColumnDefinitionPayload.Initialize(ref m_columnDefinitionPayloadCache[column], payloadBytesSegment);
+
+							// if there was a prepared statement, update its cached columns too
+							if (preparedColumns is not null)
+								ColumnDefinitionPayload.Initialize(ref preparedColumns[column], payloadBytesSegment);
 
-							var columnDefinition = ColumnDefinitionPayload.Create(new ResizableArraySegment<byte>(m_columnDefinitionPayloads, m_columnDefinitionPayloadUsedBytes, payloadLength));
-							ColumnDefinitions[column] = columnDefinition;
 							m_columnDefinitionPayloadUsedBytes += payloadLength;
 						}
-
-						// server supports metadata caching, but has re-sent it, so something has changed since last prepare/execution
-						if (Session.SupportsCachedPreparedMetadata && DataReader.LastUsedPreparedStatement is { } preparedStatement)
-							preparedStatement.Columns = ColumnDefinitions;
 					}
 
 					if (!Session.SupportsDeprecateEof)
@@ -277,7 +289,7 @@ public async Task<bool> ReadAsync(IOBehavior ioBehavior, CancellationToken cance
 
 	public string GetName(int ordinal)
 	{
-		if (ColumnDefinitions is null)
+		if (!HasResultSet)
 			throw new InvalidOperationException("There is no current result set.");
 		if (ordinal < 0 || ordinal >= ColumnDefinitions.Length)
 			throw new IndexOutOfRangeException($"value must be between 0 and {ColumnDefinitions.Length - 1}");
@@ -286,7 +298,7 @@ public string GetName(int ordinal)
 
 	public string GetDataTypeName(int ordinal)
 	{
-		if (ColumnDefinitions is null)
+		if (!HasResultSet)
 			throw new InvalidOperationException("There is no current result set.");
 		if (ordinal < 0 || ordinal >= ColumnDefinitions.Length)
 			throw new IndexOutOfRangeException($"value must be between 0 and {ColumnDefinitions.Length - 1}");
@@ -299,7 +311,7 @@ public string GetDataTypeName(int ordinal)
 
 	public Type GetFieldType(int ordinal)
 	{
-		if (ColumnDefinitions is null)
+		if (!HasResultSet)
 			throw new InvalidOperationException("There is no current result set.");
 		if (ordinal < 0 || ordinal >= ColumnDefinitions.Length)
 			throw new IndexOutOfRangeException($"value must be between 0 and {ColumnDefinitions.Length - 1}");
@@ -311,9 +323,9 @@ public Type GetFieldType(int ordinal)
 	}
 
 	public MySqlDbType GetColumnType(int ordinal) =>
-		TypeMapper.ConvertToMySqlDbType(ColumnDefinitions![ordinal], Connection.TreatTinyAsBoolean, Connection.GuidFormat);
+		TypeMapper.ConvertToMySqlDbType(ColumnDefinitions[ordinal], Connection.TreatTinyAsBoolean, Connection.GuidFormat);
 
-	public int FieldCount => ColumnDefinitions?.Length ?? 0;
+	public int FieldCount => ColumnDefinitions.Length;
 
 	public bool HasRows
 	{
@@ -329,7 +341,7 @@ public int GetOrdinal(string name)
 	{
 		if (name is null)
 			throw new ArgumentNullException(nameof(name));
-		if (ColumnDefinitions is null)
+		if (!HasResultSet)
 			throw new InvalidOperationException("There is no current result set.");
 
 		for (var column = 0; column < ColumnDefinitions.Length; column++)
@@ -355,14 +367,17 @@ public Row GetCurrentRow()
 	public ServerSession Session => DataReader.Session!;
 
 	public ResultSetState BufferState { get; private set; }
-	public ColumnDefinitionPayload[]? ColumnDefinitions { get; private set; }
+	public ReadOnlySpan<ColumnDefinitionPayload> ColumnDefinitions => m_columnDefinitions.Span;
 	public int WarningCount { get; private set; }
 	public ResultSetState State { get; private set; }
+	public bool HasResultSet => !(State == ResultSetState.None || ColumnDefinitions.Length == 0);
 	public bool ContainsCommandParameters { get; private set; }
 
-	private ResizableArray<byte>? m_columnDefinitionPayloads;
+	private ResizableArray<byte>? m_columnDefinitionPayloadBytes;
 	private int m_columnDefinitionPayloadUsedBytes;
 	private Queue<Row>? m_readBuffer;
 	private Row? m_row;
 	private bool m_hasRows;
+	private ReadOnlyMemory<ColumnDefinitionPayload> m_columnDefinitions;
+	private ColumnDefinitionPayload[]? m_columnDefinitionPayloadCache;
 }
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 10a92607c..e1f94540f 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -234,7 +234,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
 					var payloadLength = payload.Span.Length;
 					Utility.Resize(ref columnsAndParameters, columnsAndParametersSize + payloadLength);
 					payload.Span.CopyTo(columnsAndParameters.AsSpan(columnsAndParametersSize));
-					parameters[i] = ColumnDefinitionPayload.Create(new(columnsAndParameters, columnsAndParametersSize, payloadLength));
+					ColumnDefinitionPayload.Initialize(ref parameters[i], new(columnsAndParameters, columnsAndParametersSize, payloadLength));
 					columnsAndParametersSize += payloadLength;
 				}
 				if (!SupportsDeprecateEof)
@@ -254,7 +254,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
 					var payloadLength = payload.Span.Length;
 					Utility.Resize(ref columnsAndParameters, columnsAndParametersSize + payloadLength);
 					payload.Span.CopyTo(columnsAndParameters.AsSpan(columnsAndParametersSize));
-					columns[i] = ColumnDefinitionPayload.Create(new(columnsAndParameters, columnsAndParametersSize, payloadLength));
+					ColumnDefinitionPayload.Initialize(ref columns[i], new(columnsAndParameters, columnsAndParametersSize, payloadLength));
 					columnsAndParametersSize += payloadLength;
 				}
 				if (!SupportsDeprecateEof)
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index e05db849e..cde036039 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -345,12 +345,16 @@ public override long GetChars(int ordinal, long dataOffset, char[]? buffer, int
 	/// <returns>A <see cref="System.Collections.ObjectModel.ReadOnlyCollection{DbColumn}"/> containing metadata about the result set.</returns>
 	public ReadOnlyCollection<DbColumn> GetColumnSchema()
 	{
+		var hasNoSchema = !m_resultSet.HasResultSet || m_resultSet.ContainsCommandParameters;
+		if (hasNoSchema)
+			return new ReadOnlyCollection<DbColumn>(Array.Empty<DbColumn>());
+
 		var columnDefinitions = m_resultSet.ColumnDefinitions;
-		var hasNoSchema = columnDefinitions is null || m_resultSet.ContainsCommandParameters;
-		return hasNoSchema ? new List<DbColumn>().AsReadOnly() :
-			columnDefinitions!
-				.Select((c, n) => (DbColumn) new MySqlDbColumn(n, c, Connection!.AllowZeroDateTime, GetResultSet().GetColumnType(n)))
-				.ToList().AsReadOnly();
+		var resultSet = GetResultSet();
+		var schema = new List<DbColumn>(columnDefinitions.Length);
+		for (var n = 0; n < columnDefinitions.Length; n++)
+			schema.Add(new MySqlDbColumn(n, columnDefinitions[n], Connection!.AllowZeroDateTime, resultSet.GetColumnType(n)));
+		return schema.AsReadOnly();
 	}
 
 	/// <summary>
@@ -459,8 +463,6 @@ internal async Task InitAsync(CommandListPosition commandListPosition, ICommandP
 			throw new InvalidOperationException("Expected m_hasMoreResults to be false");
 		if (m_resultSet.BufferState != ResultSetState.None || m_resultSet.State != ResultSetState.None)
 			throw new InvalidOperationException("Expected BufferState and State to be ResultSetState.None.");
-		if (m_resultSet.ColumnDefinitions is not null)
-			throw new InvalidOperationException("Expected ColumnDefinitions to be null");
 		m_closed = false;
 		m_hasWarnings = false;
 		RealRecordsAffected = null;
@@ -504,12 +506,11 @@ internal async Task InitAsync(CommandListPosition commandListPosition, ICommandP
 
 	internal DataTable? BuildSchemaTable()
 	{
-		var columnDefinitions = m_resultSet.ColumnDefinitions;
-		if (columnDefinitions is null || m_resultSet.ContainsCommandParameters)
+		if (!m_resultSet.HasResultSet || m_resultSet.ContainsCommandParameters)
 			return null;
 
 		var schemaTable = new DataTable("SchemaTable") { Locale = CultureInfo.InvariantCulture };
-		schemaTable.MinimumCapacity = columnDefinitions.Length;
+		schemaTable.MinimumCapacity = m_resultSet.ColumnDefinitions.Length;
 
 		var columnName = new DataColumn(SchemaTableColumn.ColumnName, typeof(string));
 		var ordinal = new DataColumn(SchemaTableColumn.ColumnOrdinal, typeof(int));
diff --git a/src/MySqlConnector/Protocol/Payloads/ColumnDefinitionPayload.cs b/src/MySqlConnector/Protocol/Payloads/ColumnDefinitionPayload.cs
index 7673c854c..3bbc54a73 100644
--- a/src/MySqlConnector/Protocol/Payloads/ColumnDefinitionPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/ColumnDefinitionPayload.cs
@@ -16,13 +16,13 @@ public string Name
 		}
 	}
 
-	public CharacterSet CharacterSet { get; }
+	public CharacterSet CharacterSet { get; private set; }
 
-	public uint ColumnLength { get; }
+	public uint ColumnLength { get; private set; }
 
-	public ColumnType ColumnType { get; }
+	public ColumnType ColumnType { get; private set; }
 
-	public ColumnFlags ColumnFlags { get; }
+	public ColumnFlags ColumnFlags { get; private set; }
 
 	public string SchemaName
 	{
@@ -74,11 +74,18 @@ public string PhysicalName
 		}
 	}
 
-	public byte Decimals { get; }
+	public byte Decimals { get; private set; }
 
-	public static ColumnDefinitionPayload Create(ResizableArraySegment<byte> arraySegment)
+	public static void Initialize(ref ColumnDefinitionPayload payload, ResizableArraySegment<byte> arraySegment)
 	{
-		var reader = new ByteArrayReader(arraySegment);
+		payload ??= new ColumnDefinitionPayload();
+		payload.Initialize(arraySegment);
+	}
+
+	private void Initialize(ResizableArraySegment<byte> originalData)
+	{
+		m_originalData = originalData;
+		var reader = new ByteArrayReader(originalData);
 		SkipLengthEncodedByteString(ref reader); // catalog
 		SkipLengthEncodedByteString(ref reader); // schema
 		SkipLengthEncodedByteString(ref reader); // table
@@ -86,15 +93,24 @@ public static ColumnDefinitionPayload Create(ResizableArraySegment<byte> arraySe
 		SkipLengthEncodedByteString(ref reader); // name
 		SkipLengthEncodedByteString(ref reader); // physical name
 		reader.ReadByte(0x0C); // length of fixed-length fields, always 0x0C
-		var characterSet = (CharacterSet) reader.ReadUInt16();
-		var columnLength = reader.ReadUInt32();
-		var columnType = (ColumnType) reader.ReadByte();
-		var columnFlags = (ColumnFlags) reader.ReadUInt16();
-		var decimals = reader.ReadByte(); // 0x00 for integers and static strings, 0x1f for dynamic strings, double, float, 0x00 to 0x51 for decimals
+		CharacterSet = (CharacterSet) reader.ReadUInt16();
+		ColumnLength = reader.ReadUInt32();
+		ColumnType = (ColumnType) reader.ReadByte();
+		ColumnFlags = (ColumnFlags) reader.ReadUInt16();
+		Decimals = reader.ReadByte(); // 0x00 for integers and static strings, 0x1f for dynamic strings, double, float, 0x00 to 0x51 for decimals
 		reader.ReadByte(0); // reserved byte 1
 		reader.ReadByte(0); // reserved byte 2
 
-		return new ColumnDefinitionPayload(arraySegment, characterSet, columnLength, columnType, columnFlags, decimals);
+		if (m_readNames)
+		{
+			m_catalogName = null;
+			m_schemaName = null;
+			m_table = null;
+			m_physicalTable = null;
+			m_name = null;
+			m_physicalName = null;
+			m_readNames = false;
+		}
 	}
 
 	private static void SkipLengthEncodedByteString(ref ByteArrayReader reader)
@@ -103,19 +119,13 @@ private static void SkipLengthEncodedByteString(ref ByteArrayReader reader)
 		reader.Offset += length;
 	}
 
-	private ColumnDefinitionPayload(ResizableArraySegment<byte> originalData, CharacterSet characterSet, uint columnLength, ColumnType columnType, ColumnFlags columnFlags, byte decimals)
+	private ColumnDefinitionPayload()
 	{
-		OriginalData = originalData;
-		CharacterSet = characterSet;
-		ColumnLength = columnLength;
-		ColumnType = columnType;
-		ColumnFlags = columnFlags;
-		Decimals = decimals;
 	}
 
 	private void ReadNames()
 	{
-		var reader = new ByteArrayReader(OriginalData);
+		var reader = new ByteArrayReader(m_originalData);
 		m_catalogName = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
 		m_schemaName = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
 		m_table = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
@@ -125,8 +135,7 @@ private void ReadNames()
 		m_readNames = true;
 	}
 
-	private ResizableArraySegment<byte> OriginalData { get; }
-
+	private ResizableArraySegment<byte> m_originalData;
 	private bool m_readNames;
 	private string? m_name;
 	private string? m_schemaName;

From 3d10049d98483881b192df719d46fc9d40ba296b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 2 Sep 2023 17:08:00 -0700
Subject: [PATCH 157/393] Add net48 TFM. Fixes #1355

This enables "TlsVesion=Tls13" to be used in the connection string.

It also supports X509Certificate2.CopyWithPrivateKey on .NET Framework 4.8.
---
 .../CompatibilitySuppressions.xml             | 55 +++++++++++++------
 src/MySqlConnector/Core/ConnectionSettings.cs |  2 +-
 src/MySqlConnector/MySqlConnector.csproj      |  8 +--
 .../Utilities/NullableAttributes.cs           |  2 +-
 4 files changed, 44 insertions(+), 23 deletions(-)

diff --git a/src/MySqlConnector/CompatibilitySuppressions.xml b/src/MySqlConnector/CompatibilitySuppressions.xml
index 1bb44ab01..35851e7b5 100644
--- a/src/MySqlConnector/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector/CompatibilitySuppressions.xml
@@ -1,11 +1,31 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
+<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:System.Data.Common.DbColumn</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:System.Data.Common.DbColumn</Target>
+    <Left>lib/net462/MySqlConnector.dll</Left>
+    <Right>lib/net471/MySqlConnector.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:System.Data.Common.DbDataSource</Target>
     <Left>lib/net6.0/MySqlConnector.dll</Left>
     <Right>lib/net7.0/MySqlConnector.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:MySqlConnector.MySqlDataReader</Target>
+    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
+    <Right>lib/net462/MySqlConnector.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0008</DiagnosticId>
     <Target>T:MySqlConnector.MySqlBatchCommandCollection</Target>
@@ -13,29 +33,32 @@
     <Right>lib/net6.0/MySqlConnector.dll</Right>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:System.Data.Common.DbColumn</Target>
-    <Left>lib/net461/MySqlConnector.dll</Left>
-    <Right>lib/net471/MySqlConnector.dll</Right>
+    <DiagnosticId>CP1002</DiagnosticId>
+    <Target>System.Data.Common, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
+    <Left>lib/net5.0/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:System.Data.Common.DbColumn</Target>
-    <Left>lib/net461/MySqlConnector.dll</Left>
-    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <DiagnosticId>CP1002</DiagnosticId>
+    <Target>System.Runtime, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
+    <Left>lib/net5.0/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
     <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP0001</DiagnosticId>
-    <Target>T:System.Data.Common.DbColumn</Target>
-    <Left>lib/net462/MySqlConnector.dll</Left>
-    <Right>lib/net471/MySqlConnector.dll</Right>
+    <DiagnosticId>CP1002</DiagnosticId>
+    <Target>System.Data.Common, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
+    <Left>lib/netcoreapp3.1/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP0008</DiagnosticId>
-    <Target>T:MySqlConnector.MySqlDataReader</Target>
-    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
-    <Right>lib/net462/MySqlConnector.dll</Right>
+    <DiagnosticId>CP1002</DiagnosticId>
+    <Target>System.Runtime, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
+    <Left>lib/netcoreapp3.1/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>
diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index 768357de6..99c66d702 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -72,7 +72,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
 					TlsVersions |= SslProtocols.Tls11;
 				else if (minorVersion == '2')
 					TlsVersions |= SslProtocols.Tls12;
-#if NETCOREAPP3_0_OR_GREATER
+#if NETCOREAPP3_0_OR_GREATER || NET48_OR_GREATER
 				else if (minorVersion == '3')
 					TlsVersions |= SslProtocols.Tls13;
 #endif
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 4ae3c8e46..bf3a491bd 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;net471;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
+    <TargetFrameworks>net462;net471;net48;netstandard2.0;netstandard2.1;net6.0;net7.0</TargetFrameworks>
     <Description>A truly async MySQL ADO.NET provider, supporting MySQL Server, MariaDB, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL, and more.</Description>
     <Copyright>Copyright 2016–2023 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
@@ -19,15 +19,13 @@
     <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' ">
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471'  OR '$(TargetFramework)' == 'net48' ">
     <Reference Include="System.Transactions" />
     <Using Remove="System.Net.Http" />
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'netstandard2.0' ">
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'net48' OR '$(TargetFramework)' == 'netstandard2.0' ">
     <PackageReference Include="IndexRange" Version="1.0.2" PrivateAssets="All" />
-    <PackageReference Include="System.Buffers" Version="4.5.1" />
-    <PackageReference Include="System.Memory" Version="4.5.5" />
     <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
   </ItemGroup>
 
diff --git a/src/MySqlConnector/Utilities/NullableAttributes.cs b/src/MySqlConnector/Utilities/NullableAttributes.cs
index 39beefa27..b444c36c0 100644
--- a/src/MySqlConnector/Utilities/NullableAttributes.cs
+++ b/src/MySqlConnector/Utilities/NullableAttributes.cs
@@ -1,7 +1,7 @@
 // Supports using nullable attributes on older frameworks.
 // Copied from https://github.com/dotnet/corefx/blob/master/src/Common/src/CoreLib/System/Diagnostics/CodeAnalysis/NullableAttributes.cs
 
-#if NET462 || NET471 || NETSTANDARD2_0 || NETCOREAPP2_1
+#if NET462 || NET471 || NET48 || NETSTANDARD2_0 || NETCOREAPP2_1
 namespace System.Diagnostics.CodeAnalysis;
 
 /// <summary>Specifies that null is allowed as an input even if the corresponding type disallows it.</summary>

From 761c1c2322abfe92002b40ffa24147239e0cb33a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 2 Sep 2023 17:57:05 -0700
Subject: [PATCH 158/393] Unify MySqlBulkCopy API. Fixes #1364

---
 src/MySqlConnector/MySqlBulkCopy.cs   | 47 ---------------------------
 src/MySqlConnector/MySqlConnection.cs | 12 +------
 2 files changed, 1 insertion(+), 58 deletions(-)

diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs
index 2c0c52dd5..09e37a567 100644
--- a/src/MySqlConnector/MySqlBulkCopy.cs
+++ b/src/MySqlConnector/MySqlBulkCopy.cs
@@ -117,7 +117,6 @@ public MySqlBulkCopyResult WriteToServer(DataTable dataTable)
 #pragma warning restore CA2012
 	}
 
-#if NETCOREAPP || NETSTANDARD2_0_OR_GREATER
 	/// <summary>
 	/// Asynchronously copies all rows in the supplied <see cref="DataTable"/> to the destination table specified by the
 	/// <see cref="DestinationTableName"/> property of the <see cref="MySqlBulkCopy"/> object.
@@ -130,20 +129,6 @@ public async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(DataTable dataTab
 		m_valuesEnumerator = DataRowsValuesEnumerator.Create(dataTable ?? throw new ArgumentNullException(nameof(dataTable)));
 		return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false);
 	}
-#else
-	/// <summary>
-	/// Asynchronously copies all rows in the supplied <see cref="DataTable"/> to the destination table specified by the
-	/// <see cref="DestinationTableName"/> property of the <see cref="MySqlBulkCopy"/> object.
-	/// </summary>
-	/// <param name="dataTable">The <see cref="DataTable"/> to copy.</param>
-	/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
-	/// <returns>A <see cref="MySqlBulkCopyResult"/> with the result of the bulk copy operation.</returns>
-	public async Task<MySqlBulkCopyResult> WriteToServerAsync(DataTable dataTable, CancellationToken cancellationToken = default)
-	{
-		m_valuesEnumerator = DataRowsValuesEnumerator.Create(dataTable ?? throw new ArgumentNullException(nameof(dataTable)));
-		return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false);
-	}
-#endif
 
 	/// <summary>
 	/// Copies all rows in the supplied sequence of <see cref="DataRow"/> objects to the destination table specified by the
@@ -161,7 +146,6 @@ public MySqlBulkCopyResult WriteToServer(IEnumerable<DataRow> dataRows, int colu
 #pragma warning restore CA2012
 	}
 
-#if NETCOREAPP || NETSTANDARD2_0_OR_GREATER
 	/// <summary>
 	/// Asynchronously copies all rows in the supplied sequence of <see cref="DataRow"/> objects to the destination table specified by the
 	/// <see cref="DestinationTableName"/> property of the <see cref="MySqlBulkCopy"/> object. The number of columns
@@ -176,22 +160,6 @@ public async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IEnumerable<DataR
 		m_valuesEnumerator = new DataRowsValuesEnumerator(dataRows ?? throw new ArgumentNullException(nameof(dataRows)), columnCount);
 		return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false);
 	}
-#else
-	/// <summary>
-	/// Asynchronously copies all rows in the supplied sequence of <see cref="DataRow"/> objects to the destination table specified by the
-	/// <see cref="DestinationTableName"/> property of the <see cref="MySqlBulkCopy"/> object. The number of columns
-	/// to be read from the <see cref="DataRow"/> objects must be specified in advance.
-	/// </summary>
-	/// <param name="dataRows">The collection of <see cref="DataRow"/> objects.</param>
-	/// <param name="columnCount">The number of columns to copy (in each row).</param>
-	/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
-	/// <returns>A <see cref="MySqlBulkCopyResult"/> with the result of the bulk copy operation.</returns>
-	public async Task<MySqlBulkCopyResult> WriteToServerAsync(IEnumerable<DataRow> dataRows, int columnCount, CancellationToken cancellationToken = default)
-	{
-		m_valuesEnumerator = new DataRowsValuesEnumerator(dataRows ?? throw new ArgumentNullException(nameof(dataRows)), columnCount);
-		return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false);
-	}
-#endif
 
 	/// <summary>
 	/// Copies all rows in the supplied <see cref="IDataReader"/> to the destination table specified by the
@@ -207,7 +175,6 @@ public MySqlBulkCopyResult WriteToServer(IDataReader dataReader)
 #pragma warning restore CA2012
 	}
 
-#if NETCOREAPP || NETSTANDARD2_0_OR_GREATER
 	/// <summary>
 	/// Asynchronously copies all rows in the supplied <see cref="IDataReader"/> to the destination table specified by the
 	/// <see cref="DestinationTableName"/> property of the <see cref="MySqlBulkCopy"/> object.
@@ -220,20 +187,6 @@ public async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IDataReader dataR
 		m_valuesEnumerator = DataReaderValuesEnumerator.Create(dataReader ?? throw new ArgumentNullException(nameof(dataReader)));
 		return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false);
 	}
-#else
-	/// <summary>
-	/// Asynchronously copies all rows in the supplied <see cref="IDataReader"/> to the destination table specified by the
-	/// <see cref="DestinationTableName"/> property of the <see cref="MySqlBulkCopy"/> object.
-	/// </summary>
-	/// <param name="dataReader">The <see cref="IDataReader"/> to copy from.</param>
-	/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
-	/// <returns>A <see cref="MySqlBulkCopyResult"/> with the result of the bulk copy operation.</returns>
-	public async Task<MySqlBulkCopyResult> WriteToServerAsync(IDataReader dataReader, CancellationToken cancellationToken = default)
-	{
-		m_valuesEnumerator = DataReaderValuesEnumerator.Create(dataReader ?? throw new ArgumentNullException(nameof(dataReader)));
-		return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false);
-	}
-#endif
 
 	private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index c8ec05b5b..b5802327d 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -98,16 +98,6 @@ private MySqlConnection(string connectionString, MySqlConnectorLoggingConfigurat
 	/// <remarks>Transactions may not be nested.</remarks>
 	public new ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel isolationLevel, CancellationToken cancellationToken = default) => BeginTransactionAsync(isolationLevel, default, AsyncIOBehavior, cancellationToken);
 
-	/// <summary>
-	/// Begins a database transaction asynchronously.
-	/// </summary>
-	/// <param name="isolationLevel">The <see cref="IsolationLevel"/> for the transaction.</param>
-	/// <param name="isReadOnly">If <c>true</c>, changes to tables used in the transaction are prohibited; otherwise, they are permitted.</param>
-	/// <param name="cancellationToken">A token to cancel the asynchronous operation.</param>
-	/// <returns>A <see cref="Task{MySqlTransaction}"/> representing the new database transaction.</returns>
-	/// <remarks>Transactions may not be nested.</remarks>
-	public ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel isolationLevel, bool isReadOnly, CancellationToken cancellationToken = default) => BeginTransactionAsync(isolationLevel, isReadOnly, AsyncIOBehavior, cancellationToken);
-
 	/// <summary>
 	/// Begins a database transaction asynchronously.
 	/// </summary>
@@ -133,6 +123,7 @@ protected override async ValueTask<DbTransaction> BeginDbTransactionAsync(Isolat
 	/// <returns>A <see cref="Task{MySqlTransaction}"/> representing the new database transaction.</returns>
 	/// <remarks>Transactions may not be nested.</remarks>
 	public ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel isolationLevel, CancellationToken cancellationToken = default) => BeginTransactionAsync(isolationLevel, default, AsyncIOBehavior, cancellationToken);
+#endif
 
 	/// <summary>
 	/// Begins a database transaction asynchronously.
@@ -143,7 +134,6 @@ protected override async ValueTask<DbTransaction> BeginDbTransactionAsync(Isolat
 	/// <returns>A <see cref="Task{MySqlTransaction}"/> representing the new database transaction.</returns>
 	/// <remarks>Transactions may not be nested.</remarks>
 	public ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel isolationLevel, bool isReadOnly, CancellationToken cancellationToken = default) => BeginTransactionAsync(isolationLevel, isReadOnly, AsyncIOBehavior, cancellationToken);
-#endif
 
 	private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel isolationLevel, bool? isReadOnly, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{

From d4ea8194b773617661dd0052fbec48810ecb67ad Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 2 Sep 2023 18:17:43 -0700
Subject: [PATCH 159/393] Use security-extended CodeQL queries.

---
 .github/workflows/codeql-analysis.yml | 10 +++-------
 1 file changed, 3 insertions(+), 7 deletions(-)

diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index cd7d2c236..88d929bcb 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -23,25 +23,21 @@ jobs:
         language: [ 'csharp' ]
 
     steps:
-    - name: Checkout repository
+    - name: Check out
       uses: actions/checkout@v3
 
     - name: Set up .NET 7.0
       uses: actions/setup-dotnet@v3
       with:
         dotnet-version: 7.x
-        dotnet-quality: preview
 
     - name: Initialize CodeQL
       uses: github/codeql-action/init@v2
       with:
         languages: ${{ matrix.language }}
-        # If you wish to specify custom queries, you can do so here or in a config file.
-        # By default, queries listed here will override any specified in a config file.
-        # Prefix the list here with "+" to use these queries and those in the config file.
-        # queries: ./path/to/local/query, your-org/your-repo/queries@main
+        queries: security-extended
 
-    - name: Autobuild
+    - name: Build
       uses: github/codeql-action/autobuild@v2
 
     - name: Perform CodeQL Analysis

From 1f1008146b8ecd5898b537bc6ed56e668bd4cb8c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 4 Sep 2023 07:47:56 -0700
Subject: [PATCH 160/393] Load column whose name begins with @. Fixes #1365

---
 src/MySqlConnector/MySqlBulkCopy.cs      |  2 +-
 tests/IntegrationTests/BulkLoaderSync.cs | 46 ++++++++++++++++++++++--
 2 files changed, 45 insertions(+), 3 deletions(-)

diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs
index 09e37a567..1babc5dab 100644
--- a/src/MySqlConnector/MySqlBulkCopy.cs
+++ b/src/MySqlConnector/MySqlBulkCopy.cs
@@ -265,7 +265,7 @@ private async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IOBehavior ioBeh
 			{
 				if (columnMapping.DestinationColumn.Length == 0)
 					throw new InvalidOperationException($"MySqlBulkCopyColumnMapping.DestinationName is not set for SourceOrdinal {columnMapping.SourceOrdinal}");
-				if (columnMapping.DestinationColumn[0] == '@')
+				if (columnMapping.DestinationColumn[0] == '@' && columnMapping.Expression is not null)
 					bulkLoader.Columns.Add(columnMapping.DestinationColumn);
 				else
 					bulkLoader.Columns.Add(QuoteIdentifier(columnMapping.DestinationColumn));
diff --git a/tests/IntegrationTests/BulkLoaderSync.cs b/tests/IntegrationTests/BulkLoaderSync.cs
index c141eb688..7c40a36bc 100644
--- a/tests/IntegrationTests/BulkLoaderSync.cs
+++ b/tests/IntegrationTests/BulkLoaderSync.cs
@@ -530,7 +530,6 @@ public void BulkCopyNullDataTable()
 		Assert.Throws<ArgumentNullException>(() => bulkCopy.WriteToServer(default(DataTable)));
 	}
 
-#if !MYSQL_DATA
 	[Fact]
 	public void BulkCopyDataTableWithMySqlDecimal()
 	{
@@ -655,7 +654,6 @@ public void BulkCopyDataTableWithTimeOnly()
 			Assert.Equal(new TimeOnly(1, 2, 3, 456), reader.GetTimeOnly(2));
 		}
 	}
-#endif
 #endif
 
 	public static IEnumerable<object[]> GetBulkCopyData() =>
@@ -713,6 +711,50 @@ public void BulkCopyDataTable(string columnType, object[] rows)
 		}
 	}
 
+	[Fact]
+	public void BulkCopyToColumnNeedingQuoting()
+	{
+		var dataTable = new DataTable()
+		{
+			Columns =
+			{
+				new DataColumn("id", typeof(int)),
+				new DataColumn("@a", typeof(string)),
+			},
+		};
+		dataTable.Rows.Add(2, "two");
+
+		using var connection = new MySqlConnection(GetLocalConnectionString());
+		connection.Open();
+		using (var cmd = new MySqlCommand($"""
+			drop table if exists bulk_load_quoted_identifier;
+			create table bulk_load_quoted_identifier(id int, `@a` text);
+			insert into bulk_load_quoted_identifier values (1, 'one');
+			""", connection))
+		{
+			cmd.ExecuteNonQuery();
+		}
+
+		var bulkCopy = new MySqlBulkCopy(connection)
+		{
+			DestinationTableName = "bulk_load_quoted_identifier",
+		};
+		var result = bulkCopy.WriteToServer(dataTable);
+		Assert.Equal(1, result.RowsInserted);
+		Assert.Empty(result.Warnings);
+
+		using (var cmd = new MySqlCommand(@"select `@a` from bulk_load_quoted_identifier order by id;", connection))
+		{
+			using var reader = cmd.ExecuteReader();
+			Assert.True(reader.Read());
+			Assert.Equal("one", reader.GetString(0));
+			Assert.True(reader.Read());
+			Assert.Equal("two", reader.GetString(0));
+			Assert.False(reader.Read());
+			Assert.False(reader.NextResult());
+		}
+	}
+
 	[Fact]
 	public void BulkCopyDataTableWithLongBlob()
 	{

From 59bf391ed297b793d91fdb7fa7ad9c980331dab5 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 4 Sep 2023 07:54:16 -0700
Subject: [PATCH 161/393] Add MySQL 8.1 to integration test suite.

---
 .ci/docker-run.sh                           | 2 +-
 azure-pipelines.yml                         | 8 ++++----
 docs/content/home.md                        | 2 +-
 tests/IntegrationTests/CharacterSetTests.cs | 2 +-
 4 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index 0d2425a00..d694c1c81 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -25,7 +25,7 @@ OMIT_FEATURES=$3
 MYSQL_EXTRA=
 MYSQL=mysql
 
-if [ "$IMAGE" == "mysql:8.0" ]; then
+if [[ "$IMAGE" == mysql:8.0 || "$IMAGE" == mysql:8.1 ]]; then
   MYSQL_EXTRA='--default-authentication-plugin=mysql_native_password'
 fi
 if [[ "$IMAGE" == mariadb* ]]; then
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index dc81df8b3..91efcf8e0 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -181,10 +181,6 @@ jobs:
     vmimage: 'ubuntu-20.04'
   strategy:
     matrix:
-      'MySQL 5.6':
-        image: 'mysql:5.6'
-        connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,Ed25519,Json,LargePackets,QueryAttributes,ResetConnection,SessionTrack,Sha256Password,Tls11,Tls13,UuidToBin'
       'MySQL 5.7':
         image: 'mysql:5.7'
         connectionStringExtra: 'Tls Cipher Suites=TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256'
@@ -193,6 +189,10 @@ jobs:
         image: 'mysql:8.0'
         connectionStringExtra: ''
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
+      'MySQL 8.1':
+        image: 'mysql:8.1'
+        connectionStringExtra: ''
+        unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
       'MariaDB 10.6':
         image: 'mariadb:10.6'
         connectionStringExtra: ''
diff --git a/docs/content/home.md b/docs/content/home.md
index 05d86eb67..77d1a4852 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -65,7 +65,7 @@ Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://m
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
 MariaDB | 10.x, 11.x (**10.6**, **10.11**, **11.0**) |
-MySQL | 5.5, **5.6**, **5.7**, **8.0** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 is EOL
+MySQL | 5.5, 5.6, **5.7**, **8.0**, **8.1** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 is EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
 ProxySQL | 2.x | Some [compatibility issues](https://github.com/search?q=repo%3Amysql-net%2FMySqlConnector+proxysql&type=issues)
diff --git a/tests/IntegrationTests/CharacterSetTests.cs b/tests/IntegrationTests/CharacterSetTests.cs
index 2c2971f7e..2af81a97a 100644
--- a/tests/IntegrationTests/CharacterSetTests.cs
+++ b/tests/IntegrationTests/CharacterSetTests.cs
@@ -78,7 +78,7 @@ public void CollationConnection(bool reopenConnection)
 		}
 
 		var collation = connection.Query<string>(@"select @@collation_connection;").Single();
-		var expected = connection.ServerVersion.StartsWith("8.0") ? "utf8mb4_0900_ai_ci" : "utf8mb4_general_ci";
+		var expected = connection.ServerVersion.StartsWith("8.", StringComparison.Ordinal) ? "utf8mb4_0900_ai_ci" : "utf8mb4_general_ci";
 		Assert.Equal(expected, collation);
 	}
 

From c881f7620726b50647424f92010c67b6ef2a782f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 4 Sep 2023 09:03:03 -0700
Subject: [PATCH 162/393] Support ZEROFILL MySqlDecimal columns. Fixes #1354

---
 src/MySqlConnector/MySqlDecimal.cs            |  2 +-
 tests/IntegrationTests/InsertTests.cs         | 34 +++++++++++++++++++
 .../MySqlConnector.Tests/MySqlDecimalTests.cs | 20 +++--------
 3 files changed, 39 insertions(+), 17 deletions(-)

diff --git a/src/MySqlConnector/MySqlDecimal.cs b/src/MySqlConnector/MySqlDecimal.cs
index 45b8afc18..b798578af 100644
--- a/src/MySqlConnector/MySqlDecimal.cs
+++ b/src/MySqlConnector/MySqlDecimal.cs
@@ -46,7 +46,7 @@ internal MySqlDecimal(string value)
 		throw new FormatException($"Could not parse the value as a MySqlDecimal: {value}");
 	}
 
-	private static readonly Regex s_pattern = new(@"^-?([1-9][0-9]*|0)(\.([0-9]+))?$");
+	private static readonly Regex s_pattern = new(@"^-?([0-9]+)(\.([0-9]+))?$");
 
 	private readonly string m_value;
 }
diff --git a/tests/IntegrationTests/InsertTests.cs b/tests/IntegrationTests/InsertTests.cs
index b47a1539b..573d63962 100644
--- a/tests/IntegrationTests/InsertTests.cs
+++ b/tests/IntegrationTests/InsertTests.cs
@@ -514,6 +514,40 @@ public void ReadMySqlDecimalUsingReader(bool prepare)
 #endif
 	}
 
+	[Theory]
+	[InlineData(false)]
+	[InlineData(true)]
+	public void ReadMySqlDecimalZeroFill(bool prepare)
+	{
+		using MySqlConnection connection = new MySqlConnection(AppConfig.ConnectionString);
+		connection.Open();
+		connection.Execute("""
+			drop table if exists mysql_decimal_zerofill;
+			create table mysql_decimal_zerofill(rowid integer not null primary key auto_increment, value decimal(20, 10) zerofill);
+			insert into mysql_decimal_zerofill(value) values(0),(1),(0.1);
+			""");
+
+		using var cmd = connection.CreateCommand();
+		cmd.CommandText = @"select value from mysql_decimal_zerofill order by rowid;";
+		if (prepare)
+			cmd.Prepare();
+		using var reader = cmd.ExecuteReader();
+
+		Assert.True(reader.Read());
+		Assert.Equal("0000000000.0000000000", reader.GetMySqlDecimal("value").ToString());
+		Assert.Equal(0m, reader.GetDecimal(0));
+
+		Assert.True(reader.Read());
+		Assert.Equal("0000000001.0000000000", reader.GetMySqlDecimal("value").ToString());
+		Assert.Equal(1m, reader.GetDecimal(0));
+
+		Assert.True(reader.Read());
+		Assert.Equal("0000000000.1000000000", reader.GetMySqlDecimal("value").ToString());
+		Assert.Equal(0.1m, reader.GetDecimal(0));
+
+		Assert.False(reader.Read());
+	}
+
 	[Theory]
 	[InlineData(false)]
 	[InlineData(true)]
diff --git a/tests/MySqlConnector.Tests/MySqlDecimalTests.cs b/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
index 8a32827c2..2df97f8a7 100644
--- a/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
@@ -30,20 +30,6 @@ public void TestToDecimal()
 		Assert.Equal(doubleVal, mySqlDecimal.Value);
 	}
 
-	[Fact]
-	public void TestInvalidFormatWithDecimalPostive()
-	{
-		var invalidValue = "0323.323";
-		Assert.Throws<FormatException>(() => new MySqlDecimal(invalidValue));
-	}
-
-	[Fact]
-	public void TestInvalidFormatWithDecimalNegative()
-	{
-		var invalidValue = "-0323.323";
-		Assert.Throws<FormatException>(() => new MySqlDecimal(invalidValue));
-	}
-
 	[Fact]
 	public void TestValidFormatWithDecimalNegative68Length()
 	{
@@ -109,6 +95,10 @@ public void TestValidFormatWithDecimalNegative67Length()
 	[InlineData("-0.1")]
 	[InlineData("1.0")]
 	[InlineData("1.23")]
+	[InlineData("00")]
+	[InlineData("01")]
+	[InlineData("0323.323")]
+	[InlineData("-0323.323")]
 	[InlineData("12345678901234567890123456789012345678901234567890123456789012345")]
 	[InlineData("-12345678901234567890123456789012345678901234567890123456789012345")]
 	[InlineData("12345678901234567890123456789012345.012345678901234567890123456789")]
@@ -120,8 +110,6 @@ public void ValidDecimalValues(string input) =>
 	[InlineData("")]
 	[InlineData("-0")]
 	[InlineData("-0.0")]
-	[InlineData("00")]
-	[InlineData("01")]
 	[InlineData("123456789012345678901234567890123456789012345678901234567890123456")]
 	[InlineData("-123456789012345678901234567890123456789012345678901234567890123456")]
 	[InlineData("123456789012345678901234567890123456.012345678901234567890123456789")]

From 869a91c67787c6e998002647e01141349ec6bf23 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 4 Sep 2023 16:30:12 +0000
Subject: [PATCH 163/393] Bump actions/checkout from 3 to 4

Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/ci.yml              | 2 +-
 .github/workflows/codeql-analysis.yml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f7bc72786..fad91c006 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -15,7 +15,7 @@ jobs:
 
     steps:
     - name: Checkout repository
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: Set up .NET 7.0
       uses: actions/setup-dotnet@v3
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 88d929bcb..4a4d082d3 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -24,7 +24,7 @@ jobs:
 
     steps:
     - name: Check out
-      uses: actions/checkout@v3
+      uses: actions/checkout@v4
 
     - name: Set up .NET 7.0
       uses: actions/setup-dotnet@v3

From 4652f5c0d852e40295d0b44f5dbb0ff8af53f159 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 4 Sep 2023 13:54:52 -0700
Subject: [PATCH 164/393] Use C# 12 collection expressions.

---
 src/Directory.Build.props                     |   2 +-
 .../Core/BatchedCommandPayloadCreator.cs      |   2 +-
 src/MySqlConnector/Core/ConnectionSettings.cs |   4 +-
 src/MySqlConnector/Core/SchemaProvider.cs     |   6 +-
 src/MySqlConnector/Core/SchemaProvider.g.cs   | 174 +++++++++---------
 src/MySqlConnector/Core/ServerSession.cs      |   2 +-
 src/MySqlConnector/Core/TypeMapper.cs         |  46 ++---
 src/MySqlConnector/MySqlBulkCopy.cs           |   2 +-
 .../MySqlConnectionStringBuilder.cs           | 112 +++++------
 .../Payloads/HandshakeResponse41Payload.cs    |   2 +-
 .../Payloads/InitialHandshakePayload.cs       |   4 +-
 .../Protocol/Payloads/PingPayload.cs          |   2 +-
 .../Protocol/Payloads/QuitPayload.cs          |   2 +-
 .../Payloads/ResetConnectionPayload.cs        |   2 +-
 src/MySqlConnector/Utilities/Utility.cs       |   2 +-
 .../SchemaCollectionGenerator.cs              |   6 +-
 16 files changed, 186 insertions(+), 184 deletions(-)

diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 5cabd4938..fda7a84d9 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -17,7 +17,7 @@
     <PackageValidationBaselineVersion>2.0.0</PackageValidationBaselineVersion>
     <EnableStrictModeForCompatibleFrameworksInPackageValidation>true</EnableStrictModeForCompatibleFrameworksInPackageValidation>
     <EnableStrictModeForCompatibleTfms>true</EnableStrictModeForCompatibleTfms>
-    <LangVersion>11.0</LangVersion>
+    <LangVersion>preview</LangVersion>
     <AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
     <AnalysisLevel>latest-all</AnalysisLevel>
     <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
diff --git a/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs b/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs
index 3b28ebe35..c17c53335 100644
--- a/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs
@@ -13,7 +13,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 		writer.Write((byte) CommandKind.Multi);
 		bool? firstResult = default;
 		bool wroteCommand;
-		ReadOnlySpan<byte> padding = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+		ReadOnlySpan<byte> padding = [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
 		do
 		{
 			// save room for command length
diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index 99c66d702..c690a8a07 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -26,7 +26,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
 			if (csb.LoadBalance != MySqlLoadBalance.RoundRobin)
 				throw new NotSupportedException("LoadBalance not supported when ConnectionProtocol=NamedPipe");
 			ConnectionProtocol = MySqlConnectionProtocol.NamedPipe;
-			HostNames = (csb.Server == "." || string.Equals(csb.Server, "localhost", StringComparison.OrdinalIgnoreCase)) ? s_localhostPipeServer : new[] { csb.Server };
+			HostNames = (csb.Server == "." || string.Equals(csb.Server, "localhost", StringComparison.OrdinalIgnoreCase)) ? s_localhostPipeServer : [ csb.Server ];
 			PipeName = csb.PipeName;
 		}
 		else if (csb.ConnectionProtocol == MySqlConnectionProtocol.SharedMemory)
@@ -272,7 +272,7 @@ private ConnectionSettings(ConnectionSettings other, string host, int port, stri
 		ConnectionString = other.ConnectionString;
 
 		ConnectionProtocol = MySqlConnectionProtocol.Sockets;
-		HostNames = new[] { host };
+		HostNames = [ host ];
 		LoadBalance = other.LoadBalance;
 		Port = port;
 		PipeName = other.PipeName;
diff --git a/src/MySqlConnector/Core/SchemaProvider.cs b/src/MySqlConnector/Core/SchemaProvider.cs
index 3c87b0de1..a8dfe26f6 100644
--- a/src/MySqlConnector/Core/SchemaProvider.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.cs
@@ -116,8 +116,8 @@ private static void DoFillReservedWords(DataTable dataTable)
 		// (but has a higher version number)
 
 		// select word from information_schema.keywords where reserved = 1; on MySQL Server 8.0.18
-		var reservedWords = new[]
-		{
+		string[] reservedWords =
+		[
 			"ACCESSIBLE",
 			"ADD",
 			"ALL",
@@ -380,7 +380,7 @@ private static void DoFillReservedWords(DataTable dataTable)
 			"XOR",
 			"YEAR_MONTH",
 			"ZEROFILL",
-		};
+		];
 
 		foreach (string word in reservedWords)
 			dataTable.Rows.Add(word);
diff --git a/src/MySqlConnector/Core/SchemaProvider.g.cs b/src/MySqlConnector/Core/SchemaProvider.g.cs
index 7a9c5843a..ce47a726e 100644
--- a/src/MySqlConnector/Core/SchemaProvider.g.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.g.cs
@@ -83,12 +83,12 @@ private Task FillMetaDataCollectionsAsync(IOBehavior ioBehavior, DataTable dataT
 			throw new ArgumentException("restrictionValues is not supported for schema 'MetaDataCollections'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("CollectionName", typeof(string)),
 			new("NumberOfRestrictions", typeof(int)),
 			new("NumberOfIdentifierParts", typeof(int)),
-		});
+		]);
 
 		dataTable.Rows.Add("MetaDataCollections", 0, 0);
 		dataTable.Rows.Add("CharacterSets", 0, 0);
@@ -129,13 +129,13 @@ private async Task FillCharacterSetsAsync(IOBehavior ioBehavior, DataTable dataT
 			throw new ArgumentException("restrictionValues is not supported for schema 'CharacterSets'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("CHARACTER_SET_NAME", typeof(string)),
 			new("DEFAULT_COLLATE_NAME", typeof(string)),
 			new("DESCRIPTION", typeof(string)),
 			new("MAXLEN", typeof(int)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "CHARACTER_SETS", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -146,15 +146,15 @@ private async Task FillCollationsAsync(IOBehavior ioBehavior, DataTable dataTabl
 			throw new ArgumentException("restrictionValues is not supported for schema 'Collations'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("COLLATION_NAME", typeof(string)),
 			new("CHARACTER_SET_NAME", typeof(string)),
 			new("ID", typeof(int)),
 			new("IS_DEFAULT", typeof(string)),
 			new("IS_COMPILED", typeof(string)),
 			new("SORTLEN", typeof(int)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "COLLATIONS", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -165,11 +165,11 @@ private async Task FillCollationCharacterSetApplicabilityAsync(IOBehavior ioBeha
 			throw new ArgumentException("restrictionValues is not supported for schema 'CollationCharacterSetApplicability'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("COLLATION_NAME", typeof(string)),
 			new("CHARACTER_SET_NAME", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "COLLATION_CHARACTER_SET_APPLICABILITY", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -180,8 +180,8 @@ private async Task FillColumnsAsync(IOBehavior ioBehavior, DataTable dataTable,
 			throw new ArgumentException("More than 4 restrictionValues are not supported for schema 'Columns'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("TABLE_CATALOG", typeof(string)),
 			new("TABLE_SCHEMA", typeof(string)),
 			new("TABLE_NAME", typeof(string)),
@@ -203,7 +203,7 @@ private async Task FillColumnsAsync(IOBehavior ioBehavior, DataTable dataTable,
 			new("COLUMN_COMMENT", typeof(string)),
 			new("GENERATION_EXPRESSION", typeof(string)),
 			new("SRS_ID", typeof(string)),
-		});
+		]);
 
 		var columns = new List<KeyValuePair<string, string>>();
 		if (restrictionValues is not null)
@@ -227,14 +227,14 @@ private async Task FillDatabasesAsync(IOBehavior ioBehavior, DataTable dataTable
 			throw new ArgumentException("restrictionValues is not supported for schema 'Databases'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("CATALOG_NAME", typeof(string)),
 			new("SCHEMA_NAME", typeof(string)),
 			new("DEFAULT_CHARACTER_SET_NAME", typeof(string)),
 			new("DEFAULT_COLLATION_NAME", typeof(string)),
 			new("SQL_PATH", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "SCHEMATA", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -245,8 +245,8 @@ private Task FillDataSourceInformationAsync(IOBehavior ioBehavior, DataTable dat
 			throw new ArgumentException("restrictionValues is not supported for schema 'DataSourceInformation'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("CompositeIdentifierSeparatorPattern", typeof(string)),
 			new("DataSourceProductName", typeof(string)),
 			new("DataSourceProductVersion", typeof(string)),
@@ -264,7 +264,7 @@ private Task FillDataSourceInformationAsync(IOBehavior ioBehavior, DataTable dat
 			new("StatementSeparatorPattern", typeof(string)),
 			new("StringLiteralPattern", typeof(string)),
 			new("SupportedJoinOperators", typeof(SupportedJoinOperators)),
-		});
+		]);
 
 		DoFillDataSourceInformation(dataTable);
 
@@ -277,8 +277,8 @@ private Task FillDataTypesAsync(IOBehavior ioBehavior, DataTable dataTable, stri
 			throw new ArgumentException("restrictionValues is not supported for schema 'DataTypes'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("TypeName", typeof(string)),
 			new("ProviderDbType", typeof(int)),
 			new("ColumnSize", typeof(long)),
@@ -302,7 +302,7 @@ private Task FillDataTypesAsync(IOBehavior ioBehavior, DataTable dataTable, stri
 			new("LiteralPrefix", typeof(string)),
 			new("LiteralSuffix", typeof(string)),
 			new("NativeDataType", typeof(string)),
-		});
+		]);
 
 		DoFillDataTypes(dataTable);
 
@@ -315,15 +315,15 @@ private async Task FillEnginesAsync(IOBehavior ioBehavior, DataTable dataTable,
 			throw new ArgumentException("restrictionValues is not supported for schema 'Engines'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("ENGINE", typeof(string)),
 			new("SUPPORT", typeof(string)),
 			new("COMMENT", typeof(string)),
 			new("TRANSACTIONS", typeof(string)),
 			new("XA", typeof(string)),
 			new("SAVEPOINTS", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "ENGINES", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -334,8 +334,8 @@ private async Task FillKeyColumnUsageAsync(IOBehavior ioBehavior, DataTable data
 			throw new ArgumentException("restrictionValues is not supported for schema 'KeyColumnUsage'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("CONSTRAINT_CATALOG", typeof(string)),
 			new("CONSTRAINT_SCHEMA", typeof(string)),
 			new("CONSTRAINT_NAME", typeof(string)),
@@ -348,7 +348,7 @@ private async Task FillKeyColumnUsageAsync(IOBehavior ioBehavior, DataTable data
 			new("REFERENCED_TABLE_SCHEMA", typeof(string)),
 			new("REFERENCED_TABLE_NAME", typeof(string)),
 			new("REFERENCED_COLUMN_NAME", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "KEY_COLUMN_USAGE", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -359,11 +359,11 @@ private async Task FillKeyWordsAsync(IOBehavior ioBehavior, DataTable dataTable,
 			throw new ArgumentException("restrictionValues is not supported for schema 'KeyWords'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("WORD", typeof(string)),
 			new("RESERVED", typeof(int)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "KEYWORDS", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -374,8 +374,8 @@ private async Task FillParametersAsync(IOBehavior ioBehavior, DataTable dataTabl
 			throw new ArgumentException("restrictionValues is not supported for schema 'Parameters'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("SPECIFIC_CATALOG", typeof(string)),
 			new("SPECIFIC_SCHEMA", typeof(string)),
 			new("SPECIFIC_NAME", typeof(string)),
@@ -392,7 +392,7 @@ private async Task FillParametersAsync(IOBehavior ioBehavior, DataTable dataTabl
 			new("COLLATION_NAME", typeof(string)),
 			new("DTD_IDENTIFIER", typeof(string)),
 			new("ROUTINE_TYPE", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "PARAMETERS", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -403,8 +403,8 @@ private async Task FillPartitionsAsync(IOBehavior ioBehavior, DataTable dataTabl
 			throw new ArgumentException("restrictionValues is not supported for schema 'Partitions'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("TABLE_CATALOG", typeof(string)),
 			new("TABLE_SCHEMA", typeof(string)),
 			new("TABLE_NAME", typeof(string)),
@@ -430,7 +430,7 @@ private async Task FillPartitionsAsync(IOBehavior ioBehavior, DataTable dataTabl
 			new("PARTITION_COMMENT", typeof(string)),
 			new("NODEGROUP", typeof(string)),
 			new("TABLESPACE_NAME", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "PARTITIONS", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -441,8 +441,8 @@ private async Task FillPluginsAsync(IOBehavior ioBehavior, DataTable dataTable,
 			throw new ArgumentException("restrictionValues is not supported for schema 'Plugins'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("PLUGIN_NAME", typeof(string)),
 			new("PLUGIN_VERSION", typeof(string)),
 			new("PLUGIN_STATUS", typeof(string)),
@@ -454,7 +454,7 @@ private async Task FillPluginsAsync(IOBehavior ioBehavior, DataTable dataTable,
 			new("PLUGIN_DESCRIPTION", typeof(string)),
 			new("PLUGIN_LICENSE", typeof(string)),
 			new("LOAD_OPTION", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "PLUGINS", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -465,8 +465,8 @@ private async Task FillProceduresAsync(IOBehavior ioBehavior, DataTable dataTabl
 			throw new ArgumentException("restrictionValues is not supported for schema 'Procedures'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("SPECIFIC_NAME", typeof(string)),
 			new("ROUTINE_CATALOG", typeof(string)),
 			new("ROUTINE_SCHEMA", typeof(string)),
@@ -487,7 +487,7 @@ private async Task FillProceduresAsync(IOBehavior ioBehavior, DataTable dataTabl
 			new("SQL_MODE", typeof(string)),
 			new("ROUTINE_COMMENT", typeof(string)),
 			new("DEFINER", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "ROUTINES", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -498,8 +498,8 @@ private async Task FillProcessListAsync(IOBehavior ioBehavior, DataTable dataTab
 			throw new ArgumentException("restrictionValues is not supported for schema 'ProcessList'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("ID", typeof(long)),
 			new("USER", typeof(string)),
 			new("HOST", typeof(string)),
@@ -508,7 +508,7 @@ private async Task FillProcessListAsync(IOBehavior ioBehavior, DataTable dataTab
 			new("TIME", typeof(int)),
 			new("STATE", typeof(string)),
 			new("INFO", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "PROCESSLIST", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -519,8 +519,8 @@ private async Task FillProfilingAsync(IOBehavior ioBehavior, DataTable dataTable
 			throw new ArgumentException("restrictionValues is not supported for schema 'Profiling'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("QUERY_ID", typeof(int)),
 			new("SEQ", typeof(int)),
 			new("STATE", typeof(string)),
@@ -539,7 +539,7 @@ private async Task FillProfilingAsync(IOBehavior ioBehavior, DataTable dataTable
 			new("SOURCE_FUNCTION", typeof(string)),
 			new("SOURCE_FILE", typeof(string)),
 			new("SOURCE_LINE", typeof(int)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "PROFILING", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -550,8 +550,8 @@ private async Task FillReferentialConstraintsAsync(IOBehavior ioBehavior, DataTa
 			throw new ArgumentException("restrictionValues is not supported for schema 'ReferentialConstraints'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("CONSTRAINT_CATALOG", typeof(string)),
 			new("CONSTRAINT_SCHEMA", typeof(string)),
 			new("CONSTRAINT_NAME", typeof(string)),
@@ -563,7 +563,7 @@ private async Task FillReferentialConstraintsAsync(IOBehavior ioBehavior, DataTa
 			new("DELETE_RULE", typeof(string)),
 			new("TABLE_NAME", typeof(string)),
 			new("REFERENCED_TABLE_NAME", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "REFERENTIAL_CONSTRAINTS", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -574,10 +574,10 @@ private Task FillReservedWordsAsync(IOBehavior ioBehavior, DataTable dataTable,
 			throw new ArgumentException("restrictionValues is not supported for schema 'ReservedWords'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("ReservedWord", typeof(string)),
-		});
+		]);
 
 		DoFillReservedWords(dataTable);
 
@@ -590,14 +590,14 @@ private async Task FillResourceGroupsAsync(IOBehavior ioBehavior, DataTable data
 			throw new ArgumentException("restrictionValues is not supported for schema 'ResourceGroups'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("RESOURCE_GROUP_NAME", typeof(string)),
 			new("RESOURCE_GROUP_TYPE", typeof(string)),
 			new("RESOURCE_GROUP_ENABLED", typeof(int)),
 			new("VCPU_IDS", typeof(string)),
 			new("THREAD_PRIORITY", typeof(int)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "RESOURCE_GROUPS", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -608,13 +608,13 @@ private Task FillRestrictionsAsync(IOBehavior ioBehavior, DataTable dataTable, s
 			throw new ArgumentException("restrictionValues is not supported for schema 'Restrictions'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("CollectionName", typeof(string)),
 			new("RestrictionName", typeof(string)),
 			new("RestrictionDefault", typeof(string)),
 			new("RestrictionNumber", typeof(int)),
-		});
+		]);
 
 		dataTable.Rows.Add("Columns", "Catalog", "TABLE_CATALOG", 1);
 		dataTable.Rows.Add("Columns", "Schema", "TABLE_SCHEMA", 2);
@@ -634,14 +634,14 @@ private async Task FillSchemaPrivilegesAsync(IOBehavior ioBehavior, DataTable da
 			throw new ArgumentException("restrictionValues is not supported for schema 'SchemaPrivileges'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("GRANTEE", typeof(string)),
 			new("TABLE_CATALOG", typeof(string)),
 			new("TABLE_SCHEMA", typeof(string)),
 			new("PRIVILEGE_TYPE", typeof(string)),
 			new("IS_GRANTABLE", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "SCHEMA_PRIVILEGES", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -652,8 +652,8 @@ private async Task FillTablesAsync(IOBehavior ioBehavior, DataTable dataTable, s
 			throw new ArgumentException("More than 4 restrictionValues are not supported for schema 'Tables'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("TABLE_CATALOG", typeof(string)),
 			new("TABLE_SCHEMA", typeof(string)),
 			new("TABLE_NAME", typeof(string)),
@@ -675,7 +675,7 @@ private async Task FillTablesAsync(IOBehavior ioBehavior, DataTable dataTable, s
 			new("CHECKSUM", typeof(string)),
 			new("CREATE_OPTIONS", typeof(string)),
 			new("TABLE_COMMENT", typeof(string)),
-		});
+		]);
 
 		var columns = new List<KeyValuePair<string, string>>();
 		if (restrictionValues is not null)
@@ -699,15 +699,15 @@ private async Task FillTableConstraintsAsync(IOBehavior ioBehavior, DataTable da
 			throw new ArgumentException("restrictionValues is not supported for schema 'TableConstraints'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("CONSTRAINT_CATALOG", typeof(string)),
 			new("CONSTRAINT_SCHEMA", typeof(string)),
 			new("CONSTRAINT_NAME", typeof(string)),
 			new("TABLE_SCHEMA", typeof(string)),
 			new("TABLE_NAME", typeof(string)),
 			new("CONSTRAINT_TYPE", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "TABLE_CONSTRAINTS", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -718,15 +718,15 @@ private async Task FillTablePrivilegesAsync(IOBehavior ioBehavior, DataTable dat
 			throw new ArgumentException("restrictionValues is not supported for schema 'TablePrivileges'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("GRANTEE", typeof(string)),
 			new("TABLE_CATALOG", typeof(string)),
 			new("TABLE_SCHEMA", typeof(string)),
 			new("TABLE_NAME", typeof(string)),
 			new("PRIVILEGE_TYPE", typeof(string)),
 			new("IS_GRANTABLE", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "TABLE_PRIVILEGES", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -737,8 +737,8 @@ private async Task FillTableSpacesAsync(IOBehavior ioBehavior, DataTable dataTab
 			throw new ArgumentException("restrictionValues is not supported for schema 'TableSpaces'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("TABLESPACE_NAME", typeof(string)),
 			new("ENGINE", typeof(string)),
 			new("TABLESPACE_TYPE", typeof(string)),
@@ -748,7 +748,7 @@ private async Task FillTableSpacesAsync(IOBehavior ioBehavior, DataTable dataTab
 			new("MAXIMUM_SIZE", typeof(long)),
 			new("NODEGROUP_ID", typeof(long)),
 			new("TABLESPACE_COMMENT", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "TABLESPACES", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -759,8 +759,8 @@ private async Task FillTriggersAsync(IOBehavior ioBehavior, DataTable dataTable,
 			throw new ArgumentException("restrictionValues is not supported for schema 'Triggers'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("TRIGGER_CATALOG", typeof(string)),
 			new("TRIGGER_SCHEMA", typeof(string)),
 			new("TRIGGER_NAME", typeof(string)),
@@ -783,7 +783,7 @@ private async Task FillTriggersAsync(IOBehavior ioBehavior, DataTable dataTable,
 			new("CHARACTER_SET_CLIENT", typeof(string)),
 			new("COLLATION_CONNECTION", typeof(string)),
 			new("DATABASE_COLLATION", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "TRIGGERS", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -794,13 +794,13 @@ private async Task FillUserPrivilegesAsync(IOBehavior ioBehavior, DataTable data
 			throw new ArgumentException("restrictionValues is not supported for schema 'UserPrivileges'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("GRANTEE", typeof(string)),
 			new("TABLE_CATALOG", typeof(string)),
 			new("PRIVILEGE_TYPE", typeof(string)),
 			new("IS_GRANTABLE", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "USER_PRIVILEGES", null, cancellationToken).ConfigureAwait(false);
 	}
@@ -811,8 +811,8 @@ private async Task FillViewsAsync(IOBehavior ioBehavior, DataTable dataTable, st
 			throw new ArgumentException("restrictionValues is not supported for schema 'Views'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(new DataColumn[]
-		{
+		dataTable.Columns.AddRange(
+		[
 			new("TABLE_CATALOG", typeof(string)),
 			new("TABLE_SCHEMA", typeof(string)),
 			new("TABLE_NAME", typeof(string)),
@@ -823,7 +823,7 @@ private async Task FillViewsAsync(IOBehavior ioBehavior, DataTable dataTable, st
 			new("SECURITY_TYPE", typeof(string)),
 			new("CHARACTER_SET_CLIENT", typeof(string)),
 			new("COLLATION_CONNECTION", typeof(string)),
-		});
+		]);
 
 		await FillDataTableAsync(ioBehavior, dataTable, "VIEWS", null, cancellationToken).ConfigureAwait(false);
 	}
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 3657f851e..3e4910349 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -850,7 +850,7 @@ private async Task<string> GetRsaPublicKeyAsync(string switchRequestName, Connec
 		{
 			// request the RSA public key
 			var payloadContent = switchRequestName == "caching_sha2_password" ? (byte) 0x02 : (byte) 0x01;
-			await SendReplyAsync(new PayloadData(new[] { payloadContent }), ioBehavior, cancellationToken).ConfigureAwait(false);
+			await SendReplyAsync(new PayloadData([ payloadContent ]), ioBehavior, cancellationToken).ConfigureAwait(false);
 			var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 			var publicKeyPayload = AuthenticationMoreDataPayload.Create(payload.Span);
 			return Encoding.ASCII.GetString(publicKeyPayload.Data);
diff --git a/src/MySqlConnector/Core/TypeMapper.cs b/src/MySqlConnector/Core/TypeMapper.cs
index 2888ce05e..3c5589ec4 100644
--- a/src/MySqlConnector/Core/TypeMapper.cs
+++ b/src/MySqlConnector/Core/TypeMapper.cs
@@ -20,18 +20,18 @@ private TypeMapper()
 		m_mySqlDbTypeToColumnTypeMetadata = new();
 
 		// boolean
-		var typeBoolean = AddDbTypeMapping(new(typeof(bool), new[] { DbType.Boolean }, convert: static o => Convert.ToBoolean(o, CultureInfo.InvariantCulture)));
+		var typeBoolean = AddDbTypeMapping(new(typeof(bool), [ DbType.Boolean ], convert: static o => Convert.ToBoolean(o, CultureInfo.InvariantCulture)));
 		AddColumnTypeMetadata(new("TINYINT", typeBoolean, MySqlDbType.Bool, isUnsigned: false, length: 1, columnSize: 1, simpleDataTypeName: "BOOL", createFormat: "BOOL"));
 
 		// integers
-		var typeSbyte = AddDbTypeMapping(new(typeof(sbyte), new[] { DbType.SByte }, convert: static o => Convert.ToSByte(o, CultureInfo.InvariantCulture)));
-		var typeByte = AddDbTypeMapping(new(typeof(byte), new[] { DbType.Byte }, convert: static o => Convert.ToByte(o, CultureInfo.InvariantCulture)));
-		var typeShort = AddDbTypeMapping(new(typeof(short), new[] { DbType.Int16 }, convert: static o => Convert.ToInt16(o, CultureInfo.InvariantCulture)));
-		var typeUshort = AddDbTypeMapping(new(typeof(ushort), new[] { DbType.UInt16 }, convert: static o => Convert.ToUInt16(o, CultureInfo.InvariantCulture)));
-		var typeInt = AddDbTypeMapping(new(typeof(int), new[] { DbType.Int32 }, convert: static o => Convert.ToInt32(o, CultureInfo.InvariantCulture)));
-		var typeUint = AddDbTypeMapping(new(typeof(uint), new[] { DbType.UInt32 }, convert: static o => Convert.ToUInt32(o, CultureInfo.InvariantCulture)));
-		var typeLong = AddDbTypeMapping(new(typeof(long), new[] { DbType.Int64 }, convert: static o => Convert.ToInt64(o, CultureInfo.InvariantCulture)));
-		var typeUlong = AddDbTypeMapping(new(typeof(ulong), new[] { DbType.UInt64 }, convert: static o => Convert.ToUInt64(o, CultureInfo.InvariantCulture)));
+		var typeSbyte = AddDbTypeMapping(new(typeof(sbyte), [ DbType.SByte ], convert: static o => Convert.ToSByte(o, CultureInfo.InvariantCulture)));
+		var typeByte = AddDbTypeMapping(new(typeof(byte), [ DbType.Byte ], convert: static o => Convert.ToByte(o, CultureInfo.InvariantCulture)));
+		var typeShort = AddDbTypeMapping(new(typeof(short), [ DbType.Int16 ], convert: static o => Convert.ToInt16(o, CultureInfo.InvariantCulture)));
+		var typeUshort = AddDbTypeMapping(new(typeof(ushort), [ DbType.UInt16 ], convert: static o => Convert.ToUInt16(o, CultureInfo.InvariantCulture)));
+		var typeInt = AddDbTypeMapping(new(typeof(int), [ DbType.Int32 ], convert: static o => Convert.ToInt32(o, CultureInfo.InvariantCulture)));
+		var typeUint = AddDbTypeMapping(new(typeof(uint), [ DbType.UInt32 ], convert: static o => Convert.ToUInt32(o, CultureInfo.InvariantCulture)));
+		var typeLong = AddDbTypeMapping(new(typeof(long), [ DbType.Int64 ], convert: static o => Convert.ToInt64(o, CultureInfo.InvariantCulture)));
+		var typeUlong = AddDbTypeMapping(new(typeof(ulong), [ DbType.UInt64 ], convert: static o => Convert.ToUInt64(o, CultureInfo.InvariantCulture)));
 		AddColumnTypeMetadata(new("TINYINT", typeSbyte, MySqlDbType.Byte, isUnsigned: false));
 		AddColumnTypeMetadata(new("TINYINT", typeByte, MySqlDbType.UByte, isUnsigned: true, length: 1));
 		AddColumnTypeMetadata(new("TINYINT", typeByte, MySqlDbType.UByte, isUnsigned: true));
@@ -46,9 +46,9 @@ private TypeMapper()
 		AddColumnTypeMetadata(new("BIT", typeUlong, MySqlDbType.Bit));
 
 		// decimals
-		var typeDecimal = AddDbTypeMapping(new(typeof(decimal), new[] { DbType.Decimal, DbType.Currency, DbType.VarNumeric }, convert: static o => Convert.ToDecimal(o, CultureInfo.InvariantCulture)));
-		var typeDouble = AddDbTypeMapping(new(typeof(double), new[] { DbType.Double }, convert: static o => Convert.ToDouble(o, CultureInfo.InvariantCulture)));
-		var typeFloat = AddDbTypeMapping(new(typeof(float), new[] { DbType.Single }, convert: static o => Convert.ToSingle(o, CultureInfo.InvariantCulture)));
+		var typeDecimal = AddDbTypeMapping(new(typeof(decimal), [ DbType.Decimal, DbType.Currency, DbType.VarNumeric ], convert: static o => Convert.ToDecimal(o, CultureInfo.InvariantCulture)));
+		var typeDouble = AddDbTypeMapping(new(typeof(double), [ DbType.Double ], convert: static o => Convert.ToDouble(o, CultureInfo.InvariantCulture)));
+		var typeFloat = AddDbTypeMapping(new(typeof(float), [ DbType.Single ], convert: static o => Convert.ToSingle(o, CultureInfo.InvariantCulture)));
 		AddColumnTypeMetadata(new("DECIMAL", typeDecimal, MySqlDbType.NewDecimal, createFormat: "DECIMAL({0},{1});precision,scale"));
 		AddColumnTypeMetadata(new("DECIMAL", typeDecimal, MySqlDbType.NewDecimal, isUnsigned: true, createFormat: "DECIMAL({0},{1}) UNSIGNED;precision,scale"));
 		AddColumnTypeMetadata(new("DECIMAL", typeDecimal, MySqlDbType.Decimal));
@@ -56,8 +56,8 @@ private TypeMapper()
 		AddColumnTypeMetadata(new("FLOAT", typeFloat, MySqlDbType.Float));
 
 		// string
-		var typeFixedString = AddDbTypeMapping(new(typeof(string), new[] { DbType.StringFixedLength, DbType.AnsiStringFixedLength }, convert: Convert.ToString!));
-		var typeString = AddDbTypeMapping(new(typeof(string), new[] { DbType.String, DbType.AnsiString, DbType.Xml }, convert: Convert.ToString!));
+		var typeFixedString = AddDbTypeMapping(new(typeof(string), [ DbType.StringFixedLength, DbType.AnsiStringFixedLength ], convert: Convert.ToString!));
+		var typeString = AddDbTypeMapping(new(typeof(string), [ DbType.String, DbType.AnsiString, DbType.Xml ], convert: Convert.ToString!));
 		AddColumnTypeMetadata(new("VARCHAR", typeString, MySqlDbType.VarChar, createFormat: "VARCHAR({0});size"));
 		AddColumnTypeMetadata(new("VARCHAR", typeString, MySqlDbType.VarString));
 		AddColumnTypeMetadata(new("CHAR", typeFixedString, MySqlDbType.String, createFormat: "CHAR({0});size"));
@@ -70,7 +70,7 @@ private TypeMapper()
 		AddColumnTypeMetadata(new("JSON", typeString, MySqlDbType.JSON));
 
 		// binary
-		var typeBinary = AddDbTypeMapping(new(typeof(byte[]), new[] { DbType.Binary }));
+		var typeBinary = AddDbTypeMapping(new(typeof(byte[]), [ DbType.Binary ]));
 		AddColumnTypeMetadata(new("BLOB", typeBinary, MySqlDbType.Blob, binary: true, columnSize: ushort.MaxValue, simpleDataTypeName: "BLOB"));
 		AddColumnTypeMetadata(new("BINARY", typeBinary, MySqlDbType.Binary, binary: true, simpleDataTypeName: "BLOB", createFormat: "BINARY({0});length"));
 		AddColumnTypeMetadata(new("VARBINARY", typeBinary, MySqlDbType.VarBinary, binary: true, simpleDataTypeName: "BLOB", createFormat: "VARBINARY({0});length"));
@@ -91,15 +91,15 @@ private TypeMapper()
 
 		// date/time
 #if NET6_0_OR_GREATER
-		AddDbTypeMapping(new(typeof(DateOnly), new[] { DbType.Date }));
+		AddDbTypeMapping(new(typeof(DateOnly), [ DbType.Date ]));
 #endif
-		var typeDate = AddDbTypeMapping(new(typeof(DateTime), new[] { DbType.Date }));
-		var typeDateTime = AddDbTypeMapping(new(typeof(DateTime), new[] { DbType.DateTime, DbType.DateTime2, DbType.DateTimeOffset }));
-		AddDbTypeMapping(new(typeof(DateTimeOffset), new[] { DbType.DateTimeOffset }));
+		var typeDate = AddDbTypeMapping(new(typeof(DateTime), [ DbType.Date ]));
+		var typeDateTime = AddDbTypeMapping(new(typeof(DateTime), [ DbType.DateTime, DbType.DateTime2, DbType.DateTimeOffset ]));
+		AddDbTypeMapping(new(typeof(DateTimeOffset), [ DbType.DateTimeOffset ]));
 #if NET6_0_OR_GREATER
-		AddDbTypeMapping(new(typeof(TimeOnly), new[] { DbType.Time }));
+		AddDbTypeMapping(new(typeof(TimeOnly), [ DbType.Time ]));
 #endif
-		var typeTime = AddDbTypeMapping(new(typeof(TimeSpan), new[] { DbType.Time }, convert: static o => o is string s ? Utility.ParseTimeSpan(Encoding.UTF8.GetBytes(s)) : Convert.ChangeType(o, typeof(TimeSpan), CultureInfo.InvariantCulture)));
+		var typeTime = AddDbTypeMapping(new(typeof(TimeSpan), [ DbType.Time ], convert: static o => o is string s ? Utility.ParseTimeSpan(Encoding.UTF8.GetBytes(s)) : Convert.ChangeType(o, typeof(TimeSpan), CultureInfo.InvariantCulture)));
 		AddColumnTypeMetadata(new("DATETIME", typeDateTime, MySqlDbType.DateTime));
 		AddColumnTypeMetadata(new("DATE", typeDate, MySqlDbType.Date));
 		AddColumnTypeMetadata(new("DATE", typeDate, MySqlDbType.Newdate));
@@ -113,11 +113,11 @@ private TypeMapper()
 #else
 		Func<object, object> convertGuid = static o => Guid.Parse(Convert.ToString(o, CultureInfo.InvariantCulture)!);
 #endif
-		var typeGuid = AddDbTypeMapping(new(typeof(Guid), new[] { DbType.Guid }, convert: convertGuid));
+		var typeGuid = AddDbTypeMapping(new(typeof(Guid), [ DbType.Guid ], convert: convertGuid));
 		AddColumnTypeMetadata(new("CHAR", typeGuid, MySqlDbType.Guid, length: 36, simpleDataTypeName: "CHAR(36)", createFormat: "CHAR(36)"));
 
 		// null
-		var typeNull = AddDbTypeMapping(new(typeof(object), new[] { DbType.Object }));
+		var typeNull = AddDbTypeMapping(new(typeof(object), [ DbType.Object ]));
 		AddColumnTypeMetadata(new("NULL", typeNull, MySqlDbType.Null));
 	}
 
diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs
index bcd41ac4e..1724bb411 100644
--- a/src/MySqlConnector/MySqlBulkCopy.cs
+++ b/src/MySqlConnector/MySqlBulkCopy.cs
@@ -658,7 +658,7 @@ static bool WriteBytes(ReadOnlySpan<byte> value, ref int inputIndex, Span<byte>
 		}
 	}
 
-	private static readonly char[] s_specialCharacters = new char[] { '\t', '\\', '\n' };
+	private static readonly char[] s_specialCharacters = [ '\t', '\\', '\n' ];
 	private static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
 
 	private readonly MySqlConnection m_connection;
diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index f0ed5961f..ec9a72509 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -989,72 +989,72 @@ static MySqlConnectionStringOption()
 		// Base Options
 #pragma warning disable SA1118 // Parameter should not span multiple lines
 		AddOption(options, Server = new(
-			keys: new[] { "Server", "Host", "Data Source", "DataSource", "Address", "Addr", "Network Address" },
+			keys: [ "Server", "Host", "Data Source", "DataSource", "Address", "Addr", "Network Address" ],
 			defaultValue: ""));
 
 		AddOption(options, Port = new(
-			keys: new[] { "Port" },
+			keys: [ "Port" ],
 			defaultValue: 3306u));
 
 		AddOption(options, UserID = new(
-			keys: new[] { "User ID", "UserID", "Username", "Uid", "User name", "User" },
+			keys: [ "User ID", "UserID", "Username", "Uid", "User name", "User" ],
 			defaultValue: ""));
 
 		AddOption(options, Password = new(
-			keys: new[] { "Password", "pwd" },
+			keys: [ "Password", "pwd" ],
 			defaultValue: ""));
 
 		AddOption(options, Database = new(
-			keys: new[] { "Database", "Initial Catalog" },
+			keys: [ "Database", "Initial Catalog" ],
 			defaultValue: ""));
 
 		AddOption(options, LoadBalance = new(
-			keys: new[] { "Load Balance", "LoadBalance" },
+			keys: [ "Load Balance", "LoadBalance" ],
 			defaultValue: MySqlLoadBalance.RoundRobin));
 
 		AddOption(options, ConnectionProtocol = new(
-			keys: new[] { "Connection Protocol", "ConnectionProtocol", "Protocol" },
+			keys: [ "Connection Protocol", "ConnectionProtocol", "Protocol" ],
 			defaultValue: MySqlConnectionProtocol.Socket));
 
 		AddOption(options, PipeName = new(
-			keys: new[] { "Pipe Name", "PipeName", "Pipe" },
+			keys: [ "Pipe Name", "PipeName", "Pipe" ],
 			defaultValue: "MYSQL"));
 
 		// SSL/TLS Options
 		AddOption(options, SslMode = new(
-			keys: new[] { "SSL Mode", "SslMode" },
+			keys: [ "SSL Mode", "SslMode" ],
 			defaultValue: MySqlSslMode.Preferred));
 
 		AddOption(options, CertificateFile = new(
-			keys: new[] { "Certificate File", "CertificateFile" },
+			keys: [ "Certificate File", "CertificateFile" ],
 			defaultValue: ""));
 
 		AddOption(options, CertificatePassword = new(
-			keys: new[] { "Certificate Password", "CertificatePassword" },
+			keys: [ "Certificate Password", "CertificatePassword" ],
 			defaultValue: ""));
 
 		AddOption(options, CertificateStoreLocation = new(
-			keys: new[] { "Certificate Store Location", "CertificateStoreLocation" },
+			keys: [ "Certificate Store Location", "CertificateStoreLocation" ],
 			defaultValue: MySqlCertificateStoreLocation.None));
 
 		AddOption(options, CertificateThumbprint = new(
-			keys: new[] { "Certificate Thumbprint", "CertificateThumbprint", "Certificate Thumb Print" },
+			keys: [ "Certificate Thumbprint", "CertificateThumbprint", "Certificate Thumb Print" ],
 			defaultValue: ""));
 
 		AddOption(options, SslCert = new(
-			keys: new[] { "SSL Cert", "SslCert", "Ssl-Cert" },
+			keys: [ "SSL Cert", "SslCert", "Ssl-Cert" ],
 			defaultValue: ""));
 
 		AddOption(options, SslKey = new(
-			keys: new[] { "SSL Key", "SslKey", "Ssl-Key" },
+			keys: [ "SSL Key", "SslKey", "Ssl-Key" ],
 			defaultValue: ""));
 
 		AddOption(options, SslCa = new(
-			keys: new[] { "SSL CA", "CACertificateFile", "CA Certificate File", "SslCa", "Ssl-Ca" },
+			keys: [ "SSL CA", "CACertificateFile", "CA Certificate File", "SslCa", "Ssl-Ca" ],
 			defaultValue: ""));
 
 		AddOption(options, TlsVersion = new(
-			keys: new[] { "TLS Version", "TlsVersion", "Tls-Version" },
+			keys: [ "TLS Version", "TlsVersion", "Tls-Version" ],
 			defaultValue: "",
 			coerce: value =>
 			{
@@ -1097,73 +1097,73 @@ static MySqlConnectionStringOption()
 			}));
 
 		AddOption(options, TlsCipherSuites = new(
-			keys: new[] { "TLS Cipher Suites", "TlsCipherSuites" },
+			keys: [ "TLS Cipher Suites", "TlsCipherSuites" ],
 			defaultValue: ""));
 
 		// Connection Pooling Options
 		AddOption(options, Pooling = new(
-			keys: new[] { "Pooling" },
+			keys: [ "Pooling" ],
 			defaultValue: true));
 
 		AddOption(options, ConnectionLifeTime = new(
-			keys: new[] { "Connection Lifetime", "ConnectionLifeTime" },
+			keys: [ "Connection Lifetime", "ConnectionLifeTime" ],
 			defaultValue: 0u));
 
 		AddOption(options, ConnectionReset = new(
-			keys: new[] { "Connection Reset", "ConnectionReset" },
+			keys: [ "Connection Reset", "ConnectionReset" ],
 			defaultValue: true));
 
 		AddOption(options, DeferConnectionReset = new(
-			keys: new[] { "Defer Connection Reset", "DeferConnectionReset" },
+			keys: [ "Defer Connection Reset", "DeferConnectionReset" ],
 			defaultValue: true));
 
 		AddOption(options, ConnectionIdlePingTime = new(
-			keys: new[] { "Connection Idle Ping Time", "ConnectionIdlePingTime" },
+			keys: [ "Connection Idle Ping Time", "ConnectionIdlePingTime" ],
 			defaultValue: 0u));
 
 		AddOption(options, ConnectionIdleTimeout = new(
-			keys: new[] { "Connection Idle Timeout", "ConnectionIdleTimeout" },
+			keys: [ "Connection Idle Timeout", "ConnectionIdleTimeout" ],
 			defaultValue: 180u));
 
 		AddOption(options, MinimumPoolSize = new(
-			keys: new[] { "Minimum Pool Size", "Min Pool Size", "MinimumPoolSize", "minpoolsize" },
+			keys: [ "Minimum Pool Size", "Min Pool Size", "MinimumPoolSize", "minpoolsize" ],
 			defaultValue: 0u));
 
 		AddOption(options, MaximumPoolSize = new(
-			keys: new[] { "Maximum Pool Size", "Max Pool Size", "MaximumPoolSize", "maxpoolsize" },
+			keys: [ "Maximum Pool Size", "Max Pool Size", "MaximumPoolSize", "maxpoolsize" ],
 			defaultValue: 100u));
 
 		AddOption(options, DnsCheckInterval = new(
-			keys: new[] { "DNS Check Interval", "DnsCheckInterval" },
+			keys: [ "DNS Check Interval", "DnsCheckInterval" ],
 			defaultValue: 0u));
 
 		// Other Options
 		AddOption(options, AllowLoadLocalInfile = new(
-			keys: new[] { "Allow Load Local Infile", "AllowLoadLocalInfile" },
+			keys: [ "Allow Load Local Infile", "AllowLoadLocalInfile" ],
 			defaultValue: false));
 
 		AddOption(options, AllowPublicKeyRetrieval = new(
-			keys: new[] { "Allow Public Key Retrieval", "AllowPublicKeyRetrieval" },
+			keys: [ "Allow Public Key Retrieval", "AllowPublicKeyRetrieval" ],
 			defaultValue: false));
 
 		AddOption(options, AllowUserVariables = new(
-			keys: new[] { "Allow User Variables", "AllowUserVariables" },
+			keys: [ "Allow User Variables", "AllowUserVariables" ],
 			defaultValue: false));
 
 		AddOption(options, AllowZeroDateTime = new(
-			keys: new[] { "Allow Zero DateTime", "AllowZeroDateTime" },
+			keys: [ "Allow Zero DateTime", "AllowZeroDateTime" ],
 			defaultValue: false));
 
 		AddOption(options, ApplicationName = new(
-			keys: new[] { "Application Name", "ApplicationName" },
+			keys: [ "Application Name", "ApplicationName" ],
 			defaultValue: ""));
 
 		AddOption(options, AutoEnlist = new(
-			keys: new[] { "Auto Enlist", "AutoEnlist" },
+			keys: [ "Auto Enlist", "AutoEnlist" ],
 			defaultValue: true));
 
 		AddOption(options, CancellationTimeout = new(
-			keys: new[] { "Cancellation Timeout", "CancellationTimeout" },
+			keys: [ "Cancellation Timeout", "CancellationTimeout" ],
 			defaultValue: 2,
 			coerce: x =>
 			{
@@ -1173,91 +1173,91 @@ static MySqlConnectionStringOption()
 			}));
 
 		AddOption(options, CharacterSet = new(
-			keys: new[] { "Character Set", "CharSet", "CharacterSet" },
+			keys: [ "Character Set", "CharSet", "CharacterSet" ],
 			defaultValue: ""));
 
 		AddOption(options, ConnectionTimeout = new(
-			keys: new[] { "Connection Timeout", "ConnectionTimeout", "Connect Timeout" },
+			keys: [ "Connection Timeout", "ConnectionTimeout", "Connect Timeout" ],
 			defaultValue: 15u));
 
 		AddOption(options, ConvertZeroDateTime = new(
-			keys: new[] { "Convert Zero DateTime", "ConvertZeroDateTime" },
+			keys: [ "Convert Zero DateTime", "ConvertZeroDateTime" ],
 			defaultValue: false));
 
 		AddOption(options, DateTimeKind = new(
-			keys: new[] { "DateTime Kind", "DateTimeKind" },
+			keys: [ "DateTime Kind", "DateTimeKind" ],
 			defaultValue: MySqlDateTimeKind.Unspecified));
 
 		AddOption(options, DefaultCommandTimeout = new(
-			keys: new[] { "Default Command Timeout", "DefaultCommandTimeout", "Command Timeout" },
+			keys: [ "Default Command Timeout", "DefaultCommandTimeout", "Command Timeout" ],
 			defaultValue: 30u));
 
 		AddOption(options, ForceSynchronous = new(
-			keys: new[] { "Force Synchronous", "ForceSynchronous" },
+			keys: [ "Force Synchronous", "ForceSynchronous" ],
 			defaultValue: false));
 
 		AddOption(options, GuidFormat = new(
-			keys: new[] { "GUID Format", "GuidFormat" },
+			keys: [ "GUID Format", "GuidFormat" ],
 			defaultValue: MySqlGuidFormat.Default));
 
 		AddOption(options, IgnoreCommandTransaction = new(
-			keys: new[] { "Ignore Command Transaction", "IgnoreCommandTransaction" },
+			keys: [ "Ignore Command Transaction", "IgnoreCommandTransaction" ],
 			defaultValue: false));
 
 		AddOption(options, IgnorePrepare = new(
-			keys: new[] { "Ignore Prepare", "IgnorePrepare" },
+			keys: [ "Ignore Prepare", "IgnorePrepare" ],
 			defaultValue: false));
 
 		AddOption(options, InteractiveSession = new(
-			keys: new[] { "Interactive Session", "InteractiveSession", "Interactive" },
+			keys: [ "Interactive Session", "InteractiveSession", "Interactive" ],
 			defaultValue: false));
 
 		AddOption(options, Keepalive = new(
-			keys: new[] { "Keep Alive", "Keepalive" },
+			keys: [ "Keep Alive", "Keepalive" ],
 			defaultValue: 0u));
 
 		AddOption(options, NoBackslashEscapes = new(
-			keys: new[] { "No Backslash Escapes", "NoBackslashEscapes" },
+			keys: [ "No Backslash Escapes", "NoBackslashEscapes" ],
 			defaultValue: false));
 
 		AddOption(options, OldGuids = new(
-			keys: new[] { "Old Guids", "OldGuids" },
+			keys: [ "Old Guids", "OldGuids" ],
 			defaultValue: false));
 
 		AddOption(options, PersistSecurityInfo = new(
-			keys: new[] { "Persist Security Info", "PersistSecurityInfo" },
+			keys: [ "Persist Security Info", "PersistSecurityInfo" ],
 			defaultValue: false));
 
 		AddOption(options, Pipelining = new(
-			keys: new[] { "Pipelining" },
+			keys: [ "Pipelining" ],
 			defaultValue: true));
 
 		AddOption(options, ServerRedirectionMode = new(
-			keys: new[] { "Server Redirection Mode", "ServerRedirectionMode" },
+			keys: [ "Server Redirection Mode", "ServerRedirectionMode" ],
 			defaultValue: MySqlServerRedirectionMode.Disabled));
 
 		AddOption(options, ServerRsaPublicKeyFile = new(
-			keys: new[] { "Server RSA Public Key File", "ServerRsaPublicKeyFile" },
+			keys: [ "Server RSA Public Key File", "ServerRsaPublicKeyFile" ],
 			defaultValue: ""));
 
 		AddOption(options, ServerSPN = new(
-			keys: new[] { "Server SPN", "ServerSPN" },
+			keys: [ "Server SPN", "ServerSPN" ],
 			defaultValue: ""));
 
 		AddOption(options, TreatTinyAsBoolean = new(
-			keys: new[] { "Treat Tiny As Boolean", "TreatTinyAsBoolean" },
+			keys: [ "Treat Tiny As Boolean", "TreatTinyAsBoolean" ],
 			defaultValue: true));
 
 		AddOption(options, UseAffectedRows = new(
-			keys: new[] { "Use Affected Rows", "UseAffectedRows" },
+			keys: [ "Use Affected Rows", "UseAffectedRows" ],
 			defaultValue: false));
 
 		AddOption(options, UseCompression = new(
-			keys: new[] { "Use Compression", "Compress", "UseCompression" },
+			keys: [ "Use Compression", "Compress", "UseCompression" ],
 			defaultValue: false));
 
 		AddOption(options, UseXaTransactions = new(
-			keys: new[] { "Use XA Transactions", "UseXaTransactions" },
+			keys: [ "Use XA Transactions", "UseXaTransactions" ],
 			defaultValue: true));
 #pragma warning restore SA1118 // Parameter should not span multiple lines
 
diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
index 72c23b59e..cc8dce3af 100644
--- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
@@ -37,7 +37,7 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 		writer.Write((byte) characterSet);
 
 		// NOTE: not new byte[19]; see https://github.com/dotnet/roslyn/issues/33088
-		ReadOnlySpan<byte> padding = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+		ReadOnlySpan<byte> padding = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
 		writer.Write(padding);
 
 		if ((serverCapabilities & ProtocolCapabilities.LongPassword) == 0)
diff --git a/src/MySqlConnector/Protocol/Payloads/InitialHandshakePayload.cs b/src/MySqlConnector/Protocol/Payloads/InitialHandshakePayload.cs
index e9c6f53fc..2beed8ed3 100644
--- a/src/MySqlConnector/Protocol/Payloads/InitialHandshakePayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/InitialHandshakePayload.cs
@@ -42,9 +42,11 @@ public static InitialHandshakePayload Create(ReadOnlySpan<byte> span)
 			if ((protocolCapabilities & ProtocolCapabilities.SecureConnection) != 0)
 			{
 				var authPluginData2 = reader.ReadByteString(Math.Max(13, authPluginDataLength - 8));
+
+				// TODO: authPluginData = [..authPluginData1, ..authPluginData2]; when codegen doesn't use an intermediate List<byte>
 				authPluginData = new byte[authPluginData1.Length + authPluginData2.Length];
 				authPluginData1.CopyTo(authPluginData);
-				authPluginData2.CopyTo(new Span<byte>(authPluginData)[authPluginData1.Length..]);
+				authPluginData2.CopyTo(authPluginData.AsSpan(authPluginData1.Length));
 			}
 			if ((protocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
 				authPluginName = Encoding.UTF8.GetString(reader.ReadNullOrEofTerminatedByteString());
diff --git a/src/MySqlConnector/Protocol/Payloads/PingPayload.cs b/src/MySqlConnector/Protocol/Payloads/PingPayload.cs
index 9b9eee3d1..d6b02f768 100644
--- a/src/MySqlConnector/Protocol/Payloads/PingPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/PingPayload.cs
@@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads;
 
 internal static class PingPayload
 {
-	public static PayloadData Instance { get; } = new(new[] { (byte) CommandKind.Ping });
+	public static PayloadData Instance { get; } = new([ (byte) CommandKind.Ping ]);
 }
diff --git a/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs b/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs
index 72f83b5e0..1d416eddd 100644
--- a/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs
@@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads;
 
 internal static class QuitPayload
 {
-	public static PayloadData Instance { get; } = new(new[] { (byte) CommandKind.Quit });
+	public static PayloadData Instance { get; } = new([ (byte) CommandKind.Quit ]);
 }
diff --git a/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs b/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs
index c2285511e..fe1ab10a2 100644
--- a/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs
@@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads;
 
 internal static class ResetConnectionPayload
 {
-	public static PayloadData Instance { get; } = new(new[] { (byte) CommandKind.ResetConnection });
+	public static PayloadData Instance { get; } = new([ (byte) CommandKind.ResetConnection ]);
 }
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index c4b9b30d1..f3d4e0d17 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -180,7 +180,7 @@ private static RSAParameters GetRsaParameters(ReadOnlySpan<byte> data, bool isPr
 		if (!isPrivate)
 		{
 			// encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
-			ReadOnlySpan<byte> rsaOid = new byte[] { 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 };
+			ReadOnlySpan<byte> rsaOid = [ 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 ];
 			if (!data.Slice(0, rsaOid.Length).SequenceEqual(rsaOid))
 				throw new FormatException($"Expected RSA OID but read {BitConverter.ToString(data.Slice(0, 15).ToArray())}");
 			data = data.Slice(rsaOid.Length);
diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
index 77475e4ba..a93e40b1a 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
+++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
@@ -72,8 +72,8 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 	codeWriter.WriteLine("""
 
 				dataTable.TableName = tableName;
-				dataTable.Columns.AddRange(new DataColumn[]
-				{
+				dataTable.Columns.AddRange(
+				[
 		""");
 	foreach (var column in schema.Columns)
 	{
@@ -82,7 +82,7 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 			""");
 	}
 	codeWriter.WriteLine("""
-				});
+				]);
 
 		""");
 	if (schema.Table is string table)

From 78420bfd9e548729ae4e0ab46a6916b7c3f5e347 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 4 Sep 2023 14:39:58 -0700
Subject: [PATCH 165/393] Use C# 12 primary constructors.

---
 src/MySqlConnector/Core/DbTypeMapping.cs      | 17 ++----
 src/MySqlConnector/Core/IValuesEnumerator.cs  | 38 +++++---------
 src/MySqlConnector/Core/ParsedStatements.cs   | 19 ++-----
 src/MySqlConnector/Core/PreparedStatement.cs  | 18 ++-----
 src/MySqlConnector/Core/PreparedStatements.cs | 16 ++----
 src/MySqlConnector/Core/ResultSet.cs          |  9 +---
 src/MySqlConnector/Core/Row.cs                | 12 ++---
 src/MySqlConnector/Core/SchemaProvider.cs     | 25 ++++-----
 src/MySqlConnector/Core/StatementPreparer.cs  | 42 ++++++---------
 .../Core/XaEnlistedTransaction.cs             |  7 +--
 src/MySqlConnector/MySqlAttribute.cs          | 19 ++-----
 .../MySqlBulkCopyColumnMapping.cs             | 26 +++-------
 src/MySqlConnector/MySqlDateTime.cs           | 52 ++++++-------------
 .../Utilities/ResizableArraySegment.cs        | 15 ++----
 14 files changed, 95 insertions(+), 220 deletions(-)

diff --git a/src/MySqlConnector/Core/DbTypeMapping.cs b/src/MySqlConnector/Core/DbTypeMapping.cs
index 6fe3b096d..f72611ee7 100644
--- a/src/MySqlConnector/Core/DbTypeMapping.cs
+++ b/src/MySqlConnector/Core/DbTypeMapping.cs
@@ -2,24 +2,15 @@
 
 namespace MySqlConnector.Core;
 
-internal sealed class DbTypeMapping
+internal sealed class DbTypeMapping(Type clrType, DbType[] dbTypes, Func<object, object>? convert = null)
 {
-	public DbTypeMapping(Type clrType, DbType[] dbTypes, Func<object, object>? convert = null)
-	{
-		ClrType = clrType;
-		DbTypes = dbTypes;
-		m_convert = convert;
-	}
-
-	public Type ClrType { get; }
-	public DbType[] DbTypes { get; }
+	public Type ClrType { get; } = clrType;
+	public DbType[] DbTypes { get; } = dbTypes;
 
 	public object DoConversion(object obj)
 	{
 		if (obj.GetType() == ClrType)
 			return obj;
-		return m_convert is null ? Convert.ChangeType(obj, ClrType, CultureInfo.InvariantCulture)! : m_convert(obj);
+		return convert is null ? Convert.ChangeType(obj, ClrType, CultureInfo.InvariantCulture)! : convert(obj);
 	}
-
-	private readonly Func<object, object>? m_convert;
 }
diff --git a/src/MySqlConnector/Core/IValuesEnumerator.cs b/src/MySqlConnector/Core/IValuesEnumerator.cs
index f53a481df..0d3cac1d3 100644
--- a/src/MySqlConnector/Core/IValuesEnumerator.cs
+++ b/src/MySqlConnector/Core/IValuesEnumerator.cs
@@ -12,49 +12,35 @@ internal interface IValuesEnumerator
 	void GetValues(object[] values);
 }
 
-internal sealed class DbDataReaderValuesEnumerator : IValuesEnumerator
+internal sealed class DbDataReaderValuesEnumerator(DbDataReader dataReader) : IValuesEnumerator
 {
-	public DbDataReaderValuesEnumerator(DbDataReader dataReader) => m_dataReader = dataReader;
+	public int FieldCount => dataReader.FieldCount;
 
-	public int FieldCount => m_dataReader.FieldCount;
+	public ValueTask<bool> MoveNextAsync() => new(dataReader.ReadAsync());
 
-	public ValueTask<bool> MoveNextAsync() => new ValueTask<bool>(m_dataReader.ReadAsync());
+	public bool MoveNext() => dataReader.Read();
 
-	public bool MoveNext() => m_dataReader.Read();
-
-	public void GetValues(object[] values) => m_dataReader.GetValues(values);
-
-	private readonly DbDataReader m_dataReader;
+	public void GetValues(object[] values) => dataReader.GetValues(values);
 }
 
-internal sealed class DataReaderValuesEnumerator : IValuesEnumerator
+internal sealed class DataReaderValuesEnumerator(IDataReader dataReader) : IValuesEnumerator
 {
 	public static IValuesEnumerator Create(IDataReader dataReader) => dataReader is DbDataReader dbDataReader ? (IValuesEnumerator) new DbDataReaderValuesEnumerator(dbDataReader) : new DataReaderValuesEnumerator(dataReader);
 
-	public DataReaderValuesEnumerator(IDataReader dataReader) => m_dataReader = dataReader;
-
-	public int FieldCount => m_dataReader.FieldCount;
+	public int FieldCount => dataReader.FieldCount;
 
 	public ValueTask<bool> MoveNextAsync() => new(MoveNext());
 
-	public bool MoveNext() => m_dataReader.Read();
-
-	public void GetValues(object[] values) => m_dataReader.GetValues(values);
+	public bool MoveNext() => dataReader.Read();
 
-	private readonly IDataReader m_dataReader;
+	public void GetValues(object[] values) => dataReader.GetValues(values);
 }
 
-internal sealed class DataRowsValuesEnumerator : IValuesEnumerator
+internal sealed class DataRowsValuesEnumerator(IEnumerable<DataRow> dataRows, int columnCount) : IValuesEnumerator
 {
 	public static IValuesEnumerator Create(DataTable dataTable) => new DataRowsValuesEnumerator(dataTable.Rows.Cast<DataRow>().Where(static x => x is not null).Select(static x => x!), dataTable.Columns.Count);
 
-	public DataRowsValuesEnumerator(IEnumerable<DataRow> dataRows, int columnCount)
-	{
-		m_dataRows = dataRows.GetEnumerator();
-		FieldCount = columnCount;
-	}
-
-	public int FieldCount { get; }
+	public int FieldCount { get; } = columnCount;
 
 	public ValueTask<bool> MoveNextAsync() => new(MoveNext());
 
@@ -73,5 +59,5 @@ public void GetValues(object[] values)
 			values[i] = row[i];
 	}
 
-	private readonly IEnumerator<DataRow> m_dataRows;
+	private readonly IEnumerator<DataRow> m_dataRows = dataRows.GetEnumerator();
 }
diff --git a/src/MySqlConnector/Core/ParsedStatements.cs b/src/MySqlConnector/Core/ParsedStatements.cs
index 9c224ebc8..fa429edf0 100644
--- a/src/MySqlConnector/Core/ParsedStatements.cs
+++ b/src/MySqlConnector/Core/ParsedStatements.cs
@@ -6,23 +6,14 @@ namespace MySqlConnector.Core;
 /// <see cref="ParsedStatements"/> wraps a collection of <see cref="ParsedStatement"/> objects.
 /// It implements <see cref="IDisposable"/> to return the memory backing the statements to a shared pool.
 /// </summary>
-internal sealed class ParsedStatements : IDisposable
+internal sealed class ParsedStatements(List<ParsedStatement> statements, PayloadData payloadData) : IDisposable
 {
-	public IReadOnlyList<ParsedStatement> Statements => m_statements;
+	public IReadOnlyList<ParsedStatement> Statements => statements;
 
 	public void Dispose()
 	{
-		m_statements.Clear();
-		m_payloadData.Dispose();
-		m_payloadData = default;
+		statements.Clear();
+		payloadData.Dispose();
+		payloadData = default;
 	}
-
-	internal ParsedStatements(List<ParsedStatement> statements, PayloadData payloadData)
-	{
-		m_statements = statements;
-		m_payloadData = payloadData;
-	}
-
-	private readonly List<ParsedStatement> m_statements;
-	private PayloadData m_payloadData;
 }
diff --git a/src/MySqlConnector/Core/PreparedStatement.cs b/src/MySqlConnector/Core/PreparedStatement.cs
index ea9622d55..cbba24ea6 100644
--- a/src/MySqlConnector/Core/PreparedStatement.cs
+++ b/src/MySqlConnector/Core/PreparedStatement.cs
@@ -5,18 +5,10 @@ namespace MySqlConnector.Core;
 /// <summary>
 /// <see cref="PreparedStatement"/> is a statement that has been prepared on the MySQL Server.
 /// </summary>
-internal sealed class PreparedStatement
+internal sealed class PreparedStatement(int statementId, ParsedStatement statement, ColumnDefinitionPayload[]? columns, ColumnDefinitionPayload[]? parameters)
 {
-	public PreparedStatement(int statementId, ParsedStatement statement, ColumnDefinitionPayload[]? columns, ColumnDefinitionPayload[]? parameters)
-	{
-		StatementId = statementId;
-		Statement = statement;
-		Columns = columns;
-		Parameters = parameters;
-	}
-
-	public int StatementId { get; }
-	public ParsedStatement Statement { get; }
-	public ColumnDefinitionPayload[]? Columns { get; set; }
-	public ColumnDefinitionPayload[]? Parameters { get; }
+	public int StatementId { get; } = statementId;
+	public ParsedStatement Statement { get; } = statement;
+	public ColumnDefinitionPayload[]? Columns { get; set; } = columns;
+	public ColumnDefinitionPayload[]? Parameters { get; } = parameters;
 }
diff --git a/src/MySqlConnector/Core/PreparedStatements.cs b/src/MySqlConnector/Core/PreparedStatements.cs
index f4798a141..1bbd32e5a 100644
--- a/src/MySqlConnector/Core/PreparedStatements.cs
+++ b/src/MySqlConnector/Core/PreparedStatements.cs
@@ -1,20 +1,12 @@
 namespace MySqlConnector.Core;
 
-internal sealed class PreparedStatements : IDisposable
+internal sealed class PreparedStatements(IReadOnlyList<PreparedStatement> preparedStatements, ParsedStatements parsedStatements) : IDisposable
 {
-	public IReadOnlyList<PreparedStatement> Statements { get; }
-
-	public PreparedStatements(IReadOnlyList<PreparedStatement> preparedStatements, ParsedStatements parsedStatements)
-	{
-		Statements = preparedStatements;
-		m_parsedStatements = parsedStatements;
-	}
+	public IReadOnlyList<PreparedStatement> Statements { get; } = preparedStatements;
 
 	public void Dispose()
 	{
-		m_parsedStatements?.Dispose();
-		m_parsedStatements = null;
+		parsedStatements?.Dispose();
+		parsedStatements = null!;
 	}
-
-	private ParsedStatements? m_parsedStatements;
 }
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 9511722b3..37484088c 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -9,13 +9,8 @@
 
 namespace MySqlConnector.Core;
 
-internal sealed class ResultSet
+internal sealed class ResultSet(MySqlDataReader dataReader)
 {
-	public ResultSet(MySqlDataReader dataReader)
-	{
-		DataReader = dataReader;
-	}
-
 	public void Reset()
 	{
 		// ResultSet can be re-used, so initialize everything
@@ -360,7 +355,7 @@ public Row GetCurrentRow()
 		return m_row ?? throw new InvalidOperationException("There is no current row.");
 	}
 
-	public MySqlDataReader DataReader { get; }
+	public MySqlDataReader DataReader { get; } = dataReader;
 	public ExceptionDispatchInfo? ReadResultSetHeaderException { get; private set; }
 	public IMySqlCommand Command => DataReader.Command!;
 	public MySqlConnection Connection => DataReader.Connection!;
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index e5b2c06f0..d096c10f0 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -465,14 +465,8 @@ private static void CheckBufferArguments<T>(long dataOffset, T[] buffer, int buf
 			throw new ArgumentException(nameof(bufferOffset) + " + " + nameof(length) + " cannot exceed " + nameof(buffer) + "." + nameof(buffer.Length), nameof(length));
 	}
 
-	private readonly struct OffsetLength
+	private readonly struct OffsetLength(int offset, int length)
 	{
-		public OffsetLength(int offset, int length)
-		{
-			Offset = offset;
-			Length = length;
-		}
-
 		public static implicit operator OffsetLength((int Offset, int Length) x) => new(x.Offset, x.Length);
 
 		public void Deconstruct(out int offset, out int length)
@@ -481,8 +475,8 @@ public void Deconstruct(out int offset, out int length)
 			length = Length;
 		}
 
-		public int Offset { get; }
-		public int Length { get; }
+		public int Offset { get; } = offset;
+		public int Length { get; } = length;
 	}
 
 	private readonly bool m_isBinary;
diff --git a/src/MySqlConnector/Core/SchemaProvider.cs b/src/MySqlConnector/Core/SchemaProvider.cs
index a8dfe26f6..1ca8b015e 100644
--- a/src/MySqlConnector/Core/SchemaProvider.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.cs
@@ -3,20 +3,15 @@
 
 namespace MySqlConnector.Core;
 
-internal sealed partial class SchemaProvider
+internal sealed partial class SchemaProvider(MySqlConnection connection)
 {
-	public SchemaProvider(MySqlConnection connection)
-	{
-		m_connection = connection;
-	}
-
 	private void DoFillDataSourceInformation(DataTable dataTable)
 	{
 		var row = dataTable.NewRow();
 		row["CompositeIdentifierSeparatorPattern"] = @"\.";
 		row["DataSourceProductName"] = "MySQL";
-		row["DataSourceProductVersion"] = m_connection.ServerVersion;
-		row["DataSourceProductVersionNormalized"] = GetVersion(m_connection.Session.ServerVersion.Version);
+		row["DataSourceProductVersion"] = connection.ServerVersion;
+		row["DataSourceProductVersionNormalized"] = GetVersion(connection.Session.ServerVersion.Version);
 		row["GroupByBehavior"] = GroupByBehavior.Unrelated;
 		row["IdentifierPattern"] = @"(^\[\p{Lo}\p{Lu}\p{Ll}_@#][\p{Lo}\p{Lu}\p{Ll}\p{Nd}@$#_]*$)|(^\[[^\]\0]|\]\]+\]$)|(^\""[^\""\0]|\""\""+\""$)";
 		row["IdentifierCase"] = IdentifierCase.Insensitive;
@@ -389,29 +384,29 @@ private static void DoFillReservedWords(DataTable dataTable)
 	private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, List<KeyValuePair<string, string>>? columns, CancellationToken cancellationToken)
 	{
 		Action? close = null;
-		if (m_connection.State != ConnectionState.Open)
+		if (connection.State != ConnectionState.Open)
 		{
-			await m_connection.OpenAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			close = m_connection.Close;
+			await connection.OpenAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			close = connection.Close;
 		}
 
 		// remove columns that the server doesn't support
 		if (dataTable.TableName == "Columns")
 		{
-			using (var command = new MySqlCommand("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = 'information_schema' AND table_name = 'COLUMNS' AND column_name = 'GENERATION_EXPRESSION';", m_connection))
+			using (var command = new MySqlCommand("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = 'information_schema' AND table_name = 'COLUMNS' AND column_name = 'GENERATION_EXPRESSION';", connection))
 			{
 				if (await command.ExecuteScalarAsync(ioBehavior, cancellationToken).ConfigureAwait(false) is null)
 					dataTable.Columns.Remove("GENERATION_EXPRESSION");
 			}
 
-			using (var command = new MySqlCommand("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = 'information_schema' AND table_name = 'COLUMNS' AND column_name = 'SRS_ID';", m_connection))
+			using (var command = new MySqlCommand("SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE table_schema = 'information_schema' AND table_name = 'COLUMNS' AND column_name = 'SRS_ID';", connection))
 			{
 				if (await command.ExecuteScalarAsync(ioBehavior, cancellationToken).ConfigureAwait(false) is null)
 					dataTable.Columns.Remove("SRS_ID");
 			}
 		}
 
-		using (var command = m_connection.CreateCommand())
+		using (var command = connection.CreateCommand())
 		{
 #pragma warning disable CA2100
 			command.CommandText = "SELECT " + string.Join(", ", dataTable.Columns.Cast<DataColumn>().Select(static x => x!.ColumnName)) + " FROM INFORMATION_SCHEMA." + tableName;
@@ -434,6 +429,4 @@ private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable
 
 		close?.Invoke();
 	}
-
-	private readonly MySqlConnection m_connection;
 }
diff --git a/src/MySqlConnector/Core/StatementPreparer.cs b/src/MySqlConnector/Core/StatementPreparer.cs
index 12e8d5689..c4880a537 100644
--- a/src/MySqlConnector/Core/StatementPreparer.cs
+++ b/src/MySqlConnector/Core/StatementPreparer.cs
@@ -4,24 +4,17 @@
 
 namespace MySqlConnector.Core;
 
-internal sealed class StatementPreparer
+internal sealed class StatementPreparer(string commandText, MySqlParameterCollection? parameters, StatementPreparerOptions options)
 {
-	public StatementPreparerOptions Options { get; }
-
-	public StatementPreparer(string commandText, MySqlParameterCollection? parameters, StatementPreparerOptions options)
-	{
-		m_commandText = commandText;
-		m_parameters = parameters;
-		Options = options;
-	}
+	public StatementPreparerOptions Options { get; } = options;
 
 	public ParsedStatements SplitStatements()
 	{
 		var statements = new List<ParsedStatement>();
 		var statementStartEndIndexes = new List<int>();
-		var writer = new ByteBufferWriter(m_commandText.Length + 1);
+		var writer = new ByteBufferWriter(CommandText.Length + 1);
 		var parser = new PreparedCommandSqlParser(this, statements, statementStartEndIndexes, writer);
-		parser.Parse(m_commandText);
+		parser.Parse(CommandText);
 		for (var i = 0; i < statements.Count; i++)
 #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
 			statements[i].StatementBytes = writer.ArraySegment[statementStartEndIndexes[i * 2]..statementStartEndIndexes[i * 2 + 1]];
@@ -33,10 +26,10 @@ public ParsedStatements SplitStatements()
 
 	public bool ParseAndBindParameters(ByteBufferWriter writer)
 	{
-		if (!string.IsNullOrWhiteSpace(m_commandText))
+		if (!string.IsNullOrWhiteSpace(CommandText))
 		{
 			var parser = new ParameterSqlParser(this, writer);
-			parser.Parse(m_commandText);
+			parser.Parse(CommandText);
 			return parser.IsComplete;
 		}
 		return true;
@@ -44,7 +37,7 @@ public bool ParseAndBindParameters(ByteBufferWriter writer)
 
 	private int GetParameterIndex(string name)
 	{
-		var index = m_parameters?.NormalizedIndexOf(name) ?? -1;
+		var index = parameters?.NormalizedIndexOf(name) ?? -1;
 		if (index == -1 && (Options & StatementPreparerOptions.AllowUserVariables) == 0)
 			throw new MySqlException($"Parameter '{name}' must be defined. To use this as a variable, set 'Allow User Variables=true' in the connection string.");
 		return index;
@@ -52,9 +45,9 @@ private int GetParameterIndex(string name)
 
 	private MySqlParameter GetInputParameter(int index)
 	{
-		if (index >= (m_parameters?.Count ?? 0))
-			throw new MySqlException($"Parameter index {index} is invalid when only {m_parameters?.Count ?? 0} parameter{(m_parameters?.Count == 1 ? " is" : "s are")} defined.");
-		var parameter = m_parameters![index];
+		if (index >= (parameters?.Count ?? 0))
+			throw new MySqlException($"Parameter index {index} is invalid when only {parameters?.Count ?? 0} parameter{(parameters?.Count == 1 ? " is" : "s are")} defined.");
+		var parameter = parameters![index];
 		if (parameter.Direction != ParameterDirection.Input && (Options & StatementPreparerOptions.AllowOutputParameters) == 0)
 			throw new MySqlException($"Only ParameterDirection.Input is supported when CommandType is Text (parameter name: {parameter.ParameterName})");
 		return parameter;
@@ -72,7 +65,7 @@ public ParameterSqlParser(StatementPreparer preparer, ByteBufferWriter writer)
 
 		protected override void OnNamedParameter(int index, int length)
 		{
-			var parameterIndex = Preparer.GetParameterIndex(Preparer.m_commandText.Substring(index, length));
+			var parameterIndex = Preparer.GetParameterIndex(Preparer.CommandText.Substring(index, length));
 			if (parameterIndex != -1)
 				DoAppendParameter(parameterIndex, index, length);
 		}
@@ -85,7 +78,7 @@ protected override void OnPositionalParameter(int index)
 
 		private void DoAppendParameter(int parameterIndex, int textIndex, int textLength)
 		{
-			m_writer.Write(Preparer.m_commandText, m_lastIndex, textIndex - m_lastIndex);
+			m_writer.Write(Preparer.CommandText, m_lastIndex, textIndex - m_lastIndex);
 			var parameter = Preparer.GetInputParameter(parameterIndex);
 			parameter.AppendSqlString(m_writer, Preparer.Options);
 			m_lastIndex = textIndex + textLength;
@@ -93,7 +86,7 @@ private void DoAppendParameter(int parameterIndex, int textIndex, int textLength
 
 		protected override void OnParsed(FinalParseStates states)
 		{
-			m_writer.Write(Preparer.m_commandText, m_lastIndex, Preparer.m_commandText.Length - m_lastIndex);
+			m_writer.Write(Preparer.CommandText, m_lastIndex, Preparer.CommandText.Length - m_lastIndex);
 			if ((states & FinalParseStates.NeedsNewline) == FinalParseStates.NeedsNewline)
 				m_writer.Write((byte) '\n');
 			if ((states & FinalParseStates.NeedsSemicolon) == FinalParseStates.NeedsSemicolon && (Preparer.Options & StatementPreparerOptions.AppendSemicolon) == StatementPreparerOptions.AppendSemicolon)
@@ -126,7 +119,7 @@ protected override void OnStatementBegin(int index)
 
 		protected override void OnNamedParameter(int index, int length)
 		{
-			var parameterName = Preparer.m_commandText.Substring(index, length);
+			var parameterName = Preparer.CommandText.Substring(index, length);
 			DoAppendParameter(parameterName, -1, index, length);
 		}
 
@@ -139,7 +132,7 @@ protected override void OnPositionalParameter(int index)
 		private void DoAppendParameter(string? parameterName, int parameterIndex, int textIndex, int textLength)
 		{
 			// write all SQL up to the parameter
-			m_writer.Write(Preparer.m_commandText, m_lastIndex, textIndex - m_lastIndex);
+			m_writer.Write(Preparer.CommandText, m_lastIndex, textIndex - m_lastIndex);
 			m_lastIndex = textIndex + textLength;
 
 			// replace the parameter with a ? placeholder
@@ -153,7 +146,7 @@ private void DoAppendParameter(string? parameterName, int parameterIndex, int te
 
 		protected override void OnStatementEnd(int index)
 		{
-			m_writer.Write(Preparer.m_commandText, m_lastIndex, index - m_lastIndex);
+			m_writer.Write(Preparer.CommandText, m_lastIndex, index - m_lastIndex);
 			m_lastIndex = index;
 			m_statementStartEndIndexes.Add(m_writer.Position);
 		}
@@ -165,6 +158,5 @@ protected override void OnStatementEnd(int index)
 		private int m_lastIndex;
 	}
 
-	private readonly string m_commandText;
-	private readonly MySqlParameterCollection? m_parameters;
+	private string CommandText { get; } = commandText;
 }
diff --git a/src/MySqlConnector/Core/XaEnlistedTransaction.cs b/src/MySqlConnector/Core/XaEnlistedTransaction.cs
index 69ef49255..9f1ffe73c 100644
--- a/src/MySqlConnector/Core/XaEnlistedTransaction.cs
+++ b/src/MySqlConnector/Core/XaEnlistedTransaction.cs
@@ -3,13 +3,8 @@
 
 namespace MySqlConnector.Core;
 
-internal sealed class XaEnlistedTransaction : EnlistedTransactionBase
+internal sealed class XaEnlistedTransaction(Transaction transaction, MySqlConnection connection) : EnlistedTransactionBase(transaction, connection)
 {
-	public XaEnlistedTransaction(Transaction transaction, MySqlConnection connection)
-		: base(transaction, connection)
-	{
-	}
-
 	protected override void OnStart()
 	{
 		// generate an "xid" with "gtrid" (Global TRansaction ID) from the .NET Transaction and "bqual" (Branch QUALifier)
diff --git a/src/MySqlConnector/MySqlAttribute.cs b/src/MySqlConnector/MySqlAttribute.cs
index 613cd9cf9..5ff72b879 100644
--- a/src/MySqlConnector/MySqlAttribute.cs
+++ b/src/MySqlConnector/MySqlAttribute.cs
@@ -5,40 +5,31 @@ namespace MySqlConnector
 	/// </summary>
 	/// <remarks>See <a href="https://dev.mysql.com/doc/refman/8.0/en/query-attributes.html">Query Attributes</a> for information on using query attributes.</remarks>
 #pragma warning disable CA1711 // Identifiers should not have incorrect suffix
-	public sealed class MySqlAttribute : ICloneable
+	public sealed class MySqlAttribute(string attributeName, object? value) : ICloneable
 #pragma warning restore CA1711 // Identifiers should not have incorrect suffix
 	{
 		/// <summary>
 		/// Initializes a new <see cref="MySqlAttribute"/>.
 		/// </summary>
 		public MySqlAttribute()
+			: this("", null)
 		{
-			AttributeName = "";
-		}
-
-		/// <summary>
-		/// Initializes a new <see cref="MySqlAttribute"/> with the specified attribute name and value.
-		/// </summary>
-		public MySqlAttribute(string attributeName, object? value)
-		{
-			AttributeName = attributeName ?? "";
-			Value = value;
 		}
 
 		/// <summary>
 		/// Gets or sets the attribute name.
 		/// </summary>
-		public string AttributeName { get; set; }
+		public string AttributeName { get; set; } = attributeName ?? "";
 
 		/// <summary>
 		/// Gets or sets the attribute value.
 		/// </summary>
-		public object? Value { get; set; }
+		public object? Value { get; set; } = value;
 
 		/// <summary>
 		/// Returns a new <see cref="MySqlAttribute"/> with the same property values as this instance.
 		/// </summary>
-		public MySqlAttribute Clone() => new MySqlAttribute(AttributeName, Value);
+		public MySqlAttribute Clone() => new(AttributeName, Value);
 
 		object ICloneable.Clone() => Clone();
 
diff --git a/src/MySqlConnector/MySqlBulkCopyColumnMapping.cs b/src/MySqlConnector/MySqlBulkCopyColumnMapping.cs
index 9de514437..0a3328707 100644
--- a/src/MySqlConnector/MySqlBulkCopyColumnMapping.cs
+++ b/src/MySqlConnector/MySqlBulkCopyColumnMapping.cs
@@ -28,38 +28,28 @@ namespace MySqlConnector;
 /// },
 /// </code>
 /// </summary>
-public sealed class MySqlBulkCopyColumnMapping
+/// <param name="sourceOrdinal">The zero-based ordinal position of the source column.</param>
+/// <param name="destinationColumn">The name of the destination column.</param>
+/// <param name="expression">The optional expression to be used to set the destination column.</param>
+public sealed class MySqlBulkCopyColumnMapping(int sourceOrdinal, string destinationColumn, string? expression = null)
 {
 	/// <summary>
 	/// Initializes <see cref="MySqlBulkCopyColumnMapping"/> with the default values.
 	/// </summary>
 	public MySqlBulkCopyColumnMapping()
+		: this(0, "", null)
 	{
-		DestinationColumn = "";
-	}
-
-	/// <summary>
-	/// Initializes <see cref="MySqlBulkCopyColumnMapping"/> to the specified values.
-	/// </summary>
-	/// <param name="sourceOrdinal">The zero-based ordinal position of the source column.</param>
-	/// <param name="destinationColumn">The name of the destination column.</param>
-	/// <param name="expression">The optional expression to be used to set the destination column.</param>
-	public MySqlBulkCopyColumnMapping(int sourceOrdinal, string destinationColumn, string? expression = null)
-	{
-		SourceOrdinal = sourceOrdinal;
-		DestinationColumn = destinationColumn ?? throw new ArgumentNullException(nameof(destinationColumn));
-		Expression = expression;
 	}
 
 	/// <summary>
 	/// The zero-based ordinal position of the source column to map from.
 	/// </summary>
-	public int SourceOrdinal { get; set; }
+	public int SourceOrdinal { get; set; } = sourceOrdinal;
 
 	/// <summary>
 	/// The name of the destination column to copy to. To use an expression, this should be the name of a unique user-defined variable.
 	/// </summary>
-	public string DestinationColumn { get; set; }
+	public string DestinationColumn { get; set; } = destinationColumn;
 
 	/// <summary>
 	/// An optional expression for setting a destination column. To use an expression, the <see cref="DestinationColumn"/> should
@@ -67,5 +57,5 @@ public MySqlBulkCopyColumnMapping(int sourceOrdinal, string destinationColumn, s
 	/// </summary>
 	/// <remarks>To populate a binary column, you must set <see cref="DestinationColumn"/> to a variable name, and <see cref="Expression"/> to an
 	/// expression that uses <code>UNHEX</code> to set the column value, e.g., <code>`destColumn` = UNHEX(@variableName)</code>.</remarks>
-	public string? Expression { get; set; }
+	public string? Expression { get; set; } = expression;
 }
diff --git a/src/MySqlConnector/MySqlDateTime.cs b/src/MySqlConnector/MySqlDateTime.cs
index f468842d3..909880506 100644
--- a/src/MySqlConnector/MySqlDateTime.cs
+++ b/src/MySqlConnector/MySqlDateTime.cs
@@ -7,29 +7,15 @@ namespace MySqlConnector;
 /// as <c>0000-00-00</c> that can be stored in MySQL (when <see cref="MySqlConnectionStringBuilder.AllowZeroDateTime"/>
 /// is true) but can't be stored in a <see cref="DateTime"/> value.
 /// </summary>
-public struct MySqlDateTime : IComparable, IComparable<MySqlDateTime>, IConvertible, IEquatable<MySqlDateTime>
+/// <param name="year">The year.</param>
+/// <param name="month">The (one-based) month.</param>
+/// <param name="day">The (one-based) day of the month.</param>
+/// <param name="hour">The hour.</param>
+/// <param name="minute">The minute.</param>
+/// <param name="second">The second.</param>
+/// <param name="microsecond">The microsecond.</param>
+public struct MySqlDateTime(int year, int month, int day, int hour, int minute, int second, int microsecond) : IComparable, IComparable<MySqlDateTime>, IConvertible, IEquatable<MySqlDateTime>
 {
-	/// <summary>
-	/// Initializes a new instance of <see cref="MySqlDateTime"/>.
-	/// </summary>
-	/// <param name="year">The year.</param>
-	/// <param name="month">The (one-based) month.</param>
-	/// <param name="day">The (one-based) day of the month.</param>
-	/// <param name="hour">The hour.</param>
-	/// <param name="minute">The minute.</param>
-	/// <param name="second">The second.</param>
-	/// <param name="microsecond">The microsecond.</param>
-	public MySqlDateTime(int year, int month, int day, int hour, int minute, int second, int microsecond)
-	{
-		Year = year;
-		Month = month;
-		Day = day;
-		Hour = hour;
-		Minute = minute;
-		Second = second;
-		Microsecond = microsecond;
-	}
-
 	/// <summary>
 	/// Initializes a new instance of <see cref="MySqlDateTime"/> from a <see cref="DateTime"/>.
 	/// </summary>
@@ -44,14 +30,8 @@ public MySqlDateTime(DateTime dt)
 	/// </summary>
 	/// <param name="other">The <see cref="MySqlDateTime"/> whose values will be copied.</param>
 	public MySqlDateTime(MySqlDateTime other)
+		: this(other.Year, other.Month, other.Day, other.Hour, other.Minute, other.Second, other.Microsecond)
 	{
-		Year = other.Year;
-		Month = other.Month;
-		Day = other.Day;
-		Hour = other.Hour;
-		Minute = other.Minute;
-		Second = other.Second;
-		Microsecond = other.Microsecond;
 	}
 
 	/// <summary>
@@ -62,37 +42,37 @@ public MySqlDateTime(MySqlDateTime other)
 	/// <summary>
 	/// Gets or sets the year.
 	/// </summary>
-	public int Year { get; set; }
+	public int Year { get; set; } = year;
 
 	/// <summary>
 	/// Gets or sets the month.
 	/// </summary>
-	public int Month { get; set; }
+	public int Month { get; set; } = month;
 
 	/// <summary>
 	/// Gets or sets the day of the month.
 	/// </summary>
-	public int Day { get; set; }
+	public int Day { get; set; } = day;
 
 	/// <summary>
 	/// Gets or sets the hour.
 	/// </summary>
-	public int Hour { get; set; }
+	public int Hour { get; set; } = hour;
 
 	/// <summary>
 	/// Gets or sets the minute.
 	/// </summary>
-	public int Minute { get; set; }
+	public int Minute { get; set; } = minute;
 
 	/// <summary>
 	/// Gets or sets the second.
 	/// </summary>
-	public int Second { get; set; }
+	public int Second { get; set; } = second;
 
 	/// <summary>
 	/// Gets or sets the microseconds.
 	/// </summary>
-	public int Microsecond { get; set; }
+	public int Microsecond { get; set; } = microsecond;
 
 	/// <summary>
 	/// Gets or sets the milliseconds.
diff --git a/src/MySqlConnector/Utilities/ResizableArraySegment.cs b/src/MySqlConnector/Utilities/ResizableArraySegment.cs
index d2a0d09b7..1137ddde8 100644
--- a/src/MySqlConnector/Utilities/ResizableArraySegment.cs
+++ b/src/MySqlConnector/Utilities/ResizableArraySegment.cs
@@ -3,19 +3,12 @@ namespace MySqlConnector.Utilities;
 /// <summary>
 /// An <see cref="ArraySegment{T}"/> that supports having its underlying array reallocated and resized.
 /// </summary>
-internal readonly struct ResizableArraySegment<T>
+internal readonly struct ResizableArraySegment<T>(ResizableArray<T> array, int offset, int count)
 	where T : notnull
 {
-	public ResizableArraySegment(ResizableArray<T> array, int offset, int count)
-	{
-		Array = array;
-		Offset = offset;
-		Count = count;
-	}
-
-	public ResizableArray<T> Array { get; }
-	public int Offset { get; }
-	public int Count { get; }
+	public ResizableArray<T> Array { get; } = array;
+	public int Offset { get; } = offset;
+	public int Count { get; } = count;
 
 	public static implicit operator ReadOnlySpan<T>(ResizableArraySegment<T> segment) => new ReadOnlySpan<T>(segment.Array.Array, segment.Offset, segment.Count);
 }

From c2a31c9718e2a91d1672d2e8fb7bbb1f33a6c05b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 12 Sep 2023 16:55:50 -0700
Subject: [PATCH 166/393] Update to .NET 8.0 RC 1.

---
 src/MySqlConnector/Core/ConnectionSettings.cs | 2 +-
 src/MySqlConnector/Core/ServerSession.cs      | 2 +-
 src/MySqlConnector/Core/TypeMapper.cs         | 5 +++++
 src/MySqlConnector/Utilities/Utility.cs       | 8 ++++++++
 4 files changed, 15 insertions(+), 2 deletions(-)

diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index c690a8a07..b4e9c87be 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -11,7 +11,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
 		ConnectionStringBuilder = csb;
 		ConnectionString = csb.ConnectionString;
 
-		if (csb.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket || (!Utility.IsWindows() && (csb.Server.StartsWith("/", StringComparison.Ordinal) || csb.Server.StartsWith("./", StringComparison.Ordinal))))
+		if (csb.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket || (!Utility.IsWindows() && (csb.Server.StartsWith('/') || csb.Server.StartsWith("./", StringComparison.Ordinal))))
 		{
 			if (csb.LoadBalance != MySqlLoadBalance.RoundRobin)
 				throw new NotSupportedException("LoadBalance not supported when ConnectionProtocol=UnixSocket");
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 3e4910349..258310638 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1624,7 +1624,7 @@ private bool ShouldGetRealServerDetails(ConnectionSettings cs)
 			return true;
 
 		// detect Azure Database for MySQL DNS suffixes, if a "user@host" user ID is being used
-		if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets && cs.UserID.IndexOf('@') != -1)
+		if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets && cs.UserID.Contains('@'))
 		{
 			return HostName.EndsWith(".mysql.database.azure.com", StringComparison.OrdinalIgnoreCase) ||
 				HostName.EndsWith(".database.windows.net", StringComparison.OrdinalIgnoreCase) ||
diff --git a/src/MySqlConnector/Core/TypeMapper.cs b/src/MySqlConnector/Core/TypeMapper.cs
index 3c5589ec4..ee30727ff 100644
--- a/src/MySqlConnector/Core/TypeMapper.cs
+++ b/src/MySqlConnector/Core/TypeMapper.cs
@@ -154,10 +154,15 @@ private void AddColumnTypeMetadata(ColumnTypeMetadata columnTypeMetadata)
 	{
 		m_columnTypeMetadata.Add(columnTypeMetadata);
 		var lookupKey = columnTypeMetadata.CreateLookupKey();
+#if !NETCOREAPP2_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
 		if (!m_columnTypeMetadataLookup.ContainsKey(lookupKey))
 			m_columnTypeMetadataLookup.Add(lookupKey, columnTypeMetadata);
 		if (!m_mySqlDbTypeToColumnTypeMetadata.ContainsKey(columnTypeMetadata.MySqlDbType))
 			m_mySqlDbTypeToColumnTypeMetadata.Add(columnTypeMetadata.MySqlDbType, columnTypeMetadata);
+#else
+		m_columnTypeMetadataLookup.TryAdd(lookupKey, columnTypeMetadata);
+		m_mySqlDbTypeToColumnTypeMetadata.TryAdd(columnTypeMetadata.MySqlDbType, columnTypeMetadata);
+#endif
 	}
 
 	internal DbTypeMapping? GetDbTypeMapping(Type clrType)
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index f3d4e0d17..651ef5bff 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -511,9 +511,17 @@ public static Task WriteAsync(this Stream stream, ReadOnlyMemory<byte> data)
 	public static void Write(this Stream stream, ReadOnlyMemory<byte> data) => stream.Write(data.Span);
 #endif
 
+#if !NETCOREAPP2_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
+	public static bool StartsWith(this string str, char value) => !string.IsNullOrEmpty(str) && str[0] == value;
+#endif
+
 	public static void SwapBytes(Span<byte> bytes, int offset1, int offset2)
 	{
+#if NET8_0_OR_GREATER
+		ref var first = ref Unsafe.AsRef(ref bytes[0]);
+#else
 		ref var first = ref Unsafe.AsRef(bytes[0]);
+#endif
 		(Unsafe.Add(ref first, offset2), Unsafe.Add(ref first, offset1)) = (Unsafe.Add(ref first, offset1), Unsafe.Add(ref first, offset2));
 	}
 

From 264d5ec14c5b5271fe7849ca45c49d4ff4a03836 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 12 Sep 2023 17:23:30 -0700
Subject: [PATCH 167/393] Implement DbBatchCommand.CreateParameter. Fixes #1352

---
 src/MySqlConnector/MySqlBatchCommand.cs | 14 ++++++++++++++
 tests/IntegrationTests/BatchTests.cs    | 12 ++++++++++++
 2 files changed, 26 insertions(+)

diff --git a/src/MySqlConnector/MySqlBatchCommand.cs b/src/MySqlConnector/MySqlBatchCommand.cs
index 1fc7393d7..5520644ae 100644
--- a/src/MySqlConnector/MySqlBatchCommand.cs
+++ b/src/MySqlConnector/MySqlBatchCommand.cs
@@ -48,6 +48,20 @@ public MySqlBatchCommand(string? commandText)
 #endif
 		m_parameterCollection ??= new();
 
+#pragma warning disable CA1822 // Mark members as static
+	public
+#if NET8_0_OR_GREATER
+		override
+#endif
+		DbParameter CreateParameter() => new MySqlParameter();
+
+	public
+#if NET8_0_OR_GREATER
+		override
+#endif
+		bool CanCreateParameter => true;
+#pragma warning restore CA1822 // Mark members as static
+
 #if NET6_0_OR_GREATER
 	protected override DbParameterCollection DbParameterCollection => Parameters;
 #endif
diff --git a/tests/IntegrationTests/BatchTests.cs b/tests/IntegrationTests/BatchTests.cs
index 0755d67d9..04b5bb17c 100644
--- a/tests/IntegrationTests/BatchTests.cs
+++ b/tests/IntegrationTests/BatchTests.cs
@@ -8,6 +8,18 @@ public BatchTests(DatabaseFixture database)
 	{
 	}
 
+	[Fact]
+	public void CanCreateParameter()
+	{
+		Assert.True(new MySqlBatchCommand().CanCreateParameter);
+	}
+
+	[Fact]
+	public void CreateParameter()
+	{
+		Assert.IsType<MySqlParameter>(new MySqlBatchCommand().CreateParameter());
+	}
+
 	[Fact]
 	public void NeedsConnection()
 	{

From c7bfe080f7a540f89216435426bcc9e8618c7fe2 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 13 Sep 2023 05:48:15 -0700
Subject: [PATCH 168/393] Replace Array.Empty with C# 12 collection expression.

---
 src/MySqlConnector/MySqlDataReader.cs                |  2 +-
 .../AuthenticationMethodSwitchRequestPayload.cs      |  2 +-
 src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs |  2 +-
 .../Protocol/Serialization/AuthenticationUtility.cs  | 12 +++---------
 4 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index cde036039..4bdc9c8ac 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -347,7 +347,7 @@ public ReadOnlyCollection<DbColumn> GetColumnSchema()
 	{
 		var hasNoSchema = !m_resultSet.HasResultSet || m_resultSet.ContainsCommandParameters;
 		if (hasNoSchema)
-			return new ReadOnlyCollection<DbColumn>(Array.Empty<DbColumn>());
+			return new ReadOnlyCollection<DbColumn>([]);
 
 		var columnDefinitions = m_resultSet.ColumnDefinitions;
 		var resultSet = GetResultSet();
diff --git a/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs b/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs
index c79aedd81..275daa133 100644
--- a/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/AuthenticationMethodSwitchRequestPayload.cs
@@ -22,7 +22,7 @@ public static AuthenticationMethodSwitchRequestPayload Create(ReadOnlySpan<byte>
 			// if the packet is just the header byte (0xFE), it's an "Old Authentication Method Switch Request Packet"
 			// (possibly sent by a server that doesn't support CLIENT_PLUGIN_AUTH)
 			name = "mysql_old_password";
-			data = Array.Empty<byte>();
+			data = [];
 		}
 		else
 		{
diff --git a/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs b/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs
index 898b0b4f8..6a01a808c 100644
--- a/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/EmptyPayload.cs
@@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads;
 
 internal static class EmptyPayload
 {
-	public static PayloadData Instance { get; } = new(Array.Empty<byte>());
+	public static PayloadData Instance { get; } = new([]);
 }
diff --git a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
index 5c90acfe9..5fca2ba4f 100644
--- a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
@@ -10,7 +10,7 @@ namespace MySqlConnector.Protocol.Serialization;
 internal static class AuthenticationUtility
 {
 	public static byte[] CreateAuthenticationResponse(ReadOnlySpan<byte> challenge, string password) =>
-		string.IsNullOrEmpty(password) ? Array.Empty<byte>() : HashPassword(challenge, password);
+		string.IsNullOrEmpty(password) ? [] : HashPassword(challenge, password);
 
 	/// <summary>
 	/// Hashes a password with the "Secure Password Authentication" method.
@@ -54,14 +54,8 @@ public static byte[] HashPassword(ReadOnlySpan<byte> challenge, string password)
 		return hashedPassword.ToArray();
 	}
 
-	public static byte[] CreateScrambleResponse(ReadOnlySpan<byte> nonce, string password)
-	{
-		var scrambleResponse = string.IsNullOrEmpty(password)
-			? Array.Empty<byte>()
-			: HashPasswordWithNonce(nonce, password);
-
-		return scrambleResponse;
-	}
+	public static byte[] CreateScrambleResponse(ReadOnlySpan<byte> nonce, string password) =>
+		string.IsNullOrEmpty(password) ? [] : HashPasswordWithNonce(nonce, password);
 
 #if NET5_0_OR_GREATER
 	[SkipLocalsInit]

From f3767b4664a160836ffb042475718e3af0457c56 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 13 Sep 2023 15:45:08 -0700
Subject: [PATCH 169/393] Opt in to NuGet security audits.

---
 src/Directory.Build.props | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index fda7a84d9..b0baee81e 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -29,6 +29,8 @@
     <NoWarn>$(NoWarn);1591;CA1708;CA1835;CA2215;CA5397;NU5105;SYSLIB0039</NoWarn>
     <MinVerDefaultPreReleaseIdentifiers>beta.0</MinVerDefaultPreReleaseIdentifiers>
     <ArtifactsPath>$(MSBuildThisFileDirectory)..\artifacts</ArtifactsPath>
+    <NuGetAudit>true</NuGetAudit>
+    <NuGetAuditLevel>low</NuGetAuditLevel>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">

From 7a5993d5df1f7ed08d22d60c10d5d56e3bac91ce Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 16 Sep 2023 17:18:42 -0700
Subject: [PATCH 170/393] Use Central Package Management.

Use the new simplified artifact output paths.
---
 .ci/build-steps.yml                           |  8 ++---
 .ci/config/config.compression+ssl.json        |  8 ++---
 .ci/config/config.compression.json            |  6 ++--
 .ci/config/config.json                        |  6 ++--
 .ci/config/config.ssl.json                    |  8 ++---
 .ci/config/config.uds+ssl.json                |  8 ++---
 .ci/config/config.uds.json                    |  6 ++--
 .editorconfig                                 |  1 +
 Directory.Build.props                         | 22 ++++++++++++
 Directory.Packages.props                      | 34 +++++++++++++++++++
 NuGet.config                                  |  1 +
 azure-pipelines.yml                           |  2 +-
 src/Directory.Build.props                     | 26 ++------------
 .../MySqlConnector.DependencyInjection.csproj |  4 +--
 ...ogging.Microsoft.Extensions.Logging.csproj |  4 +--
 .../MySqlConnector.Logging.NLog.csproj        |  4 +--
 .../MySqlConnector.Logging.Serilog.csproj     |  4 +--
 .../MySqlConnector.Logging.log4net.csproj     |  4 +--
 src/MySqlConnector/MySqlConnector.csproj      | 10 +++---
 .../Conformance.Tests.csproj                  |  8 ++---
 .../IntegrationTests/IntegrationTests.csproj  | 16 ++++-----
 .../MySqlConnector.Tests.csproj               | 12 +++----
 .../SchemaCollectionGenerator.cs              | 14 +++++---
 .../SchemaCollectionGenerator.csproj          |  3 +-
 24 files changed, 129 insertions(+), 90 deletions(-)
 create mode 100644 Directory.Build.props
 create mode 100644 Directory.Packages.props

diff --git a/.ci/build-steps.yml b/.ci/build-steps.yml
index a021ba623..c95f7acf3 100644
--- a/.ci/build-steps.yml
+++ b/.ci/build-steps.yml
@@ -33,7 +33,7 @@ steps:
 - task: PublishPipelineArtifact@0
   inputs:
     artifactName: 'MySqlConnector.Tests-8.0-$(Agent.OS)'
-    targetPath: 'tests/MySqlConnector.Tests/bin/Release/net8.0/publish'
+    targetPath: 'artifacts/publish/MySqlConnector.Tests/release_net8.0'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish Conformance.Tests'
@@ -45,7 +45,7 @@ steps:
 - task: PublishPipelineArtifact@0
   inputs:
     artifactName: 'Conformance.Tests-8.0-$(Agent.OS)'
-    targetPath: 'tests/Conformance.Tests/bin/Release/net8.0/publish'
+    targetPath: 'artifacts/publish/Conformance.Tests/release_net8.0'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish IntegrationTests (7.0)'
@@ -57,7 +57,7 @@ steps:
 - task: PublishPipelineArtifact@0
   inputs:
     artifactName: 'IntegrationTests-net8.0-$(Agent.OS)'
-    targetPath: 'tests/IntegrationTests/bin/Release/net8.0/publish'
+    targetPath: 'artifacts/publish/IntegrationTests/release_net8.0'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish IntegrationTests (6.0)'
@@ -69,4 +69,4 @@ steps:
 - task: PublishPipelineArtifact@0
   inputs:
     artifactName: 'IntegrationTests-net6.0-$(Agent.OS)'
-    targetPath: 'tests/IntegrationTests/bin/Release/net6.0/publish'
+    targetPath: 'artifacts/publish/IntegrationTests/release_net6.0'
diff --git a/.ci/config/config.compression+ssl.json b/.ci/config/config.compression+ssl.json
index 206c84969..8a11c95bf 100644
--- a/.ci/config/config.compression+ssl.json
+++ b/.ci/config/config.compression+ssl.json
@@ -1,12 +1,12 @@
 {
     "Data": {
       "ConnectionString": "server=127.0.0.1;user id=ssltest;password=test;port=3306;database=mysqltest;ssl mode=required;use compression=true;DefaultCommandTimeout=3600",
-      "SocketPath": "./../../../../../.ci/run/mysql/mysqld.sock",
+      "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
       "UnsupportedFeatures": "RsaEncryption,CachingSha2Password,Tls12,Tls13,UuidToBin",
-      "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
-      "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
-      "CertificatesPath": "../../../../../.ci/server/certs"
+      "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
+      "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
+      "CertificatesPath": "../../../../.ci/server/certs"
     }
 }
diff --git a/.ci/config/config.compression.json b/.ci/config/config.compression.json
index afd6f6bee..ca3199da0 100644
--- a/.ci/config/config.compression.json
+++ b/.ci/config/config.compression.json
@@ -1,11 +1,11 @@
 {
     "Data": {
       "ConnectionString": "server=127.0.0.1;user id=mysqltest;password=test;port=3306;database=mysqltest;ssl mode=none;UseCompression=true;DefaultCommandTimeout=3600",
-      "SocketPath": "./../../../../../.ci/run/mysql/mysqld.sock",
+      "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
       "UnsupportedFeatures": "Ed25519,RsaEncryption,CachingSha2Password,QueryAttributes,Tls12,Tls13,UnixDomainSocket,UuidToBin",
-      "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
-      "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV"
+      "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
+      "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
 }
diff --git a/.ci/config/config.json b/.ci/config/config.json
index b0332563d..fa5908d80 100644
--- a/.ci/config/config.json
+++ b/.ci/config/config.json
@@ -1,11 +1,11 @@
 {
     "Data": {
       "ConnectionString": "server=127.0.0.1;user id=mysqltest;password=test;port=3306;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600",
-      "SocketPath": "./../../../../../.ci/run/mysql/mysqld.sock",
+      "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
       "UnsupportedFeatures": "Ed25519,RsaEncryption,CachingSha2Password,QueryAttributes,Tls12,Tls13,UnixDomainSocket,UuidToBin",
-      "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
-      "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV"
+      "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
+      "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
 }
diff --git a/.ci/config/config.ssl.json b/.ci/config/config.ssl.json
index d2132e290..ef4a5afb0 100644
--- a/.ci/config/config.ssl.json
+++ b/.ci/config/config.ssl.json
@@ -1,12 +1,12 @@
 {
     "Data": {
       "ConnectionString": "server=127.0.0.1;user id=ssltest;password=test;port=3306;database=mysqltest;ssl mode=required;certificate file=../../../../../.ci/server/certs/ssl-client.pfx;DefaultCommandTimeout=3600",
-      "SocketPath": "./../../../../../.ci/run/mysql/mysqld.sock",
+      "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
       "UnsupportedFeatures": "RsaEncryption,CachingSha2Password,Tls12,Tls13,UuidToBin",
-      "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
-      "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
-      "CertificatesPath": "../../../../../.ci/server/certs"
+      "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
+      "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
+      "CertificatesPath": "../../../../.ci/server/certs"
     }
 }
diff --git a/.ci/config/config.uds+ssl.json b/.ci/config/config.uds+ssl.json
index 26d67f019..eb754da59 100644
--- a/.ci/config/config.uds+ssl.json
+++ b/.ci/config/config.uds+ssl.json
@@ -1,11 +1,11 @@
 {
     "Data": {
-      "ConnectionString": "server=./../../../../../.ci/mysqld/mysqld.sock;user id=ssltest;password=test;database=mysqltest;ssl mode=required;DefaultCommandTimeout=3600",
+      "ConnectionString": "server=./../../../../.ci/mysqld/mysqld.sock;user id=ssltest;password=test;database=mysqltest;ssl mode=required;DefaultCommandTimeout=3600",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
       "UnsupportedFeatures": "None",
-      "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
-      "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
-      "CertificatesPath": "../../../../../.ci/server/certs"
+      "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
+      "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
+      "CertificatesPath": "../../../../.ci/server/certs"
     }
 }
diff --git a/.ci/config/config.uds.json b/.ci/config/config.uds.json
index d3e90c036..6b209d734 100644
--- a/.ci/config/config.uds.json
+++ b/.ci/config/config.uds.json
@@ -1,10 +1,10 @@
 {
     "Data": {
-      "ConnectionString": "server=./../../../../../.ci/run/mysql/mysqld.sock;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600",
+      "ConnectionString": "server=./../../../../.ci/run/mysql/mysqld.sock;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
       "UnsupportedFeatures": "None",
-      "MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
-      "MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV"
+      "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
+      "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
 }
diff --git a/.editorconfig b/.editorconfig
index 76db29296..1765a3ee5 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -110,6 +110,7 @@ dotnet_diagnostic.SA1500.severity = none # Braces for multi-line statements shou
 dotnet_diagnostic.SA1503.severity = none # Braces should not be omitted.
 dotnet_diagnostic.SA1513.severity = none # Closing brace should be followed by blank line.
 dotnet_diagnostic.SA1516.severity = none # Elements should be separated by blank line.
+dotnet_diagnostic.SA1519.severity = none # Braces should not be omitted from multi-line child statement
 dotnet_diagnostic.SA1600.severity = none # Elements should be documented.
 dotnet_diagnostic.SA1601.severity = none # Partial elements should be documented.
 dotnet_diagnostic.SA1602.severity = none # Enumeration items should be documented.
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 000000000..884af96c3
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,22 @@
+<Project>
+
+  <PropertyGroup>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <LangVersion>preview</LangVersion>
+    <AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
+    <NoWarn>$(NoWarn);1591;CA1708;CA1835;CA2215;CA5397;NU5105;SYSLIB0039</NoWarn>
+    <UseArtifactsOutput>true</UseArtifactsOutput>
+    <NuGetAudit>true</NuGetAudit>
+    <NuGetAuditLevel>low</NuGetAuditLevel>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
+    <RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
+  </PropertyGroup>
+
+  <PropertyGroup Condition=" '$(GITHUB_ACTIONS)' == 'true' OR '$(APPVEYOR)' == 'True' OR '$(TF_BUILD)' == 'True' ">
+    <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
+    <Deterministic>true</Deterministic>
+  </PropertyGroup>
+
+</Project>
diff --git a/Directory.Packages.props b/Directory.Packages.props
new file mode 100644
index 000000000..c8f093de4
--- /dev/null
+++ b/Directory.Packages.props
@@ -0,0 +1,34 @@
+<Project>
+  <PropertyGroup>
+    <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
+    <CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageVersion Include="AdoNet.Specification.Tests" Version="2.0.0-beta.1" />
+    <PackageVersion Include="Dapper.StrongName" Version="2.0.143" />
+    <PackageVersion Include="IndexRange" Version="1.0.2" />
+    <PackageVersion Include="log4net" Version="2.0.10" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
+    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
+    <PackageVersion Include="MySql.Data" Version="8.1.0" />
+    <PackageVersion Include="NLog" Version="4.5.0" />
+    <PackageVersion Include="Serilog" Version="2.6.0" />
+    <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
+    <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
+    <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
+    <PackageVersion Include="xunit" Version="2.4.2" />
+    <PackageVersion Include="xunit.runner.visualstudio" Version="2.4.5" />
+    <PackageVersion Include="YamlDotNet" Version="13.1.1" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
+    <GlobalPackageReference Include="MinVer" Version="4.3.0" />
+  </ItemGroup>
+
+</Project>
diff --git a/NuGet.config b/NuGet.config
index 6873eb959..4d736c19e 100644
--- a/NuGet.config
+++ b/NuGet.config
@@ -1,6 +1,7 @@
 <?xml version="1.0" encoding="utf-8"?>
 <configuration>
   <packageSources>
+    <clear />
     <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
   </packageSources>
 </configuration>
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 1827b3345..43cda8c76 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -26,7 +26,7 @@ jobs:
   - task: PublishPipelineArtifact@0
     inputs:
       artifactName: 'IntegrationTests-net472-$(Agent.OS)'
-      targetPath: 'tests/IntegrationTests/bin/Release/net472/publish'
+      targetPath: 'artifacts/publish/IntegrationTests/release_net472'
 
 - job: windows_mysql_data
   displayName: 'MySql.Data Tests'
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index b0baee81e..0968ec93d 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -1,10 +1,10 @@
 <Project>
+  <Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
 
   <PropertyGroup>
     <SignAssembly>true</SignAssembly>
     <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
     <AssemblyOriginatorKeyFile>..\..\MySqlConnector.snk</AssemblyOriginatorKeyFile>
-    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
     <PackageReleaseNotes>https://mysqlconnector.net/overview/version-history/</PackageReleaseNotes>
     <PackageProjectUrl>https://mysqlconnector.net/</PackageProjectUrl>
     <PackageLicenseExpression>MIT</PackageLicenseExpression>
@@ -17,8 +17,6 @@
     <PackageValidationBaselineVersion>2.0.0</PackageValidationBaselineVersion>
     <EnableStrictModeForCompatibleFrameworksInPackageValidation>true</EnableStrictModeForCompatibleFrameworksInPackageValidation>
     <EnableStrictModeForCompatibleTfms>true</EnableStrictModeForCompatibleTfms>
-    <LangVersion>preview</LangVersion>
-    <AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
     <AnalysisLevel>latest-all</AnalysisLevel>
     <RunAnalyzersDuringBuild>false</RunAnalyzersDuringBuild>
     <RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
@@ -26,36 +24,18 @@
     <PublishRepositoryUrl>true</PublishRepositoryUrl>
     <EmbedUntrackedSources>true</EmbedUntrackedSources>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>
-    <NoWarn>$(NoWarn);1591;CA1708;CA1835;CA2215;CA5397;NU5105;SYSLIB0039</NoWarn>
     <MinVerDefaultPreReleaseIdentifiers>beta.0</MinVerDefaultPreReleaseIdentifiers>
-    <ArtifactsPath>$(MSBuildThisFileDirectory)..\artifacts</ArtifactsPath>
-    <NuGetAudit>true</NuGetAudit>
-    <NuGetAuditLevel>low</NuGetAuditLevel>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
-    <RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
-  </PropertyGroup>
-
-  <PropertyGroup Condition=" '$(GITHUB_ACTIONS)' == 'true' OR '$(APPVEYOR)' == 'True' OR '$(TF_BUILD)' == 'True' ">
-    <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
-    <Deterministic>true</Deterministic>
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All"/>
-    <PackageReference Include="MinVer" Version="4.3.0">
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-      <PrivateAssets>all</PrivateAssets>
-    </PackageReference>
-    <PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.507">
+    <PackageReference Include="StyleCop.Analyzers">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
   </ItemGroup>
 
   <ItemGroup>
-    <None Include="..\..\docs\static\img\logo.png" Pack="true" PackagePath="\"/>
+    <None Include="..\..\docs\static\img\logo.png" Pack="true" PackagePath="\" />
   </ItemGroup>
 
 </Project>
diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
index 5e1488102..d1e793b74 100644
--- a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
+++ b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
@@ -14,7 +14,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
   </ItemGroup>
 
   <ItemGroup>
@@ -22,7 +22,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <None Include="docs\README.md" Pack="true" PackagePath="\"/>
+    <None Include="docs\README.md" Pack="true" PackagePath="\" />
   </ItemGroup>
 
 </Project>
diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
index 5233621fb..92ba235e8 100644
--- a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
+++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
@@ -12,7 +12,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
   </ItemGroup>
 
   <ItemGroup>
@@ -20,7 +20,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <None Include="docs\README.md" Pack="true" PackagePath="\"/>
+    <None Include="docs\README.md" Pack="true" PackagePath="\" />
   </ItemGroup>
 
 </Project>
diff --git a/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj b/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj
index 343e2fc58..b0cb9e194 100644
--- a/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj
+++ b/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj
@@ -12,7 +12,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="NLog" Version="4.5.0" />
+    <PackageReference Include="NLog" />
   </ItemGroup>
 
   <ItemGroup>
@@ -20,7 +20,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <None Include="docs\README.md" Pack="true" PackagePath="\"/>
+    <None Include="docs\README.md" Pack="true" PackagePath="\" />
   </ItemGroup>
 
 </Project>
diff --git a/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj b/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj
index b3344f37d..316d42263 100644
--- a/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj
+++ b/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj
@@ -12,7 +12,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Serilog" Version="2.6.0" />
+    <PackageReference Include="Serilog" />
   </ItemGroup>
 
   <ItemGroup>
@@ -20,7 +20,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <None Include="docs\README.md" Pack="true" PackagePath="\"/>
+    <None Include="docs\README.md" Pack="true" PackagePath="\" />
   </ItemGroup>
 
 </Project>
diff --git a/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj b/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj
index 3e25ac19c..6e655a9e4 100644
--- a/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj
+++ b/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj
@@ -12,7 +12,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="log4net" Version="2.0.10" />
+    <PackageReference Include="log4net" />
   </ItemGroup>
 
   <ItemGroup>
@@ -20,7 +20,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <None Include="docs\README.md" Pack="true" PackagePath="\"/>
+    <None Include="docs\README.md" Pack="true" PackagePath="\" />
   </ItemGroup>
 
 </Project>
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 93c15c573..a8e3df037 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -16,7 +16,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
+    <PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
   </ItemGroup>
 
   <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471'  OR '$(TargetFramework)' == 'net48' ">
@@ -25,12 +25,12 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net471' OR '$(TargetFramework)' == 'net48' OR '$(TargetFramework)' == 'netstandard2.0' ">
-    <PackageReference Include="IndexRange" Version="1.0.2" PrivateAssets="All" />
-    <PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
+    <PackageReference Include="IndexRange" PrivateAssets="All" />
+    <PackageReference Include="System.Threading.Tasks.Extensions" />
   </ItemGroup>
 
   <ItemGroup Condition=" '$(TargetFrameworkIdentifier)' != '.NETCoreApp' ">
-    <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
+    <PackageReference Include="System.Diagnostics.DiagnosticSource" />
   </ItemGroup>
 
   <ItemGroup>
@@ -41,7 +41,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <None Include="docs\README.md" Pack="true" PackagePath="\"/>
+    <None Include="docs\README.md" Pack="true" PackagePath="\" />
   </ItemGroup>
 
 </Project>
diff --git a/tests/Conformance.Tests/Conformance.Tests.csproj b/tests/Conformance.Tests/Conformance.Tests.csproj
index 497ae0b19..246efa5b1 100644
--- a/tests/Conformance.Tests/Conformance.Tests.csproj
+++ b/tests/Conformance.Tests/Conformance.Tests.csproj
@@ -11,10 +11,10 @@
   </PropertyGroup>
 
  <ItemGroup>
-    <PackageReference Include="AdoNet.Specification.Tests" Version="2.0.0-beta.1" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
-    <PackageReference Include="xunit" Version="2.4.2" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+    <PackageReference Include="AdoNet.Specification.Tests" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" />
+    <PackageReference Include="xunit" />
+    <PackageReference Include="xunit.runner.visualstudio">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index 84106a327..ec47468ed 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -28,17 +28,17 @@
 
   <ItemGroup>
     <!--testing packages-->
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
-    <PackageReference Include="xunit" Version="2.5.0" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
+    <PackageReference Include="Microsoft.NET.Test.Sdk" />
+    <PackageReference Include="xunit" />
+    <PackageReference Include="xunit.runner.visualstudio">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
     <!--app packages-->
-    <PackageReference Include="Dapper.StrongName" Version="2.0.143" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
-    <PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
+    <PackageReference Include="Dapper.StrongName" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
+    <PackageReference Include="Microsoft.Extensions.Configuration.Json" />
     <Using Include="System" />
     <Using Include="System.Data" />
     <Using Include="System.Data.Common" />
@@ -55,7 +55,7 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <PackageReference Include="MySql.Data" Version="8.1.0" />
+    <PackageReference Include="MySql.Data" />
     <Using Include="MySql.Data.MySqlClient" />
   </ItemGroup>
 
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 5686c0e48..8c056a9b5 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -19,16 +19,12 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="xunit" Version="2.5.0" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-      <PrivateAssets>all</PrivateAssets>
-    </PackageReference>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
-    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3">
+    <PackageReference Include="xunit" />
+    <PackageReference Include="xunit.runner.visualstudio">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
+    <PackageReference Include="Microsoft.NET.Test.Sdk" />
     <Using Include="System" />
     <Using Include="System.Collections.Generic" />
     <Using Include="System.Data" />
@@ -46,7 +42,7 @@
   </ItemGroup>
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <PackageReference Include="MySql.Data" Version="8.1.0" />
+    <PackageReference Include="MySql.Data" />
     <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
     <Using Include="MySql.Data.MySqlClient" />
     <Using Include="MySql.Data.Types" />
diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
index a93e40b1a..0a616eb69 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
+++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
@@ -1,3 +1,4 @@
+using System.Globalization;
 using System.Reflection;
 using YamlDotNet.Serialization;
 using YamlDotNet.Serialization.NamingConventions;
@@ -168,7 +169,7 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 docWriter.Write($"""
 	---
 	date: 2021-04-24
-	lastmod: {DateTime.UtcNow.ToString("yyyy-MM-dd")}
+	lastmod: {DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)}
 	menu:
 	  main:
 	    parent: getting started
@@ -185,6 +186,7 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 
 
 	""");
+#pragma warning disable CA1308 // Normalize strings to uppercase
 foreach (var schema in schemaCollections)
 	docWriter.Write($@"* `{schema.Name}`{(schema.Description is not null ? "—[" + schema.Description + "](../schema/" + schema.Name.ToLowerInvariant() + "/)" : "")}
 ");
@@ -192,10 +194,11 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 foreach (var schema in schemaCollections.Where(x => x.Description is not null))
 {
 	using var schemaDocWriter = new StreamWriter($@"..\..\..\..\..\docs\content\overview\schema\{schema.Name.ToLowerInvariant()}.md");
+#pragma warning restore CA1308 // Normalize strings to uppercase
 	schemaDocWriter.Write($"""
 		---
 		date: 2022-07-10
-		lastmod: {DateTime.UtcNow.ToString("yyyy-MM-dd")}
+		lastmod: {DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture)}
 		title: {schema.Name} Schema
 		---
 
@@ -226,7 +229,8 @@ Restriction Name | Restriction Default | Restriction Number
 	}
 }
 
-class Schema
+#pragma warning disable CA1812 // Avoid uninstantiated internal classes
+internal sealed class Schema
 {
 	public required string Name { get; init; }
 	public string? Description { get; init; }
@@ -237,7 +241,7 @@ class Schema
 	public List<Restriction>? Restrictions { get; init; }
 }
 
-class Column
+internal sealed class Column
 {
 	public required string Name { get; init; }
 	public required string Type { get; init; }
@@ -245,7 +249,7 @@ class Column
 	public bool Optional { get; init; }
 }
 
-class Restriction
+internal sealed class Restriction
 {
 	public required string Name { get; init; }
 	public required string Default { get; init; }
diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
index 5c9ed11ba..73e62aa5e 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
+++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
@@ -5,6 +5,7 @@
     <TargetFramework>net8.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
+    <GenerateDocumentationFile>true</GenerateDocumentationFile>
   </PropertyGroup>
 
   <ItemGroup>
@@ -12,7 +13,7 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="YamlDotNet" Version="13.1.1" />
+    <PackageReference Include="YamlDotNet" />
   </ItemGroup>
 
 </Project>

From fc1b0fc868a8c8f8e4f6fdc3199cade035b27528 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 17 Sep 2023 08:10:28 -0700
Subject: [PATCH 171/393] Update setup-dotnet version to 8.0.

---
 .github/workflows/ci.yml              | 4 ++--
 .github/workflows/codeql-analysis.yml | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fad91c006..215437db1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -17,10 +17,10 @@ jobs:
     - name: Checkout repository
       uses: actions/checkout@v4
 
-    - name: Set up .NET 7.0
+    - name: Set up .NET 8.0
       uses: actions/setup-dotnet@v3
       with:
-        dotnet-version: 7.x
+        dotnet-version: 8.x
 
     - name: Restore
       run: dotnet restore
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 4a4d082d3..d9c51efda 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -26,10 +26,10 @@ jobs:
     - name: Check out
       uses: actions/checkout@v4
 
-    - name: Set up .NET 7.0
+    - name: Set up .NET 8.0
       uses: actions/setup-dotnet@v3
       with:
-        dotnet-version: 7.x
+        dotnet-version: 8.x
 
     - name: Initialize CodeQL
       uses: github/codeql-action/init@v2

From 143f6192b71bc035dcc8668d6b45833d683b216b Mon Sep 17 00:00:00 2001
From: Nickolay Batov <n.batov@dodopizza.com>
Date: Sun, 17 Sep 2023 22:47:13 +0300
Subject: [PATCH 172/393] fix ExpectedSessionState6 call

Signed-off-by: Nickolay Batov <stilettk@gmail.com>
---
 src/MySqlConnector/Core/ServerSession.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 0b79453ec..133f4dcf0 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1770,7 +1770,7 @@ private void VerifyState(State state1, State state2, State state3, State state4,
 	{
 		if (m_state != state1 && m_state != state2 && m_state != state3 && m_state != state4 && m_state != state5 && m_state != state6)
 		{
-			ExpectedSessionState6(m_logger, Id, state1, state2, state3, state4, state5, state5, m_state);
+			ExpectedSessionState6(m_logger, Id, state1, state2, state3, state4, state5, state6, m_state);
 			throw new InvalidOperationException($"Expected state to be ({state1}|{state2}|{state3}|{state4}|{state5}|{state6}) but was {m_state}.");
 		}
 	}

From 3b99ed2db452d6fda4df21c35f63fa206b978abc Mon Sep 17 00:00:00 2001
From: Nickolay Batov <n.batov@dodopizza.com>
Date: Sun, 17 Sep 2023 23:05:28 +0300
Subject: [PATCH 173/393] dispose X509Store

Signed-off-by: Nickolay Batov <stilettk@gmail.com>
---
 src/MySqlConnector/Core/ServerSession.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 133f4dcf0..2b82b2ee0 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1291,7 +1291,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 			try
 			{
 				var storeLocation = (cs.CertificateStoreLocation == MySqlCertificateStoreLocation.CurrentUser) ? StoreLocation.CurrentUser : StoreLocation.LocalMachine;
-				var store = new X509Store(StoreName.My, storeLocation);
+				using var store = new X509Store(StoreName.My, storeLocation);
 				store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly);
 
 				if (cs.CertificateThumbprint.Length == 0)

From 849ad4667241c415044fed9966e5e3cf03f095a7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 17 Sep 2023 20:10:24 -0700
Subject: [PATCH 174/393] Don't run component detection on PRs.

---
 .github/workflows/ci.yml | 2 --
 1 file changed, 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index fad91c006..c60d67252 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -3,8 +3,6 @@ name: "Continuous Integration"
 on:
   push:
     branches: [ master, test ]
-  pull_request:
-    branches: [ master ]
 
 jobs:
   build:

From 91b5de07c098f925da914f128c6cd58fda9e0554 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 21 Sep 2023 07:00:12 -0700
Subject: [PATCH 175/393] Use Ascii.FromUtf16 on .NET 8.

---
 .../Protocol/Serialization/ByteBufferWriter.cs               | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
index 0cfdf970b..6deb3e6a2 100644
--- a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
@@ -144,7 +144,12 @@ public void WriteAscii(ReadOnlySpan<char> chars)
 	{
 		if (m_output.Length < chars.Length)
 			Reallocate(chars.Length);
+#if NET8_0_OR_GREATER
+		Ascii.FromUtf16(chars, m_output.Span, out var bytesWritten);
+		m_output = m_output[bytesWritten..];
+#else
 		m_output = m_output[Encoding.ASCII.GetBytes(chars, m_output.Span)..];
+#endif
 	}
 
 	public void WriteLengthEncodedString(StringBuilder stringBuilder)

From fabee62cf4ad39f53e2ce02b32185bc525284219 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 21 Sep 2023 07:13:54 -0700
Subject: [PATCH 176/393] Use big-endian Guid methods in .NET 8.0.

---
 .../ColumnReaders/GuidBinary16ColumnReader.cs        |  4 +++-
 .../GuidLittleEndianBinary16ColumnReader.cs          |  8 +++++---
 src/MySqlConnector/MySqlParameter.cs                 | 12 ++++++++++++
 3 files changed, 20 insertions(+), 4 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
index 8cc2dd94f..0f4df3ff6 100644
--- a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
@@ -15,7 +15,9 @@ public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayloa
 #endif
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
 	public static Guid ReadGuid(ReadOnlySpan<byte> data) =>
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET8_0_OR_GREATER
+		new(data, bigEndian: true);
+#elif NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 		new(stackalloc byte[16] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #else
 		new(new[] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
diff --git a/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
index 0bd7c2996..99e9fd96f 100644
--- a/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidLittleEndianBinary16ColumnReader.cs
@@ -12,9 +12,11 @@ public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayloa
 
 	[MethodImpl(MethodImplOptions.AggressiveInlining)]
 	public static Guid ReadGuid(ReadOnlySpan<byte> data) =>
-#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-		new Guid(data);
+#if NET8_0_OR_GREATER
+		new(data, bigEndian: false);
+#elif NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		new(data);
 #else
-		new Guid(data.ToArray());
+		new(data.ToArray());
 #endif
 }
diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index f3975457c..ef13dd099 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -454,16 +454,22 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 			{
 #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
 				Span<byte> bytes = stackalloc byte[16];
+#if NET8_0_OR_GREATER
+				guidValue.TryWriteBytes(bytes, bigEndian: guidOptions != StatementPreparerOptions.GuidFormatLittleEndianBinary16, out _);
+#else
 				guidValue.TryWriteBytes(bytes);
+#endif
 #else
 				var bytes = guidValue.ToByteArray();
 #endif
 				if (guidOptions != StatementPreparerOptions.GuidFormatLittleEndianBinary16)
 				{
+#if !NET8_0_OR_GREATER
 					Utility.SwapBytes(bytes, 0, 3);
 					Utility.SwapBytes(bytes, 1, 2);
 					Utility.SwapBytes(bytes, 4, 5);
 					Utility.SwapBytes(bytes, 6, 7);
+#endif
 
 					if (guidOptions == StatementPreparerOptions.GuidFormatTimeSwapBinary16)
 					{
@@ -765,16 +771,22 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
 			{
 #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
 				Span<byte> bytes = stackalloc byte[16];
+#if NET8_0_OR_GREATER
+				guidValue.TryWriteBytes(bytes, bigEndian: guidOptions != StatementPreparerOptions.GuidFormatLittleEndianBinary16, out _);
+#else
 				guidValue.TryWriteBytes(bytes);
+#endif
 #else
 				var bytes = guidValue.ToByteArray();
 #endif
 				if (guidOptions != StatementPreparerOptions.GuidFormatLittleEndianBinary16)
 				{
+#if !NET8_0_OR_GREATER
 					Utility.SwapBytes(bytes, 0, 3);
 					Utility.SwapBytes(bytes, 1, 2);
 					Utility.SwapBytes(bytes, 4, 5);
 					Utility.SwapBytes(bytes, 6, 7);
+#endif
 
 					if (guidOptions == StatementPreparerOptions.GuidFormatTimeSwapBinary16)
 					{

From 8517e9adad001288b678a66b9fabc1e123146420 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 30 Sep 2023 18:39:22 -0700
Subject: [PATCH 177/393] Dispose TcpListener.

---
 tests/MySqlConnector.Tests/FakeMySqlServer.cs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tests/MySqlConnector.Tests/FakeMySqlServer.cs b/tests/MySqlConnector.Tests/FakeMySqlServer.cs
index 71c78dd4d..d1f6d5a9d 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServer.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServer.cs
@@ -36,6 +36,9 @@ public void Stop()
 			}
 			m_connections.Clear();
 			m_tasks.Clear();
+#if NET8_0_OR_GREATER
+			m_tcpListener.Dispose();
+#endif
 			m_cts.Dispose();
 			m_cts = null;
 		}

From 226a75c72ecc6d3c977ab4ad48f4834069326c1c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 30 Sep 2023 19:45:01 -0700
Subject: [PATCH 178/393] Use argument throwing helpers.

These methods may allow more aggressive inlining of the methods that call them, due to moving the 'throw' out of the method body.
---
 .../Authentication/AuthenticationPlugins.cs   |  9 ++-
 src/MySqlConnector/Core/ResultSet.cs          |  4 ++
 src/MySqlConnector/Core/Row.cs                | 21 ++++++-
 src/MySqlConnector/Core/SchemaProvider.g.cs   |  4 ++
 src/MySqlConnector/Core/SqlParser.cs          |  8 ++-
 .../MySqlAttributeCollection.cs               | 16 +++++-
 src/MySqlConnector/MySqlBulkCopy.cs           | 56 ++++++++++++++++---
 src/MySqlConnector/MySqlCommand.cs            |  8 ++-
 src/MySqlConnector/MySqlCommandBuilder.cs     |  4 ++
 src/MySqlConnector/MySqlConnection.cs         | 12 ++++
 src/MySqlConnector/MySqlHelper.cs             |  4 ++
 src/MySqlConnector/MySqlParameter.cs          |  8 ++-
 .../MySqlParameterCollection.cs               | 24 +++++++-
 src/MySqlConnector/MySqlTransaction.cs        |  8 +++
 .../Protocol/Serialization/ByteArrayReader.cs | 22 +++++++-
 .../Serialization/StandardPayloadHandler.cs   |  8 ++-
 src/MySqlConnector/Utilities/TimerQueue.cs    |  4 ++
 .../SchemaCollectionGenerator.cs              |  4 ++
 18 files changed, 206 insertions(+), 18 deletions(-)

diff --git a/src/MySqlConnector/Authentication/AuthenticationPlugins.cs b/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
index 0b1c1246c..88d390fa6 100644
--- a/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
+++ b/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
@@ -13,11 +13,18 @@ public static class AuthenticationPlugins
 	/// <param name="plugin">The authentication plugin.</param>
 	public static void Register(IAuthenticationPlugin plugin)
 	{
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(plugin);
+#else
 		if (plugin is null)
 			throw new ArgumentNullException(nameof(plugin));
+#endif
+#if NET8_0_OR_GREATER
+		ArgumentException.ThrowIfNullOrEmpty(plugin.Name);
+#else
 		if (string.IsNullOrEmpty(plugin.Name))
 			throw new ArgumentException("Invalid plugin name.", nameof(plugin));
-
+#endif
 		lock (s_lock)
 			s_plugins.Add(plugin.Name, plugin);
 	}
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 37484088c..c3704b826 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -334,8 +334,12 @@ public bool HasRows
 
 	public int GetOrdinal(string name)
 	{
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(name);
+#else
 		if (name is null)
 			throw new ArgumentNullException(nameof(name));
+#endif
 		if (!HasResultSet)
 			throw new InvalidOperationException("There is no current result set.");
 
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index d096c10f0..7025a8b30 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -76,8 +76,13 @@ public void SetData(ReadOnlyMemory<byte> data)
 
 	public object GetValue(int ordinal)
 	{
+#if NET8_0_OR_GREATER
+		ArgumentOutOfRangeException.ThrowIfNegative(ordinal);
+		ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(ordinal, ResultSet.ColumnDefinitions!.Length);
+#else
 		if (ordinal < 0 || ordinal >= ResultSet.ColumnDefinitions!.Length)
 			throw new ArgumentOutOfRangeException(nameof(ordinal), $"value must be between 0 and {ResultSet.ColumnDefinitions!.Length - 1}");
+#endif
 
 		if (m_dataOffsetLengths[ordinal].Offset == -1)
 			return DBNull.Value;
@@ -418,7 +423,13 @@ public MySqlDecimal GetMySqlDecimal(int ordinal)
 
 	public int GetValues(object[] values)
 	{
-		int count = Math.Min((values ?? throw new ArgumentNullException(nameof(values))).Length, ResultSet.ColumnDefinitions!.Length);
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(values);
+#else
+		if (values is null)
+			throw new ArgumentNullException(nameof(values));
+#endif
+		int count = Math.Min(values.Length, ResultSet.ColumnDefinitions!.Length);
 		for (int i = 0; i < count; i++)
 			values[i] = GetValue(i);
 		return count;
@@ -451,6 +462,13 @@ private void CheckBinaryColumn(int ordinal)
 
 	private static void CheckBufferArguments<T>(long dataOffset, T[] buffer, int bufferOffset, int length)
 	{
+#if NET8_0_OR_GREATER
+		ArgumentOutOfRangeException.ThrowIfNegative(dataOffset);
+		ArgumentOutOfRangeException.ThrowIfGreaterThan(dataOffset, int.MaxValue);
+		ArgumentOutOfRangeException.ThrowIfNegative(length);
+		ArgumentOutOfRangeException.ThrowIfNegative(bufferOffset);
+		ArgumentOutOfRangeException.ThrowIfGreaterThan(bufferOffset, buffer.Length);
+#else
 		if (dataOffset < 0)
 			throw new ArgumentOutOfRangeException(nameof(dataOffset), dataOffset, nameof(dataOffset) + " must be non-negative");
 		if (dataOffset > int.MaxValue)
@@ -461,6 +479,7 @@ private static void CheckBufferArguments<T>(long dataOffset, T[] buffer, int buf
 			throw new ArgumentOutOfRangeException(nameof(bufferOffset), bufferOffset, nameof(bufferOffset) + " must be non-negative");
 		if (bufferOffset > buffer.Length)
 			throw new ArgumentOutOfRangeException(nameof(bufferOffset), bufferOffset, nameof(bufferOffset) + " must be within the buffer");
+#endif
 		if (checked(bufferOffset + length) > buffer.Length)
 			throw new ArgumentException(nameof(bufferOffset) + " + " + nameof(length) + " cannot exceed " + nameof(buffer) + "." + nameof(buffer.Length), nameof(length));
 	}
diff --git a/src/MySqlConnector/Core/SchemaProvider.g.cs b/src/MySqlConnector/Core/SchemaProvider.g.cs
index ce47a726e..11c75e185 100644
--- a/src/MySqlConnector/Core/SchemaProvider.g.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.g.cs
@@ -9,8 +9,12 @@ internal sealed partial class SchemaProvider
 {
 	public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string collectionName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(collectionName);
+#else
 		if (collectionName is null)
 			throw new ArgumentNullException(nameof(collectionName));
+#endif
 
 		var dataTable = new DataTable();
 		if (string.Equals(collectionName, "MetaDataCollections", StringComparison.OrdinalIgnoreCase))
diff --git a/src/MySqlConnector/Core/SqlParser.cs b/src/MySqlConnector/Core/SqlParser.cs
index a5c312e0e..128d62560 100644
--- a/src/MySqlConnector/Core/SqlParser.cs
+++ b/src/MySqlConnector/Core/SqlParser.cs
@@ -10,7 +10,13 @@ internal abstract class SqlParser
 
 	public void Parse(string sql)
 	{
-		OnBeforeParse(sql ?? throw new ArgumentNullException(nameof(sql)));
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(sql);
+#else
+		if (sql is null)
+			throw new ArgumentNullException(nameof(sql));
+#endif
+		OnBeforeParse(sql);
 
 		int parameterStartIndex = -1;
 		var noBackslashEscapes = (Preparer.Options & StatementPreparerOptions.NoBackslashEscapes) == StatementPreparerOptions.NoBackslashEscapes;
diff --git a/src/MySqlConnector/MySqlAttributeCollection.cs b/src/MySqlConnector/MySqlAttributeCollection.cs
index ed44a8907..e3ea8c3f7 100644
--- a/src/MySqlConnector/MySqlAttributeCollection.cs
+++ b/src/MySqlConnector/MySqlAttributeCollection.cs
@@ -19,8 +19,18 @@ public sealed class MySqlAttributeCollection : IEnumerable<MySqlAttribute>
 		/// <remarks>The attribute name must not be empty, and must not already exist in the collection.</remarks>
 		public void Add(MySqlAttribute attribute)
 		{
-			if (string.IsNullOrEmpty((attribute ?? throw new ArgumentNullException(nameof(attribute))).AttributeName))
+#if NET6_0_OR_GREATER
+			ArgumentNullException.ThrowIfNull(attribute);
+#else
+			if (attribute is null)
+				throw new ArgumentNullException(nameof(attribute));
+#endif
+#if NET8_0_OR_GREATER
+			ArgumentException.ThrowIfNullOrEmpty(attribute.AttributeName);
+#else
+			if (string.IsNullOrEmpty(attribute.AttributeName))
 				throw new ArgumentException("Attribute name must not be empty", nameof(attribute));
+#endif
 			foreach (var existingAttribute in m_attributes)
 			{
 				if (existingAttribute.AttributeName == attribute.AttributeName)
@@ -36,8 +46,12 @@ public void Add(MySqlAttribute attribute)
 		/// <param name="value">The attribute value.</param>
 		public void SetAttribute(string attributeName, object? value)
 		{
+#if NET8_0_OR_GREATER
+			ArgumentException.ThrowIfNullOrEmpty(attributeName);
+#else
 			if (string.IsNullOrEmpty(attributeName))
 				throw new ArgumentException("Attribute name must not be empty", nameof(attributeName));
+#endif
 			for (var i = 0; i < m_attributes.Count; i++)
 			{
 				if (m_attributes[i].AttributeName == attributeName)
diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs
index 1724bb411..481be2e67 100644
--- a/src/MySqlConnector/MySqlBulkCopy.cs
+++ b/src/MySqlConnector/MySqlBulkCopy.cs
@@ -52,7 +52,13 @@ public sealed class MySqlBulkCopy
 	/// <param name="transaction">(Optional) The <see cref="MySqlTransaction"/> to use.</param>
 	public MySqlBulkCopy(MySqlConnection connection, MySqlTransaction? transaction = null)
 	{
-		m_connection = connection ?? throw new ArgumentNullException(nameof(connection));
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(connection);
+#else
+		if (connection is null)
+			throw new ArgumentNullException(nameof(connection));
+#endif
+		m_connection = connection;
 		m_transaction = transaction;
 		m_logger = m_connection.LoggingConfiguration.BulkCopyLogger;
 		ColumnMappings = new();
@@ -111,7 +117,13 @@ public MySqlBulkCopy(MySqlConnection connection, MySqlTransaction? transaction =
 	/// <returns>A <see cref="MySqlBulkCopyResult"/> with the result of the bulk copy operation.</returns>
 	public MySqlBulkCopyResult WriteToServer(DataTable dataTable)
 	{
-		m_valuesEnumerator = DataRowsValuesEnumerator.Create(dataTable ?? throw new ArgumentNullException(nameof(dataTable)));
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(dataTable);
+#else
+		if (dataTable is null)
+			throw new ArgumentNullException(nameof(dataTable));
+#endif
+		m_valuesEnumerator = DataRowsValuesEnumerator.Create(dataTable);
 #pragma warning disable CA2012 // Safe because method completes synchronously
 		return WriteToServerAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 #pragma warning restore CA2012
@@ -126,7 +138,13 @@ public MySqlBulkCopyResult WriteToServer(DataTable dataTable)
 	/// <returns>A <see cref="MySqlBulkCopyResult"/> with the result of the bulk copy operation.</returns>
 	public async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(DataTable dataTable, CancellationToken cancellationToken = default)
 	{
-		m_valuesEnumerator = DataRowsValuesEnumerator.Create(dataTable ?? throw new ArgumentNullException(nameof(dataTable)));
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(dataTable);
+#else
+		if (dataTable is null)
+			throw new ArgumentNullException(nameof(dataTable));
+#endif
+		m_valuesEnumerator = DataRowsValuesEnumerator.Create(dataTable);
 		return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false);
 	}
 
@@ -140,7 +158,13 @@ public async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(DataTable dataTab
 	/// <returns>A <see cref="MySqlBulkCopyResult"/> with the result of the bulk copy operation.</returns>
 	public MySqlBulkCopyResult WriteToServer(IEnumerable<DataRow> dataRows, int columnCount)
 	{
-		m_valuesEnumerator = new DataRowsValuesEnumerator(dataRows ?? throw new ArgumentNullException(nameof(dataRows)), columnCount);
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(dataRows);
+#else
+		if (dataRows is null)
+			throw new ArgumentNullException(nameof(dataRows));
+#endif
+		m_valuesEnumerator = new DataRowsValuesEnumerator(dataRows, columnCount);
 #pragma warning disable CA2012 // Safe because method completes synchronously
 		return WriteToServerAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 #pragma warning restore CA2012
@@ -157,7 +181,13 @@ public MySqlBulkCopyResult WriteToServer(IEnumerable<DataRow> dataRows, int colu
 	/// <returns>A <see cref="MySqlBulkCopyResult"/> with the result of the bulk copy operation.</returns>
 	public async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IEnumerable<DataRow> dataRows, int columnCount, CancellationToken cancellationToken = default)
 	{
-		m_valuesEnumerator = new DataRowsValuesEnumerator(dataRows ?? throw new ArgumentNullException(nameof(dataRows)), columnCount);
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(dataRows);
+#else
+		if (dataRows is null)
+			throw new ArgumentNullException(nameof(dataRows));
+#endif
+		m_valuesEnumerator = new DataRowsValuesEnumerator(dataRows, columnCount);
 		return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false);
 	}
 
@@ -169,7 +199,13 @@ public async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IEnumerable<DataR
 	/// <returns>A <see cref="MySqlBulkCopyResult"/> with the result of the bulk copy operation.</returns>
 	public MySqlBulkCopyResult WriteToServer(IDataReader dataReader)
 	{
-		m_valuesEnumerator = DataReaderValuesEnumerator.Create(dataReader ?? throw new ArgumentNullException(nameof(dataReader)));
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(dataReader);
+#else
+		if (dataReader is null)
+			throw new ArgumentNullException(nameof(dataReader));
+#endif
+		m_valuesEnumerator = DataReaderValuesEnumerator.Create(dataReader);
 #pragma warning disable CA2012 // Safe because method completes synchronously
 		return WriteToServerAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 #pragma warning restore CA2012
@@ -184,7 +220,13 @@ public MySqlBulkCopyResult WriteToServer(IDataReader dataReader)
 	/// <returns>A <see cref="MySqlBulkCopyResult"/> with the result of the bulk copy operation.</returns>
 	public async ValueTask<MySqlBulkCopyResult> WriteToServerAsync(IDataReader dataReader, CancellationToken cancellationToken = default)
 	{
-		m_valuesEnumerator = DataReaderValuesEnumerator.Create(dataReader ?? throw new ArgumentNullException(nameof(dataReader)));
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(dataReader);
+#else
+		if (dataReader is null)
+			throw new ArgumentNullException(nameof(dataReader));
+#endif
+		m_valuesEnumerator = DataReaderValuesEnumerator.Create(dataReader);
 		return await WriteToServerAsync(IOBehavior.Asynchronous, cancellationToken).ConfigureAwait(false);
 	}
 
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index 5cdf1082a..09ebbbfce 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -230,7 +230,13 @@ public override int CommandTimeout
 		get => Math.Min(m_commandTimeout ?? Connection?.DefaultCommandTimeout ?? 0, int.MaxValue / 1000);
 		set
 		{
-			m_commandTimeout = value >= 0 ? value : throw new ArgumentOutOfRangeException(nameof(value), "CommandTimeout must be greater than or equal to zero.");
+#if NET8_0_OR_GREATER
+			ArgumentOutOfRangeException.ThrowIfNegative(value);
+#else
+			if (value < 0)
+				throw new ArgumentOutOfRangeException(nameof(value), "CommandTimeout must be greater than or equal to zero.");
+#endif
+			m_commandTimeout = value;
 			((ICancellableCommand) this).EffectiveCommandTimeout = null;
 		}
 	}
diff --git a/src/MySqlConnector/MySqlCommandBuilder.cs b/src/MySqlConnector/MySqlCommandBuilder.cs
index 5a9d361e8..b69b9444d 100644
--- a/src/MySqlConnector/MySqlCommandBuilder.cs
+++ b/src/MySqlConnector/MySqlCommandBuilder.cs
@@ -12,8 +12,12 @@ public sealed class MySqlCommandBuilder : DbCommandBuilder
 
 	private static async Task DeriveParametersAsync(IOBehavior ioBehavior, MySqlCommand command, CancellationToken cancellationToken)
 	{
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(command);
+#else
 		if (command is null)
 			throw new ArgumentNullException(nameof(command));
+#endif
 		if (command.CommandType != CommandType.StoredProcedure)
 			throw new ArgumentException($"MySqlCommand.CommandType must be StoredProcedure not {command.CommandType}", nameof(command));
 		if (string.IsNullOrWhiteSpace(command.CommandText))
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index b5802327d..b7bfaf02b 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -294,8 +294,12 @@ internal void UnenlistTransaction()
 	private void TakeSessionFrom(MySqlConnection other)
 	{
 #if DEBUG
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(other);
+#else
 		if (other is null)
 			throw new ArgumentNullException(nameof(other));
+#endif
 		if (m_session is not null)
 			throw new InvalidOperationException("This connection must not have a session");
 		if (other.m_session is null)
@@ -555,8 +559,12 @@ public override string ConnectionString
 
 	private static async Task ClearPoolAsync(MySqlConnection connection, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(connection);
+#else
 		if (connection is null)
 			throw new ArgumentNullException(nameof(connection));
+#endif
 
 		var pool = ConnectionPool.GetPool(connection.m_connectionString, null, createIfNotFound: false);
 		if (pool is not null)
@@ -855,8 +863,12 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 
 	internal void SetActiveReader(MySqlDataReader dataReader)
 	{
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(dataReader);
+#else
 		if (dataReader is null)
 			throw new ArgumentNullException(nameof(dataReader));
+#endif
 		if (m_activeReader is not null)
 			throw new InvalidOperationException("Can't replace active reader.");
 		m_activeReader = dataReader;
diff --git a/src/MySqlConnector/MySqlHelper.cs b/src/MySqlConnector/MySqlHelper.cs
index a5fcdb09b..e78090a9c 100644
--- a/src/MySqlConnector/MySqlHelper.cs
+++ b/src/MySqlConnector/MySqlHelper.cs
@@ -12,8 +12,12 @@ public sealed class MySqlHelper
 	/// </summary>
 	public static string EscapeString(string value)
 	{
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(value);
+#else
 		if (value is null)
 			throw new ArgumentNullException(nameof(value));
+#endif
 
 		StringBuilder? sb = null;
 		int last = -1;
diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index ef13dd099..e9a9b8800 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -179,7 +179,13 @@ private MySqlParameter(MySqlParameter other)
 	private MySqlParameter(MySqlParameter other, string parameterName)
 		: this(other)
 	{
-		ParameterName = parameterName ?? throw new ArgumentNullException(nameof(parameterName));
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(parameterName);
+#else
+		if (parameterName is null)
+			throw new ArgumentNullException(nameof(parameterName));
+#endif
+		ParameterName = parameterName;
 	}
 
 	internal bool HasSetDirection => m_direction.HasValue;
diff --git a/src/MySqlConnector/MySqlParameterCollection.cs b/src/MySqlConnector/MySqlParameterCollection.cs
index 655778dc6..0b8d428a8 100644
--- a/src/MySqlConnector/MySqlParameterCollection.cs
+++ b/src/MySqlConnector/MySqlParameterCollection.cs
@@ -25,13 +25,25 @@ public MySqlParameter Add(string parameterName, DbType dbType)
 
 	public override int Add(object value)
 	{
-		AddParameter((MySqlParameter) (value ?? throw new ArgumentNullException(nameof(value))), m_parameters.Count);
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(value);
+#else
+		if (value is null)
+			throw new ArgumentNullException(nameof(value));
+#endif
+		AddParameter((MySqlParameter) value, m_parameters.Count);
 		return m_parameters.Count - 1;
 	}
 
 	public MySqlParameter Add(MySqlParameter parameter)
 	{
-		AddParameter(parameter ?? throw new ArgumentNullException(nameof(parameter)), m_parameters.Count);
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(parameter);
+#else
+		if (parameter is null)
+			throw new ArgumentNullException(nameof(parameter));
+#endif
+		AddParameter(parameter, m_parameters.Count);
 		return parameter;
 	}
 
@@ -123,7 +135,13 @@ public override void RemoveAt(int index)
 
 	protected override void SetParameter(int index, DbParameter value)
 	{
-		var newParameter = (MySqlParameter) (value ?? throw new ArgumentNullException(nameof(value)));
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(value);
+#else
+		if (value is null)
+			throw new ArgumentNullException(nameof(value));
+#endif
+		var newParameter = (MySqlParameter) value;
 		var oldParameter = m_parameters[index];
 		if (oldParameter.NormalizedParameterName is not null)
 			m_nameToIndex.Remove(oldParameter.NormalizedParameterName);
diff --git a/src/MySqlConnector/MySqlTransaction.cs b/src/MySqlConnector/MySqlTransaction.cs
index fffd938c6..6883cc69b 100644
--- a/src/MySqlConnector/MySqlTransaction.cs
+++ b/src/MySqlConnector/MySqlTransaction.cs
@@ -149,10 +149,18 @@ private async Task ExecuteSavepointAsync(string command, string savepointName, I
 	{
 		VerifyValid();
 
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(savepointName);
+#else
 		if (savepointName is null)
 			throw new ArgumentNullException(nameof(savepointName));
+#endif
+#if NET8_0_OR_GREATER
+		ArgumentException.ThrowIfNullOrEmpty(savepointName);
+#else
 		if (savepointName.Length == 0)
 			throw new ArgumentException("savepointName must not be empty", nameof(savepointName));
+#endif
 
 		using var cmd = new MySqlCommand(command + "savepoint " + QuoteIdentifier(savepointName), Connection, this) { NoActivity = true };
 		await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
diff --git a/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs b/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs
index fa597c38a..5d15150ec 100644
--- a/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs
@@ -14,7 +14,17 @@ public ByteArrayReader(ReadOnlySpan<byte> buffer)
 	public int Offset
 	{
 		readonly get => m_offset;
-		set => m_offset = value >= 0 && value <= m_maxOffset ? value : throw new ArgumentOutOfRangeException(nameof(value), $"value must be between 0 and {m_maxOffset:d}");
+		set
+		{
+#if NET8_0_OR_GREATER
+			ArgumentOutOfRangeException.ThrowIfNegative(value);
+			ArgumentOutOfRangeException.ThrowIfGreaterThan(value, m_maxOffset);
+#else
+			if (value < 0 || value > m_maxOffset)
+				throw new ArgumentOutOfRangeException(nameof(value), $"value must be between 0 and {m_maxOffset:d}");
+#endif
+			m_offset = value; 
+		}
 	}
 
 	public byte ReadByte()
@@ -63,8 +73,13 @@ public uint ReadUInt32()
 
 	public uint ReadFixedLengthUInt32(int length)
 	{
+#if NET8_0_OR_GREATER
+		ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(length, 0);
+		ArgumentOutOfRangeException.ThrowIfGreaterThan(length, 4);
+#else
 		if (length is <= 0 or > 4)
 			throw new ArgumentOutOfRangeException(nameof(length));
+#endif
 		VerifyRead(length);
 		uint result = 0;
 		for (var i = 0; i < length; i++)
@@ -75,8 +90,13 @@ public uint ReadFixedLengthUInt32(int length)
 
 	public ulong ReadFixedLengthUInt64(int length)
 	{
+#if NET8_0_OR_GREATER
+		ArgumentOutOfRangeException.ThrowIfLessThanOrEqual(length, 0);
+		ArgumentOutOfRangeException.ThrowIfGreaterThan(length, 8);
+#else
 		if (length is <= 0 or > 8)
 			throw new ArgumentOutOfRangeException(nameof(length));
+#endif
 		VerifyRead(length);
 		ulong result = 0;
 		for (var i = 0; i < length; i++)
diff --git a/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs b/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs
index 337418667..490a0e137 100644
--- a/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs
+++ b/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs
@@ -33,7 +33,13 @@ public IByteHandler ByteHandler
 		set
 		{
 			var oldByteHandler = m_byteHandler;
-			m_byteHandler = value ?? throw new ArgumentNullException(nameof(value));
+#if NET6_0_OR_GREATER
+			ArgumentNullException.ThrowIfNull(value);
+#else
+			if (value is null)
+				throw new ArgumentNullException(nameof(value));
+#endif
+			m_byteHandler = value;
 			oldByteHandler?.Dispose();
 			m_bufferedByteReader = new();
 		}
diff --git a/src/MySqlConnector/Utilities/TimerQueue.cs b/src/MySqlConnector/Utilities/TimerQueue.cs
index 44ee1f8e0..916cf64d1 100644
--- a/src/MySqlConnector/Utilities/TimerQueue.cs
+++ b/src/MySqlConnector/Utilities/TimerQueue.cs
@@ -14,8 +14,12 @@ internal sealed class TimerQueue
 	/// <returns>A timer ID that can be passed to <see cref="Remove"/> to cancel the timer.</returns>
 	public uint Add(int delay, Action action)
 	{
+#if NET8_0_OR_GREATER
+		ArgumentOutOfRangeException.ThrowIfNegative(delay);
+#else
 		if (delay < 0)
 			throw new ArgumentOutOfRangeException(nameof(delay), $"delay must not be negative: {delay}");
+#endif
 
 		var current = Environment.TickCount;
 		lock (m_lock)
diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
index 0a616eb69..dac24b5bf 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
+++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
@@ -24,8 +24,12 @@ internal sealed partial class SchemaProvider
 	{
 		public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string collectionName, string?[]? restrictionValues, CancellationToken cancellationToken)
 		{
+	#if NET6_0_OR_GREATER
+			ArgumentNullException.ThrowIfNull(collectionName);
+	#else
 			if (collectionName is null)
 				throw new ArgumentNullException(nameof(collectionName));
+	#endif
 
 			var dataTable = new DataTable();
 	""");

From e4dde0e1b4f3a544308b362ae5556965e00344e4 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 6 Oct 2023 14:50:40 -0700
Subject: [PATCH 179/393] Use ObjectDisposedException.ThrowIf.

---
 src/MySqlConnector/MySqlConnection.cs                         | 4 ++++
 src/MySqlConnector/MySqlDataSource.cs                         | 4 ++++
 src/MySqlConnector/MySqlTransaction.cs                        | 4 ++++
 .../Protocol/Serialization/StandardPayloadHandler.cs          | 4 ++++
 4 files changed, 16 insertions(+)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index b7bfaf02b..8f47d6769 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -991,8 +991,12 @@ private MySqlConnection(MySqlConnection other, MySqlDataSource? dataSource, stri
 
 	private void VerifyNotDisposed()
 	{
+#if NET7_0_OR_GREATER
+		ObjectDisposedException.ThrowIf(m_isDisposed, this);
+#else
 		if (m_isDisposed)
 			throw new ObjectDisposedException(GetType().Name);
+#endif
 	}
 
 	private async Task CloseAsync(bool changeState, IOBehavior ioBehavior)
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index c3499ef0e..25a34d431 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -128,8 +128,12 @@ public string Password
 
 	protected override DbConnection CreateDbConnection()
 	{
+#if NET7_0_OR_GREATER
+		ObjectDisposedException.ThrowIf(m_isDisposed, this);
+#else
 		if (m_isDisposed)
 			throw new ObjectDisposedException(nameof(MySqlDataSource));
+#endif
 		return new MySqlConnection(this)
 		{
 			ProvideClientCertificatesCallback = m_clientCertificatesCallback,
diff --git a/src/MySqlConnector/MySqlTransaction.cs b/src/MySqlConnector/MySqlTransaction.cs
index 6883cc69b..7039da08a 100644
--- a/src/MySqlConnector/MySqlTransaction.cs
+++ b/src/MySqlConnector/MySqlTransaction.cs
@@ -277,8 +277,12 @@ private async Task DoRollback(IOBehavior ioBehavior, CancellationToken cancellat
 
 	private void VerifyValid()
 	{
+#if NET7_0_OR_GREATER
+		ObjectDisposedException.ThrowIf(m_isDisposed, this);
+#else
 		if (m_isDisposed)
 			throw new ObjectDisposedException(nameof(MySqlTransaction));
+#endif
 		if (Connection is null)
 			throw new InvalidOperationException("Already committed or rolled back.");
 		if (Connection.CurrentTransaction is null)
diff --git a/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs b/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs
index 490a0e137..6650dc563 100644
--- a/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs
+++ b/src/MySqlConnector/Protocol/Serialization/StandardPayloadHandler.cs
@@ -26,8 +26,12 @@ public IByteHandler ByteHandler
 	{
 		get
 		{
+#if NET7_0_OR_GREATER
+			ObjectDisposedException.ThrowIf(m_byteHandler is null, this);
+#else
 			if (m_byteHandler is null)
 				throw new ObjectDisposedException(nameof(StandardPayloadHandler));
+#endif
 			return m_byteHandler;
 		}
 		set

From 57dfceacad77362ece8889af2f2ea0124f40a2fb Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 9 Oct 2023 13:00:30 -0700
Subject: [PATCH 180/393] Add tests for TextDateTimeColumnReader.

---
 .../MySqlConnector.Tests/ColumnReaderTests.cs | 71 +++++++++++++++++++
 .../MySqlConnector.Tests.csproj               |  2 +-
 2 files changed, 72 insertions(+), 1 deletion(-)
 create mode 100644 tests/MySqlConnector.Tests/ColumnReaderTests.cs

diff --git a/tests/MySqlConnector.Tests/ColumnReaderTests.cs b/tests/MySqlConnector.Tests/ColumnReaderTests.cs
new file mode 100644
index 000000000..999d08206
--- /dev/null
+++ b/tests/MySqlConnector.Tests/ColumnReaderTests.cs
@@ -0,0 +1,71 @@
+using System.Text;
+using MySqlConnector.ColumnReaders;
+
+namespace MySqlConnector.Tests;
+
+public class ColumnReaderTests
+{
+	[Theory]
+	[InlineData("0001-01-01", "0001-01-01T00:00:00.0000000")]
+	[InlineData("2345-12-31", "2345-12-31T00:00:00.0000000")]
+	[InlineData("2345-12-31 12:34:56", "2345-12-31T12:34:56.0000000")]
+	[InlineData("2345-12-31 12:34:56.1", "2345-12-31T12:34:56.1000000")]
+	[InlineData("2345-12-31 12:34:56.12", "2345-12-31T12:34:56.1200000")]
+	[InlineData("2345-12-31 12:34:56.123", "2345-12-31T12:34:56.1230000")]
+	[InlineData("2345-12-31 12:34:56.1234", "2345-12-31T12:34:56.1234000")]
+	[InlineData("2345-12-31 12:34:56.12345", "2345-12-31T12:34:56.1234500")]
+	[InlineData("2345-12-31 12:34:56.123456", "2345-12-31T12:34:56.1234560")]
+	[InlineData("2345-12-31 12:34:56.01", "2345-12-31T12:34:56.0100000")]
+	[InlineData("2345-12-31 12:34:56.001", "2345-12-31T12:34:56.0010000")]
+	[InlineData("2345-12-31 12:34:56.0001", "2345-12-31T12:34:56.0001000")]
+	[InlineData("2345-12-31 12:34:56.00001", "2345-12-31T12:34:56.0000100")]
+	[InlineData("2345-12-31 12:34:56.000001", "2345-12-31T12:34:56.0000010")]
+	[InlineData("9999-12-31 23:59:59.999999", "9999-12-31T23:59:59.9999990")]
+	public void ParseDateTime(string input, string expected)
+	{
+		var dateTime = (DateTime) TextDateTimeColumnReader.ParseDateTime(Encoding.UTF8.GetBytes(input), convertZeroDateTime: false, allowZeroDateTime: false, dateTimeKind: DateTimeKind.Unspecified);
+		Assert.Equal(expected, dateTime.ToString("O"));
+
+		var mySqlDateTime = (MySqlDateTime) TextDateTimeColumnReader.ParseDateTime(Encoding.UTF8.GetBytes(input), convertZeroDateTime: false, allowZeroDateTime: true, dateTimeKind: DateTimeKind.Unspecified);
+		Assert.Equal(expected, mySqlDateTime.GetDateTime().ToString("O"));
+	}
+
+	[Fact]
+	public void ConvertZeroDateTime()
+	{
+		var dateTime = (DateTime) TextDateTimeColumnReader.ParseDateTime("0000-00-00"u8, convertZeroDateTime: true, allowZeroDateTime: false, DateTimeKind.Utc);
+		Assert.Equal(DateTime.MinValue, dateTime);
+	}
+
+	[Fact]
+	public void AllowZeroDateTime()
+	{
+		var mySqlDateTime = (MySqlDateTime) TextDateTimeColumnReader.ParseDateTime("0000-00-00"u8, convertZeroDateTime: false, allowZeroDateTime: true, DateTimeKind.Utc);
+		Assert.Equal(default, mySqlDateTime);
+	}
+
+	[Theory]
+	[InlineData("2")]
+	[InlineData("20")]
+	[InlineData("200")]
+	[InlineData("2000")]
+	[InlineData("2000-1")]
+	[InlineData("2000-1-1")]
+	[InlineData("2000-00-01")]
+	[InlineData("2000-13-01")]
+	[InlineData("2000-01-00")]
+	[InlineData("2000-01-32")]
+	[InlineData("10000-01-01")]
+	[InlineData("2000-01-01 01")]
+	[InlineData("2000-01-01 01:01")]
+	[InlineData("2000-01-01 01:01:60")]
+	[InlineData("2000-01-01T01:01:01")]
+	[InlineData("2000-01-01 01-01-01")]
+	[InlineData("2000-01-01 01:01:01.1234567")]
+	[InlineData("2000-01-01 01:01:01.12345678")]
+	[InlineData("2000-01-01 01:01:01.123456789")]
+	public void InvalidDateTime(string input)
+	{
+		Assert.Throws<FormatException>(() => TextDateTimeColumnReader.ParseDateTime(Encoding.UTF8.GetBytes(input), convertZeroDateTime: false, allowZeroDateTime: false, dateTimeKind: DateTimeKind.Unspecified));
+	}
+}
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 5a0c3b042..ceaa6699a 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -47,7 +47,7 @@
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
     <PackageReference Include="MySql.Data" Version="8.1.0" />
-    <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
+    <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ColumnReaderTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
     <Using Include="MySql.Data.MySqlClient" />
     <Using Include="MySql.Data.Types" />
   </ItemGroup>

From 8c04f1a0d77d6d3cd2875f672632ee8e4c00de8c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 10 Oct 2023 12:26:45 -0700
Subject: [PATCH 181/393] Fix artifacts output path.

Artifacts for source projects were being stored relative to the 'src' directory.
---
 Directory.Build.props | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Directory.Build.props b/Directory.Build.props
index 884af96c3..c475ff91a 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,7 +5,7 @@
     <LangVersion>preview</LangVersion>
     <AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
     <NoWarn>$(NoWarn);1591;CA1708;CA1835;CA2215;CA5397;NU5105;SYSLIB0039</NoWarn>
-    <UseArtifactsOutput>true</UseArtifactsOutput>
+    <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>
     <NuGetAudit>true</NuGetAudit>
     <NuGetAuditLevel>low</NuGetAuditLevel>
   </PropertyGroup>

From 021c0395ebb3e1c6d0d74e652ca7453bcd016e7e Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 10 Oct 2023 14:57:26 -0700
Subject: [PATCH 182/393] Support higher-precision timestamps. Fixes #1379

---
 .../ColumnReaders/TextDateTimeColumnReader.cs | 20 +++++-------
 .../MySqlConnector.Tests/ColumnReaderTests.cs | 31 ++++++++++++++++---
 2 files changed, 34 insertions(+), 17 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
index 1064fde65..b2863e9db 100644
--- a/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/TextDateTimeColumnReader.cs
@@ -40,13 +40,13 @@ public static object ParseDateTime(ReadOnlySpan<byte> data, bool convertZeroDate
 			throw new InvalidCastException("Unable to convert MySQL date/time to System.DateTime, set AllowZeroDateTime=True or ConvertZeroDateTime=True in the connection string. See https://mysqlconnector.net/connection-options/");
 		}
 
-		int hour, minute, second, microseconds;
+		int hour, minute, second, ticks;
 		if (data.Length == 10)
 		{
 			hour = 0;
 			minute = 0;
 			second = 0;
-			microseconds = 0;
+			ticks = 0;
 		}
 		else
 		{
@@ -65,28 +65,24 @@ public static object ParseDateTime(ReadOnlySpan<byte> data, bool convertZeroDate
 
 			if (data.Length == 19)
 			{
-				microseconds = 0;
+				ticks = 0;
 			}
 			else
 			{
 				if (data[19] != 46)
 					goto InvalidDateTime;
 
-				if (!Utf8Parser.TryParse(data[20..], out microseconds, out bytesConsumed) || bytesConsumed != data.Length - 20)
+				if (!Utf8Parser.TryParse(data[20..], out ticks, out bytesConsumed) || bytesConsumed != data.Length - 20 || bytesConsumed > 7)
 					goto InvalidDateTime;
-				for (; bytesConsumed < 6; bytesConsumed++)
-					microseconds *= 10;
+				for (; bytesConsumed < 7; bytesConsumed++)
+					ticks *= 10;
 			}
 		}
 
 		try
 		{
-			return allowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
-#if NET7_0_OR_GREATER
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, dateTimeKind);
-#else
-				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, dateTimeKind).AddTicks(microseconds % 1000 * 10);
-#endif
+			return allowZeroDateTime ? (ticks % 10 == 0 ? (object) new MySqlDateTime(year, month, day, hour, minute, second, ticks / 10) : throw new NotSupportedException("MySqlDateTime does not support sub-microsecond precision")) :
+				new DateTime(year, month, day, hour, minute, second, dateTimeKind).AddTicks(ticks);
 		}
 		catch (Exception ex)
 		{
diff --git a/tests/MySqlConnector.Tests/ColumnReaderTests.cs b/tests/MySqlConnector.Tests/ColumnReaderTests.cs
index 999d08206..88ba92d51 100644
--- a/tests/MySqlConnector.Tests/ColumnReaderTests.cs
+++ b/tests/MySqlConnector.Tests/ColumnReaderTests.cs
@@ -15,19 +15,41 @@ public class ColumnReaderTests
 	[InlineData("2345-12-31 12:34:56.1234", "2345-12-31T12:34:56.1234000")]
 	[InlineData("2345-12-31 12:34:56.12345", "2345-12-31T12:34:56.1234500")]
 	[InlineData("2345-12-31 12:34:56.123456", "2345-12-31T12:34:56.1234560")]
+	[InlineData("2345-12-31 12:34:56.1234567", "2345-12-31T12:34:56.1234567")]
 	[InlineData("2345-12-31 12:34:56.01", "2345-12-31T12:34:56.0100000")]
 	[InlineData("2345-12-31 12:34:56.001", "2345-12-31T12:34:56.0010000")]
 	[InlineData("2345-12-31 12:34:56.0001", "2345-12-31T12:34:56.0001000")]
 	[InlineData("2345-12-31 12:34:56.00001", "2345-12-31T12:34:56.0000100")]
 	[InlineData("2345-12-31 12:34:56.000001", "2345-12-31T12:34:56.0000010")]
+	[InlineData("2345-12-31 12:34:56.0000001", "2345-12-31T12:34:56.0000001")]
 	[InlineData("9999-12-31 23:59:59.999999", "9999-12-31T23:59:59.9999990")]
+	[InlineData("9999-12-31 23:59:59.9999999", "9999-12-31T23:59:59.9999999")]
 	public void ParseDateTime(string input, string expected)
 	{
-		var dateTime = (DateTime) TextDateTimeColumnReader.ParseDateTime(Encoding.UTF8.GetBytes(input), convertZeroDateTime: false, allowZeroDateTime: false, dateTimeKind: DateTimeKind.Unspecified);
-		Assert.Equal(expected, dateTime.ToString("O"));
+		var actual = (DateTime) TextDateTimeColumnReader.ParseDateTime(Encoding.UTF8.GetBytes(input), convertZeroDateTime: false, allowZeroDateTime: false, dateTimeKind: DateTimeKind.Unspecified);
+		Assert.Equal(expected, actual.ToString("O"));
+	}
 
-		var mySqlDateTime = (MySqlDateTime) TextDateTimeColumnReader.ParseDateTime(Encoding.UTF8.GetBytes(input), convertZeroDateTime: false, allowZeroDateTime: true, dateTimeKind: DateTimeKind.Unspecified);
-		Assert.Equal(expected, mySqlDateTime.GetDateTime().ToString("O"));
+	[Theory]
+	[InlineData("0001-01-01", "0001-01-01T00:00:00.0000000")]
+	[InlineData("2345-12-31", "2345-12-31T00:00:00.0000000")]
+	[InlineData("2345-12-31 12:34:56", "2345-12-31T12:34:56.0000000")]
+	[InlineData("2345-12-31 12:34:56.1", "2345-12-31T12:34:56.1000000")]
+	[InlineData("2345-12-31 12:34:56.12", "2345-12-31T12:34:56.1200000")]
+	[InlineData("2345-12-31 12:34:56.123", "2345-12-31T12:34:56.1230000")]
+	[InlineData("2345-12-31 12:34:56.1234", "2345-12-31T12:34:56.1234000")]
+	[InlineData("2345-12-31 12:34:56.12345", "2345-12-31T12:34:56.1234500")]
+	[InlineData("2345-12-31 12:34:56.123456", "2345-12-31T12:34:56.1234560")]
+	[InlineData("2345-12-31 12:34:56.01", "2345-12-31T12:34:56.0100000")]
+	[InlineData("2345-12-31 12:34:56.001", "2345-12-31T12:34:56.0010000")]
+	[InlineData("2345-12-31 12:34:56.0001", "2345-12-31T12:34:56.0001000")]
+	[InlineData("2345-12-31 12:34:56.00001", "2345-12-31T12:34:56.0000100")]
+	[InlineData("2345-12-31 12:34:56.000001", "2345-12-31T12:34:56.0000010")]
+	[InlineData("9999-12-31 23:59:59.999999", "9999-12-31T23:59:59.9999990")]
+	public void ParseMySqlDateTime(string input, string expected)
+	{
+		var actual = (MySqlDateTime) TextDateTimeColumnReader.ParseDateTime(Encoding.UTF8.GetBytes(input), convertZeroDateTime: false, allowZeroDateTime: true, dateTimeKind: DateTimeKind.Unspecified);
+		Assert.Equal(expected, actual.GetDateTime().ToString("O"));
 	}
 
 	[Fact]
@@ -61,7 +83,6 @@ public void AllowZeroDateTime()
 	[InlineData("2000-01-01 01:01:60")]
 	[InlineData("2000-01-01T01:01:01")]
 	[InlineData("2000-01-01 01-01-01")]
-	[InlineData("2000-01-01 01:01:01.1234567")]
 	[InlineData("2000-01-01 01:01:01.12345678")]
 	[InlineData("2000-01-01 01:01:01.123456789")]
 	public void InvalidDateTime(string input)

From 351e170144dd1cf1c8cb12d4b8f6c859db71f48a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 19 Oct 2023 19:43:09 -0700
Subject: [PATCH 183/393] Update xunit to 2.5.3.

Update AdoNet.Specification.Tests to 2.2.0 Beta 2 to match. Fix xunit analyzer errors (but suppress xUnit1030 for ConfigureAwait, as switching to ConfigureAwait(true) caused timeout errors on Azure Pipelines).
---
 global.json                                            | 5 +++++
 tests/Conformance.Tests/Conformance.Tests.csproj       | 8 ++++----
 tests/IntegrationTests/CancelTests.cs                  | 2 ++
 tests/IntegrationTests/ConnectionPool.cs               | 2 +-
 tests/IntegrationTests/IntegrationTests.csproj         | 9 +++++----
 tests/IntegrationTests/TransactionScopeTests.cs        | 2 ++
 tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj | 6 +++---
 7 files changed, 22 insertions(+), 12 deletions(-)
 create mode 100644 global.json

diff --git a/global.json b/global.json
new file mode 100644
index 000000000..3e3cfd9ca
--- /dev/null
+++ b/global.json
@@ -0,0 +1,5 @@
+{
+  "sdk": {
+    "version": "7.0.402"
+  }
+}
diff --git a/tests/Conformance.Tests/Conformance.Tests.csproj b/tests/Conformance.Tests/Conformance.Tests.csproj
index e04c1dc8e..bfba7909c 100644
--- a/tests/Conformance.Tests/Conformance.Tests.csproj
+++ b/tests/Conformance.Tests/Conformance.Tests.csproj
@@ -11,10 +11,10 @@
   </PropertyGroup>
 
  <ItemGroup>
-    <PackageReference Include="AdoNet.Specification.Tests" Version="2.0.0-beta.1" />
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
-    <PackageReference Include="xunit" Version="2.4.2" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
+    <PackageReference Include="AdoNet.Specification.Tests" Version="2.0.0-beta.2" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
+    <PackageReference Include="xunit" Version="2.5.3" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
diff --git a/tests/IntegrationTests/CancelTests.cs b/tests/IntegrationTests/CancelTests.cs
index e7fcdfc0a..6c37a310d 100644
--- a/tests/IntegrationTests/CancelTests.cs
+++ b/tests/IntegrationTests/CancelTests.cs
@@ -35,7 +35,9 @@ public void CancelCommand()
 		TestUtilities.AssertExecuteScalarReturnsOneOrIsCanceled(cmd);
 		Assert.InRange(stopwatch.ElapsedMilliseconds, 250, 2500);
 
+#pragma warning disable xUnit1031 // Do not use blocking task operations in test method
 		task.Wait(); // shouldn't throw
+#pragma warning restore xUnit1031 // Do not use blocking task operations in test method
 	}
 
 #if !MYSQL_DATA
diff --git a/tests/IntegrationTests/ConnectionPool.cs b/tests/IntegrationTests/ConnectionPool.cs
index 27af0d2ba..4f671623f 100644
--- a/tests/IntegrationTests/ConnectionPool.cs
+++ b/tests/IntegrationTests/ConnectionPool.cs
@@ -76,7 +76,7 @@ public async Task ExhaustConnectionPool()
 			Assert.InRange(stopwatch.ElapsedMilliseconds, 4500, 7500);
 		}
 
-		closeTask.Wait();
+		await closeTask.ConfigureAwait(true);
 
 		foreach (var connection in connections)
 			connection.Dispose();
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index 8de202d5d..04d05b2fb 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -1,4 +1,4 @@
-﻿<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup Condition=" '$(Configuration)' != 'MySqlData' ">
     <TargetFrameworks>net462;net472;net6.0;net7.0</TargetFrameworks>
@@ -24,13 +24,14 @@
     <ThreadPoolMinThreads>64</ThreadPoolMinThreads>
     <LangVersion>11.0</LangVersion>
     <ImplicitUsings>enable</ImplicitUsings>
+    <NoWarn>$(NoWarn);xUnit1030</NoWarn>
   </PropertyGroup>
 
   <ItemGroup>
     <!--testing packages-->
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
-    <PackageReference Include="xunit" Version="2.5.0" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
+    <PackageReference Include="xunit" Version="2.5.3" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
diff --git a/tests/IntegrationTests/TransactionScopeTests.cs b/tests/IntegrationTests/TransactionScopeTests.cs
index dee270ccb..ad88fed2e 100644
--- a/tests/IntegrationTests/TransactionScopeTests.cs
+++ b/tests/IntegrationTests/TransactionScopeTests.cs
@@ -766,7 +766,9 @@ PRIMARY KEY (`id`)
 
 		var task = Task.Run(() => UseTransaction());
 		UseTransaction();
+#pragma warning disable xUnit1031 // Do not use blocking task operations in test method
 		task.Wait();
+#pragma warning restore xUnit1031 // Do not use blocking task operations in test method
 
 		void UseTransaction()
 		{
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index ceaa6699a..9fff07634 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -19,12 +19,12 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="xunit" Version="2.5.0" />
-    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.0">
+    <PackageReference Include="xunit" Version="2.5.3" />
+    <PackageReference Include="xunit.runner.visualstudio" Version="2.5.3">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>
     </PackageReference>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.0" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3">
       <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
       <PrivateAssets>all</PrivateAssets>

From 4975c214c5efa5e2fda2ef78d570bb7f09dba9d7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 19 Oct 2023 20:09:48 -0700
Subject: [PATCH 184/393] Update packages.

---
 Directory.Packages.props | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 1d5c539b4..a874fb181 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,7 +6,7 @@
 
   <ItemGroup>
     <PackageVersion Include="AdoNet.Specification.Tests" Version="2.0.0-beta.2" />
-    <PackageVersion Include="Dapper.StrongName" Version="2.0.143" />
+    <PackageVersion Include="Dapper.StrongName" Version="2.1.15" />
     <PackageVersion Include="IndexRange" Version="1.0.2" />
     <PackageVersion Include="log4net" Version="2.0.10" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
@@ -23,7 +23,7 @@
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
     <PackageVersion Include="xunit" Version="2.5.3" />
     <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.3" />
-    <PackageVersion Include="YamlDotNet" Version="13.1.1" />
+    <PackageVersion Include="YamlDotNet" Version="13.7.1" />
   </ItemGroup>
 
   <ItemGroup>

From 971929ebd5904f467a3d028161f0dded514cfa77 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 19 Oct 2023 21:09:31 -0700
Subject: [PATCH 185/393] Update API compatibility suppression file.

---
 .../CompatibilitySuppressions.xml             | 126 ++++++++++++++++++
 1 file changed, 126 insertions(+)

diff --git a/src/MySqlConnector/CompatibilitySuppressions.xml b/src/MySqlConnector/CompatibilitySuppressions.xml
index 35851e7b5..07cb181f8 100644
--- a/src/MySqlConnector/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector/CompatibilitySuppressions.xml
@@ -20,6 +20,132 @@
     <Left>lib/net6.0/MySqlConnector.dll</Left>
     <Right>lib/net7.0/MySqlConnector.dll</Right>
   </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Collections.Generic.IEnumerable{System.Data.DataRow},System.Int32,System.Threading.CancellationToken)</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Data.DataTable,System.Threading.CancellationToken)</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Data.IDataReader,System.Threading.CancellationToken)</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlConnection.get_ProvideClientCertificatesCallback</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlConnection.ResetConnectionAsync(System.Threading.CancellationToken)</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Collections.Generic.IEnumerable{System.Data.DataRow},System.Int32,System.Threading.CancellationToken)</Target>
+    <Left>lib/net471/MySqlConnector.dll</Left>
+    <Right>lib/net471/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Data.DataTable,System.Threading.CancellationToken)</Target>
+    <Left>lib/net471/MySqlConnector.dll</Left>
+    <Right>lib/net471/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlBulkCopy.WriteToServerAsync(System.Data.IDataReader,System.Threading.CancellationToken)</Target>
+    <Left>lib/net471/MySqlConnector.dll</Left>
+    <Right>lib/net471/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlConnection.get_ProvideClientCertificatesCallback</Target>
+    <Left>lib/net471/MySqlConnector.dll</Left>
+    <Right>lib/net471/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlConnection.ResetConnectionAsync(System.Threading.CancellationToken)</Target>
+    <Left>lib/net471/MySqlConnector.dll</Left>
+    <Right>lib/net471/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlConnection.get_ProvideClientCertificatesCallback</Target>
+    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlConnection.ResetConnectionAsync(System.Threading.CancellationToken)</Target>
+    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlCommand.DisposeAsync</Target>
+    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlConnection.DisposeAsync</Target>
+    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlDataReader.DisposeAsync</Target>
+    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlTransaction.DisposeAsync</Target>
+    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlBatchCommandCollection.GetEnumerator</Target>
+    <Left>lib/netstandard2.1/MySqlConnector.dll</Left>
+    <Right>lib/net6.0/MySqlConnector.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlConnectorFactory.CreateBatch</Target>
+    <Left>lib/netstandard2.1/MySqlConnector.dll</Left>
+    <Right>lib/net6.0/MySqlConnector.dll</Right>
+  </Suppression>
+  <Suppression>
+    <DiagnosticId>CP0002</DiagnosticId>
+    <Target>M:MySqlConnector.MySqlConnectorFactory.CreateBatchCommand</Target>
+    <Left>lib/netstandard2.1/MySqlConnector.dll</Left>
+    <Right>lib/net6.0/MySqlConnector.dll</Right>
+  </Suppression>
   <Suppression>
     <DiagnosticId>CP0008</DiagnosticId>
     <Target>T:MySqlConnector.MySqlDataReader</Target>

From 08d57deae0b01cc13ae9c5d2951eb1f69666af5f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 19 Oct 2023 21:09:47 -0700
Subject: [PATCH 186/393] Use C# 12 primary constructors.

---
 src/MySqlConnector/Core/ColumnTypeMetadata.cs | 33 ++++------
 src/MySqlConnector/Core/ConnectionPool.cs     | 22 ++-----
 src/MySqlConnector/Core/ServerSession.cs      | 13 ++--
 src/MySqlConnector/Core/SqlParser.cs          |  6 +-
 .../Core/StandardEnlistedTransaction.cs       |  8 +--
 src/MySqlConnector/Core/StatementPreparer.cs  | 60 ++++++++-----------
 .../Logging/ConsoleLoggerProvider.cs          | 20 +++----
 .../Logging/MySqlConnectorLogManager.cs       | 20 ++-----
 .../MySqlConnectorLoggingConfiguration.cs     | 21 ++-----
 9 files changed, 67 insertions(+), 136 deletions(-)

diff --git a/src/MySqlConnector/Core/ColumnTypeMetadata.cs b/src/MySqlConnector/Core/ColumnTypeMetadata.cs
index 48e4a4b1b..eadb27de4 100644
--- a/src/MySqlConnector/Core/ColumnTypeMetadata.cs
+++ b/src/MySqlConnector/Core/ColumnTypeMetadata.cs
@@ -1,31 +1,18 @@
 namespace MySqlConnector.Core;
 
-internal sealed class ColumnTypeMetadata
+internal sealed class ColumnTypeMetadata(string dataTypeName, DbTypeMapping dbTypeMapping, MySqlDbType mySqlDbType, bool isUnsigned = false, bool binary = false, int length = 0, string? simpleDataTypeName = null, string? createFormat = null, long columnSize = 0)
 {
 	public static string CreateLookupKey(string columnTypeName, bool isUnsigned, int length) => $"{columnTypeName}|{(isUnsigned ? "u" : "s")}|{length}";
 
-	public ColumnTypeMetadata(string dataTypeName, DbTypeMapping dbTypeMapping, MySqlDbType mySqlDbType, bool isUnsigned = false, bool binary = false, int length = 0, string? simpleDataTypeName = null, string? createFormat = null, long columnSize = 0)
-	{
-		DataTypeName = dataTypeName;
-		SimpleDataTypeName = simpleDataTypeName ?? dataTypeName;
-		CreateFormat = createFormat ?? (dataTypeName + (isUnsigned ? " UNSIGNED" : ""));
-		DbTypeMapping = dbTypeMapping;
-		MySqlDbType = mySqlDbType;
-		ColumnSize = columnSize;
-		IsUnsigned = isUnsigned;
-		Binary = binary;
-		Length = length;
-	}
-
-	public string DataTypeName { get; }
-	public string SimpleDataTypeName { get; }
-	public string CreateFormat { get; }
-	public DbTypeMapping DbTypeMapping { get; }
-	public MySqlDbType MySqlDbType { get; }
-	public bool Binary { get; }
-	public long ColumnSize { get; }
-	public bool IsUnsigned { get; }
-	public int Length { get; }
+	public string DataTypeName { get; } = dataTypeName;
+	public string SimpleDataTypeName { get; } = simpleDataTypeName ?? dataTypeName;
+	public string CreateFormat { get; } = createFormat ?? (dataTypeName + (isUnsigned ? " UNSIGNED" : ""));
+	public DbTypeMapping DbTypeMapping { get; } = dbTypeMapping;
+	public MySqlDbType MySqlDbType { get; } = mySqlDbType;
+	public bool Binary { get; } = binary;
+	public long ColumnSize { get; } = columnSize;
+	public bool IsUnsigned { get; } = isUnsigned;
+	public int Length { get; } = length;
 
 	public string CreateLookupKey() => CreateLookupKey(DataTypeName, IsUnsigned, Length);
 }
diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 8fcc68e6e..89996e2ca 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -741,29 +741,19 @@ private void AdjustHostConnectionCount(ServerSession session, int delta)
 		}
 	}
 
-	private sealed class LeastConnectionsLoadBalancer : ILoadBalancer
+	private sealed class LeastConnectionsLoadBalancer(Dictionary<string, int> hostSessions) : ILoadBalancer
 	{
-		public LeastConnectionsLoadBalancer(Dictionary<string, int> hostSessions) => m_hostSessions = hostSessions;
-
 		public IReadOnlyList<string> LoadBalance(IReadOnlyList<string> hosts)
 		{
-			lock (m_hostSessions)
-				return m_hostSessions.OrderBy(static x => x.Value).Select(static x => x.Key).ToList();
+			lock (hostSessions)
+				return hostSessions.OrderBy(static x => x.Value).Select(static x => x.Key).ToList();
 		}
-
-		private readonly Dictionary<string, int> m_hostSessions;
 	}
 
-	private sealed class ConnectionStringPool
+	private sealed class ConnectionStringPool(string connectionString, ConnectionPool? pool)
 	{
-		public ConnectionStringPool(string connectionString, ConnectionPool? pool)
-		{
-			ConnectionString = connectionString;
-			Pool = pool;
-		}
-
-		public string ConnectionString { get; }
-		public ConnectionPool? Pool { get; }
+		public string ConnectionString { get; } = connectionString;
+		public ConnectionPool? Pool { get; } = pool;
 	}
 
 	static ConnectionPool()
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index ab498f5b1..8993284cd 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1884,23 +1884,18 @@ private enum State
 		Failed,
 	}
 
-	private sealed class DelimiterSqlParser : SqlParser
+	private sealed class DelimiterSqlParser(IMySqlCommand command)
+		: SqlParser(new StatementPreparer(command.CommandText!, null, command.CreateStatementPreparerOptions()))
 	{
-		public DelimiterSqlParser(IMySqlCommand command)
-			: base(new StatementPreparer(command.CommandText!, null, command.CreateStatementPreparerOptions()))
-		{
-			m_sql = command.CommandText!;
-		}
-
 		public bool HasDelimiter { get; private set; }
 
 		protected override void OnStatementBegin(int index)
 		{
-			if (index + 10 < m_sql.Length && m_sql.AsSpan(index, 10).Equals("delimiter ".AsSpan(), StringComparison.OrdinalIgnoreCase))
+			if (index + 10 < Sql.Length && Sql.AsSpan(index, 10).Equals("delimiter ".AsSpan(), StringComparison.OrdinalIgnoreCase))
 				HasDelimiter = true;
 		}
 
-		private readonly string m_sql;
+		private string Sql { get; } = command.CommandText!;
 	}
 
 	[LoggerMessage(EventIds.CannotExecuteNewCommandInState, LogLevel.Error, "Session {SessionId} can't execute new command when in state {SessionState}")]
diff --git a/src/MySqlConnector/Core/SqlParser.cs b/src/MySqlConnector/Core/SqlParser.cs
index 128d62560..b2240de89 100644
--- a/src/MySqlConnector/Core/SqlParser.cs
+++ b/src/MySqlConnector/Core/SqlParser.cs
@@ -2,11 +2,9 @@
 
 namespace MySqlConnector.Core;
 
-internal abstract class SqlParser
+internal abstract class SqlParser(StatementPreparer preparer)
 {
-	protected StatementPreparer Preparer { get; }
-
-	protected SqlParser(StatementPreparer preparer) => Preparer = preparer;
+	protected StatementPreparer Preparer { get; } = preparer;
 
 	public void Parse(string sql)
 	{
diff --git a/src/MySqlConnector/Core/StandardEnlistedTransaction.cs b/src/MySqlConnector/Core/StandardEnlistedTransaction.cs
index 45eff3b7c..32222aca5 100644
--- a/src/MySqlConnector/Core/StandardEnlistedTransaction.cs
+++ b/src/MySqlConnector/Core/StandardEnlistedTransaction.cs
@@ -3,13 +3,9 @@
 
 namespace MySqlConnector.Core;
 
-internal sealed class StandardEnlistedTransaction : EnlistedTransactionBase
+internal sealed class StandardEnlistedTransaction(Transaction transaction, MySqlConnection connection)
+	: EnlistedTransactionBase(transaction, connection)
 {
-	public StandardEnlistedTransaction(Transaction transaction, MySqlConnection connection)
-		: base(transaction, connection)
-	{
-	}
-
 	protected override void OnStart()
 	{
 		var isolationLevel = Transaction.IsolationLevel switch
diff --git a/src/MySqlConnector/Core/StatementPreparer.cs b/src/MySqlConnector/Core/StatementPreparer.cs
index c4880a537..8e53a6203 100644
--- a/src/MySqlConnector/Core/StatementPreparer.cs
+++ b/src/MySqlConnector/Core/StatementPreparer.cs
@@ -53,14 +53,9 @@ private MySqlParameter GetInputParameter(int index)
 		return parameter;
 	}
 
-	private sealed class ParameterSqlParser : SqlParser
+	private sealed class ParameterSqlParser(StatementPreparer preparer, ByteBufferWriter writer)
+		: SqlParser(preparer)
 	{
-		public ParameterSqlParser(StatementPreparer preparer, ByteBufferWriter writer)
-			: base(preparer)
-		{
-			m_writer = writer;
-		}
-
 		public bool IsComplete { get; private set; }
 
 		protected override void OnNamedParameter(int index, int length)
@@ -78,42 +73,36 @@ protected override void OnPositionalParameter(int index)
 
 		private void DoAppendParameter(int parameterIndex, int textIndex, int textLength)
 		{
-			m_writer.Write(Preparer.CommandText, m_lastIndex, textIndex - m_lastIndex);
+			Writer.Write(Preparer.CommandText, m_lastIndex, textIndex - m_lastIndex);
 			var parameter = Preparer.GetInputParameter(parameterIndex);
-			parameter.AppendSqlString(m_writer, Preparer.Options);
+			parameter.AppendSqlString(Writer, Preparer.Options);
 			m_lastIndex = textIndex + textLength;
 		}
 
 		protected override void OnParsed(FinalParseStates states)
 		{
-			m_writer.Write(Preparer.CommandText, m_lastIndex, Preparer.CommandText.Length - m_lastIndex);
+			Writer.Write(Preparer.CommandText, m_lastIndex, Preparer.CommandText.Length - m_lastIndex);
 			if ((states & FinalParseStates.NeedsNewline) == FinalParseStates.NeedsNewline)
-				m_writer.Write((byte) '\n');
+				Writer.Write((byte) '\n');
 			if ((states & FinalParseStates.NeedsSemicolon) == FinalParseStates.NeedsSemicolon && (Preparer.Options & StatementPreparerOptions.AppendSemicolon) == StatementPreparerOptions.AppendSemicolon)
-				m_writer.Write((byte) ';');
+				Writer.Write((byte) ';');
 			IsComplete = (states & FinalParseStates.Complete) == FinalParseStates.Complete;
 		}
 
-		private readonly ByteBufferWriter m_writer;
+		private ByteBufferWriter Writer { get; } = writer;
+
 		private int m_currentParameterIndex;
 		private int m_lastIndex;
 	}
 
-	private sealed class PreparedCommandSqlParser : SqlParser
+	private sealed class PreparedCommandSqlParser(StatementPreparer preparer, List<ParsedStatement> statements, List<int> statementStartEndIndexes, ByteBufferWriter writer)
+		: SqlParser(preparer)
 	{
-		public PreparedCommandSqlParser(StatementPreparer preparer, List<ParsedStatement> statements, List<int> statementStartEndIndexes, ByteBufferWriter writer)
-			: base(preparer)
-		{
-			m_statements = statements;
-			m_statementStartEndIndexes = statementStartEndIndexes;
-			m_writer = writer;
-		}
-
 		protected override void OnStatementBegin(int index)
 		{
-			m_statements.Add(new ParsedStatement());
-			m_statementStartEndIndexes.Add(m_writer.Position);
-			m_writer.Write((byte) CommandKind.StatementPrepare);
+			Statements.Add(new ParsedStatement());
+			StatementStartEndIndexes.Add(Writer.Position);
+			Writer.Write((byte) CommandKind.StatementPrepare);
 			m_lastIndex = index;
 		}
 
@@ -132,28 +121,29 @@ protected override void OnPositionalParameter(int index)
 		private void DoAppendParameter(string? parameterName, int parameterIndex, int textIndex, int textLength)
 		{
 			// write all SQL up to the parameter
-			m_writer.Write(Preparer.CommandText, m_lastIndex, textIndex - m_lastIndex);
+			Writer.Write(Preparer.CommandText, m_lastIndex, textIndex - m_lastIndex);
 			m_lastIndex = textIndex + textLength;
 
 			// replace the parameter with a ? placeholder
-			m_writer.Write((byte) '?');
+			Writer.Write((byte) '?');
 
 			// store the parameter index
-			m_statements[m_statements.Count - 1].ParameterNames.Add(parameterName);
-			m_statements[m_statements.Count - 1].NormalizedParameterNames.Add(parameterName == null ? null : MySqlParameter.NormalizeParameterName(parameterName));
-			m_statements[m_statements.Count - 1].ParameterIndexes.Add(parameterIndex);
+			Statements[Statements.Count - 1].ParameterNames.Add(parameterName);
+			Statements[Statements.Count - 1].NormalizedParameterNames.Add(parameterName == null ? null : MySqlParameter.NormalizeParameterName(parameterName));
+			Statements[Statements.Count - 1].ParameterIndexes.Add(parameterIndex);
 		}
 
 		protected override void OnStatementEnd(int index)
 		{
-			m_writer.Write(Preparer.CommandText, m_lastIndex, index - m_lastIndex);
+			Writer.Write(Preparer.CommandText, m_lastIndex, index - m_lastIndex);
 			m_lastIndex = index;
-			m_statementStartEndIndexes.Add(m_writer.Position);
+			StatementStartEndIndexes.Add(Writer.Position);
 		}
 
-		private readonly List<ParsedStatement> m_statements;
-		private readonly List<int> m_statementStartEndIndexes;
-		private readonly ByteBufferWriter m_writer;
+		private List<ParsedStatement> Statements { get; } = statements;
+		private List<int> StatementStartEndIndexes { get; } = statementStartEndIndexes;
+		private ByteBufferWriter Writer { get; } = writer;
+
 		private int m_currentParameterIndex;
 		private int m_lastIndex;
 	}
diff --git a/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs b/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs
index f264ffb54..e2e9a827f 100644
--- a/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs
+++ b/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs
@@ -16,15 +16,9 @@ public ConsoleLoggerProvider(MySqlConnectorLogLevel minimumLevel = MySqlConnecto
 
 	public IMySqlConnectorLogger CreateLogger(string name) => new ConsoleLogger(this, name);
 
-	private sealed class ConsoleLogger : IMySqlConnectorLogger
+	private sealed class ConsoleLogger(ConsoleLoggerProvider provider, string name) : IMySqlConnectorLogger
 	{
-		public ConsoleLogger(ConsoleLoggerProvider provider, string name)
-		{
-			m_provider = provider;
-			m_name = name;
-		}
-
-		public bool IsEnabled(MySqlConnectorLogLevel level) => level >= m_provider.m_minimumLevel && level <= MySqlConnectorLogLevel.Fatal;
+		public bool IsEnabled(MySqlConnectorLogLevel level) => level >= Provider.m_minimumLevel && level <= MySqlConnectorLogLevel.Fatal;
 
 		public void Log(MySqlConnectorLogLevel level, string message, object?[]? args = null, Exception? exception = null)
 		{
@@ -34,7 +28,7 @@ public void Log(MySqlConnectorLogLevel level, string message, object?[]? args =
 			var sb = new StringBuilder();
 			sb.Append(s_levels[(int) level]);
 			sb.Append('\t');
-			sb.Append(m_name);
+			sb.Append(Name);
 			sb.Append('\t');
 
 			if (args is null || args.Length == 0)
@@ -46,9 +40,9 @@ public void Log(MySqlConnectorLogLevel level, string message, object?[]? args =
 			if (exception is not null)
 				sb.AppendLine(exception.ToString());
 
-			if (m_provider.m_isColored)
+			if (Provider.m_isColored)
 			{
-				lock (m_provider)
+				lock (Provider)
 				{
 					var oldColor = Console.ForegroundColor;
 					Console.ForegroundColor = s_colors[(int) level];
@@ -84,8 +78,8 @@ public void Log(MySqlConnectorLogLevel level, string message, object?[]? args =
 			ConsoleColor.Red,
 		};
 
-		private readonly ConsoleLoggerProvider m_provider;
-		private readonly string m_name;
+		private ConsoleLoggerProvider Provider { get; } = provider;
+		private string Name { get; } = name;
 	}
 
 	private readonly MySqlConnectorLogLevel m_minimumLevel;
diff --git a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
index 9c914425b..2dd3bf9bb 100644
--- a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
+++ b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
@@ -21,40 +21,32 @@ public static IMySqlConnectorLoggerProvider Provider
 	}
 
 	// A helper class that adapts ILoggerFactory to the old-style IMySqlConnectorLoggerProvider interface.
-	private sealed class MySqlConnectorLoggerFactor : ILoggerFactory
+	private sealed class MySqlConnectorLoggerFactor(IMySqlConnectorLoggerProvider loggerProvider) : ILoggerFactory
 	{
-		public MySqlConnectorLoggerFactor(IMySqlConnectorLoggerProvider loggerProvider) =>
-			m_loggerProvider = loggerProvider;
-
 		public void AddProvider(ILoggerProvider provider) => throw new NotSupportedException();
 
 		public ILogger CreateLogger(string categoryName)
 		{
 			// assume all logger names start with "MySqlConnector." but the old API didn't expect that prefix
-			return new MySqlConnectorLogger(m_loggerProvider.CreateLogger(categoryName[15..]));
+			return new MySqlConnectorLogger(loggerProvider.CreateLogger(categoryName[15..]));
 		}
 
 		public void Dispose()
 		{
 		}
-
-		private readonly IMySqlConnectorLoggerProvider m_loggerProvider;
 	}
 
 	// A helper class that adapts ILogger to the old-style IMySqlConnectorLogger interface.
-	private sealed class MySqlConnectorLogger : ILogger
+	private sealed class MySqlConnectorLogger(IMySqlConnectorLogger logger) : ILogger
 	{
-		public MySqlConnectorLogger(IMySqlConnectorLogger logger) =>
-			m_logger = logger;
-
 		public IDisposable BeginScope<TState>(TState state)
 			where TState : notnull
 			=> throw new NotSupportedException();
 
-		public bool IsEnabled(LogLevel logLevel) => m_logger.IsEnabled(ConvertLogLevel(logLevel));
+		public bool IsEnabled(LogLevel logLevel) => logger.IsEnabled(ConvertLogLevel(logLevel));
 
 		public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter) =>
-			m_logger.Log(ConvertLogLevel(logLevel), formatter(state, exception), exception: exception);
+			logger.Log(ConvertLogLevel(logLevel), formatter(state, exception), exception: exception);
 
 		private static MySqlConnectorLogLevel ConvertLogLevel(LogLevel logLevel) =>
 			logLevel switch
@@ -67,7 +59,5 @@ private static MySqlConnectorLogLevel ConvertLogLevel(LogLevel logLevel) =>
 				LogLevel.Critical => MySqlConnectorLogLevel.Fatal,
 				_ => MySqlConnectorLogLevel.Info,
 			};
-
-		private readonly IMySqlConnectorLogger m_logger;
 	}
 }
diff --git a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
index 294eff95f..0ea00778f 100644
--- a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
+++ b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
@@ -3,22 +3,13 @@
 
 namespace MySqlConnector.Logging;
 
-internal sealed class MySqlConnectorLoggingConfiguration
+internal sealed class MySqlConnectorLoggingConfiguration(ILoggerFactory loggerFactory)
 {
-	public MySqlConnectorLoggingConfiguration(ILoggerFactory loggerFactory)
-	{
-		DataSourceLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlDataSource");
-		ConnectionLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlConnection");
-		CommandLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlCommand");
-		PoolLogger = loggerFactory.CreateLogger("MySqlConnector.ConnectionPool");
-		BulkCopyLogger = loggerFactory.CreateLogger("MySqlConnector.MySqlBulkCopy");
-	}
-
-	public ILogger DataSourceLogger { get; }
-	public ILogger ConnectionLogger { get; }
-	public ILogger CommandLogger { get; }
-	public ILogger PoolLogger { get; }
-	public ILogger BulkCopyLogger { get; }
+	public ILogger DataSourceLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.MySqlDataSource");
+	public ILogger ConnectionLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.MySqlConnection");
+	public ILogger CommandLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.MySqlCommand");
+	public ILogger PoolLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.ConnectionPool");
+	public ILogger BulkCopyLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.MySqlBulkCopy");
 
 	public static MySqlConnectorLoggingConfiguration NullConfiguration { get; } = new MySqlConnectorLoggingConfiguration(NullLoggerFactory.Instance);
 	public static MySqlConnectorLoggingConfiguration GlobalConfiguration { get; set; } = NullConfiguration;

From 91a4ca277a4ff2153038246bd410c5a5c6d33620 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 20 Oct 2023 07:24:57 -0700
Subject: [PATCH 187/393] Use C# 12 collection expressions.

---
 .../Authentication/AuthenticationPlugins.cs          |  2 +-
 src/MySqlConnector/Core/CachedProcedure.cs           |  2 +-
 src/MySqlConnector/Core/CommandExecutor.cs           |  2 +-
 src/MySqlConnector/Core/ConnectionPool.cs            |  4 ++--
 src/MySqlConnector/Core/ParsedStatement.cs           |  6 +++---
 src/MySqlConnector/Core/ServerSession.cs             | 12 ++++++------
 src/MySqlConnector/Core/TypeMapper.cs                |  8 ++++----
 src/MySqlConnector/MySqlAttributeCollection.cs       |  2 +-
 src/MySqlConnector/MySqlBatch.cs                     |  2 +-
 src/MySqlConnector/MySqlBatchCommand.cs              |  2 +-
 src/MySqlConnector/MySqlBatchCommandCollection.cs    |  2 +-
 src/MySqlConnector/MySqlBulkCopy.cs                  |  2 +-
 src/MySqlConnector/MySqlBulkLoader.cs                |  6 +++---
 src/MySqlConnector/MySqlCommand.cs                   |  4 ++--
 src/MySqlConnector/MySqlConnection.cs                |  6 +++---
 src/MySqlConnector/MySqlConnectionStringBuilder.cs   |  2 +-
 src/MySqlConnector/MySqlDataAdapter.cs               |  2 +-
 src/MySqlConnector/MySqlParameterCollection.cs       |  2 +-
 src/MySqlConnector/Utilities/TimerQueue.cs           |  2 +-
 19 files changed, 35 insertions(+), 35 deletions(-)

diff --git a/src/MySqlConnector/Authentication/AuthenticationPlugins.cs b/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
index 88d390fa6..025362e0f 100644
--- a/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
+++ b/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
@@ -36,5 +36,5 @@ internal static bool TryGetPlugin(string name, [NotNullWhen(true)] out IAuthenti
 	}
 
 	private static readonly object s_lock = new();
-	private static readonly Dictionary<string, IAuthenticationPlugin> s_plugins = new();
+	private static readonly Dictionary<string, IAuthenticationPlugin> s_plugins = [];
 }
diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index fa7b97d4d..c0f788649 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -147,7 +147,7 @@ internal static List<CachedParameter> ParseParameters(string parametersSql)
 		parametersSql = s_multipleSpaces.Replace(parametersSql, " ");
 
 		if (string.IsNullOrWhiteSpace(parametersSql))
-			return new List<CachedParameter>();
+			return [];
 
 		// strip precision specifier containing comma
 		parametersSql = s_numericTypes.Replace(parametersSql, @"$1");
diff --git a/src/MySqlConnector/Core/CommandExecutor.cs b/src/MySqlConnector/Core/CommandExecutor.cs
index 74b2b4e6a..a2581334a 100644
--- a/src/MySqlConnector/Core/CommandExecutor.cs
+++ b/src/MySqlConnector/Core/CommandExecutor.cs
@@ -26,7 +26,7 @@ public static async ValueTask<MySqlDataReader> ExecuteReaderAsync(CommandListPos
 				var command2 = commandListPosition.CommandAt(commandIndex);
 				if (command2.CommandType == CommandType.StoredProcedure)
 				{
-					cachedProcedures ??= new();
+					cachedProcedures ??= [];
 					var commandText = command2.CommandText!;
 					if (!cachedProcedures.ContainsKey(commandText))
 					{
diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 89996e2ca..ad4f917f9 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -580,10 +580,10 @@ private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration,
 		m_cleanSemaphore = new(1);
 		m_sessionSemaphore = new(cs.MaximumPoolSize);
 		m_sessions = new();
-		m_leasedSessions = new();
+		m_leasedSessions = [];
 		if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets && cs.LoadBalance == MySqlLoadBalance.LeastConnections)
 		{
-			m_hostSessions = new();
+			m_hostSessions = [];
 			foreach (var hostName in cs.HostNames!)
 				m_hostSessions[hostName] = 0;
 		}
diff --git a/src/MySqlConnector/Core/ParsedStatement.cs b/src/MySqlConnector/Core/ParsedStatement.cs
index d04ca7236..489a81a00 100644
--- a/src/MySqlConnector/Core/ParsedStatement.cs
+++ b/src/MySqlConnector/Core/ParsedStatement.cs
@@ -15,17 +15,17 @@ internal sealed class ParsedStatement
 	/// The names of the parameters (if known) of the parameters in the prepared statement. There
 	/// is one entry in this list for each parameter, which will be <c>null</c> if the name is unknown.
 	/// </summary>
-	public List<string?> ParameterNames { get; } = new();
+	public List<string?> ParameterNames { get; } = [];
 
 	/// <summary>
 	/// The normalized names of the parameters (if known) of the parameters in the prepared statement. There
 	/// is one entry in this list for each parameter, which will be <c>null</c> if the name is unknown.
 	/// </summary>
-	public List<string?> NormalizedParameterNames { get; } = new();
+	public List<string?> NormalizedParameterNames { get; } = [];
 
 	/// <summary>
 	/// The indexes of the parameters in the prepared statement. There is one entry in this list for
 	/// each parameter; it will be <c>-1</c> if the parameter is named.
 	/// </summary>
-	public List<int> ParameterIndexes { get; } = new();
+	public List<int> ParameterIndexes { get; } = [];
 }
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 8993284cd..351eda129 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -41,7 +41,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 		Pool = pool;
 		PoolGeneration = poolGeneration;
 		HostName = "";
-		m_activityTags = new ActivityTagsCollection();
+		m_activityTags = [];
 		DataReader = new();
 		Log.CreatedNewSession(m_logger, Id);
 	}
@@ -267,7 +267,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
 			preparedStatements.Add(new(response.StatementId, statement, columns, parameters));
 		}
 
-		m_preparedStatements ??= new();
+		m_preparedStatements ??= [];
 		m_preparedStatements.Add(commandText, new(preparedStatements, parsedStatements));
 	}
 
@@ -1309,7 +1309,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 						"CertificateFile should be in PKCS #12 (.pfx) format and contain both a Certificate and Private Key");
 				}
 				m_clientCertificate = certificate;
-				clientCertificates = new() { certificate };
+				clientCertificates = [certificate];
 			}
 			catch (CryptographicException ex)
 			{
@@ -1322,7 +1322,7 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 
 		if (clientCertificates is null && connection.ProvideClientCertificatesCallback is { } clientCertificatesProvider)
 		{
-			clientCertificates = new();
+			clientCertificates = [];
 			try
 			{
 				await clientCertificatesProvider(clientCertificates).ConfigureAwait(false);
@@ -1532,7 +1532,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 				m_clientCertificate = new X509Certificate2(m_clientCertificate.Export(X509ContentType.Pkcs12));
 				oldCertificate.Dispose();
 			}
-			return new() { m_clientCertificate };
+			return [m_clientCertificate];
 #else
 			Log.LoadingClientKeyFromKeyFile(m_logger, Id, sslKeyFile);
 			string keyPem;
@@ -1590,7 +1590,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 #endif
 
 				m_clientCertificate = certificate;
-				return new() { certificate };
+				return [certificate];
 			}
 			catch (CryptographicException ex)
 			{
diff --git a/src/MySqlConnector/Core/TypeMapper.cs b/src/MySqlConnector/Core/TypeMapper.cs
index ee30727ff..6d46c4822 100644
--- a/src/MySqlConnector/Core/TypeMapper.cs
+++ b/src/MySqlConnector/Core/TypeMapper.cs
@@ -13,11 +13,11 @@ internal sealed class TypeMapper
 
 	private TypeMapper()
 	{
-		m_columnTypeMetadata = new();
-		m_dbTypeMappingsByClrType = new();
-		m_dbTypeMappingsByDbType = new();
+		m_columnTypeMetadata = [];
+		m_dbTypeMappingsByClrType = [];
+		m_dbTypeMappingsByDbType = [];
 		m_columnTypeMetadataLookup = new(StringComparer.OrdinalIgnoreCase);
-		m_mySqlDbTypeToColumnTypeMetadata = new();
+		m_mySqlDbTypeToColumnTypeMetadata = [];
 
 		// boolean
 		var typeBoolean = AddDbTypeMapping(new(typeof(bool), [ DbType.Boolean ], convert: static o => Convert.ToBoolean(o, CultureInfo.InvariantCulture)));
diff --git a/src/MySqlConnector/MySqlAttributeCollection.cs b/src/MySqlConnector/MySqlAttributeCollection.cs
index e3ea8c3f7..cd7c7c273 100644
--- a/src/MySqlConnector/MySqlAttributeCollection.cs
+++ b/src/MySqlConnector/MySqlAttributeCollection.cs
@@ -92,7 +92,7 @@ public void SetAttribute(string attributeName, object? value)
 		/// </summary>
 		IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 
-		internal MySqlAttributeCollection() => m_attributes = new();
+		internal MySqlAttributeCollection() => m_attributes = [];
 
 		private readonly List<MySqlAttribute> m_attributes;
 	}
diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index 37ec88f03..1aa0a1731 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -75,7 +75,7 @@ public MySqlBatch(MySqlConnection? connection = null, MySqlTransaction? transact
 	{
 		Connection = connection;
 		Transaction = transaction;
-		BatchCommands = new();
+		BatchCommands = [];
 		m_commandId = ICancellableCommandExtensions.GetNextId();
 	}
 
diff --git a/src/MySqlConnector/MySqlBatchCommand.cs b/src/MySqlConnector/MySqlBatchCommand.cs
index 5520644ae..3a08107ee 100644
--- a/src/MySqlConnector/MySqlBatchCommand.cs
+++ b/src/MySqlConnector/MySqlBatchCommand.cs
@@ -46,7 +46,7 @@ public MySqlBatchCommand(string? commandText)
 #else
 	public MySqlParameterCollection Parameters =>
 #endif
-		m_parameterCollection ??= new();
+		m_parameterCollection ??= [];
 
 #pragma warning disable CA1822 // Mark members as static
 	public
diff --git a/src/MySqlConnector/MySqlBatchCommandCollection.cs b/src/MySqlConnector/MySqlBatchCommandCollection.cs
index 018e59678..e9eb3a04a 100644
--- a/src/MySqlConnector/MySqlBatchCommandCollection.cs
+++ b/src/MySqlConnector/MySqlBatchCommandCollection.cs
@@ -9,7 +9,7 @@ public sealed class MySqlBatchCommandCollection
 	: IList<MySqlBatchCommand>, ICollection<MySqlBatchCommand>, IEnumerable<MySqlBatchCommand>, IEnumerable
 #endif
 {
-	internal MySqlBatchCommandCollection() => m_commands = new();
+	internal MySqlBatchCommandCollection() => m_commands = [];
 
 #if NET6_0_OR_GREATER
 	public new MySqlBatchCommand this[int index] { get => (MySqlBatchCommand) base[index]; set => base[index] = value; }
diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs
index 481be2e67..88170b243 100644
--- a/src/MySqlConnector/MySqlBulkCopy.cs
+++ b/src/MySqlConnector/MySqlBulkCopy.cs
@@ -61,7 +61,7 @@ public MySqlBulkCopy(MySqlConnection connection, MySqlTransaction? transaction =
 		m_connection = connection;
 		m_transaction = transaction;
 		m_logger = m_connection.LoggingConfiguration.BulkCopyLogger;
-		ColumnMappings = new();
+		ColumnMappings = [];
 	}
 
 	/// <summary>
diff --git a/src/MySqlConnector/MySqlBulkLoader.cs b/src/MySqlConnector/MySqlBulkLoader.cs
index 3e91c8fdc..1ab11c8fc 100644
--- a/src/MySqlConnector/MySqlBulkLoader.cs
+++ b/src/MySqlConnector/MySqlBulkLoader.cs
@@ -134,8 +134,8 @@ public MySqlBulkLoader(MySqlConnection connection)
 	{
 		Connection = connection;
 		Local = true;
-		Columns = new();
-		Expressions = new();
+		Columns = [];
+		Expressions = [];
 	}
 
 	/// <summary>
@@ -320,5 +320,5 @@ internal static bool TryGetAndRemoveSource(string sourceKey, [NotNullWhen(true)]
 	private static string GenerateSourceFileName() => SourcePrefix + Guid.NewGuid().ToString("N");
 
 	private static readonly object s_lock = new();
-	private static readonly Dictionary<string, object> s_sources = new();
+	private static readonly Dictionary<string, object> s_sources = [];
 }
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index 09ebbbfce..d8a124ad8 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -81,14 +81,14 @@ private MySqlCommand(MySqlCommand other)
 	/// <summary>
 	/// The collection of <see cref="MySqlParameter"/> objects for this command.
 	/// </summary>
-	public new MySqlParameterCollection Parameters => m_parameterCollection ??= new();
+	public new MySqlParameterCollection Parameters => m_parameterCollection ??= [];
 
 	MySqlParameterCollection? IMySqlCommand.RawParameters => m_parameterCollection;
 
 	/// <summary>
 	/// The collection of <see cref="MySqlAttribute"/> objects for this command.
 	/// </summary>
-	public MySqlAttributeCollection Attributes => m_attributeCollection ??= new();
+	public MySqlAttributeCollection Attributes => m_attributeCollection ??= [];
 
 	MySqlAttributeCollection? IMySqlCommand.RawAttributes => m_attributeCollection;
 
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 8f47d6769..ddb259522 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -213,7 +213,7 @@ public override void EnlistTransaction(System.Transactions.Transaction? transact
 				lock (s_lock)
 				{
 					if (!s_transactionConnections.TryGetValue(transaction, out var enlistedTransactions))
-						s_transactionConnections[transaction] = enlistedTransactions = new();
+						s_transactionConnections[transaction] = enlistedTransactions = [];
 					enlistedTransactions.Add(m_enlistedTransaction);
 				}
 			}
@@ -799,7 +799,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 		if (cachedProcedures is null)
 		{
 			Log.PoolDoesNotHaveSharedProcedureCache(m_logger, m_session.Id, m_session.Pool?.Id);
-			cachedProcedures = m_cachedProcedures = new();
+			cachedProcedures = m_cachedProcedures = [];
 		}
 
 		var normalized = NormalizedSchema.MustNormalize(name, Database);
@@ -1113,7 +1113,7 @@ private ConnectionSettings GetConnectionSettings() =>
 	private static readonly StateChangeEventArgs s_stateChangeConnectingOpen = new(ConnectionState.Connecting, ConnectionState.Open);
 	private static readonly StateChangeEventArgs s_stateChangeOpenClosed = new(ConnectionState.Open, ConnectionState.Closed);
 	private static readonly object s_lock = new();
-	private static readonly Dictionary<System.Transactions.Transaction, List<EnlistedTransactionBase>> s_transactionConnections = new();
+	private static readonly Dictionary<System.Transactions.Transaction, List<EnlistedTransactionBase>> s_transactionConnections = [];
 
 	private readonly MySqlDataSource? m_dataSource;
 	private readonly ILogger m_logger;
diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index ec9a72509..b82352596 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -890,7 +890,7 @@ protected override void GetProperties(Hashtable propertyDescriptors)
 
 internal abstract partial class MySqlConnectionStringOption
 {
-	public static List<string> OptionNames { get; } = new();
+	public static List<string> OptionNames { get; } = [];
 
 	// Connection Options
 	public static readonly MySqlConnectionStringReferenceOption<string> Server;
diff --git a/src/MySqlConnector/MySqlDataAdapter.cs b/src/MySqlConnector/MySqlDataAdapter.cs
index ba67a5f88..f9a66d0c7 100644
--- a/src/MySqlConnector/MySqlDataAdapter.cs
+++ b/src/MySqlConnector/MySqlDataAdapter.cs
@@ -202,7 +202,7 @@ public InsertSqlParser(IMySqlCommand command)
 		{
 			CommandText = command.CommandText!;
 			m_parameters = command.RawParameters;
-			ParameterIndexes = new();
+			ParameterIndexes = [];
 		}
 
 		public List<int> ParameterIndexes { get; }
diff --git a/src/MySqlConnector/MySqlParameterCollection.cs b/src/MySqlConnector/MySqlParameterCollection.cs
index 0b8d428a8..e9012d37e 100644
--- a/src/MySqlConnector/MySqlParameterCollection.cs
+++ b/src/MySqlConnector/MySqlParameterCollection.cs
@@ -8,7 +8,7 @@ public sealed class MySqlParameterCollection : DbParameterCollection, IEnumerabl
 {
 	internal MySqlParameterCollection()
 	{
-		m_parameters = new();
+		m_parameters = [];
 		m_nameToIndex = new(StringComparer.OrdinalIgnoreCase);
 	}
 
diff --git a/src/MySqlConnector/Utilities/TimerQueue.cs b/src/MySqlConnector/Utilities/TimerQueue.cs
index 916cf64d1..5aa05f736 100644
--- a/src/MySqlConnector/Utilities/TimerQueue.cs
+++ b/src/MySqlConnector/Utilities/TimerQueue.cs
@@ -68,7 +68,7 @@ private TimerQueue()
 	{
 		m_lock = new();
 		m_timer = new(Callback, this, -1, -1);
-		m_timeoutActions = new();
+		m_timeoutActions = [];
 	}
 
 	private void Callback(object? obj)

From de31449f94bea43099235fadb8de9bac8aa7cdf1 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 10 Jun 2023 10:52:59 -0700
Subject: [PATCH 188/393] Add connection pool metrics. Fixes #491

This is based on the specification at https://github.com/open-telemetry/semantic-conventions/blob/main/specification/metrics/semantic_conventions/database-metrics.md.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ConnectionPool.cs     |  61 ++++-
 src/MySqlConnector/Core/ServerSession.cs      |  10 +-
 src/MySqlConnector/MySqlConnection.cs         |   6 +-
 src/MySqlConnector/MySqlConnector.csproj      |   2 +-
 .../Utilities/ActivitySourceHelper.cs         |  13 +-
 tests/MySqlConnector.Tests/ConnectionTests.cs |   2 +-
 tests/MySqlConnector.Tests/FakeMySqlServer.cs |   2 +-
 .../FakeMySqlServerConnection.cs              |   4 +-
 .../Metrics/ConnectionTimeTests.cs            |  92 +++++++
 .../Metrics/ConnectionsUsageTests.cs          | 245 ++++++++++++++++++
 .../Metrics/DictionaryExtensions.cs           |  12 +
 .../Metrics/MetricsTestsBase.cs               | 117 +++++++++
 .../Metrics/MinMaxConnectionsTests.cs         | 105 ++++++++
 .../MySqlConnector.Tests.csproj               |   1 +
 14 files changed, 656 insertions(+), 16 deletions(-)
 create mode 100644 tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
 create mode 100644 tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
 create mode 100644 tests/MySqlConnector.Tests/Metrics/DictionaryExtensions.cs
 create mode 100644 tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
 create mode 100644 tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs

diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index ad4f917f9..bf020186b 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -1,5 +1,6 @@
 using System.Collections.Concurrent;
 using System.Diagnostics;
+using System.Diagnostics.Metrics;
 using System.Net;
 using System.Security.Authentication;
 using Microsoft.Extensions.Logging;
@@ -19,6 +20,8 @@ internal sealed class ConnectionPool : IDisposable
 
 	public SslProtocols SslProtocols { get; set; }
 
+	public void AddPendingRequestCount(int delta) => s_pendingRequestsCounter.Add(delta, PoolNameTagList);
+
 	public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection, int startTickCount, int timeoutMilliseconds, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		cancellationToken.ThrowIfCancellationRequested();
@@ -50,12 +53,14 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			{
 				if (m_sessions.Count > 0)
 				{
+					// NOTE: s_connectionsUsageCounter updated outside lock below
 					session = m_sessions.First!.Value;
 					m_sessions.RemoveFirst();
 				}
 			}
 			if (session is not null)
 			{
+				s_connectionsUsageCounter.Add(-1, IdleStateTagList);
 				Log.FoundExistingSession(m_logger, Id);
 				bool reuseSession;
 
@@ -96,8 +101,12 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 						m_leasedSessions.Add(session.Id, session);
 						leasedSessionsCountPooled = m_leasedSessions.Count;
 					}
+					s_connectionsUsageCounter.Add(1, UsedStateTagList);
 					ActivitySourceHelper.CopyTags(session.ActivityTags, activity);
 					Log.ReturningPooledSession(m_logger, Id, session.Id, leasedSessionsCountPooled);
+
+					session.LastLeasedTicks = unchecked((uint) Environment.TickCount);
+					s_waitTimeHistory.Record(unchecked(session.LastLeasedTicks - (uint) startTickCount), PoolNameTagList);
 					return session;
 				}
 			}
@@ -112,7 +121,11 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 				m_leasedSessions.Add(session.Id, session);
 				leasedSessionsCountNew = m_leasedSessions.Count;
 			}
+			s_connectionsUsageCounter.Add(1, UsedStateTagList);
 			Log.ReturningNewSession(m_logger, Id, session.Id, leasedSessionsCountNew);
+
+			session.LastLeasedTicks = unchecked((uint) Environment.TickCount);
+			s_createTimeHistory.Record(unchecked(session.LastLeasedTicks - (uint) startTickCount), PoolNameTagList);
 			return session;
 		}
 		catch (Exception ex)
@@ -164,12 +177,14 @@ public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 		{
 			lock (m_leasedSessions)
 				m_leasedSessions.Remove(session.Id);
+			s_connectionsUsageCounter.Add(-1, UsedStateTagList);
 			session.OwningConnection = null;
 			var sessionHealth = GetSessionHealth(session);
 			if (sessionHealth == 0)
 			{
 				lock (m_sessions)
 					m_sessions.AddFirst(session);
+				s_connectionsUsageCounter.Add(1, IdleStateTagList);
 			}
 			else
 			{
@@ -243,6 +258,10 @@ public void Dispose()
 			reaperWaitHandle.WaitOne();
 		}
 #endif
+
+		s_minIdleConnectionsCounter.Add(-ConnectionSettings.MinimumPoolSize, PoolNameTagList);
+		s_maxIdleConnectionsCounter.Add(-ConnectionSettings.MaximumPoolSize, PoolNameTagList);
+		s_maxConnectionsCounter.Add(-ConnectionSettings.MaximumPoolSize, PoolNameTagList);
 	}
 
 	/// <summary>
@@ -326,12 +345,14 @@ private async Task CleanPoolAsync(IOBehavior ioBehavior, Func<ServerSession, boo
 					{
 						if (m_sessions.Count > 0)
 						{
+							// NOTE: s_connectionsUsageCounter updated outside lock below
 							session = m_sessions.Last!.Value;
 							m_sessions.RemoveLast();
 						}
 					}
 					if (session is null)
 						return;
+					s_connectionsUsageCounter.Add(-1, IdleStateTagList);
 
 					if (shouldCleanFn(session))
 					{
@@ -344,6 +365,7 @@ private async Task CleanPoolAsync(IOBehavior ioBehavior, Func<ServerSession, boo
 						// session should not be cleaned; put it back in the queue and stop iterating
 						lock (m_sessions)
 							m_sessions.AddLast(session);
+						s_connectionsUsageCounter.Add(1, IdleStateTagList);
 						return;
 					}
 				}
@@ -389,6 +411,7 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh
 				AdjustHostConnectionCount(session, 1);
 				lock (m_sessions)
 					m_sessions.AddFirst(session);
+				s_connectionsUsageCounter.Add(1, IdleStateTagList);
 			}
 			finally
 			{
@@ -594,8 +617,22 @@ private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration,
 			cs.LoadBalance == MySqlLoadBalance.LeastConnections ? new LeastConnectionsLoadBalancer(m_hostSessions!) :
 			(ILoadBalancer) new RoundRobinLoadBalancer();
 
+		// create tag lists for reporting pool metrics
+		var connectionString = cs.ConnectionStringBuilder.GetConnectionString(includePassword: false);
+		m_stateTagList =
+		[
+			new("state", "idle"),
+			new("pool.name", Name ?? connectionString),
+			new("state", "used"),
+		];
+
+		// set pool size counters
+		s_minIdleConnectionsCounter.Add(ConnectionSettings.MinimumPoolSize, PoolNameTagList);
+		s_maxIdleConnectionsCounter.Add(ConnectionSettings.MaximumPoolSize, PoolNameTagList);
+		s_maxConnectionsCounter.Add(ConnectionSettings.MaximumPoolSize, PoolNameTagList);
+
 		Id = Interlocked.Increment(ref s_poolId);
-		Log.CreatingNewConnectionPool(m_logger, Id, cs.ConnectionStringBuilder.GetConnectionString(includePassword: false));
+		Log.CreatingNewConnectionPool(m_logger, Id, connectionString);
 	}
 
 	private void StartReaperTask()
@@ -741,6 +778,13 @@ private void AdjustHostConnectionCount(ServerSession session, int delta)
 		}
 	}
 
+	// Provides a slice of m_stateTagList that contains either the 'idle' or 'used' state tag along with the pool name.
+	private ReadOnlySpan<KeyValuePair<string, object?>> IdleStateTagList => m_stateTagList.AsSpan(0, 2);
+	private ReadOnlySpan<KeyValuePair<string, object?>> UsedStateTagList => m_stateTagList.AsSpan(1, 2);
+
+	// A slice of m_stateTagList that contains only the pool name tag.
+	public ReadOnlySpan<KeyValuePair<string, object?>> PoolNameTagList => m_stateTagList.AsSpan(1, 1);
+
 	private sealed class LeastConnectionsLoadBalancer(Dictionary<string, int> hostSessions) : ILoadBalancer
 	{
 		public IReadOnlyList<string> LoadBalance(IReadOnlyList<string> hosts)
@@ -765,6 +809,20 @@ static ConnectionPool()
 	private static void OnAppDomainShutDown(object? sender, EventArgs e) =>
 		ClearPoolsAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 
+	private static readonly UpDownCounter<int> s_connectionsUsageCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.usage",
+		unit: "{connection}", description: "The number of connections that are currently in the state described by the state tag.");
+	private static readonly UpDownCounter<int> s_maxIdleConnectionsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.idle.max",
+		unit: "{connection}", description: "The maximum number of idle open connections allowed.");
+	private static readonly UpDownCounter<int> s_minIdleConnectionsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.idle.min",
+		unit: "{connection}", description: "The minimum number of idle open connections allowed.");
+	private static readonly UpDownCounter<int> s_maxConnectionsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.max",
+		unit: "{connection}", description: "The maximum number of open connections allowed.");
+	private static readonly UpDownCounter<int> s_pendingRequestsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.pending_requests",
+		unit: "{request}", description: "The number of pending requests for an open connection, cumulative for the entire pool.");
+	private static readonly Histogram<float> s_createTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.create_time",
+		unit: "ms", description: "The time it took to create a new connection.");
+	private static readonly Histogram<float> s_waitTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.wait_time",
+		unit: "ms", description: "The time it took to obtain an open connection from the pool.");
 	private static readonly ConcurrentDictionary<string, ConnectionPool?> s_pools = new();
 	private static readonly Action<ILogger, int, string, Exception?> s_createdNewSession = LoggerMessage.Define<int, string>(
 		LogLevel.Debug, new EventId(EventIds.PoolCreatedNewSession, nameof(EventIds.PoolCreatedNewSession)),
@@ -777,6 +835,7 @@ private static void OnAppDomainShutDown(object? sender, EventArgs e) =>
 
 	private readonly ILogger m_logger;
 	private readonly ILogger m_connectionLogger;
+	private readonly KeyValuePair<string, object?>[] m_stateTagList;
 	private readonly SemaphoreSlim m_cleanSemaphore;
 	private readonly SemaphoreSlim m_sessionSemaphore;
 	private readonly LinkedList<ServerSession> m_sessions;
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 351eda129..1e8788e86 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1,6 +1,7 @@
 using System.Buffers.Text;
 using System.ComponentModel;
 using System.Diagnostics;
+using System.Diagnostics.Metrics;
 using System.Globalization;
 using System.IO.Pipes;
 using System.Net;
@@ -56,6 +57,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public uint CreatedTicks { get; }
 	public ConnectionPool? Pool { get; }
 	public int PoolGeneration { get; }
+	public uint LastLeasedTicks { get; set; }
 	public uint LastReturnedTicks { get; private set; }
 	public string? DatabaseOverride { get; set; }
 	public string HostName { get; private set; }
@@ -75,7 +77,11 @@ public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? ownin
 	{
 		Log.ReturningToPool(m_logger, Id, Pool?.Id ?? 0);
 		LastReturnedTicks = unchecked((uint) Environment.TickCount);
-		return Pool is null ? default : Pool.ReturnAsync(ioBehavior, this);
+		if (Pool is null)
+			return default;
+		s_useTimeHistory.Record(unchecked(LastReturnedTicks - LastLeasedTicks), Pool.PoolNameTagList);
+		LastLeasedTicks = 0;
+		return Pool.ReturnAsync(ioBehavior, this);
 	}
 
 	public bool IsConnected
@@ -1910,6 +1916,8 @@ protected override void OnStatementBegin(int index)
 	[LoggerMessage(EventIds.ExpectedSessionState6, LogLevel.Error, "Session {SessionId} should have state {ExpectedState1} or {ExpectedState2} or {ExpectedState3} or {ExpectedState4} or {ExpectedState5} or {ExpectedState6} but was {SessionState}")]
 	private static partial void ExpectedSessionState6(ILogger logger, string sessionId, State expectedState1, State expectedState2, State expectedState3, State expectedState4, State expectedState5, State expectedState6, State sessionState);
 
+	private static readonly Histogram<float> s_useTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.use_time",
+		unit: "ms", description: "The time between borrowing a connection and returning it to the pool.");
 	private static readonly PayloadData s_setNamesUtf8NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8;"u8);
 	private static readonly PayloadData s_setNamesUtf8mb4NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8mb4;"u8);
 	private static readonly PayloadData s_setNamesUtf8WithAttributesPayload = QueryPayload.Create(true, "SET NAMES utf8;"u8);
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index ddb259522..81d13a900 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -384,6 +384,8 @@ private async ValueTask<bool> PingAsync(IOBehavior ioBehavior, CancellationToken
 
 	internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancellationToken)
 	{
+		var openStartTickCount = Environment.TickCount;
+
 		VerifyNotDisposed();
 		cancellationToken.ThrowIfCancellationRequested();
 		if (State != ConnectionState.Closed)
@@ -392,8 +394,6 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 		using var activity = ActivitySourceHelper.StartActivity(ActivitySourceHelper.OpenActivityName);
 		try
 		{
-			var openStartTickCount = Environment.TickCount;
-
 			SetState(ConnectionState.Connecting);
 
 			var pool = m_dataSource?.Pool ??
@@ -896,6 +896,7 @@ internal void FinishQuerying(bool hasWarnings)
 
 	private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool, int startTickCount, Activity? activity, IOBehavior? ioBehavior, CancellationToken cancellationToken)
 	{
+		pool?.AddPendingRequestCount(1);
 		var connectionSettings = GetInitializedConnectionSettings();
 		var actualIOBehavior = ioBehavior ?? (connectionSettings.ForceSynchronous ? IOBehavior.Synchronous : IOBehavior.Asynchronous);
 
@@ -949,6 +950,7 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 		}
 		finally
 		{
+			pool?.AddPendingRequestCount(-1);
 			linkedSource?.Dispose();
 			timeoutSource?.Dispose();
 		}
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index a8e3df037..5ce7293e0 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -29,7 +29,7 @@
     <PackageReference Include="System.Threading.Tasks.Extensions" />
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFrameworkIdentifier)' != '.NETCoreApp' ">
+  <ItemGroup Condition=" '$(TargetFrameworkIdentifier)' != '.NETCoreApp' OR '$(TargetFramework)' == 'net6.0' ">
     <PackageReference Include="System.Diagnostics.DiagnosticSource" />
   </ItemGroup>
 
diff --git a/src/MySqlConnector/Utilities/ActivitySourceHelper.cs b/src/MySqlConnector/Utilities/ActivitySourceHelper.cs
index a96dbe78e..1f2c15a0d 100644
--- a/src/MySqlConnector/Utilities/ActivitySourceHelper.cs
+++ b/src/MySqlConnector/Utilities/ActivitySourceHelper.cs
@@ -1,4 +1,5 @@
 using System.Diagnostics;
+using System.Diagnostics.Metrics;
 using System.Globalization;
 using System.Reflection;
 
@@ -55,12 +56,10 @@ public static void CopyTags(IEnumerable<KeyValuePair<string, object?>> tags, Act
 		}
 	}
 
-	private static ActivitySource ActivitySource { get; } = CreateActivitySource();
+	public static Meter Meter { get; } = new("MySqlConnector", GetVersion());
 
-	private static ActivitySource CreateActivitySource()
-	{
-		var assembly = typeof(ActivitySourceHelper).Assembly;
-		var version = assembly.GetCustomAttribute<AssemblyFileVersionAttribute>()!.Version;
-		return new("MySqlConnector", version);
-	}
+	private static ActivitySource ActivitySource { get; } = new("MySqlConnector", GetVersion());
+
+	private static string GetVersion() =>
+		typeof(ActivitySourceHelper).Assembly.GetCustomAttribute<AssemblyFileVersionAttribute>()!.Version;
 }
diff --git a/tests/MySqlConnector.Tests/ConnectionTests.cs b/tests/MySqlConnector.Tests/ConnectionTests.cs
index 62b06541f..73f7b0447 100644
--- a/tests/MySqlConnector.Tests/ConnectionTests.cs
+++ b/tests/MySqlConnector.Tests/ConnectionTests.cs
@@ -179,7 +179,7 @@ public void PingWhenClosed()
 	[Fact]
 	public void ConnectionTimeout()
 	{
-		m_server.BlockOnConnect = true;
+		m_server.ConnectDelay = TimeSpan.FromSeconds(10);
 		var csb = new MySqlConnectionStringBuilder(m_csb.ConnectionString);
 		csb.ConnectionTimeout = 4;
 		using var connection = new MySqlConnection(csb.ConnectionString);
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServer.cs b/tests/MySqlConnector.Tests/FakeMySqlServer.cs
index d1f6d5a9d..d070a085f 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServer.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServer.cs
@@ -52,7 +52,7 @@ public void Stop()
 
 	public bool SuppressAuthPluginNameTerminatingNull { get; set; }
 	public bool SendIncompletePostHandshakeResponse { get; set; }
-	public bool BlockOnConnect { get; set; }
+	public TimeSpan? ConnectDelay { get; set; }
 	public TimeSpan? ResetDelay { get; set; }
 
 	internal void CancelQuery(int connectionId)
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
index b2fec1411..cf7441dd0 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
@@ -25,8 +25,8 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 			using (client)
 			using (var stream = client.GetStream())
 			{
-				if (m_server.BlockOnConnect)
-					Thread.Sleep(TimeSpan.FromSeconds(10));
+				if (m_server.ConnectDelay is { } connectDelay)
+					await Task.Delay(connectDelay);
 
 				await SendAsync(stream, 0, WriteInitialHandshake);
 				await ReadPayloadAsync(stream, token); // handshake response
diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
new file mode 100644
index 000000000..c6c5dd500
--- /dev/null
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
@@ -0,0 +1,92 @@
+namespace MySqlConnector.Tests.Metrics;
+
+public class ConnectionTimeTests : MetricsTestsBase
+{
+	[Fact]
+	public async Task ConnectionTime()
+	{
+		var csb = CreateConnectionStringBuilder();
+		PoolName = csb.GetConnectionString(includePassword: false);
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+		var measurements = GetAndClearMeasurements("db.client.connections.create_time");
+		var time = Assert.Single(measurements);
+		Assert.InRange(time, 0, 300);
+	}
+
+	[Fact]
+	public async Task ConnectionTimeWithDelay()
+	{
+		var csb = CreateConnectionStringBuilder();
+		PoolName = csb.GetConnectionString(includePassword: false);
+		Server.ConnectDelay = TimeSpan.FromSeconds(1);
+
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+		var measurements = GetAndClearMeasurements("db.client.connections.create_time");
+		var time = Assert.Single(measurements);
+		Assert.InRange(time, 1000, 1300);
+	}
+
+	[Fact]
+	public async Task OpenFromPoolTime()
+	{
+		var csb = CreateConnectionStringBuilder();
+		PoolName = csb.GetConnectionString(includePassword: false);
+
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+		connection.Close();
+
+		await connection.OpenAsync();
+		var measurements = GetAndClearMeasurements("db.client.connections.wait_time");
+		var time = Assert.Single(measurements);
+		Assert.InRange(time, 0, 200);
+	}
+
+	[Fact]
+	public async Task OpenFromPoolTimeWithDelay()
+	{
+		var csb = CreateConnectionStringBuilder();
+		PoolName = csb.GetConnectionString(includePassword: false);
+		Server.ResetDelay = TimeSpan.FromSeconds(1);
+
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+		connection.Close();
+
+		await connection.OpenAsync();
+		var measurements = GetAndClearMeasurements("db.client.connections.wait_time");
+		var time = Assert.Single(measurements);
+		Assert.InRange(time, 1000, 1200);
+	}
+
+	[Fact]
+	public async Task UseTime()
+	{
+		var csb = CreateConnectionStringBuilder();
+		PoolName = csb.GetConnectionString(includePassword: false);
+
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+		connection.Close();
+
+		var time = Assert.Single(GetAndClearMeasurements("db.client.connections.use_time"));
+		Assert.InRange(time, 0, 100);
+	}
+
+	[Fact]
+	public async Task UseTimeWithDelay()
+	{
+		var csb = CreateConnectionStringBuilder();
+		PoolName = csb.GetConnectionString(includePassword: false);
+
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+		await Task.Delay(500);
+		connection.Close();
+
+		var time = Assert.Single(GetAndClearMeasurements("db.client.connections.use_time"));
+		Assert.InRange(time, 500, 600);
+	}
+}
diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
new file mode 100644
index 000000000..45646ff2a
--- /dev/null
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
@@ -0,0 +1,245 @@
+#nullable enable
+
+namespace MySqlConnector.Tests.Metrics;
+
+public class ConnectionsUsageTests : MetricsTestsBase
+{
+    [Fact]
+	public void NamedDataSource()
+    {
+		PoolName = "metrics-test";
+		using var dataSource = new MySqlDataSourceBuilder(CreateConnectionStringBuilder().ConnectionString)
+			.UseName(PoolName)
+			.Build();
+
+		// no connections at beginning of test
+		AssertMeasurement("db.client.connections.usage", 0);
+		AssertMeasurement("db.client.connections.usage|idle", 0);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(0, Server.ActiveConnections);
+
+		// opening a connection creates a 'used' connection
+		using (var connection = dataSource.OpenConnection())
+		{
+			AssertMeasurement("db.client.connections.usage", 1);
+			AssertMeasurement("db.client.connections.usage|idle", 0);
+			AssertMeasurement("db.client.connections.usage|used", 1);
+			Assert.Equal(1, Server.ActiveConnections);
+		}
+
+		// closing it creates an 'idle' connection
+		AssertMeasurement("db.client.connections.usage", 1);
+		AssertMeasurement("db.client.connections.usage|idle", 1);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(1, Server.ActiveConnections);
+
+		// reopening the connection transitions it back to 'used'
+		using (var connection = dataSource.OpenConnection())
+		{
+			AssertMeasurement("db.client.connections.usage", 1);
+			AssertMeasurement("db.client.connections.usage|idle", 0);
+			AssertMeasurement("db.client.connections.usage|used", 1);
+		}
+		Assert.Equal(1, Server.ActiveConnections);
+
+		// opening a second connection creates a net new 'used' connection
+		using (var connection = dataSource.OpenConnection())
+		using (var connection2 = dataSource.OpenConnection())
+		{
+			AssertMeasurement("db.client.connections.usage", 2);
+			AssertMeasurement("db.client.connections.usage|idle", 0);
+			AssertMeasurement("db.client.connections.usage|used", 2);
+			Assert.Equal(2, Server.ActiveConnections);
+		}
+
+		AssertMeasurement("db.client.connections.usage", 2);
+		AssertMeasurement("db.client.connections.usage|idle", 2);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(2, Server.ActiveConnections);
+	}
+
+	[Fact]
+	public void NamedDataSourceWithMinPoolSize()
+	{
+		var csb = CreateConnectionStringBuilder();
+		csb.MinimumPoolSize = 3;
+
+		PoolName = "minimum-pool-size";
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UseName(PoolName)
+			.Build();
+
+		// minimum pool size is created lazily when the first connection is opened
+		AssertMeasurement("db.client.connections.usage", 0);
+		AssertMeasurement("db.client.connections.usage|idle", 0);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(0, Server.ActiveConnections);
+
+		// opening a connection creates the minimum connections then takes an idle one from the pool
+		using (var connection = dataSource.OpenConnection())
+		{
+			AssertMeasurement("db.client.connections.usage", 3);
+			AssertMeasurement("db.client.connections.usage|idle", 2);
+			AssertMeasurement("db.client.connections.usage|used", 1);
+			Assert.Equal(3, Server.ActiveConnections);
+		}
+
+		// closing puts it back to idle
+		AssertMeasurement("db.client.connections.usage", 3);
+		AssertMeasurement("db.client.connections.usage|idle", 3);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(3, Server.ActiveConnections);
+	}
+
+	[Fact]
+	public void UnnamedDataSource()
+	{
+		var csb = CreateConnectionStringBuilder();
+
+		// NOTE: pool "name" is connection string (without password)
+		PoolName = csb.GetConnectionString(includePassword: false);
+
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.Build();
+
+		// no connections at beginning of test
+		AssertMeasurement("db.client.connections.usage", 0);
+		AssertMeasurement("db.client.connections.usage|idle", 0);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(0, Server.ActiveConnections);
+
+		// opening a connection creates a 'used' connection
+		using (var connection = dataSource.OpenConnection())
+		{
+			AssertMeasurement("db.client.connections.usage", 1);
+			AssertMeasurement("db.client.connections.usage|idle", 0);
+			AssertMeasurement("db.client.connections.usage|used", 1);
+			Assert.Equal(1, Server.ActiveConnections);
+		}
+
+		// closing it creates an 'idle' connection
+		AssertMeasurement("db.client.connections.usage", 1);
+		AssertMeasurement("db.client.connections.usage|idle", 1);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(1, Server.ActiveConnections);
+
+		// reopening the connection transitions it back to 'used'
+		using (var connection = dataSource.OpenConnection())
+		{
+			AssertMeasurement("db.client.connections.usage", 1);
+			AssertMeasurement("db.client.connections.usage|idle", 0);
+			AssertMeasurement("db.client.connections.usage|used", 1);
+		}
+		Assert.Equal(1, Server.ActiveConnections);
+
+		// opening a second connection creates a net new 'used' connection
+		using (var connection = dataSource.OpenConnection())
+		using (var connection2 = dataSource.OpenConnection())
+		{
+			AssertMeasurement("db.client.connections.usage", 2);
+			AssertMeasurement("db.client.connections.usage|idle", 0);
+			AssertMeasurement("db.client.connections.usage|used", 2);
+			Assert.Equal(2, Server.ActiveConnections);
+		}
+
+		AssertMeasurement("db.client.connections.usage", 2);
+		AssertMeasurement("db.client.connections.usage|idle", 2);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(2, Server.ActiveConnections);
+	}
+
+	[Fact]
+	public void NoDataSource()
+	{
+		var csb = CreateConnectionStringBuilder();
+
+		// NOTE: pool "name" is connection string (without password)
+		PoolName = csb.GetConnectionString(includePassword: false);
+
+		// no connections at beginning of test
+		AssertMeasurement("db.client.connections.usage", 0);
+		AssertMeasurement("db.client.connections.usage|idle", 0);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(0, Server.ActiveConnections);
+
+		// opening a connection creates a 'used' connection
+		using (var connection = new MySqlConnection(csb.ConnectionString))
+		{
+			connection.Open();
+			AssertMeasurement("db.client.connections.usage", 1);
+			AssertMeasurement("db.client.connections.usage|idle", 0);
+			AssertMeasurement("db.client.connections.usage|used", 1);
+			Assert.Equal(1, Server.ActiveConnections);
+		}
+
+		// closing it creates an 'idle' connection
+		AssertMeasurement("db.client.connections.usage", 1);
+		AssertMeasurement("db.client.connections.usage|idle", 1);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(1, Server.ActiveConnections);
+
+		// reopening the connection transitions it back to 'used'
+		using (var connection = new MySqlConnection(csb.ConnectionString))
+		{
+			connection.Open();
+			AssertMeasurement("db.client.connections.usage", 1);
+			AssertMeasurement("db.client.connections.usage|idle", 0);
+			AssertMeasurement("db.client.connections.usage|used", 1);
+		}
+		Assert.Equal(1, Server.ActiveConnections);
+
+		// opening a second connection creates a net new 'used' connection
+		using (var connection = new MySqlConnection(csb.ConnectionString))
+		using (var connection2 = new MySqlConnection(csb.ConnectionString))
+		{
+			connection.Open();
+			connection2.Open();
+			AssertMeasurement("db.client.connections.usage", 2);
+			AssertMeasurement("db.client.connections.usage|idle", 0);
+			AssertMeasurement("db.client.connections.usage|used", 2);
+			Assert.Equal(2, Server.ActiveConnections);
+		}
+
+		AssertMeasurement("db.client.connections.usage", 2);
+		AssertMeasurement("db.client.connections.usage|idle", 2);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(2, Server.ActiveConnections);
+	}
+
+	[Fact]
+	public async Task PendingRequestForCreation()
+	{
+		var csb = CreateConnectionStringBuilder();
+		PoolName = csb.GetConnectionString(includePassword: false);
+		Server.ConnectDelay = TimeSpan.FromSeconds(0.5);
+
+		AssertMeasurement("db.client.connections.pending_requests", 0);
+
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		var openTask = connection.OpenAsync();
+		AssertMeasurement("db.client.connections.pending_requests", 1);
+		await openTask;
+
+		AssertMeasurement("db.client.connections.pending_requests", 0);
+	}
+
+	[Fact]
+	public async Task PendingRequestForOpenFromPool()
+	{
+		var csb = CreateConnectionStringBuilder();
+		PoolName = csb.GetConnectionString(includePassword: false);
+		Server.ResetDelay = TimeSpan.FromSeconds(0.5);
+
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+		connection.Close();
+
+		AssertMeasurement("db.client.connections.pending_requests", 0);
+
+		var openTask = connection.OpenAsync();
+		AssertMeasurement("db.client.connections.pending_requests", 1);
+		await openTask;
+
+		AssertMeasurement("db.client.connections.pending_requests", 0);
+	}
+}
diff --git a/tests/MySqlConnector.Tests/Metrics/DictionaryExtensions.cs b/tests/MySqlConnector.Tests/Metrics/DictionaryExtensions.cs
new file mode 100644
index 000000000..36dc93d74
--- /dev/null
+++ b/tests/MySqlConnector.Tests/Metrics/DictionaryExtensions.cs
@@ -0,0 +1,12 @@
+#nullable enable
+
+namespace MySqlConnector.Tests.Metrics;
+
+#if !NETSTANDARD2_1 && !NETCOREAPP3_1_OR_GREATER
+internal static class DictionaryExtensions
+{
+	public static TValue GetValueOrDefault<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key)
+		where TValue : struct =>
+		dictionary.TryGetValue(key, out var value) ? value : default;
+}
+#endif
diff --git a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
new file mode 100644
index 000000000..3d2549e1e
--- /dev/null
+++ b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
@@ -0,0 +1,117 @@
+#nullable enable
+
+using System.Diagnostics.Metrics;
+
+namespace MySqlConnector.Tests.Metrics;
+
+public abstract class MetricsTestsBase : IDisposable
+{
+	public MetricsTestsBase()
+	{
+		m_measurements = new();
+		m_timeMeasurements = new();
+
+		Server = new FakeMySqlServer();
+		Server.Start();
+
+		m_meterListener = new MeterListener
+		{
+			InstrumentPublished = (instrument, listener) =>
+			{
+				if (instrument.Meter.Name == "MySqlConnector")
+					listener.EnableMeasurementEvents(instrument);
+			}
+		};
+		m_meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
+		m_meterListener.SetMeasurementEventCallback<float>(OnMeasurementRecorded);
+		m_meterListener.Start();
+	}
+
+	public void Dispose()
+	{
+		Dispose(disposing: true);
+		GC.SuppressFinalize(this);
+	}
+
+	protected virtual void Dispose(bool disposing)
+	{
+		if (disposing)
+		{
+			m_meterListener.Dispose();
+			Server.Stop();
+		}
+	}
+
+	protected string? PoolName { get; set; }
+	protected FakeMySqlServer Server { get; }
+
+	protected MySqlConnectionStringBuilder CreateConnectionStringBuilder() =>
+		new MySqlConnectionStringBuilder
+		{
+			Server = "localhost",
+			Port = (uint) Server.Port,
+			UserID = "test",
+			Password = "test",
+		};
+
+	protected void AssertMeasurement(string name, int expected)
+	{
+		lock (m_measurements)
+			Assert.Equal(expected, m_measurements.GetValueOrDefault(name));
+	}
+
+	protected List<float> GetAndClearMeasurements(string name)
+	{
+		if (!m_timeMeasurements.TryGetValue(name, out var list))
+			list = new List<float>();
+		m_timeMeasurements[name] = new List<float>();
+		return list;
+	}
+
+	private void OnMeasurementRecorded(Instrument instrument, int measurement, ReadOnlySpan<KeyValuePair<string, object?>> tags, object? state)
+	{
+		var (poolName, stateTag) = GetTags(tags);
+		if (poolName != PoolName)
+			return;
+
+		lock (m_measurements)
+		{
+			m_measurements[instrument.Name] = m_measurements.GetValueOrDefault(instrument.Name) + measurement;
+			if (stateTag.Length != 0)
+				m_measurements[$"{instrument.Name}|{stateTag}"] = m_measurements.GetValueOrDefault($"{instrument.Name}|{stateTag}") + measurement;
+		}
+	}
+
+	private void OnMeasurementRecorded(Instrument instrument, float measurement, ReadOnlySpan<KeyValuePair<string, object?>> tags, object? state)
+	{
+		var (poolName, stateTag) = GetTags(tags);
+		if (poolName != PoolName)
+			return;
+
+		lock (m_timeMeasurements)
+		{
+			if (!m_timeMeasurements.TryGetValue(instrument.Name, out var list))
+				list = m_timeMeasurements[instrument.Name] = new List<float>();
+			list.Add(measurement);
+		}
+	}
+
+	private (string PoolName, string State) GetTags(ReadOnlySpan<KeyValuePair<string, object?>> tags)
+	{
+		var poolName = "";
+		var state = "";
+		foreach (var tag in tags)
+		{
+			if (tag.Key == "pool.name" && tag.Value is string s1)
+				poolName = s1;
+			else if (tag.Key == "state" && tag.Value is string s2)
+				state = s2;
+		}
+		return (poolName, state);
+	}
+
+
+	private readonly Dictionary<string, int> m_measurements;
+	private readonly Dictionary<string, List<float>> m_timeMeasurements;
+	private readonly MeterListener m_meterListener;
+}
diff --git a/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs b/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs
new file mode 100644
index 000000000..3ec3abe31
--- /dev/null
+++ b/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs
@@ -0,0 +1,105 @@
+#nullable enable
+
+namespace MySqlConnector.Tests.Metrics;
+
+public class MinMaxConnectionsTests : MetricsTestsBase
+{
+	[Fact]
+	public void SetsMinimumIdleToDefault()
+	{
+		var csb = CreateConnectionStringBuilder();
+
+		PoolName = "min-idle";
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UseName(PoolName)
+			.Build();
+		using var connection = dataSource.OpenConnection();
+
+		AssertMeasurement("db.client.connections.idle.min", 0);
+	}
+
+	[Fact]
+	public void SetsMaximumIdleToDefault()
+	{
+		var csb = CreateConnectionStringBuilder();
+
+		PoolName = "max-idle";
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UseName(PoolName)
+			.Build();
+		using var connection = dataSource.OpenConnection();
+
+		AssertMeasurement("db.client.connections.idle.max", 100);
+	}
+
+	[Fact]
+	public void SetsMaximumToDefault()
+	{
+		var csb = CreateConnectionStringBuilder();
+
+		PoolName = "max";
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UseName(PoolName)
+			.Build();
+		using var connection = dataSource.OpenConnection();
+
+		AssertMeasurement("db.client.connections.max", 100);
+	}
+
+	[Fact]
+	public void SetsMinimumIdleToCustom()
+	{
+		var csb = CreateConnectionStringBuilder();
+		csb.MinimumPoolSize = 3;
+
+		PoolName = "min-idle";
+		using (var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UseName(PoolName)
+			.Build())
+		{
+			using var connection = dataSource.OpenConnection();
+
+			AssertMeasurement("db.client.connections.idle.min", 3);
+		}
+
+		AssertMeasurement("db.client.connections.idle.min", 0);
+	}
+
+	[Fact]
+	public void SetsMaximumIdleToCustom()
+	{
+		var csb = CreateConnectionStringBuilder();
+		csb.MaximumPoolSize = 57;
+
+		PoolName = "max-idle";
+		using (var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UseName(PoolName)
+			.Build())
+		{
+			using var connection = dataSource.OpenConnection();
+
+			AssertMeasurement("db.client.connections.idle.max", 57);
+		}
+
+		AssertMeasurement("db.client.connections.idle.max", 0);
+	}
+
+	[Fact]
+	public void SetsMaximumToCustom()
+	{
+		var csb = CreateConnectionStringBuilder();
+		csb.MaximumPoolSize = 99;
+
+		PoolName = "max";
+		using (var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UseName(PoolName)
+			.Build())
+		{
+			using var connection = dataSource.OpenConnection();
+
+			AssertMeasurement("db.client.connections.max", 99);
+		}
+
+		AssertMeasurement("db.client.connections.max", 0);
+	}
+}
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 419961696..c8c37e604 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -44,6 +44,7 @@
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
     <PackageReference Include="MySql.Data" />
     <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ColumnReaderTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
+    <Compile Remove="Metrics\*.cs" />
     <Using Include="MySql.Data.MySqlClient" />
     <Using Include="MySql.Data.Types" />
   </ItemGroup>

From 2f316002811f017a0b701fec529ccba1e062f7a8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 28 Oct 2023 19:57:09 -0700
Subject: [PATCH 189/393] Disable all the metrics tests.

The FakeMySqlServer setup fails under Appveyor and Azure Pipelines.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs    | 2 ++
 tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs  | 2 ++
 tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs | 2 ++
 3 files changed, 6 insertions(+)

diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
index c6c5dd500..1a250442e 100644
--- a/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
@@ -1,3 +1,4 @@
+#if METRICS_TESTS
 namespace MySqlConnector.Tests.Metrics;
 
 public class ConnectionTimeTests : MetricsTestsBase
@@ -90,3 +91,4 @@ public async Task UseTimeWithDelay()
 		Assert.InRange(time, 500, 600);
 	}
 }
+#endif
diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
index 45646ff2a..258c15858 100644
--- a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
@@ -1,3 +1,4 @@
+#if METRICS_TESTS
 #nullable enable
 
 namespace MySqlConnector.Tests.Metrics;
@@ -243,3 +244,4 @@ public async Task PendingRequestForOpenFromPool()
 		AssertMeasurement("db.client.connections.pending_requests", 0);
 	}
 }
+#endif
diff --git a/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs b/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs
index 3ec3abe31..246b06305 100644
--- a/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs
@@ -1,3 +1,4 @@
+#if METRICS_TESTS
 #nullable enable
 
 namespace MySqlConnector.Tests.Metrics;
@@ -103,3 +104,4 @@ public void SetsMaximumToCustom()
 		AssertMeasurement("db.client.connections.max", 0);
 	}
 }
+#endif

From 2a058ea39a1384b2d3f30c3de386b344e3ac0f7a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 28 Oct 2023 20:17:10 -0700
Subject: [PATCH 190/393] Move metrics to MetricsReporter class.

Make min/max connections metrics an ObservableUpDownCounter, as they are effectively static metrics for a pool.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ConnectionPool.cs     | 69 ++++++++-----------
 src/MySqlConnector/Core/MetricsReporter.cs    | 57 +++++++++++++++
 src/MySqlConnector/Core/ServerSession.cs      |  4 +-
 src/MySqlConnector/MySqlConnection.cs         |  4 +-
 .../Metrics/MetricsTestsBase.cs               |  8 +++
 5 files changed, 96 insertions(+), 46 deletions(-)
 create mode 100644 src/MySqlConnector/Core/MetricsReporter.cs

diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index bf020186b..674a01300 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -20,8 +20,6 @@ internal sealed class ConnectionPool : IDisposable
 
 	public SslProtocols SslProtocols { get; set; }
 
-	public void AddPendingRequestCount(int delta) => s_pendingRequestsCounter.Add(delta, PoolNameTagList);
-
 	public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection, int startTickCount, int timeoutMilliseconds, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		cancellationToken.ThrowIfCancellationRequested();
@@ -53,14 +51,14 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			{
 				if (m_sessions.Count > 0)
 				{
-					// NOTE: s_connectionsUsageCounter updated outside lock below
+					// NOTE: MetricsReporter updated outside lock below
 					session = m_sessions.First!.Value;
 					m_sessions.RemoveFirst();
 				}
 			}
 			if (session is not null)
 			{
-				s_connectionsUsageCounter.Add(-1, IdleStateTagList);
+				MetricsReporter.RemoveIdle(this);
 				Log.FoundExistingSession(m_logger, Id);
 				bool reuseSession;
 
@@ -101,12 +99,12 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 						m_leasedSessions.Add(session.Id, session);
 						leasedSessionsCountPooled = m_leasedSessions.Count;
 					}
-					s_connectionsUsageCounter.Add(1, UsedStateTagList);
+					MetricsReporter.AddUsed(this);
 					ActivitySourceHelper.CopyTags(session.ActivityTags, activity);
 					Log.ReturningPooledSession(m_logger, Id, session.Id, leasedSessionsCountPooled);
 
 					session.LastLeasedTicks = unchecked((uint) Environment.TickCount);
-					s_waitTimeHistory.Record(unchecked(session.LastLeasedTicks - (uint) startTickCount), PoolNameTagList);
+					MetricsReporter.RecordWaitTime(this, unchecked(session.LastLeasedTicks - (uint) startTickCount));
 					return session;
 				}
 			}
@@ -121,11 +119,11 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 				m_leasedSessions.Add(session.Id, session);
 				leasedSessionsCountNew = m_leasedSessions.Count;
 			}
-			s_connectionsUsageCounter.Add(1, UsedStateTagList);
+			MetricsReporter.AddUsed(this);
 			Log.ReturningNewSession(m_logger, Id, session.Id, leasedSessionsCountNew);
 
 			session.LastLeasedTicks = unchecked((uint) Environment.TickCount);
-			s_createTimeHistory.Record(unchecked(session.LastLeasedTicks - (uint) startTickCount), PoolNameTagList);
+			MetricsReporter.RecordCreateTime(this, unchecked(session.LastLeasedTicks - (uint) startTickCount));
 			return session;
 		}
 		catch (Exception ex)
@@ -177,14 +175,14 @@ public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 		{
 			lock (m_leasedSessions)
 				m_leasedSessions.Remove(session.Id);
-			s_connectionsUsageCounter.Add(-1, UsedStateTagList);
+			MetricsReporter.RemoveUsed(this);
 			session.OwningConnection = null;
 			var sessionHealth = GetSessionHealth(session);
 			if (sessionHealth == 0)
 			{
 				lock (m_sessions)
 					m_sessions.AddFirst(session);
-				s_connectionsUsageCounter.Add(1, IdleStateTagList);
+				MetricsReporter.AddIdle(this);
 			}
 			else
 			{
@@ -239,6 +237,8 @@ public async Task ReapAsync(IOBehavior ioBehavior, CancellationToken cancellatio
 	public void Dispose()
 	{
 		Log.DisposingConnectionPool(m_logger, Id);
+		lock (s_allPools)
+			s_allPools.Remove(this);
 #if NET6_0_OR_GREATER
 		m_dnsCheckTimer?.Dispose();
 		m_dnsCheckTimer = null;
@@ -258,10 +258,6 @@ public void Dispose()
 			reaperWaitHandle.WaitOne();
 		}
 #endif
-
-		s_minIdleConnectionsCounter.Add(-ConnectionSettings.MinimumPoolSize, PoolNameTagList);
-		s_maxIdleConnectionsCounter.Add(-ConnectionSettings.MaximumPoolSize, PoolNameTagList);
-		s_maxConnectionsCounter.Add(-ConnectionSettings.MaximumPoolSize, PoolNameTagList);
 	}
 
 	/// <summary>
@@ -345,14 +341,14 @@ private async Task CleanPoolAsync(IOBehavior ioBehavior, Func<ServerSession, boo
 					{
 						if (m_sessions.Count > 0)
 						{
-							// NOTE: s_connectionsUsageCounter updated outside lock below
+							// NOTE: MetricsReporter updated outside lock below
 							session = m_sessions.Last!.Value;
 							m_sessions.RemoveLast();
 						}
 					}
 					if (session is null)
 						return;
-					s_connectionsUsageCounter.Add(-1, IdleStateTagList);
+					MetricsReporter.RemoveIdle(this);
 
 					if (shouldCleanFn(session))
 					{
@@ -365,7 +361,7 @@ private async Task CleanPoolAsync(IOBehavior ioBehavior, Func<ServerSession, boo
 						// session should not be cleaned; put it back in the queue and stop iterating
 						lock (m_sessions)
 							m_sessions.AddLast(session);
-						s_connectionsUsageCounter.Add(1, IdleStateTagList);
+						MetricsReporter.AddIdle(this);
 						return;
 					}
 				}
@@ -411,7 +407,7 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh
 				AdjustHostConnectionCount(session, 1);
 				lock (m_sessions)
 					m_sessions.AddFirst(session);
-				s_connectionsUsageCounter.Add(1, IdleStateTagList);
+				MetricsReporter.AddIdle(this);
 			}
 			finally
 			{
@@ -569,6 +565,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 		else if (pool != newPool)
 		{
 			Log.CreatedPoolWillNotBeUsed(newPool.m_logger, newPool.Id);
+			newPool.Dispose();
 		}
 
 		return pool;
@@ -576,10 +573,10 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 
 	public static async Task ClearPoolsAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		foreach (var pool in GetAllPools())
+		foreach (var pool in GetCachedPools())
 			await pool.ClearAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
-		static List<ConnectionPool> GetAllPools()
+		static List<ConnectionPool> GetCachedPools()
 		{
 			var pools = new List<ConnectionPool>(s_pools.Count);
 			var uniquePools = new HashSet<ConnectionPool>();
@@ -626,12 +623,9 @@ private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration,
 			new("state", "used"),
 		];
 
-		// set pool size counters
-		s_minIdleConnectionsCounter.Add(ConnectionSettings.MinimumPoolSize, PoolNameTagList);
-		s_maxIdleConnectionsCounter.Add(ConnectionSettings.MaximumPoolSize, PoolNameTagList);
-		s_maxConnectionsCounter.Add(ConnectionSettings.MaximumPoolSize, PoolNameTagList);
-
 		Id = Interlocked.Increment(ref s_poolId);
+		lock (s_allPools)
+			s_allPools.Add(this);
 		Log.CreatingNewConnectionPool(m_logger, Id, connectionString);
 	}
 
@@ -779,12 +773,18 @@ private void AdjustHostConnectionCount(ServerSession session, int delta)
 	}
 
 	// Provides a slice of m_stateTagList that contains either the 'idle' or 'used' state tag along with the pool name.
-	private ReadOnlySpan<KeyValuePair<string, object?>> IdleStateTagList => m_stateTagList.AsSpan(0, 2);
-	private ReadOnlySpan<KeyValuePair<string, object?>> UsedStateTagList => m_stateTagList.AsSpan(1, 2);
+	public ReadOnlySpan<KeyValuePair<string, object?>> IdleStateTagList => m_stateTagList.AsSpan(0, 2);
+	public ReadOnlySpan<KeyValuePair<string, object?>> UsedStateTagList => m_stateTagList.AsSpan(1, 2);
 
 	// A slice of m_stateTagList that contains only the pool name tag.
 	public ReadOnlySpan<KeyValuePair<string, object?>> PoolNameTagList => m_stateTagList.AsSpan(1, 1);
 
+	public static List<ConnectionPool> GetAllPools()
+	{
+		lock (s_allPools)
+			return new(s_allPools);
+	}
+
 	private sealed class LeastConnectionsLoadBalancer(Dictionary<string, int> hostSessions) : ILoadBalancer
 	{
 		public IReadOnlyList<string> LoadBalance(IReadOnlyList<string> hosts)
@@ -809,21 +809,8 @@ static ConnectionPool()
 	private static void OnAppDomainShutDown(object? sender, EventArgs e) =>
 		ClearPoolsAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 
-	private static readonly UpDownCounter<int> s_connectionsUsageCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.usage",
-		unit: "{connection}", description: "The number of connections that are currently in the state described by the state tag.");
-	private static readonly UpDownCounter<int> s_maxIdleConnectionsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.idle.max",
-		unit: "{connection}", description: "The maximum number of idle open connections allowed.");
-	private static readonly UpDownCounter<int> s_minIdleConnectionsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.idle.min",
-		unit: "{connection}", description: "The minimum number of idle open connections allowed.");
-	private static readonly UpDownCounter<int> s_maxConnectionsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.max",
-		unit: "{connection}", description: "The maximum number of open connections allowed.");
-	private static readonly UpDownCounter<int> s_pendingRequestsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.pending_requests",
-		unit: "{request}", description: "The number of pending requests for an open connection, cumulative for the entire pool.");
-	private static readonly Histogram<float> s_createTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.create_time",
-		unit: "ms", description: "The time it took to create a new connection.");
-	private static readonly Histogram<float> s_waitTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.wait_time",
-		unit: "ms", description: "The time it took to obtain an open connection from the pool.");
 	private static readonly ConcurrentDictionary<string, ConnectionPool?> s_pools = new();
+	private static readonly List<ConnectionPool> s_allPools = new();
 	private static readonly Action<ILogger, int, string, Exception?> s_createdNewSession = LoggerMessage.Define<int, string>(
 		LogLevel.Debug, new EventId(EventIds.PoolCreatedNewSession, nameof(EventIds.PoolCreatedNewSession)),
 		"Pool {PoolId} has no pooled session available; created new session {SessionId}");
diff --git a/src/MySqlConnector/Core/MetricsReporter.cs b/src/MySqlConnector/Core/MetricsReporter.cs
new file mode 100644
index 000000000..0336b0f5c
--- /dev/null
+++ b/src/MySqlConnector/Core/MetricsReporter.cs
@@ -0,0 +1,57 @@
+using System.Diagnostics.Metrics;
+using MySqlConnector.Utilities;
+
+namespace MySqlConnector.Core;
+
+internal static class MetricsReporter
+{
+	public static void AddIdle(ConnectionPool pool) => s_connectionsUsageCounter.Add(1, pool.IdleStateTagList);
+	public static void RemoveIdle(ConnectionPool pool) => s_connectionsUsageCounter.Add(-1, pool.IdleStateTagList);
+	public static void AddUsed(ConnectionPool pool) => s_connectionsUsageCounter.Add(1, pool.UsedStateTagList);
+	public static void RemoveUsed(ConnectionPool pool) => s_connectionsUsageCounter.Add(-1, pool.UsedStateTagList);
+	public static void RecordCreateTime(ConnectionPool pool, uint ticks) => s_createTimeHistory.Record(ticks, pool.PoolNameTagList);
+	public static void RecordUseTime(ConnectionPool pool, uint ticks) => s_useTimeHistory.Record(ticks, pool.PoolNameTagList);
+	public static void RecordWaitTime(ConnectionPool pool, uint ticks) => s_waitTimeHistory.Record(ticks, pool.PoolNameTagList);
+
+	public static void AddPendingRequest(ConnectionPool? pool)
+	{
+		if (pool is not null)
+			s_pendingRequestsCounter.Add(1, pool.PoolNameTagList);
+	}
+
+	public static void RemovePendingRequest(ConnectionPool? pool)
+	{
+		if (pool is not null)
+			s_pendingRequestsCounter.Add(-1, pool.PoolNameTagList);
+	}
+
+	static MetricsReporter()
+	{
+		ActivitySourceHelper.Meter.CreateObservableUpDownCounter<int>("db.client.connections.idle.max",
+			observeValues: GetMaximumConnections, unit: "{connection}",
+			description: "The maximum number of idle open connections allowed; this corresponds to MaximumPoolSize in the connection string.");
+		ActivitySourceHelper.Meter.CreateObservableUpDownCounter<int>("db.client.connections.idle.min",
+			observeValues: GetMinimumConnections, unit: "{connection}",
+			description: "The minimum number of idle open connections allowed; this corresponds to MinimumPoolSize in the connection string.");
+		ActivitySourceHelper.Meter.CreateObservableUpDownCounter<int>("db.client.connections.max",
+			observeValues: GetMaximumConnections, unit: "{connection}",
+			description: "The maximum number of open connections allowed; this corresponds to MaximumPoolSize in the connection string.");
+
+		static IEnumerable<Measurement<int>> GetMaximumConnections() =>
+			ConnectionPool.GetAllPools().Select(x => new Measurement<int>(x.ConnectionSettings.MaximumPoolSize, x.PoolNameTagList));
+
+		static IEnumerable<Measurement<int>> GetMinimumConnections() =>
+			ConnectionPool.GetAllPools().Select(x => new Measurement<int>(x.ConnectionSettings.MinimumPoolSize, x.PoolNameTagList));
+	}
+
+	private static readonly UpDownCounter<int> s_connectionsUsageCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.usage",
+			unit: "{connection}", description: "The number of connections that are currently in the state described by the state tag.");
+	private static readonly UpDownCounter<int> s_pendingRequestsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.pending_requests",
+			unit: "{request}", description: "The number of pending requests for an open connection, cumulative for the entire pool.");
+	private static readonly Histogram<float> s_createTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.create_time",
+			unit: "ms", description: "The time it took to create a new connection.");
+	private static readonly Histogram<float> s_useTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.use_time",
+		unit: "ms", description: "The time between borrowing a connection and returning it to the pool.");
+	private static readonly Histogram<float> s_waitTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.wait_time",
+			unit: "ms", description: "The time it took to obtain an open connection from the pool.");
+}
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 1e8788e86..9e08c3003 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -79,7 +79,7 @@ public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? ownin
 		LastReturnedTicks = unchecked((uint) Environment.TickCount);
 		if (Pool is null)
 			return default;
-		s_useTimeHistory.Record(unchecked(LastReturnedTicks - LastLeasedTicks), Pool.PoolNameTagList);
+		MetricsReporter.RecordUseTime(Pool, unchecked(LastReturnedTicks - LastLeasedTicks));
 		LastLeasedTicks = 0;
 		return Pool.ReturnAsync(ioBehavior, this);
 	}
@@ -1916,8 +1916,6 @@ protected override void OnStatementBegin(int index)
 	[LoggerMessage(EventIds.ExpectedSessionState6, LogLevel.Error, "Session {SessionId} should have state {ExpectedState1} or {ExpectedState2} or {ExpectedState3} or {ExpectedState4} or {ExpectedState5} or {ExpectedState6} but was {SessionState}")]
 	private static partial void ExpectedSessionState6(ILogger logger, string sessionId, State expectedState1, State expectedState2, State expectedState3, State expectedState4, State expectedState5, State expectedState6, State sessionState);
 
-	private static readonly Histogram<float> s_useTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.use_time",
-		unit: "ms", description: "The time between borrowing a connection and returning it to the pool.");
 	private static readonly PayloadData s_setNamesUtf8NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8;"u8);
 	private static readonly PayloadData s_setNamesUtf8mb4NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8mb4;"u8);
 	private static readonly PayloadData s_setNamesUtf8WithAttributesPayload = QueryPayload.Create(true, "SET NAMES utf8;"u8);
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 81d13a900..dfe3a0c8b 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -896,7 +896,7 @@ internal void FinishQuerying(bool hasWarnings)
 
 	private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool, int startTickCount, Activity? activity, IOBehavior? ioBehavior, CancellationToken cancellationToken)
 	{
-		pool?.AddPendingRequestCount(1);
+		MetricsReporter.AddPendingRequest(pool);
 		var connectionSettings = GetInitializedConnectionSettings();
 		var actualIOBehavior = ioBehavior ?? (connectionSettings.ForceSynchronous ? IOBehavior.Synchronous : IOBehavior.Asynchronous);
 
@@ -950,7 +950,7 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 		}
 		finally
 		{
-			pool?.AddPendingRequestCount(-1);
+			MetricsReporter.RemovePendingRequest(pool);
 			linkedSource?.Dispose();
 			timeoutSource?.Dispose();
 		}
diff --git a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
index 3d2549e1e..7e4291124 100644
--- a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
+++ b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
@@ -56,6 +56,14 @@ protected MySqlConnectionStringBuilder CreateConnectionStringBuilder() =>
 
 	protected void AssertMeasurement(string name, int expected)
 	{
+		// clear cached measurements from observable counters
+		lock (m_measurements)
+		{
+			m_measurements.Remove("db.client.connections.idle.max");
+			m_measurements.Remove("db.client.connections.idle.min");
+			m_measurements.Remove("db.client.connections.max");
+		}
+		m_meterListener.RecordObservableInstruments();
 		lock (m_measurements)
 			Assert.Equal(expected, m_measurements.GetValueOrDefault(name));
 	}

From 9630b0802fb14b4f3e1a6fa3502141b91b72c2a5 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 29 Oct 2023 08:16:07 -0700
Subject: [PATCH 191/393] Improve metrics test skipping for CI.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Metrics/ConnectionTimeTests.cs                 | 14 ++++++--------
 .../Metrics/ConnectionsUsageTests.cs               | 14 ++++++--------
 .../Metrics/MetricsTestsBase.cs                    |  6 ++++++
 .../Metrics/MinMaxConnectionsTests.cs              | 14 ++++++--------
 .../MySqlConnector.Tests.csproj                    |  6 +++++-
 5 files changed, 29 insertions(+), 25 deletions(-)

diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
index 1a250442e..98156fe21 100644
--- a/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
@@ -1,9 +1,8 @@
-#if METRICS_TESTS
 namespace MySqlConnector.Tests.Metrics;
 
 public class ConnectionTimeTests : MetricsTestsBase
 {
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public async Task ConnectionTime()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -15,7 +14,7 @@ public async Task ConnectionTime()
 		Assert.InRange(time, 0, 300);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public async Task ConnectionTimeWithDelay()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -29,7 +28,7 @@ public async Task ConnectionTimeWithDelay()
 		Assert.InRange(time, 1000, 1300);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public async Task OpenFromPoolTime()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -45,7 +44,7 @@ public async Task OpenFromPoolTime()
 		Assert.InRange(time, 0, 200);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public async Task OpenFromPoolTimeWithDelay()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -62,7 +61,7 @@ public async Task OpenFromPoolTimeWithDelay()
 		Assert.InRange(time, 1000, 1200);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public async Task UseTime()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -76,7 +75,7 @@ public async Task UseTime()
 		Assert.InRange(time, 0, 100);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public async Task UseTimeWithDelay()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -91,4 +90,3 @@ public async Task UseTimeWithDelay()
 		Assert.InRange(time, 500, 600);
 	}
 }
-#endif
diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
index 258c15858..09e9440e8 100644
--- a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
@@ -1,11 +1,10 @@
-#if METRICS_TESTS
 #nullable enable
 
 namespace MySqlConnector.Tests.Metrics;
 
 public class ConnectionsUsageTests : MetricsTestsBase
 {
-    [Fact]
+    [Fact(Skip = MetricsSkip)]
 	public void NamedDataSource()
     {
 		PoolName = "metrics-test";
@@ -59,7 +58,7 @@ public void NamedDataSource()
 		Assert.Equal(2, Server.ActiveConnections);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public void NamedDataSourceWithMinPoolSize()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -92,7 +91,7 @@ public void NamedDataSourceWithMinPoolSize()
 		Assert.Equal(3, Server.ActiveConnections);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public void UnnamedDataSource()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -149,7 +148,7 @@ public void UnnamedDataSource()
 		Assert.Equal(2, Server.ActiveConnections);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public void NoDataSource()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -207,7 +206,7 @@ public void NoDataSource()
 		Assert.Equal(2, Server.ActiveConnections);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public async Task PendingRequestForCreation()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -224,7 +223,7 @@ public async Task PendingRequestForCreation()
 		AssertMeasurement("db.client.connections.pending_requests", 0);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public async Task PendingRequestForOpenFromPool()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -244,4 +243,3 @@ public async Task PendingRequestForOpenFromPool()
 		AssertMeasurement("db.client.connections.pending_requests", 0);
 	}
 }
-#endif
diff --git a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
index 7e4291124..6751b7477 100644
--- a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
+++ b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
@@ -76,6 +76,12 @@ protected List<float> GetAndClearMeasurements(string name)
 		return list;
 	}
 
+#if NO_METRICS_TESTS
+	protected const string MetricsSkip = "Metrics tests are skipped";
+#else
+	protected const string MetricsSkip = null;
+#endif
+
 	private void OnMeasurementRecorded(Instrument instrument, int measurement, ReadOnlySpan<KeyValuePair<string, object?>> tags, object? state)
 	{
 		var (poolName, stateTag) = GetTags(tags);
diff --git a/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs b/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs
index 246b06305..a5a4e658a 100644
--- a/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/MinMaxConnectionsTests.cs
@@ -1,11 +1,10 @@
-#if METRICS_TESTS
 #nullable enable
 
 namespace MySqlConnector.Tests.Metrics;
 
 public class MinMaxConnectionsTests : MetricsTestsBase
 {
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public void SetsMinimumIdleToDefault()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -19,7 +18,7 @@ public void SetsMinimumIdleToDefault()
 		AssertMeasurement("db.client.connections.idle.min", 0);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public void SetsMaximumIdleToDefault()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -33,7 +32,7 @@ public void SetsMaximumIdleToDefault()
 		AssertMeasurement("db.client.connections.idle.max", 100);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public void SetsMaximumToDefault()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -47,7 +46,7 @@ public void SetsMaximumToDefault()
 		AssertMeasurement("db.client.connections.max", 100);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public void SetsMinimumIdleToCustom()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -66,7 +65,7 @@ public void SetsMinimumIdleToCustom()
 		AssertMeasurement("db.client.connections.idle.min", 0);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public void SetsMaximumIdleToCustom()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -85,7 +84,7 @@ public void SetsMaximumIdleToCustom()
 		AssertMeasurement("db.client.connections.idle.max", 0);
 	}
 
-	[Fact]
+	[Fact(Skip = MetricsSkip)]
 	public void SetsMaximumToCustom()
 	{
 		var csb = CreateConnectionStringBuilder();
@@ -104,4 +103,3 @@ public void SetsMaximumToCustom()
 		AssertMeasurement("db.client.connections.max", 0);
 	}
 }
-#endif
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index c8c37e604..473c72caa 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -6,7 +6,7 @@
 
  <PropertyGroup Condition=" '$(Configuration)' == 'MySqlData' ">
     <TargetFrameworks>net472</TargetFrameworks>
-    <DefineConstants>MYSQL_DATA</DefineConstants>
+    <DefineConstants>$(DefineConstants);MYSQL_DATA</DefineConstants>
     <NoWarn>$(NoWarn);MSB3246</NoWarn>
   </PropertyGroup>
 
@@ -18,6 +18,10 @@
     <LangVersion>11.0</LangVersion>
   </PropertyGroup>
 
+  <PropertyGroup Condition=" '$(GITHUB_ACTIONS)' == 'true' OR '$(APPVEYOR)' == 'True' OR '$(TF_BUILD)' == 'True' ">
+    <DefineConstants>$(DefineConstants);NO_METRICS_TESTS</DefineConstants>
+  </PropertyGroup>
+
   <ItemGroup>
     <PackageReference Include="xunit" />
     <PackageReference Include="xunit.runner.visualstudio">

From 63362c210e100112d1c3cfd4780614444b8b38f2 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 29 Oct 2023 12:44:57 -0700
Subject: [PATCH 192/393] Update CI to MariaDB 11.1.

This is the current STS release.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .ci/docker-run.sh    | 2 +-
 azure-pipelines.yml  | 4 ++--
 docs/content/home.md | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index d694c1c81..88bbe9504 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -31,7 +31,7 @@ fi
 if [[ "$IMAGE" == mariadb* ]]; then
   MYSQL_EXTRA='--in-predicate-conversion-threshold=100000'
 fi
-if [ "$IMAGE" == "mariadb:11.0" ]; then
+if [ "$IMAGE" == "mariadb:11.1" ]; then
   MYSQL='mariadb'
 fi
 
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 43cda8c76..4049dbfe7 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -204,8 +204,8 @@ jobs:
         image: 'mariadb:10.11'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
-      'MariaDB 11.0':
-        image: 'mariadb:11.0'
+      'MariaDB 11.1':
+        image: 'mariadb:11.1'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
   steps:
diff --git a/docs/content/home.md b/docs/content/home.md
index 77d1a4852..f3c63f9f6 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -64,7 +64,7 @@ Server  | Versions | Notes
 Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://mysqlconnector.net/troubleshooting/aurora-freeze/)
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
-MariaDB | 10.x, 11.x (**10.6**, **10.11**, **11.0**) |
+MariaDB | 10.x, 11.x (**10.6**, **10.11**, **11.1**) |
 MySQL | 5.5, 5.6, **5.7**, **8.0**, **8.1** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 is EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)

From 5ad4cf3b09da78d9cbb28b4d01f104f3b49b20b0 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 29 Oct 2023 12:56:58 -0700
Subject: [PATCH 193/393] Remove COM_MULTI implementation. Fixes #946

The only server that supported this capability bit was MariaDB prior to version 10.6. Additionally, this code is never regularly tested because CI only runs against MariaDB 10.6 and later.
---
 .../Core/BatchedCommandPayloadCreator.cs      |  37 ------
 src/MySqlConnector/Core/ServerSession.cs      |   3 -
 src/MySqlConnector/MySqlBatch.cs              |   3 +-
 src/MySqlConnector/Protocol/CommandKind.cs    |   1 -
 .../Payloads/HandshakeResponse41Payload.cs    |   1 -
 tests/IntegrationTests/BatchTests.cs          |  23 ----
 tests/IntegrationTests/CancelTests.cs         | 112 ------------------
 7 files changed, 1 insertion(+), 179 deletions(-)
 delete mode 100644 src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs

diff --git a/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs b/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs
deleted file mode 100644
index c17c53335..000000000
--- a/src/MySqlConnector/Core/BatchedCommandPayloadCreator.cs
+++ /dev/null
@@ -1,37 +0,0 @@
-using System.Buffers.Binary;
-using MySqlConnector.Protocol;
-using MySqlConnector.Protocol.Serialization;
-
-namespace MySqlConnector.Core;
-
-internal sealed class BatchedCommandPayloadCreator : ICommandPayloadCreator
-{
-	public static ICommandPayloadCreator Instance { get; } = new BatchedCommandPayloadCreator();
-
-	public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDictionary<string, CachedProcedure?> cachedProcedures, ByteBufferWriter writer, bool appendSemicolon)
-	{
-		writer.Write((byte) CommandKind.Multi);
-		bool? firstResult = default;
-		bool wroteCommand;
-		ReadOnlySpan<byte> padding = [ 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
-		do
-		{
-			// save room for command length
-			var position = writer.Position;
-			writer.Write(padding);
-
-			wroteCommand = SingleCommandPayloadCreator.Instance.WriteQueryCommand(ref commandListPosition, cachedProcedures, writer, appendSemicolon);
-			firstResult ??= wroteCommand;
-
-			// write command length
-			var commandLength = writer.Position - position - padding.Length;
-			var span = writer.ArraySegment.AsSpan(position);
-			span[0] = 0xFE;
-			BinaryPrimitives.WriteUInt64LittleEndian(span[1..], (ulong) commandLength);
-		} while (wroteCommand);
-
-		// remove the padding that was saved for the final command (which wasn't written)
-		writer.TrimEnd(padding.Length);
-		return firstResult.Value;
-	}
-}
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 351eda129..9ba24fd8c 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -62,7 +62,6 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public IPEndPoint? IPEndPoint => m_tcpClient?.Client.RemoteEndPoint as IPEndPoint;
 	public string? UserID { get; private set; }
 	public WeakReference<MySqlConnection>? OwningConnection { get; set; }
-	public bool SupportsComMulti => m_supportsComMulti;
 	public bool SupportsDeprecateEof => m_supportsDeprecateEof;
 	public bool SupportsCachedPreparedMetadata { get; private set; }
 	public bool SupportsQueryAttributes { get; private set; }
@@ -485,7 +484,6 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 						activity.SetTag(ActivitySourceHelper.DatabaseConnectionIdTagName, connectionId);
 				}
 
-				m_supportsComMulti = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbComMulti) != 0;
 				m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
 				m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
 				SupportsCachedPreparedMetadata = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
@@ -1933,7 +1931,6 @@ protected override void OnStatementBegin(int index)
 	private IPayloadHandler? m_payloadHandler;
 	private bool m_useCompression;
 	private bool m_isSecureConnection;
-	private bool m_supportsComMulti;
 	private bool m_supportsConnectionAttributes;
 	private bool m_supportsDeprecateEof;
 	private bool m_supportsSessionTrack;
diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index 1aa0a1731..b3ed8d0cb 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -160,8 +160,7 @@ private ValueTask<MySqlDataReader> ExecuteReaderAsync(CommandBehavior behavior,
 		foreach (MySqlBatchCommand batchCommand in BatchCommands)
 			batchCommand.Batch = this;
 
-		var payloadCreator = Connection!.Session.SupportsComMulti ? BatchedCommandPayloadCreator.Instance :
-			IsPrepared ? SingleCommandPayloadCreator.Instance :
+		var payloadCreator = IsPrepared ? SingleCommandPayloadCreator.Instance :
 			ConcatenatedCommandPayloadCreator.Instance;
 		return CommandExecutor.ExecuteReaderAsync(new(BatchCommands!.Commands), payloadCreator, behavior, default, ioBehavior, cancellationToken);
 	}
diff --git a/src/MySqlConnector/Protocol/CommandKind.cs b/src/MySqlConnector/Protocol/CommandKind.cs
index 8b232707f..6e384a733 100644
--- a/src/MySqlConnector/Protocol/CommandKind.cs
+++ b/src/MySqlConnector/Protocol/CommandKind.cs
@@ -10,5 +10,4 @@ internal enum CommandKind
 	StatementPrepare = 22,
 	StatementExecute = 23,
 	ResetConnection = 31,
-	Multi = 254,
 }
diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
index cc8dce3af..bd048f0ee 100644
--- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
@@ -28,7 +28,6 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 			ProtocolCapabilities.SessionTrack |
 			ProtocolCapabilities.DeprecateEof |
 			ProtocolCapabilities.QueryAttributes |
-			ProtocolCapabilities.MariaDbComMulti |
 			ProtocolCapabilities.MariaDbCacheMetadata |
 			additionalCapabilities) & serverCapabilities;
 
diff --git a/tests/IntegrationTests/BatchTests.cs b/tests/IntegrationTests/BatchTests.cs
index 04b5bb17c..dc0b5a947 100644
--- a/tests/IntegrationTests/BatchTests.cs
+++ b/tests/IntegrationTests/BatchTests.cs
@@ -223,29 +223,6 @@ public void ExecuteBatch(string suffix)
 		Assert.Equal(16, total);
 	}
 
-	[Fact(Skip = "COM_MULTI")]
-	public void ExecuteInvalidSqlBatch()
-	{
-		using var connection = new MySqlConnection(AppConfig.ConnectionString);
-		connection.Open();
-		using var batch = new MySqlBatch(connection)
-		{
-			BatchCommands =
-			{
-				new MySqlBatchCommand("SELECT 1;"),
-				new MySqlBatchCommand("SELECT 2 /* incomplete"),
-				new MySqlBatchCommand("SELECT 3;"),
-			},
-		};
-		using var reader = batch.ExecuteReader();
-		Assert.True(reader.Read());
-		Assert.Equal(1, reader.GetInt32(0));
-		Assert.False(reader.Read());
-
-		var ex = Assert.Throws<MySqlException>(() => reader.NextResult());
-		Assert.Equal(MySqlErrorCode.ParseError, ex.ErrorCode);
-	}
-
 	[Theory]
 	[InlineData(false)]
 	[InlineData(true)]
diff --git a/tests/IntegrationTests/CancelTests.cs b/tests/IntegrationTests/CancelTests.cs
index 6c37a310d..28f9d4e61 100644
--- a/tests/IntegrationTests/CancelTests.cs
+++ b/tests/IntegrationTests/CancelTests.cs
@@ -519,50 +519,6 @@ public void CancelBatchBeforeRead()
 		Assert.InRange(rows, 0, 10000000);
 	}
 
-	[SkippableFact(ServerFeatures.StreamingResults | ServerFeatures.Timeout, Skip = "COM_MULTI")]
-	public void CancelMultiCommandBatchReader()
-	{
-		using var barrier = new Barrier(2);
-		using var batch = new MySqlBatch(m_database.Connection)
-		{
-			BatchCommands =
-			{
-				new MySqlBatchCommand(c_hugeQuery),
-				new MySqlBatchCommand(c_hugeQuery),
-				new MySqlBatchCommand(c_hugeQuery),
-			},
-		};
-		var task = Task.Run(() =>
-		{
-			barrier.SignalAndWait();
-			batch.Cancel();
-		});
-
-		int rows = 0;
-		using (var reader = batch.ExecuteReader())
-		{
-			Assert.True(reader.Read());
-
-			barrier.SignalAndWait();
-			try
-			{
-				while (reader.Read())
-					rows++;
-			}
-			catch (MySqlException ex)
-			{
-				Assert.Equal(MySqlErrorCode.QueryInterrupted, ex.ErrorCode);
-			}
-
-			// query returns 25 billion rows; we shouldn't have read many of them
-			Assert.InRange(rows, 0, 10000000);
-
-			Assert.False(reader.NextResult());
-		}
-
-		task.Wait(); // shouldn't throw
-	}
-
 	[Fact]
 	public async Task CancelBatchWithTokenBeforeExecuteScalar()
 	{
@@ -688,41 +644,6 @@ public async Task CancelHugeQueryBatchWithTokenAfterExecuteReader()
 		Assert.False(reader.NextResult());
 	}
 
-	[SkippableFact(ServerFeatures.StreamingResults | ServerFeatures.Timeout, Skip = "COM_MULTI")]
-	public async Task CancelHugeQueryBatchWithTokenInNextResult()
-	{
-		using var batch = new MySqlBatch(m_database.Connection)
-		{
-			BatchCommands =
-			{
-				new MySqlBatchCommand(c_hugeQuery),
-				new MySqlBatchCommand("select 1, 2, 3;"),
-			},
-		};
-		using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(0.5));
-		using var reader = await batch.ExecuteReaderAsync(cts.Token);
-
-		// read first result set
-		Assert.True(await reader.ReadAsync(cts.Token));
-
-		try
-		{
-			// skip to the next result set
-			Assert.True(await reader.NextResultAsync(cts.Token));
-
-			// shouldn't get here
-			Assert.True(false);
-		}
-		catch (OperationCanceledException ex)
-		{
-			Assert.Equal(cts.Token, ex.CancellationToken);
-		}
-
-		// no more result sets
-		Assert.False(reader.Read());
-		Assert.False(reader.NextResult());
-	}
-
 	[SkippableFact(ServerFeatures.Timeout)]
 	public async Task CancelSlowQueryBatchWithTokenAfterExecuteReader()
 	{
@@ -796,39 +717,6 @@ public async Task CancelSlowQueryBatchWithTokenAfterNextResult()
 
 		Assert.False(await reader.NextResultAsync());
 	}
-
-	[SkippableFact(ServerFeatures.StreamingResults | ServerFeatures.Timeout, Skip = "COM_MULTI")]
-	public async Task CancelMultiStatementBatchInRead()
-	{
-		using var batch = new MySqlBatch(m_database.Connection)
-		{
-			BatchCommands =
-			{
-				new MySqlBatchCommand(c_hugeQuery),
-				new MySqlBatchCommand(c_hugeQuery),
-				new MySqlBatchCommand(c_hugeQuery),
-			},
-		};
-		using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(0.5));
-		using var reader = await batch.ExecuteReaderAsync();
-		var rows = 0;
-		try
-		{
-			while (await reader.ReadAsync(cts.Token))
-				rows++;
-
-			Assert.True(false);
-		}
-		catch (OperationCanceledException ex)
-		{
-			Assert.Equal(cts.Token, ex.CancellationToken);
-			Assert.InRange(rows, 0, 10000000);
-		}
-
-		// no more result sets; the whole command was cancelled
-		Assert.False(reader.Read());
-		Assert.False(reader.NextResult());
-	}
 #endif
 
 	private static CancellationToken GetCanceledToken()

From 0b7a4fec0f8ef97935d48cc6d18079a9e18d1f30 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 2 Nov 2023 07:47:51 -0700
Subject: [PATCH 194/393] Update MySql.Data tests to v8.2.0.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 Directory.Packages.props                               | 2 +-
 appveyor.yml                                           | 2 +-
 docs/content/tutorials/migrating-from-connector-net.md | 6 +++---
 tests/IntegrationTests/app.config                      | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index a874fb181..a3cfb60b6 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -15,7 +15,7 @@
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
-    <PackageVersion Include="MySql.Data" Version="8.1.0" />
+    <PackageVersion Include="MySql.Data" Version="8.2.0" />
     <PackageVersion Include="NLog" Version="4.5.0" />
     <PackageVersion Include="Serilog" Version="2.6.0" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
diff --git a/appveyor.yml b/appveyor.yml
index 1f30f422b..7e9cc531f 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-image: Visual Studio 2022
+image: Previous Visual Studio 2022
 services:
 - mysql
 environment:
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 4182b43bd..3c8f26f74 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -41,7 +41,7 @@ In a .NET Framework application, make the following `app.config` change to regis
     <!-- add name="MySQL Data Provider"
       invariant="MySql.Data.MySqlClient"
       description=".Net Framework Data Provider for MySQL"
-      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.1.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.2.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
     <add name="MySqlConnector"
@@ -324,9 +324,9 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * ~~[#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.~~
 * ~~[#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.~~
 * ~~[#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'~~
-* [#110717](https://bugs.mysql.com/bug.php?id=110717), [#111759](https://bugs.mysql.com/bug.php?id=111759): MySql.Data is not thread-safe
+* ~~[#110717](https://bugs.mysql.com/bug.php?id=110717), [#111759](https://bugs.mysql.com/bug.php?id=111759): MySql.Data is not thread-safe~~
 * [#110789](https://bugs.mysql.com/bug.php?id=110789): `OpenAsync` throws unhandled exception from thread pool
 * [#110790](https://bugs.mysql.com/bug.php?id=110790): `ExecuteReaderAsync` hangs instead of cancelling query after `CommandTimeout`
 * [#110791](https://bugs.mysql.com/bug.php?id=110791): `OpenAsync(CancellationToken)` doesn't throw for cancelled token
-* [#110797](https://bugs.mysql.com/bug.php?id=110797): Certain sequence of special characters can break connection string validation.
+* [#111797](https://bugs.mysql.com/bug.php?id=111797): Certain sequence of special characters can break connection string validation.
 * [#112088](https://bugs.mysql.com/bug.php?id=112088): `BIT(n)` parameter size incorrectly returns 0
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index 59296b86a..ecc810d31 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -3,7 +3,7 @@
   <system.data>
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.1.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.2.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>

From 4cb2720779d669dc5595c990dc8947ba43cc2605 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 2 Nov 2023 08:26:04 -0700
Subject: [PATCH 195/393] Update MySQL tests to MySQL Server 8.2.

---
 .ci/docker-run.sh    | 2 +-
 azure-pipelines.yml  | 4 ++--
 docs/content/home.md | 2 +-
 tests/README.md      | 2 +-
 4 files changed, 5 insertions(+), 5 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index 88bbe9504..8bae51e2f 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -25,7 +25,7 @@ OMIT_FEATURES=$3
 MYSQL_EXTRA=
 MYSQL=mysql
 
-if [[ "$IMAGE" == mysql:8.0 || "$IMAGE" == mysql:8.1 ]]; then
+if [[ "$IMAGE" == mysql:8.0 || "$IMAGE" == mysql:8.2 ]]; then
   MYSQL_EXTRA='--default-authentication-plugin=mysql_native_password'
 fi
 if [[ "$IMAGE" == mariadb* ]]; then
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 4049dbfe7..5b69d7ce4 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -192,8 +192,8 @@ jobs:
         image: 'mysql:8.0'
         connectionStringExtra: ''
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
-      'MySQL 8.1':
-        image: 'mysql:8.1'
+      'MySQL 8.2':
+        image: 'mysql:8.2'
         connectionStringExtra: ''
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
       'MariaDB 10.6':
diff --git a/docs/content/home.md b/docs/content/home.md
index f3c63f9f6..f6dbc5e1e 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -65,7 +65,7 @@ Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://m
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
 MariaDB | 10.x, 11.x (**10.6**, **10.11**, **11.1**) |
-MySQL | 5.5, 5.6, **5.7**, **8.0**, **8.1** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 is EOL
+MySQL | 5.5, 5.6, **5.7**, **8.0**, 8.1, **8.2** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
 ProxySQL | 2.x | Some [compatibility issues](https://github.com/search?q=repo%3Amysql-net%2FMySqlConnector+proxysql&type=issues)
diff --git a/tests/README.md b/tests/README.md
index 5e3db593d..e132c52c1 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,7 @@ It also verifies that MySqlConnector and MySQL Connector/NET (MySql.Data) have s
 
 The tests require a MySQL server. The simplest way to run one is with [Docker](https://www.docker.com/community-edition):
 
-    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:8.0 --max-allowed-packet=96M --character-set-server=utf8mb4 --log-bin-trust-function-creators=1 --local-infile=1 --max-connections=250
+    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:8.2 --max-allowed-packet=96M --character-set-server=utf8mb4 --log-bin-trust-function-creators=1 --local-infile=1 --max-connections=250
     docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes';"
 
 Copy the file `IntegrationTests/config.json.example` to `IntegrationTests/config.json`, then edit

From fe64d6200d5c577d401cb0abb36ce3472485fbc4 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 2 Nov 2023 22:05:36 -0700
Subject: [PATCH 196/393] Update xUnit to 2.6.1.

---
 Directory.Packages.props | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index a3cfb60b6..d39817311 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -21,7 +21,7 @@
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageVersion Include="xunit" Version="2.5.3" />
+    <PackageVersion Include="xunit" Version="2.6.1" />
     <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.3" />
     <PackageVersion Include="YamlDotNet" Version="13.7.1" />
   </ItemGroup>

From 80bec170ac05ebce480a00746153eb30589724b1 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 4 Nov 2023 13:09:12 -0700
Subject: [PATCH 197/393] Ignore MySqlDbType when serializing enum values.
 Fixes #1384

---
 src/MySqlConnector/MySqlParameter.cs | 36 ++++++++++++++--------------
 tests/IntegrationTests/QueryTests.cs | 27 +++++++++++++++++++++
 2 files changed, 45 insertions(+), 18 deletions(-)

diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index e9a9b8800..841051780 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -520,6 +520,16 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 			WriteString(writer, noBackslashEscapes, stringBuilder.ToString().AsSpan());
 #endif
 		}
+		else if ((MySqlDbType is MySqlDbType.String or MySqlDbType.VarChar) && HasSetDbType && Value is Enum stringEnumValue)
+		{
+			writer.Write((byte) '\'');
+			writer.Write(stringEnumValue.ToString("G"));
+			writer.Write((byte) '\'');
+		}
+		else if (Value is Enum enumValue)
+		{
+			writer.Write(enumValue.ToString("d"));
+		}
 		else if (MySqlDbType == MySqlDbType.Int16)
 		{
 			writer.WriteString((short) Value);
@@ -544,16 +554,6 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		{
 			writer.WriteString((ulong) Value);
 		}
-		else if ((MySqlDbType is MySqlDbType.String or MySqlDbType.VarChar) && HasSetDbType && Value is Enum stringEnumValue)
-		{
-			writer.Write((byte) '\'');
-			writer.Write(stringEnumValue.ToString("G"));
-			writer.Write((byte) '\'');
-		}
-		else if (Value is Enum enumValue)
-		{
-			writer.Write(enumValue.ToString("d"));
-		}
 		else
 		{
 			throw new NotSupportedException($"Parameter type {Value.GetType().Name} is not supported; see https://fl.vu/mysql-param-type. Value: {Value}");
@@ -829,6 +829,14 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
 		{
 			writer.WriteLengthEncodedString(stringBuilder);
 		}
+		else if ((MySqlDbType is MySqlDbType.String or MySqlDbType.VarChar) && HasSetDbType && Value is Enum stringEnumValue)
+		{
+			writer.WriteLengthEncodedString(stringEnumValue.ToString("G"));
+		}
+		else if (Value is Enum)
+		{
+			writer.Write(Convert.ToInt32(Value, CultureInfo.InvariantCulture));
+		}
 		else if (MySqlDbType == MySqlDbType.Int16)
 		{
 			writer.Write((ushort) (short) Value);
@@ -853,14 +861,6 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
 		{
 			writer.Write((ulong) Value);
 		}
-		else if ((MySqlDbType is MySqlDbType.String or MySqlDbType.VarChar) && HasSetDbType && Value is Enum stringEnumValue)
-		{
-			writer.WriteLengthEncodedString(stringEnumValue.ToString("G"));
-		}
-		else if (Value is Enum)
-		{
-			writer.Write(Convert.ToInt32(Value, CultureInfo.InvariantCulture));
-		}
 		else
 		{
 			throw new NotSupportedException($"Parameter type {Value.GetType().Name} is not supported; see https://fl.vu/mysql-param-type. Value: {Value}");
diff --git a/tests/IntegrationTests/QueryTests.cs b/tests/IntegrationTests/QueryTests.cs
index fc531a90e..cd0181739 100644
--- a/tests/IntegrationTests/QueryTests.cs
+++ b/tests/IntegrationTests/QueryTests.cs
@@ -1081,6 +1081,33 @@ public void Int64EnumParameter()
 		Assert.False(reader.Read());
 	}
 
+	[Theory]
+	[InlineData(false)]
+	[InlineData(true)]
+	public void QueryEnumWithMySqlDbTypeInt16(bool prepare)
+	{
+		m_database.Connection.Execute(@"drop table if exists bug_1384;
+create table bug_1384(name text not null, enum1 smallint not null, enum2 smallint not null);
+insert into bug_1384 values('CreateNew', 1, 1), ('OpenOrCreate', 4, 4);
+");
+
+		using var command = new MySqlCommand("select * from bug_1384 where enum1 = @enum1 or enum2 = @enum2;", m_database.Connection)
+		{
+			Parameters =
+			{
+				new MySqlParameter("@enum1", MySqlDbType.Int16) { Value = (short) FileMode.OpenOrCreate },
+				new MySqlParameter("@enum2", MySqlDbType.Int16) { Value = FileMode.OpenOrCreate },
+			},
+		};
+		if (prepare)
+			command.Prepare();
+
+		using var reader = command.ExecuteReader();
+		Assert.True(reader.Read());
+		Assert.Equal("OpenOrCreate", reader.GetValue(0));
+		Assert.False(reader.Read());
+	}
+
 	[Fact]
 	public void ReturnDerivedTypes()
 	{

From d40fe25880226bad106eb577e8032b147e57bcfe Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 4 Nov 2023 14:03:27 -0700
Subject: [PATCH 198/393] Release 2.3.0 Beta 4.

---
 docs/content/overview/version-history.md | 19 ++++++++++++++++++-
 1 file changed, 18 insertions(+), 1 deletion(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 30e29e489..6b96b2971 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-07-30
+lastmod: 2023-11-04
 date: 2017-03-27
 menu:
   main:
@@ -11,6 +11,23 @@ weight: 30
 
 # Version History
 
+### 2.3.0 Beta 4
+
+* Support .NET 8.0.
+  * Mark some serialization APIs as `[Obsolete]`.
+  * Implement `DbBatchCommand.CreateParameter`: [#1352](https://github.com/mysql-net/MySqlConnector/issues/1352).
+  * Optimizations: Use `IUtf8SpanFormattable`, `Ascii.FromUtf16`, `Guid(bigEndian: true)` constructor, argument-throwing helpers, etc.
+* Expose connection pool metrics: [#491](https://github.com/mysql-net/MySqlConnector/issues/491).
+* Support .NET Framework 4.8 (`net48`) TFM: [#1355](https://github.com/mysql-net/MySqlConnector/discussions/1355)
+  * This allows `TlsVersion = TLSv1.3` to be used on .NET Framework 4.8.
+* Remove `COM_MULTI` protocol support: [#946](https://github.com/mysql-net/MySqlConnector/issues/946).
+* Reduce allocations on common code paths.
+* Support `ZEROFILL` columns in `MySqlDecimal`: [#1354](https://github.com/mysql-net/MySqlConnector/issues/1354).
+* Support higher-precision `DateTime` values: [#1379](https://github.com/mysql-net/MySqlConnector/issues/1379).
+* Use `ValueTask` in `MySqlBulkCopy` API for all TFMs: [#1364](https://github.com/mysql-net/MySqlConnector/issues/1364).
+* Fix bug when column name begins with `@` in `MySqlBulkCopy`: [#1365](https://github.com/mysql-net/MySqlConnector/issues/1365).
+* Ignore `MySqlDbType` when serializing enum values: [#1384](https://github.com/mysql-net/MySqlConnector/issues/1384).
+
 ### 2.3.0 Beta 3
 
 * Fix version parsing for MariaDB 11.0 and later: [#1311](https://github.com/mysql-net/MySqlConnector/pull/1311).

From ab510282d07a738d33905ce0445f39b13925bfd4 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 9 Nov 2023 07:01:56 -0800
Subject: [PATCH 199/393] Update Microsoft.NET.Test.Sdk to 17.8.0.

---
 Directory.Packages.props | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index d39817311..b319ba3da 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -14,7 +14,7 @@
     <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
-    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.7.2" />
+    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
     <PackageVersion Include="MySql.Data" Version="8.2.0" />
     <PackageVersion Include="NLog" Version="4.5.0" />
     <PackageVersion Include="Serilog" Version="2.6.0" />

From 768090947579c3cd71b3bb8214ebb652d9710658 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 9 Nov 2023 07:13:00 -0800
Subject: [PATCH 200/393] Fix minor typo in private class name.

---
 src/MySqlConnector/Logging/MySqlConnectorLogManager.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
index 2dd3bf9bb..da0ce1963 100644
--- a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
+++ b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
@@ -16,12 +16,12 @@ public static IMySqlConnectorLoggerProvider Provider
 	{
 		set
 		{
-			MySqlConnectorLoggingConfiguration.GlobalConfiguration = new(new MySqlConnectorLoggerFactor(value));
+			MySqlConnectorLoggingConfiguration.GlobalConfiguration = new(new MySqlConnectorLoggerFactory(value));
 		}
 	}
 
 	// A helper class that adapts ILoggerFactory to the old-style IMySqlConnectorLoggerProvider interface.
-	private sealed class MySqlConnectorLoggerFactor(IMySqlConnectorLoggerProvider loggerProvider) : ILoggerFactory
+	private sealed class MySqlConnectorLoggerFactory(IMySqlConnectorLoggerProvider loggerProvider) : ILoggerFactory
 	{
 		public void AddProvider(ILoggerProvider provider) => throw new NotSupportedException();
 

From dd0d5813be3032e843a60c5d0f589772ae6b4812 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 10 Nov 2023 19:25:51 -0800
Subject: [PATCH 201/393] Use MySqlConnector namespace for DI methods.

This aligns with the latest official guidance to NOT use the Microsoft.Extensions.DependencyInjection namespace: https://github.com/dotnet/AspNetCore.Docs/pull/23211, https://github.com/dotnet/AspNetCore.Docs/pull/27018.
---
 .../MySqlConnectorServiceCollectionExtensions.cs              | 4 ++--
 .../MySqlConnectorLoggingExtensions.cs                        | 3 +--
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs b/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
index 4359192cb..782ac6183 100644
--- a/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
+++ b/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
@@ -1,9 +1,9 @@
 using System.Data.Common;
+using Microsoft.Extensions.DependencyInjection;
 using Microsoft.Extensions.DependencyInjection.Extensions;
 using Microsoft.Extensions.Logging;
-using MySqlConnector;
 
-namespace Microsoft.Extensions.DependencyInjection;
+namespace MySqlConnector;
 
 /// <summary>
 /// Extension method for setting up MySqlConnector services in an <see cref="IServiceCollection" />.
diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnectorLoggingExtensions.cs b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnectorLoggingExtensions.cs
index fac1d5c83..98c90db85 100644
--- a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnectorLoggingExtensions.cs
+++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnectorLoggingExtensions.cs
@@ -1,8 +1,7 @@
 using System;
 using Microsoft.Extensions.Logging;
-using MySqlConnector.Logging;
 
-namespace Microsoft.Extensions.DependencyInjection;
+namespace MySqlConnector.Logging;
 
 public static class MySqlConnectorLoggingExtensions
 {

From 58a917820ad9bc295074ff0053c335ece557443d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 10 Nov 2023 20:15:37 -0800
Subject: [PATCH 202/393] Allow a breaking change to the namespace.

---
 .../CompatibilitySuppressions.xml                     | 11 +++++++++++
 1 file changed, 11 insertions(+)
 create mode 100644 src/MySqlConnector.Logging.Microsoft.Extensions.Logging/CompatibilitySuppressions.xml

diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/CompatibilitySuppressions.xml b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/CompatibilitySuppressions.xml
new file mode 100644
index 000000000..15ae18d7e
--- /dev/null
+++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/CompatibilitySuppressions.xml
@@ -0,0 +1,11 @@
+﻿<?xml version="1.0" encoding="utf-8"?>
+<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
+<Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
+  <Suppression>
+    <DiagnosticId>CP0001</DiagnosticId>
+    <Target>T:Microsoft.Extensions.DependencyInjection.MySqlConnectorLoggingExtensions</Target>
+    <Left>lib/netstandard2.0/MySqlConnector.Logging.Microsoft.Extensions.Logging.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.Logging.Microsoft.Extensions.Logging.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
+  </Suppression>
+</Suppressions>
\ No newline at end of file

From 82ec85c3d2b6edfb00cbdc72a4347cebfa0c9350 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 10 Nov 2023 20:29:44 -0800
Subject: [PATCH 203/393] Update example code in documentation.

Use .NET 8.0, C# 12, and MySqlConnector 2.3.0.
---
 docs/content/tutorials/connect-to-mysql.md |  21 +-
 docs/content/tutorials/net-core-mvc.md     | 429 ++++++++-------------
 2 files changed, 165 insertions(+), 285 deletions(-)

diff --git a/docs/content/tutorials/connect-to-mysql.md b/docs/content/tutorials/connect-to-mysql.md
index 1fdc42816..6e10af7bd 100644
--- a/docs/content/tutorials/connect-to-mysql.md
+++ b/docs/content/tutorials/connect-to-mysql.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-01-21
+lastmod: 2023-11-10
 date: 2019-11-18
 menu:
   main:
@@ -49,27 +49,22 @@ If you are using ASP.NET Core, your connection string will usually be stored in
 
 ## 3. Configure Service (ASP.NET Core)
 
-### .NET 6.0
-
 If using ASP.NET Core, you will want to register a database connection in `Program.cs`:
 
+**Recommended** Install [MySqlConnector.DependencyInjection](https://www.nuget.org/packages/MySqlConnector.DependencyInjection/) via `dotnet add package MySqlConnector.DependencyInjection`. Then add the following to `Program.cs`:
+
 ```csharp
-builder.services.AddTransient<MySqlConnection>(_ =>
-    new MySqlConnection(builder.Configuration.GetConnectionString["Default"]));
+builder.Services.AddMySqlDataSource(builder.Configuration.GetConnectionString("Default")!);
 ```
 
-### .NET 5.0 and earlier
-
-For .NET 5.0 and earlier, register a database connection in `Startup.cs`:
+Alternatively, register a transient `MySqlConnection` object explicitly:
 
 ```csharp
-public void ConfigureServices(IServiceCollection services)
-{
-    // ...
-    services.AddTransient<MySqlConnection>(_ => new MySqlConnection(Configuration["ConnectionStrings:Default"]));
-}
+builder.services.AddTransient<MySqlConnection>(_ =>
+    new MySqlConnection(builder.Configuration.GetConnectionString["Default"]));
 ```
 
+The advantage of using `AddMySqlDataSource` is that it will automatically integrate with logging, and also register `DbDataSource` and `DbConnection` with the service collection.
 
 ## 4. Open and Use the Connection
 
diff --git a/docs/content/tutorials/net-core-mvc.md b/docs/content/tutorials/net-core-mvc.md
index aba61d55a..fec267d11 100644
--- a/docs/content/tutorials/net-core-mvc.md
+++ b/docs/content/tutorials/net-core-mvc.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2020-06-14
+lastmod: 2023-11-10
 date: 2016-10-16
 menu:
   main:
@@ -33,7 +33,7 @@ CREATE TABLE IF NOT EXISTS `BlogPost` (
 ### Initialize ASP.NET Core Web API
 
 Create a folder named `BlogPostApi`, then run `dotnet new webapi` at the root to create the initial project.
-Run `dotnet add package MySqlConnector`. You should have a working project at this point, use `dotnet run`
+Run `dotnet add package MySqlConnector.DependencyInjection`. You should have a working project at this point, use `dotnet run`
 to verify the project builds and runs successfully.
 
 ### Update Configuration Files
@@ -48,334 +48,219 @@ to verify the project builds and runs successfully.
             "Microsoft.Hosting.Lifetime": "Information"
         }
     },
+    "AllowedHosts": "*",
     "ConnectionStrings": {
-        "DefaultConnection": "Server=127.0.0.1;User ID=root;Password=pass;Port=3306;Database=blog"
-    }
-}
-```
-
-`AppDb.cs` is a disposable [Application Database Object](/overview/configuration/), adapted to read the ConnectionString
-from the Configuration Object:
-
-```csharp
-using System;
-using MySqlConnector;
-
-namespace BlogPostApi
-{
-    public class AppDb : IDisposable
-    {
-        public MySqlConnection Connection { get; }
-
-        public AppDb(string connectionString)
-        {
-            Connection = new MySqlConnection(connectionString);
-        }
-
-        public void Dispose() => Connection.Dispose();
+        "Default": "Server=127.0.0.1;User ID=root;Password=pass;Port=3306;Database=blog"
     }
 }
 ```
 
 ### .NET Core Startup
 
-`Startup.cs` contains runtime configuration and framework services. Add this call to `ConfigureServices` to make an instance of `AppDb` available to controller methods.
+`Program.cs` contains runtime configuration and framework services.
+Add this call (before `var app = builder.Build();`) to register a MySQL data source:
 
 ```csharp
-services.AddTransient<AppDb>(_ => new AppDb(Configuration["ConnectionStrings:DefaultConnection"]));
+builder.Services.AddMySqlDataSource(builder.Configuration.GetConnectionString("Default")!);
 ```
 
 Now our app is configured and we can focus on writing the core functionality!
 
 ### Models
 
-`BlogPost.cs` represents a single Blog Post, and contains Insert, Update, and Delete methods.
+`BlogPost.cs` is a Plain Old C# Object that represents a single Blog Post.
 
 ```csharp
-using System.Data;
-using System.Threading.Tasks;
-using MySqlConnector;
+namespace BlogPostApi;
 
-namespace BlogPostApi
+public class BlogPost
 {
-    public class BlogPost
-    {
-        public int Id { get; set; }
-        public string Title { get; set; }
-        public string Content { get; set; }
-
-        internal AppDb Db { get; set; }
-
-        public BlogPost()
-        {
-        }
-
-        internal BlogPost(AppDb db)
-        {
-            Db = db;
-        }
-
-        public async Task InsertAsync()
-        {
-            using var cmd = Db.Connection.CreateCommand();
-            cmd.CommandText = @"INSERT INTO `BlogPost` (`Title`, `Content`) VALUES (@title, @content);";
-            BindParams(cmd);
-            await cmd.ExecuteNonQueryAsync();
-            Id = (int) cmd.LastInsertedId;
-        }
-
-        public async Task UpdateAsync()
-        {
-            using var cmd = Db.Connection.CreateCommand();
-            cmd.CommandText = @"UPDATE `BlogPost` SET `Title` = @title, `Content` = @content WHERE `Id` = @id;";
-            BindParams(cmd);
-            BindId(cmd);
-            await cmd.ExecuteNonQueryAsync();
-        }
-
-        public async Task DeleteAsync()
-        {
-            using var cmd = Db.Connection.CreateCommand();
-            cmd.CommandText = @"DELETE FROM `BlogPost` WHERE `Id` = @id;";
-            BindId(cmd);
-            await cmd.ExecuteNonQueryAsync();
-        }
-
-        private void BindId(MySqlCommand cmd)
-        {
-            cmd.Parameters.Add(new MySqlParameter
-            {
-                ParameterName = "@id",
-                DbType = DbType.Int32,
-                Value = Id,
-            });
-        }
-
-        private void BindParams(MySqlCommand cmd)
-        {
-            cmd.Parameters.Add(new MySqlParameter
-            {
-                ParameterName = "@title",
-                DbType = DbType.String,
-                Value = Title,
-            });
-            cmd.Parameters.Add(new MySqlParameter
-            {
-                ParameterName = "@content",
-                DbType = DbType.String,
-                Value = Content,
-            });
-        }
-    }
+    public int Id { get; set; }
+    public string? Title { get; set; }
+    public string? Content { get; set; }
 }
 ```
 
-`BlogPostQuery.cs` contains commands to query Blog Posts from the database:
+`BlogPostRepository.cs` contains commands to create, retrieve, update, and delete blog posts from the database:
 
 ```csharp
-using System.Collections.Generic;
-using System.Data;
 using System.Data.Common;
-using System.Threading.Tasks;
 using MySqlConnector;
 
-namespace BlogPostApi
+namespace BlogPostApi;
+
+public class BlogPostRepository(MySqlDataSource database)
 {
-    public class BlogPostQuery
+    public async Task<BlogPost?> FindOneAsync(int id)
     {
-        public AppDb Db { get; }
+        using var connection = await database.OpenConnectionAsync();
+        using var command = connection.CreateCommand();
+        command.CommandText = @"SELECT `Id`, `Title`, `Content` FROM `BlogPost` WHERE `Id` = @id";
+        command.Parameters.AddWithValue("@id", id);
+        var result = await ReadAllAsync(await command.ExecuteReaderAsync());
+        return result.FirstOrDefault();
+    }
 
-        public BlogPostQuery(AppDb db)
-        {
-            Db = db;
-        }
+    public async Task<IReadOnlyList<BlogPost>> LatestPostsAsync()
+    {
+        using var connection = await database.OpenConnectionAsync();
+        using var command = connection.CreateCommand();
+        command.CommandText = @"SELECT `Id`, `Title`, `Content` FROM `BlogPost` ORDER BY `Id` DESC LIMIT 10;";
+        return await ReadAllAsync(await command.ExecuteReaderAsync());
+    }
 
-        public async Task<BlogPost> FindOneAsync(int id)
-        {
-            using var cmd = Db.Connection.CreateCommand();
-            cmd.CommandText = @"SELECT `Id`, `Title`, `Content` FROM `BlogPost` WHERE `Id` = @id";
-            cmd.Parameters.Add(new MySqlParameter
-            {
-                ParameterName = "@id",
-                DbType = DbType.Int32,
-                Value = id,
-            });
-            var result = await ReadAllAsync(await cmd.ExecuteReaderAsync());
-            return result.Count > 0 ? result[0] : null;
-        }
+    public async Task DeleteAllAsync()
+    {
+        using var connection = await database.OpenConnectionAsync();
+        using var command = connection.CreateCommand();
+        command.CommandText = @"DELETE FROM `BlogPost`";
+        await command.ExecuteNonQueryAsync();
+    }
 
-        public async Task<List<BlogPost>> LatestPostsAsync()
-        {
-            using var cmd = Db.Connection.CreateCommand();
-            cmd.CommandText = @"SELECT `Id`, `Title`, `Content` FROM `BlogPost` ORDER BY `Id` DESC LIMIT 10;";
-            return await ReadAllAsync(await cmd.ExecuteReaderAsync());
-        }
+    public async Task InsertAsync(BlogPost blogPost)
+    {
+        using var connection = await database.OpenConnectionAsync();
+        using var command = connection.CreateCommand();
+        command.CommandText = @"INSERT INTO `BlogPost` (`Title`, `Content`) VALUES (@title, @content);";
+        BindParams(command, blogPost);
+        await command.ExecuteNonQueryAsync();
+        blogPost.Id = (int)command.LastInsertedId;
+    }
 
-        public async Task DeleteAllAsync()
-        {
-            using var txn = await Db.Connection.BeginTransactionAsync();
-            using var cmd = Db.Connection.CreateCommand();
-            cmd.CommandText = @"DELETE FROM `BlogPost`";
-            await cmd.ExecuteNonQueryAsync();
-            await txn.CommitAsync();
-        }
+    public async Task UpdateAsync(BlogPost blogPost)
+    {
+        using var connection = await database.OpenConnectionAsync();
+        using var command = connection.CreateCommand();
+        command.CommandText = @"UPDATE `BlogPost` SET `Title` = @title, `Content` = @content WHERE `Id` = @id;";
+        BindParams(command, blogPost);
+        BindId(command, blogPost);
+        await command.ExecuteNonQueryAsync();
+    }
+
+    public async Task DeleteAsync(BlogPost blogPost)
+    {
+        using var connection = await database.OpenConnectionAsync();
+        using var command = connection.CreateCommand();
+        command.CommandText = @"DELETE FROM `BlogPost` WHERE `Id` = @id;";
+        BindId(command, blogPost);
+        await command.ExecuteNonQueryAsync();
+    }
 
-        private async Task<List<BlogPost>> ReadAllAsync(DbDataReader reader)
+    private async Task<IReadOnlyList<BlogPost>> ReadAllAsync(DbDataReader reader)
+    {
+        var posts = new List<BlogPost>();
+        using (reader)
         {
-            var posts = new List<BlogPost>();
-            using (reader)
+            while (await reader.ReadAsync())
             {
-                while (await reader.ReadAsync())
+                var post = new BlogPost
                 {
-                    var post = new BlogPost(Db)
-                    {
-                        Id = reader.GetInt32(0),
-                        Title = reader.GetString(1),
-                        Content = reader.GetString(2),
-                    };
-                    posts.Add(post);
-                }
+                    Id = reader.GetInt32(0),
+                    Title = reader.GetString(1),
+                    Content = reader.GetString(2),
+                };
+                posts.Add(post);
             }
-            return posts;
         }
+        return posts;
+    }
+
+    private static void BindId(MySqlCommand cmd, BlogPost blogPost)
+    {
+        cmd.Parameters.AddWithValue("@id", blogPost.Id);
+    }
+
+    private static void BindParams(MySqlCommand cmd, BlogPost blogPost)
+    {
+        cmd.Parameters.AddWithValue("@title", blogPost.Title);
+        cmd.Parameters.AddWithValue("@content", blogPost.Content);
     }
 }
 ```
 
-### Controller
+### Program.cs
 
-`BlogController.cs` expose Async API Endpoints for CRUD operations on Blog Posts:
+`Program.cs` exposes Async API Endpoints for CRUD operations on Blog Posts (using an ASP.NET Minimal API).
+Add the following methods before `app.Run();`:
 
 ```csharp
-using System.Threading.Tasks;
-using Microsoft.AspNetCore.Mvc;
-
-namespace BlogPostApi.Controllers
+// GET api/blog
+app.MapGet("/api/blog", async ([FromServices] MySqlDataSource db) =>
 {
-    [Route("api/[controller]")]
-    public class BlogController : ControllerBase
-    {
-        public BlogController(AppDb db)
-        {
-            Db = db;
-        }
-
-        // GET api/blog
-        [HttpGet]
-        public async Task<IActionResult> GetLatest()
-        {
-            await Db.Connection.OpenAsync();
-            var query = new BlogPostQuery(Db);
-            var result = await query.LatestPostsAsync();
-            return new OkObjectResult(result);
-        }
+    var repository = new BlogPostRepository(db);
+    return await repository.LatestPostsAsync();
+});
 
-        // GET api/blog/5
-        [HttpGet("{id}")]
-        public async Task<IActionResult> GetOne(int id)
-        {
-            await Db.Connection.OpenAsync();
-            var query = new BlogPostQuery(Db);
-            var result = await query.FindOneAsync(id);
-            if (result is null)
-                return new NotFoundResult();
-            return new OkObjectResult(result);
-        }
-
-        // POST api/blog
-        [HttpPost]
-        public async Task<IActionResult> Post([FromBody]BlogPost body)
-        {
-            await Db.Connection.OpenAsync();
-            body.Db = Db;
-            await body.InsertAsync();
-            return new OkObjectResult(body);
-        }
-
-        // PUT api/blog/5
-        [HttpPut("{id}")]
-        public async Task<IActionResult> PutOne(int id, [FromBody]BlogPost body)
-        {
-            await Db.Connection.OpenAsync();
-            var query = new BlogPostQuery(Db);
-            var result = await query.FindOneAsync(id);
-            if (result is null)
-                return new NotFoundResult();
-            result.Title = body.Title;
-            result.Content = body.Content;
-            await result.UpdateAsync();
-            return new OkObjectResult(result);
-        }
-
-        // DELETE api/blog/5
-        [HttpDelete("{id}")]
-        public async Task<IActionResult> DeleteOne(int id)
-        {
-            await Db.Connection.OpenAsync();
-            var query = new BlogPostQuery(Db);
-            var result = await query.FindOneAsync(id);
-            if (result is null)
-                return new NotFoundResult();
-            await result.DeleteAsync();
-            return new OkResult();
-        }
+// GET api/blog/5
+app.MapGet("/api/blog/{id}", async ([FromServices] MySqlDataSource db, int id) =>
+{
+    var repository = new BlogPostRepository(db);
+    var result = await repository.FindOneAsync(id);
+    return result is null ? Results.NotFound() : Results.Ok(result);
+});
 
-        // DELETE api/blog
-        [HttpDelete]
-        public async Task<IActionResult> DeleteAll()
-        {
-            await Db.Connection.OpenAsync();
-            var query = new BlogPostQuery(Db);
-            await query.DeleteAllAsync();
-            return new OkResult();
-        }
+// POST api/blog
+app.MapPost("/api/blog", async ([FromServices] MySqlDataSource db, [FromBody] BlogPost body) =>
+{
+    var repository = new BlogPostRepository(db);
+    await repository.InsertAsync(body);
+    return body;
+});
 
-        public AppDb Db { get; }
-    }
-}
+// PUT api/blog/5
+app.MapPut("/api/blog/{id}", async (int id, [FromServices] MySqlDataSource db, [FromBody] BlogPost body) =>
+{
+    var repository = new BlogPostRepository(db);
+    var result = await repository.FindOneAsync(id);
+    if (result is null)
+        return Results.NotFound();
+    result.Title = body.Title;
+    result.Content = body.Content;
+    await repository.UpdateAsync(result);
+    return Results.Ok(result);
+});
+
+// DELETE api/blog/5
+app.MapDelete("/api/blog/{id}", async ([FromServices] MySqlDataSource db, int id) =>
+{
+    var repository = new BlogPostRepository(db);
+    var result = await repository.FindOneAsync(id);
+    if (result is null)
+        return Results.NotFound();
+    await repository.DeleteAsync(result);
+    return Results.NoContent();
+});
+
+// DELETE api/blog
+app.MapDelete("/api/blog", async ([FromServices] MySqlDataSource db) =>
+{
+    var repository = new BlogPostRepository(db);
+    await repository.DeleteAllAsync();
+    return Results.NoContent();
+});
 ```
 
 ### Run the App
 
 Congratulations, you should have a fully functional app at this point!  You should be able to run `dotnet run` to start your application.
 
-The following API Endpoints should work.  Note to set `Content-Type: application/json` headers on `POST` and `PUT` methods.
-
-```txt
-POST https://localhost:5001/api/blog
-{ "Title": "One", "Content": "First Blog Post!" }
-
-POST https://localhost:5001/api/blog
-{ "Title": "Two", "Content": "Second Blog Post!" }
-
-POST https://localhost:5001/api/blog
-{ "Title": "Three", "Content": "Third Blog Post!" }
-
-GET https://localhost:5001/api/blog
-// Output:
-[
-    { "id": 3, "title": "Three", "content": "Third Blog Post!" },
-    { "id": 2, "title": "Two", "content": "Second Blog Post!" },
-    { "id": 1, "title": "One", "content": "First Blog Post!"}
-]
-
-DELETE https://localhost:5001/api/blog/1
-// blog post 1 is gone
+The following API Endpoints should work:
 
-PUT https://localhost:5001/api/blog/2
-{ "Title": "Two", "Content": "Second Blog Post Revised" }
+```http
+@BlostPostApi_HostAddress = http://localhost:5001
 
-GET https://localhost:5001/api/blog
-// Output:
-[
-    { "id": 3, "title": "Three", "content": "Third Blog Post!" },
-    { "id": 2, "title": "Two", "content": "Second Blog Post Revised" },
-]
+GET {{BlostPostApi_HostAddress}}/api/blog
+Accept: application/json
+###
+POST {{BlostPostApi_HostAddress}}/api/blog
+Accept: application/json
+Content-Type: application/json
 
-DELETE https://localhost:5001/api/blog
-// all blog posts are gone
+{"title":"test", "content":"test content"}
+###
+PUT {{BlostPostApi_HostAddress}}/api/blog/1
+Accept: application/json
+Content-Type: application/json
 
-GET https://localhost:5001/api/blog
-// Output:
-[]
+{"title":"test put", "content":"test content updated"}
 ```

From f1a728ab714948ae8b25a07ae26c845c7758c3e2 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 11 Nov 2023 16:09:56 -0800
Subject: [PATCH 204/393] Consolidate Diagnostics documentation.

Move Logging and Tracing to new Diagnostics section and add a page for Metrics.
---
 docs/config.toml                              | 17 +++++---
 .../{overview => diagnostics}/logging.md      |  6 +--
 docs/content/diagnostics/metrics.md           | 42 +++++++++++++++++++
 .../{tutorials => diagnostics}/tracing.md     |  5 ++-
 netlify.toml                                  | 12 ++++++
 5 files changed, 71 insertions(+), 11 deletions(-)
 rename docs/content/{overview => diagnostics}/logging.md (97%)
 create mode 100644 docs/content/diagnostics/metrics.md
 rename docs/content/{tutorials => diagnostics}/tracing.md (93%)

diff --git a/docs/config.toml b/docs/config.toml
index 47b118779..2c9814d63 100644
--- a/docs/config.toml
+++ b/docs/config.toml
@@ -27,17 +27,22 @@ disablePathToLower = true
     identifier = "getting started"
     pre = "<i class='fa fa-road'></i>"
     weight = 20
-[[menu.main]]
-    name = "Tutorials"
-    identifier = "tutorials"
-    pre = "<i class='fa fa-lightbulb-o'></i>"
-    weight = 40
 [[menu.main]]
     name = "API"
     identifier = "api"
     pre = "<i class='fa fa-file-text'></i>"
     url = "/api/MySqlConnectorAssembly/"
-    weight = 35
+    weight = 30
+[[menu.main]]
+    name = "Diagnostics"
+    identifier = "diagnostics"
+    pre = "<i class='fa fa-road'></i>"
+    weight = 40
+[[menu.main]]
+    name = "Tutorials"
+    identifier = "tutorials"
+    pre = "<i class='fa fa-lightbulb-o'></i>"
+    weight = 50
 [[menu.main]]
     name = "Troubleshooting"
     identifier = "troubleshooting"
diff --git a/docs/content/overview/logging.md b/docs/content/diagnostics/logging.md
similarity index 97%
rename from docs/content/overview/logging.md
rename to docs/content/diagnostics/logging.md
index a08f622ae..958e62b96 100644
--- a/docs/content/overview/logging.md
+++ b/docs/content/diagnostics/logging.md
@@ -1,11 +1,11 @@
 ---
-lastmod: 2023-07-07
+lastmod: 2023-11-11
 date: 2018-01-20
 menu:
   main:
-    parent: getting started
+    parent: diagnostics
 title: Logging
-weight: 50
+weight: 10
 ---
 
 Logging
diff --git a/docs/content/diagnostics/metrics.md b/docs/content/diagnostics/metrics.md
new file mode 100644
index 000000000..720615cf1
--- /dev/null
+++ b/docs/content/diagnostics/metrics.md
@@ -0,0 +1,42 @@
+---
+date: 2023-11-11
+menu:
+  main:
+    parent: diagnostics
+title: Metrics
+weight: 30
+---
+
+# Metrics
+
+MySqlConnector exposes metrics that follow the [OpenTelemetry Semantic Conventions for Database Metrics](https://opentelemetry.io/docs/specs/semconv/database/database-metrics/) via types in the [System.Diagnostics.Metrics](https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.metrics) namespace.
+
+<blockquote class="note">Metrics are only supported in MySqlConnector 2.3.0 or later.</blockquote>
+
+The name used for the meter is `MySqlConnector`.
+
+Connect a listener using the `MeterListener` class:
+
+```csharp
+new MeterListener
+{
+    InstrumentPublished = (instrument, listener) =>
+    {
+        if (instrument.Meter.Name == "MySqlConnector")
+            listener.EnableMeasurementEvents(instrument);
+    }
+};
+```
+
+## Supported Instruments
+
+Name | Type | Unit | Description
+--- | --- | --- | ---
+`db.client.connections.usage` | UpDownCounter | `{connection}` | The number of connections that are currently in the state described by the state tag.
+`db.client.connections.pending_requests` | UpDownCounter | `{request}` | The number of pending requests for an open connection, cumulative for the entire pool.
+`db.client.connections.create_time` | Histogram | `ms` | The time it took to create a new connection.
+`db.client.connections.use_time` | Histogram | `ms` | The time between borrowing a connection and returning it to the pool.
+`db.client.connections.wait_time` | Histogram | `ms` | The time it took to obtain an open connection from the pool.
+`db.client.connections.idle.max` | UpDownCounter | `{connection}` | The maximum number of idle open connections allowed; this corresponds to `MaximumPoolSize` in the connection string.
+`db.client.connections.idle.min` | UpDownCounter | `{connection}` | The minimum number of idle open connections allowed; this corresponds to `MinimumPoolSize` in the connection string.
+`db.client.connections.max` | UpDownCounter | `{connection}` | The maximum number of open connections allowed; this corresponds to `MaximumPoolSize` in the connection string.
diff --git a/docs/content/tutorials/tracing.md b/docs/content/diagnostics/tracing.md
similarity index 93%
rename from docs/content/tutorials/tracing.md
rename to docs/content/diagnostics/tracing.md
index 107645489..07002a253 100644
--- a/docs/content/tutorials/tracing.md
+++ b/docs/content/diagnostics/tracing.md
@@ -1,10 +1,11 @@
 ---
+lastmod: 2023-11-11
 date: 2021-10-16
 menu:
   main:
-    parent: tutorials
+    parent: diagnostics
 title: Tracing
-weight: 13
+weight: 20
 ---
 
 # Tracing
diff --git a/netlify.toml b/netlify.toml
index f058c77da..f2ee87083 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -100,3 +100,15 @@ from = "/tutorials/"
 to = "/"
 status = 302
 force = true
+
+[[redirects]]
+from = "/overview/logging/"
+to = "/diagnostics/logging/"
+status = 302
+force = true
+
+[[redirects]]
+from = "/tutorials/tracing/"
+to = "/diagnostics/tracing/"
+status = 302
+force = true

From b4238ce86dd0f94ab83fece94987fffc84d99643 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 11 Nov 2023 18:06:57 -0800
Subject: [PATCH 205/393] Update logging documentation for 2.3.0.

---
 docs/content/diagnostics/logging.md | 54 +++++++++++++++++++++++++++--
 1 file changed, 51 insertions(+), 3 deletions(-)

diff --git a/docs/content/diagnostics/logging.md b/docs/content/diagnostics/logging.md
index 958e62b96..384980ca0 100644
--- a/docs/content/diagnostics/logging.md
+++ b/docs/content/diagnostics/logging.md
@@ -8,8 +8,56 @@ title: Logging
 weight: 10
 ---
 
-Logging
-=======
+# Logging
+
+MySqlConnector 2.3.0 and later supports logging through the standard [Microsoft.Extensions.Logging](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging) interfaces.
+
+## Console Programs
+
+To set up logging in MySqlConnector, create your `ILoggerFactory` as usual, and then configure a `MySqlDataSource` with it.
+Any use of connections handed out by the data source will log via your provided logger factory.
+
+The following shows a minimal console application logging to the console via [Microsoft.Extensions.Logging.Console](https://www.nuget.org/packages/Microsoft.Extensions.Logging.Console):
+
+```csharp
+using Microsoft.Extensions.Logging;
+using MySqlConnector;
+
+// Create a Microsoft.Extensions.Logging LoggerFactory, configuring it with the providers,
+// log levels and other desired configuration.
+var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
+
+// Create a MySqlDataSourceBuilder, configuring it with our LoggerFactory.
+var dataSourceBuilder = new MySqlDataSourceBuilder("Host=localhost;User ID=root;Password=pass");
+dataSourceBuilder.UseLoggerFactory(loggerFactory);
+await using var dataSource = dataSourceBuilder.Build();
+
+// Any connections handed out by the data source will log via the LoggerFactory:
+await using var connection = await dataSource.OpenConnectionAsync();
+await using var command = new MySqlCommand("SELECT 1", connection);
+_ = await command.ExecuteScalarAsync();
+```
+
+## ASP.NET and Dependency Injection
+
+If you're using ASP.NET, you can use the additional [MySqlConnector.DependencyInjection package](https://www.nuget.org/packages/MySqlConnector.DependencyInjection), which provides seamless integration with dependency injection and logging:
+
+```csharp
+var builder = WebApplication.CreateBuilder(args);
+builder.Logging.AddConsole();
+builder.Services.AddMySqlDataSource("Host=localhost;User ID=root;Password=pass");
+```
+
+The `AddMySqlDataSource` method registers a data source with the DI container.
+This data source automatically uses the logger factory configured by ASP.NET by default.
+This allows your endpoints to get injected with MySQL connections which log to the application's logger factory.
+
+## Global Logging
+
+<blockquote class="warning">
+The following information is for MySqlConnector versions prior to 2.3.0.
+The global logging interface that is described is deprecated and shouldn't be used in new code.
+</blockquote>
 
 MySqlConnector implements a custom logging framework with the `IMySqlConnectorLogger` and `IMySqlConnectorLoggerProvider` interfaces.
 To connect MySqlConnector to an existing logging framework, write an implementation of `IMySqlConnectorLoggerProvider` that adapts
@@ -17,7 +65,7 @@ the existing logging framework, and install it by setting `MySqlConnector.Loggin
 
 The `MySqlConnectorLogManager.Provider` property may only be set once, and must be set before any other MySqlConnector library methods are called.
 
-Debug-level logging is useful for diagnosing problems with MySqlConnector itself; it is recommended that applications limit MySqlConnector
+Trace-level logging is useful for diagnosing problems with MySqlConnector itself; it is recommended that applications limit MySqlConnector
 logging to Info or higher.
 
 ### Existing Logging Providers

From 1d3557c9f8218ad349899d75a71bb4ccab8c4bfe Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 12 Nov 2023 14:36:36 -0800
Subject: [PATCH 206/393] Reword end-of-stream message to be more generic.
 Fixes #1383

Introduce MySqlEndOfStreamException which holds the expected and read byte counts; these are logged as an error and a more generic exception message is thrown.
---
 src/MySqlConnector/Core/ServerSession.cs          |  2 ++
 src/MySqlConnector/Logging/EventIds.cs            |  1 +
 src/MySqlConnector/Logging/Log.cs                 |  3 +++
 src/MySqlConnector/MySqlEndOfStreamException.cs   | 15 +++++++++++++++
 .../Serialization/CompressedPayloadHandler.cs     |  5 +++--
 .../Protocol/Serialization/ProtocolUtility.cs     |  4 ++--
 6 files changed, 26 insertions(+), 4 deletions(-)
 create mode 100644 src/MySqlConnector/MySqlEndOfStreamException.cs

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 2558c9d5b..097450b1b 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -925,6 +925,8 @@ public async ValueTask<PayloadData> ReceiveReplyAsync(IOBehavior ioBehavior, Can
 		}
 		catch (Exception ex)
 		{
+			if (ex is MySqlEndOfStreamException endOfStream)
+				Log.ExpectedToReadMoreBytes(m_logger, Id, endOfStream.ExpectedByteCount, endOfStream.ReadByteCount);
 			SetFailed(ex);
 			throw;
 		}
diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index eb3318e75..02fe0b91a 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -22,6 +22,7 @@ internal static class EventIds
 	public const int FailedInSendReplyAsync = 2007;
 	public const int FailedInReceiveReplyAsync = 2008;
 	public const int SettingStateToFailed = 2009;
+	public const int ExpectedToReadMoreBytes = 2010;
 	public const int ExpectedSessionState1 = 2011;
 	public const int ExpectedSessionState6 = 2016;
 
diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index cd9437173..bcad0be27 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -282,6 +282,9 @@ internal static partial class Log
 	[LoggerMessage(EventIds.SettingStateToFailed, LogLevel.Debug, "Session {SessionId} setting state to Failed")]
 	public static partial void SettingStateToFailed(ILogger logger, Exception exception, string sessionId);
 
+	[LoggerMessage(EventIds.ExpectedToReadMoreBytes, LogLevel.Error, "Session {SessionId} expected to read {ExpectedByteCount} bytes but only read {ReadByteCount}")]
+	public static partial void ExpectedToReadMoreBytes(ILogger logger, string sessionId, int expectedByteCount, int readByteCount);
+
 	[LoggerMessage(EventIds.ErrorPayload, LogLevel.Debug, "Session {SessionId} got error payload: {ErrorCode}, {State}, {Message}")]
 	public static partial void ErrorPayload(ILogger logger, string sessionId, int errorCode, string state, string message);
 
diff --git a/src/MySqlConnector/MySqlEndOfStreamException.cs b/src/MySqlConnector/MySqlEndOfStreamException.cs
new file mode 100644
index 000000000..a446584cd
--- /dev/null
+++ b/src/MySqlConnector/MySqlEndOfStreamException.cs
@@ -0,0 +1,15 @@
+namespace MySqlConnector;
+
+[Serializable]
+public sealed class MySqlEndOfStreamException : EndOfStreamException
+{
+	internal MySqlEndOfStreamException(int expectedByteCount, int readByteCount)
+		: base("An incomplete response was received from the server")
+	{
+		ExpectedByteCount = expectedByteCount;
+		ReadByteCount = readByteCount;
+	}
+
+	public int ExpectedByteCount { get; }
+	public int ReadByteCount { get; }
+}
diff --git a/src/MySqlConnector/Protocol/Serialization/CompressedPayloadHandler.cs b/src/MySqlConnector/Protocol/Serialization/CompressedPayloadHandler.cs
index c9e455d51..101bbeaea 100644
--- a/src/MySqlConnector/Protocol/Serialization/CompressedPayloadHandler.cs
+++ b/src/MySqlConnector/Protocol/Serialization/CompressedPayloadHandler.cs
@@ -130,7 +130,8 @@ private async ValueTask<int> ReadBytesAsync(Memory<byte> buffer, ProtocolErrorBe
 			} while (bytesRead > 0);
 #endif
 			if (totalBytesRead != uncompressedLength && protocolErrorBehavior == ProtocolErrorBehavior.Throw)
-				throw new InvalidOperationException($"Expected to read {uncompressedLength:d} uncompressed bytes but only read {totalBytesRead:d}");
+				throw new MySqlEndOfStreamException(uncompressedLength, totalBytesRead);
+
 			m_remainingData = new(uncompressedData, 0, totalBytesRead);
 #else
 			// check CMF (Compression Method and Flags) and FLG (Flags) bytes for expected values
@@ -160,7 +161,7 @@ private async ValueTask<int> ReadBytesAsync(Memory<byte> buffer, ProtocolErrorBe
 				totalBytesRead += bytesRead;
 			} while (bytesRead > 0);
 			if (totalBytesRead != uncompressedLength && protocolErrorBehavior == ProtocolErrorBehavior.Throw)
-				throw new InvalidOperationException($"Expected to read {uncompressedLength:d} uncompressed bytes but only read {totalBytesRead:d}");
+				throw new MySqlEndOfStreamException(uncompressedLength, totalBytesRead);
 			m_remainingData = new(uncompressedData, 0, totalBytesRead);
 
 			var checksum = Adler32.Calculate(uncompressedData.AsSpan(0, totalBytesRead));
diff --git a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
index 4c3096001..d3c1fc0ff 100644
--- a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
@@ -422,7 +422,7 @@ public static async ValueTask<ArraySegment<byte>> ReadPayloadAsync(BufferedByteR
 			if (headerBytes.Count < 4)
 			{
 				return protocolErrorBehavior == ProtocolErrorBehavior.Ignore ? default :
-					throw new EndOfStreamException($"Expected to read 4 header bytes but only received {headerBytes.Count:d}.");
+					throw new MySqlEndOfStreamException(4, headerBytes.Count);
 			}
 
 			// read values from the header before the memory is potentially overwritten by ReadBytesAsync
@@ -450,7 +450,7 @@ public static async ValueTask<ArraySegment<byte>> ReadPayloadAsync(BufferedByteR
 			else
 			{
 				packet = payloadBytes.Count >= payloadLength ? new(payloadBytes) :
-					protocolErrorBehavior == ProtocolErrorBehavior.Throw ? throw new EndOfStreamException($"Expected to read {payloadLength:d} payload bytes but only received {payloadBytes.Count:d}.") :
+					protocolErrorBehavior == ProtocolErrorBehavior.Throw ? throw new MySqlEndOfStreamException(payloadLength, payloadBytes.Count) :
 					default;
 			}
 

From ccd2fe1b3fc9f3be0c5968133f803f191012eec8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 12 Nov 2023 15:13:20 -0800
Subject: [PATCH 207/393] Update package readme for DI and logging. Fixes #1280

---
 .../docs/README.md                            |  4 +--
 src/MySqlConnector/docs/README.md             | 27 ++++++++++++++++++-
 2 files changed, 28 insertions(+), 3 deletions(-)

diff --git a/src/MySqlConnector.DependencyInjection/docs/README.md b/src/MySqlConnector.DependencyInjection/docs/README.md
index 533064457..d28d69389 100644
--- a/src/MySqlConnector.DependencyInjection/docs/README.md
+++ b/src/MySqlConnector.DependencyInjection/docs/README.md
@@ -11,7 +11,7 @@ For example, if using the ASP.NET minimal web API, use the following to register
 ```csharp
 var builder = WebApplication.CreateBuilder(args);
 
-builder.Services.AddMySqlDataSource("Server=server;User ID=test;Password=test;Database=test");
+builder.Services.AddMySqlDataSource(builder.Configuration.GetConnectionString("Default"));
 ```
 
 This registers a transient `MySqlConnection` which can get injected into your controllers:
@@ -21,7 +21,7 @@ app.MapGet("/", async (MySqlConnection connection) =>
 {
     await connection.OpenAsync();
     await using var command = connection.CreateCommand();
-	command.CommandText = "SELECT name FROM users LIMIT 1";
+    command.CommandText = "SELECT name FROM users LIMIT 1";
     return "Hello World: " + await command.ExecuteScalarAsync();
 });
 ```
diff --git a/src/MySqlConnector/docs/README.md b/src/MySqlConnector/docs/README.md
index 6f11353c3..855145b43 100644
--- a/src/MySqlConnector/docs/README.md
+++ b/src/MySqlConnector/docs/README.md
@@ -35,6 +35,31 @@ while (reader.Read())
 }
 ```
 
+### ASP.NET
+
+For ASP.NET, use the [MySqlConnector.DependencyInjection package](https://www.nuget.org/packages/MySqlConnector.DependencyInjection/) to integrate with dependency injection and logging.
+
+```csharp
+var builder = WebApplication.CreateBuilder(args);
+
+// use AddMySqlDataSource to configure MySqlConnector
+builder.Services.AddMySqlDataSource(builder.Configuration.GetConnectionString("Default"));
+
+var app = builder.Build();
+
+// use dependency injection to get a MySqlConnection in minimal APIs or in controllers
+app.MapGet("/", async (MySqlConnection connection) =>
+{
+    // open and use the connection here
+    await connection.OpenAsync();
+    await using var command = connection.CreateCommand();
+    command.CommandText = "SELECT name FROM users LIMIT 1";
+    return "Hello World: " + await command.ExecuteScalarAsync();
+});
+
+app.Run();
+```
+
 ## Key Features
 
 * Full support for async I/O
@@ -58,8 +83,8 @@ The main types provided by this library are:
 
 ## Related Packages
 
+* Dependency Injection: [MySqlConnector.DependencyInjection](https://www.nuget.org/packages/MySqlConnector.DependencyInjection/)
 * Entity Framework Core: [Pomelo.EntityFrameworkCore.MySql](https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql/)
-* Logging: [log4net](https://www.nuget.org/packages/MySqlConnector.Logging.log4net/), [Microsoft.Extensions.Logging](https://www.nuget.org/packages/MySqlConnector.Logging.Microsoft.Extensions.Logging/), [NLog](https://www.nuget.org/packages/MySqlConnector.Logging.NLog/), [Serilog](https://www.nuget.org/packages/MySqlConnector.Logging.Serilog/)
 
 ## Feedback
 

From 16b956ee4d17363bfd61502b82bcb06b7e08f93a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 14 Nov 2023 08:05:23 -0800
Subject: [PATCH 208/393] Build with .NET 8.0 RTM.

---
 global.json                                   |  3 +-
 .../CompatibilitySuppressions.xml             | 45 ++++++++-----------
 2 files changed, 20 insertions(+), 28 deletions(-)

diff --git a/global.json b/global.json
index 7a8f3d994..3a69170ca 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,5 @@
 {
   "sdk": {
-    "version": "8.0",
-    "allowPrerelease": true
+    "version": "8.0"
   }
 }
diff --git a/src/MySqlConnector/CompatibilitySuppressions.xml b/src/MySqlConnector/CompatibilitySuppressions.xml
index 07cb181f8..7471076ba 100644
--- a/src/MySqlConnector/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector/CompatibilitySuppressions.xml
@@ -148,43 +148,36 @@
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0008</DiagnosticId>
-    <Target>T:MySqlConnector.MySqlDataReader</Target>
-    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
-    <Right>lib/net462/MySqlConnector.dll</Right>
+    <Target>T:MySqlConnector.MySqlConversionException</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
+    <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0008</DiagnosticId>
-    <Target>T:MySqlConnector.MySqlBatchCommandCollection</Target>
-    <Left>lib/netstandard2.1/MySqlConnector.dll</Left>
-    <Right>lib/net6.0/MySqlConnector.dll</Right>
-  </Suppression>
-  <Suppression>
-    <DiagnosticId>CP1002</DiagnosticId>
-    <Target>System.Data.Common, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
-    <Left>lib/net5.0/MySqlConnector.dll</Left>
-    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+    <Target>T:MySqlConnector.MySqlException</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
     <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP1002</DiagnosticId>
-    <Target>System.Runtime, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
-    <Left>lib/net5.0/MySqlConnector.dll</Left>
-    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:MySqlConnector.MySqlProtocolException</Target>
+    <Left>lib/net461/MySqlConnector.dll</Left>
+    <Right>lib/netstandard2.0/MySqlConnector.dll</Right>
     <IsBaselineSuppression>true</IsBaselineSuppression>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP1002</DiagnosticId>
-    <Target>System.Data.Common, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
-    <Left>lib/netcoreapp3.1/MySqlConnector.dll</Left>
-    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
-    <IsBaselineSuppression>true</IsBaselineSuppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:MySqlConnector.MySqlDataReader</Target>
+    <Left>lib/netstandard2.0/MySqlConnector.dll</Left>
+    <Right>lib/net462/MySqlConnector.dll</Right>
   </Suppression>
   <Suppression>
-    <DiagnosticId>CP1002</DiagnosticId>
-    <Target>System.Runtime, Version=4.1.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</Target>
-    <Left>lib/netcoreapp3.1/MySqlConnector.dll</Left>
-    <Right>lib/netstandard2.1/MySqlConnector.dll</Right>
-    <IsBaselineSuppression>true</IsBaselineSuppression>
+    <DiagnosticId>CP0008</DiagnosticId>
+    <Target>T:MySqlConnector.MySqlBatchCommandCollection</Target>
+    <Left>lib/netstandard2.1/MySqlConnector.dll</Left>
+    <Right>lib/net6.0/MySqlConnector.dll</Right>
   </Suppression>
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>

From d895afc013a5849d33a123a7061442e2cbb9ce76 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 14 Nov 2023 08:53:51 -0800
Subject: [PATCH 209/393] Release 2.3.0.

---
 docs/content/overview/version-history.md | 67 ++++++++++--------------
 1 file changed, 29 insertions(+), 38 deletions(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 6b96b2971..633ed7d0e 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-11-04
+lastmod: 2023-11-14
 date: 2017-03-27
 menu:
   main:
@@ -11,63 +11,54 @@ weight: 30
 
 # Version History
 
-### 2.3.0 Beta 4
+### 2.3.0
 
 * Support .NET 8.0.
   * Mark some serialization APIs as `[Obsolete]`.
   * Implement `DbBatchCommand.CreateParameter`: [#1352](https://github.com/mysql-net/MySqlConnector/issues/1352).
   * Optimizations: Use `IUtf8SpanFormattable`, `Ascii.FromUtf16`, `Guid(bigEndian: true)` constructor, argument-throwing helpers, etc.
-* Expose connection pool metrics: [#491](https://github.com/mysql-net/MySqlConnector/issues/491).
 * Support .NET Framework 4.8 (`net48`) TFM: [#1355](https://github.com/mysql-net/MySqlConnector/discussions/1355)
   * This allows `TlsVersion = TLSv1.3` to be used on .NET Framework 4.8.
+* Drop support for .NET 4.6.1 and .NET Core 3.1: [#636](https://github.com/mysql-net/MySqlConnector/issues/636), [#1273](https://github.com/mysql-net/MySqlConnector/issues/1273).
+  * .NET 4.6.1 support ended on 26 April 2022, and .NET Core 3.1 on 13 December 2022.
+  * The minimum supported versions are now .NET Framework 4.6.2 and .NET 6.0, although other frameworks should be supported via `netstandard2.0`.
+* `MySqlDataSource` is now available for all TFMs, not just .NET 7.0: [#1269](https://github.com/mysql-net/MySqlConnector/pull/1269).
+  * This provides a single place to configure a MySQL connection and makes it easier to register `MySqlConnection` with dependency injection.
+  * Add `MySqlDataSourceBuilder` class to configure `MySqlDataSource` instances.
+  * Add `MySqlDataSource.Name` and `MySqlDataSourceBuilder.UseName`.
+* Microsoft.Extensions.Logging is now used as the core logging abstraction: [#1110](https://github.com/mysql-net/MySqlConnector/issues/1110).
+  * `MySqlConnectorLogManager.Provider` can still be used to add a logging destination, but it is now deprecated.
+  * Use `MySqlDataSourceBuilder.UseLoggerFactory` to configure logging.
+* Add new MySqlConnector.DependencyInjection package: [#1271](https://github.com/mysql-net/MySqlConnector/issues/1271).
+  * `MySqlDataSource` and `MySqlConnection` can be registered with dependency injection by using `builder.Services.AddMySqlDataSource(connectionString)`.
+  * This also configures logging automatically.
+* Expose connection pool metrics: [#491](https://github.com/mysql-net/MySqlConnector/issues/491).
 * Remove `COM_MULTI` protocol support: [#946](https://github.com/mysql-net/MySqlConnector/issues/946).
-* Reduce allocations on common code paths.
 * Support `ZEROFILL` columns in `MySqlDecimal`: [#1354](https://github.com/mysql-net/MySqlConnector/issues/1354).
 * Support higher-precision `DateTime` values: [#1379](https://github.com/mysql-net/MySqlConnector/issues/1379).
 * Use `ValueTask` in `MySqlBulkCopy` API for all TFMs: [#1364](https://github.com/mysql-net/MySqlConnector/issues/1364).
-* Fix bug when column name begins with `@` in `MySqlBulkCopy`: [#1365](https://github.com/mysql-net/MySqlConnector/issues/1365).
-* Ignore `MySqlDbType` when serializing enum values: [#1384](https://github.com/mysql-net/MySqlConnector/issues/1384).
-
-### 2.3.0 Beta 3
-
-* Fix version parsing for MariaDB 11.0 and later: [#1311](https://github.com/mysql-net/MySqlConnector/pull/1311).
-* Support per-query variables for `CommandBehavior.SchemaOnly` and `SingleRow`: [#1312](https://github.com/mysql-net/MySqlConnector/pull/1312).
-* Perform XA rollback when preparing a transaction fails: [#1348](https://github.com/mysql-net/MySqlConnector/issues/1348).
-* Implement faster parsing for result sets with multiple rows: [#1330](https://github.com/mysql-net/MySqlConnector/pull/1330).
-* Add `MySqlDataSource.Name` and `MySqlDataSourceBuilder.UseName`.
-* Fix potential error in reallocating an internal buffer when writing ASCII text.
-* Update handling of `ActivityStatus` to latest conventions: [#1334](https://github.com/mysql-net/MySqlConnector/pull/1334).
-* Reduce overhead of `CommandTimeout`: [#1338](https://github.com/mysql-net/MySqlConnector/pull/1338).
-* Thanks to [Diego Dupin](https://github.com/rusher), [JackBOBO](https://github.com/JackBOBO), and [Piotr Kiełkowicz](https://github.com/Kielek) for contributions to this release.
-
-### 2.3.0 Beta 2
-
+  * **Breaking** This changes the return type of `WriteToServerAsync` from `Task<MySqlBulkCopyResult>` to `ValueTask<MySqlBulkCopyResult>` on .NET Framework
 * Support skipping metadata for prepared statements with MariaDB 10.6 and later: [#1301](https://github.com/mysql-net/MySqlConnector/pull/1301).
 * Support multiple authentication methods when connecting: [#1303](https://github.com/mysql-net/MySqlConnector/pull/1303).
+* Support per-query variables for `CommandBehavior.SchemaOnly` and `SingleRow`: [#1312](https://github.com/mysql-net/MySqlConnector/pull/1312).
 * Recycle `MySqlDataReader` objects: [#1277](https://github.com/mysql-net/MySqlConnector/issues/1277).
+* Perform XA rollback when preparing a transaction fails: [#1348](https://github.com/mysql-net/MySqlConnector/issues/1348).
+* Implement faster parsing for result sets with multiple rows: [#1330](https://github.com/mysql-net/MySqlConnector/pull/1330).
 * Optimize parameter encoding for ASCII strings: [#1296](https://github.com/mysql-net/MySqlConnector/pull/1296).
 * Use `TcpClient.ConnectAsync` overload with `CancellationToken` on .NET 5.0 and later: [#1291](https://github.com/mysql-net/MySqlConnector/pull/1291).
 * Fix cancellation when using a redirected connection: [#1305](https://github.com/mysql-net/MySqlConnector/pull/1305).
 * Fix `MySqlConnection.CloneWith` for connections created from a `MySqlDataSource`: [#1306](https://github.com/mysql-net/MySqlConnector/issues/1306).
 * Work around ephemeral PEM bug on Windows: [#1278](https://github.com/mysql-net/MySqlConnector/issues/1278).
-* Remove some unnecessary allocations.
-* Thanks to [Guillaume Boucher](https://github.com/gboucher90),  [Diego Dupin](https://github.com/rusher), and [Nickolay Batov](https://github.com/stilettk) for contributions to this release.
-
-### 2.3.0 Beta 1
-
-* `MySqlDataSource` is now available for all TFMs, not just .NET 7.0: [#1269](https://github.com/mysql-net/MySqlConnector/pull/1269).
-  * This provides a single place to configure a MySQL connection and makes it easier to register `MySqlConnection` with dependency injection.
-  * Add `MySqlDataSourceBuilder` class to configure `MySqlDataSource` instances.
-* Microsoft.Extensions.Logging is now used as the core logging abstraction: [#1110](https://github.com/mysql-net/MySqlConnector/issues/1110).
-  * `MySqlConnectorLogManager.Provider` can still be used to add a logging destination, but it is now deprecated.
-  * Use `MySqlDataSourceBuilder.UseLoggerFactory` to configure logging.
-* Add new MySqlConnector.DependencyInjection package: [#1271](https://github.com/mysql-net/MySqlConnector/issues/1271).
-  * `MySqlDataSource` and `MySqlConnection` can be registered with dependency injection by using `builder.Services.AddMySqlDataSource(connectionString)`.
-  * This also configures logging automatically.
-* Drop support for .NET 4.6.1 and .NET Core 3.1: [#636](https://github.com/mysql-net/MySqlConnector/issues/636), [#1273](https://github.com/mysql-net/MySqlConnector/issues/1273).
-  * .NET 4.6.1 support ended on 26 April 2022, and .NET Core 3.1 on 13 December 2022.
-  * The minimum supported versions are now .NET Framework 4.6.2 and .NET 6.0, although other frameworks should be supported via `netstandard2.0`.
+* Reduce allocations on common code paths.
+* Fix bug when column name begins with `@` in `MySqlBulkCopy`: [#1365](https://github.com/mysql-net/MySqlConnector/issues/1365).
+* Ignore `MySqlDbType` when serializing enum values: [#1384](https://github.com/mysql-net/MySqlConnector/issues/1384).
 * Fix bug that didn't copy `MySqlDataSource` in `MySqlConnection.Clone`: [#1267](https://github.com/mysql-net/MySqlConnector/issues/1267).
+* Fix version parsing for MariaDB 11.0 and later: [#1311](https://github.com/mysql-net/MySqlConnector/pull/1311).
+* Fix potential error in reallocating an internal buffer when writing ASCII text.
+* Update handling of `ActivityStatus` to latest conventions: [#1334](https://github.com/mysql-net/MySqlConnector/pull/1334).
+* Reduce overhead of `CommandTimeout`: [#1338](https://github.com/mysql-net/MySqlConnector/pull/1338).
+* Reword end-of-stream message to be more generic: [#1383](https://github.com/mysql-net/MySqlConnector/issues/1383).
+* Thanks to [Guillaume Boucher](https://github.com/gboucher90), [JackBOBO](https://github.com/JackBOBO), [Piotr Kiełkowicz](https://github.com/Kielek), [Diego Dupin](https://github.com/rusher), and [Nickolay Batov](https://github.com/stilettk) for contributions to this release.
 
 ### 2.2.7
 

From d32826e281552063852b71dac0f3c2e4bf318e03 Mon Sep 17 00:00:00 2001
From: Tuomas Hietanen <thorium@iki.fi>
Date: Wed, 15 Nov 2023 13:39:47 +0000
Subject: [PATCH 210/393] Support empty array as restrictionValues

Signed-off-by: Tuomas Hietanen <thorium@iki.fi>
---
 src/MySqlConnector/Core/SchemaProvider.g.cs   | 54 +++++++++----------
 .../SchemaCollectionGenerator.cs              |  2 +-
 2 files changed, 28 insertions(+), 28 deletions(-)

diff --git a/src/MySqlConnector/Core/SchemaProvider.g.cs b/src/MySqlConnector/Core/SchemaProvider.g.cs
index 11c75e185..98471c6b5 100644
--- a/src/MySqlConnector/Core/SchemaProvider.g.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.g.cs
@@ -83,7 +83,7 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 
 	private Task FillMetaDataCollectionsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'MetaDataCollections'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -129,7 +129,7 @@ private Task FillMetaDataCollectionsAsync(IOBehavior ioBehavior, DataTable dataT
 
 	private async Task FillCharacterSetsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'CharacterSets'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -146,7 +146,7 @@ private async Task FillCharacterSetsAsync(IOBehavior ioBehavior, DataTable dataT
 
 	private async Task FillCollationsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Collations'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -165,7 +165,7 @@ private async Task FillCollationsAsync(IOBehavior ioBehavior, DataTable dataTabl
 
 	private async Task FillCollationCharacterSetApplicabilityAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'CollationCharacterSetApplicability'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -227,7 +227,7 @@ private async Task FillColumnsAsync(IOBehavior ioBehavior, DataTable dataTable,
 
 	private async Task FillDatabasesAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Databases'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -245,7 +245,7 @@ private async Task FillDatabasesAsync(IOBehavior ioBehavior, DataTable dataTable
 
 	private Task FillDataSourceInformationAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'DataSourceInformation'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -277,7 +277,7 @@ private Task FillDataSourceInformationAsync(IOBehavior ioBehavior, DataTable dat
 
 	private Task FillDataTypesAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'DataTypes'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -315,7 +315,7 @@ private Task FillDataTypesAsync(IOBehavior ioBehavior, DataTable dataTable, stri
 
 	private async Task FillEnginesAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Engines'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -334,7 +334,7 @@ private async Task FillEnginesAsync(IOBehavior ioBehavior, DataTable dataTable,
 
 	private async Task FillKeyColumnUsageAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'KeyColumnUsage'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -359,7 +359,7 @@ private async Task FillKeyColumnUsageAsync(IOBehavior ioBehavior, DataTable data
 
 	private async Task FillKeyWordsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'KeyWords'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -374,7 +374,7 @@ private async Task FillKeyWordsAsync(IOBehavior ioBehavior, DataTable dataTable,
 
 	private async Task FillParametersAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Parameters'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -403,7 +403,7 @@ private async Task FillParametersAsync(IOBehavior ioBehavior, DataTable dataTabl
 
 	private async Task FillPartitionsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Partitions'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -441,7 +441,7 @@ private async Task FillPartitionsAsync(IOBehavior ioBehavior, DataTable dataTabl
 
 	private async Task FillPluginsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Plugins'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -465,7 +465,7 @@ private async Task FillPluginsAsync(IOBehavior ioBehavior, DataTable dataTable,
 
 	private async Task FillProceduresAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Procedures'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -498,7 +498,7 @@ private async Task FillProceduresAsync(IOBehavior ioBehavior, DataTable dataTabl
 
 	private async Task FillProcessListAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'ProcessList'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -519,7 +519,7 @@ private async Task FillProcessListAsync(IOBehavior ioBehavior, DataTable dataTab
 
 	private async Task FillProfilingAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Profiling'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -550,7 +550,7 @@ private async Task FillProfilingAsync(IOBehavior ioBehavior, DataTable dataTable
 
 	private async Task FillReferentialConstraintsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'ReferentialConstraints'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -574,7 +574,7 @@ private async Task FillReferentialConstraintsAsync(IOBehavior ioBehavior, DataTa
 
 	private Task FillReservedWordsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'ReservedWords'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -590,7 +590,7 @@ private Task FillReservedWordsAsync(IOBehavior ioBehavior, DataTable dataTable,
 
 	private async Task FillResourceGroupsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'ResourceGroups'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -608,7 +608,7 @@ private async Task FillResourceGroupsAsync(IOBehavior ioBehavior, DataTable data
 
 	private Task FillRestrictionsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Restrictions'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -634,7 +634,7 @@ private Task FillRestrictionsAsync(IOBehavior ioBehavior, DataTable dataTable, s
 
 	private async Task FillSchemaPrivilegesAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'SchemaPrivileges'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -699,7 +699,7 @@ private async Task FillTablesAsync(IOBehavior ioBehavior, DataTable dataTable, s
 
 	private async Task FillTableConstraintsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'TableConstraints'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -718,7 +718,7 @@ private async Task FillTableConstraintsAsync(IOBehavior ioBehavior, DataTable da
 
 	private async Task FillTablePrivilegesAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'TablePrivileges'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -737,7 +737,7 @@ private async Task FillTablePrivilegesAsync(IOBehavior ioBehavior, DataTable dat
 
 	private async Task FillTableSpacesAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'TableSpaces'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -759,7 +759,7 @@ private async Task FillTableSpacesAsync(IOBehavior ioBehavior, DataTable dataTab
 
 	private async Task FillTriggersAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Triggers'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -794,7 +794,7 @@ private async Task FillTriggersAsync(IOBehavior ioBehavior, DataTable dataTable,
 
 	private async Task FillUserPrivilegesAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'UserPrivileges'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
@@ -811,7 +811,7 @@ private async Task FillUserPrivilegesAsync(IOBehavior ioBehavior, DataTable data
 
 	private async Task FillViewsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is not null)
+		if (restrictionValues is not null && restrictionValues.Length > 0)
 			throw new ArgumentException("restrictionValues is not supported for schema 'Views'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
index dac24b5bf..347c2bf6a 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
+++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
@@ -62,7 +62,7 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 	if (!supportsRestrictions)
 	{
 		codeWriter.WriteLine($"""
-					if (restrictionValues is not null)
+					if (restrictionValues is not null && restrictionValues.Length > 0)
 						throw new ArgumentException("restrictionValues is not supported for schema '{schema.Name}'.", nameof(restrictionValues));
 			""");
 	}

From 8d56d859a874f7f0120217e29f9293df38557636 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 15 Nov 2023 15:59:23 -0800
Subject: [PATCH 211/393] Use higher-resolution durations for metrics. Fixes
 #1395

---
 src/MySqlConnector/Core/ConnectionPool.cs  | 26 +++++++++++-----------
 src/MySqlConnector/Core/MetricsReporter.cs |  6 ++---
 src/MySqlConnector/Core/ServerSession.cs   | 24 ++++++++++----------
 src/MySqlConnector/MySqlConnection.cs      | 12 +++++-----
 src/MySqlConnector/Utilities/Utility.cs    | 21 +++++++++++++++++
 5 files changed, 55 insertions(+), 34 deletions(-)

diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 674a01300..b3be668d2 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -20,7 +20,7 @@ internal sealed class ConnectionPool : IDisposable
 
 	public SslProtocols SslProtocols { get; set; }
 
-	public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection, int startTickCount, int timeoutMilliseconds, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection, long startingTimestamp, int timeoutMilliseconds, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		cancellationToken.ThrowIfCancellationRequested();
 
@@ -72,7 +72,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 					if (ConnectionSettings.ConnectionReset || session.DatabaseOverride is not null)
 					{
 						if (timeoutMilliseconds != 0)
-							session.SetTimeout(Math.Max(1, timeoutMilliseconds - (Environment.TickCount - startTickCount)));
+							session.SetTimeout(Math.Max(1, timeoutMilliseconds - (int) Utility.GetElapsedMilliseconds(startingTimestamp)));
 						reuseSession = await session.TryResetConnectionAsync(ConnectionSettings, connection, ioBehavior, cancellationToken).ConfigureAwait(false);
 						session.SetTimeout(Constants.InfiniteTimeout);
 					}
@@ -103,14 +103,14 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 					ActivitySourceHelper.CopyTags(session.ActivityTags, activity);
 					Log.ReturningPooledSession(m_logger, Id, session.Id, leasedSessionsCountPooled);
 
-					session.LastLeasedTicks = unchecked((uint) Environment.TickCount);
-					MetricsReporter.RecordWaitTime(this, unchecked(session.LastLeasedTicks - (uint) startTickCount));
+					session.LastLeasedTimestamp = Stopwatch.GetTimestamp();
+					MetricsReporter.RecordWaitTime(this, Utility.GetElapsedMilliseconds(startingTimestamp, session.LastLeasedTimestamp));
 					return session;
 				}
 			}
 
 			// create a new session
-			session = await ConnectSessionAsync(connection, s_createdNewSession, startTickCount, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+			session = await ConnectSessionAsync(connection, s_createdNewSession, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
 			AdjustHostConnectionCount(session, 1);
 			session.OwningConnection = new(connection);
 			int leasedSessionsCountNew;
@@ -122,8 +122,8 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			MetricsReporter.AddUsed(this);
 			Log.ReturningNewSession(m_logger, Id, session.Id, leasedSessionsCountNew);
 
-			session.LastLeasedTicks = unchecked((uint) Environment.TickCount);
-			MetricsReporter.RecordCreateTime(this, unchecked(session.LastLeasedTicks - (uint) startTickCount));
+			session.LastLeasedTimestamp = Stopwatch.GetTimestamp();
+			MetricsReporter.RecordCreateTime(this, Utility.GetElapsedMilliseconds(startingTimestamp, session.LastLeasedTimestamp));
 			return session;
 		}
 		catch (Exception ex)
@@ -161,7 +161,7 @@ private int GetSessionHealth(ServerSession session)
 		if (session.PoolGeneration != m_generation)
 			return 2;
 		if (ConnectionSettings.ConnectionLifeTime > 0
-			&& unchecked((uint) Environment.TickCount) - session.CreatedTicks >= ConnectionSettings.ConnectionLifeTime)
+			&& Utility.GetElapsedMilliseconds(session.CreatedTimestamp) >= ConnectionSettings.ConnectionLifeTime)
 			return 3;
 
 		return 0;
@@ -214,7 +214,7 @@ public async Task ReapAsync(IOBehavior ioBehavior, CancellationToken cancellatio
 	{
 		Log.ReapingConnectionPool(m_logger, Id);
 		await RecoverLeakedSessionsAsync(ioBehavior).ConfigureAwait(false);
-		await CleanPoolAsync(ioBehavior, session => (unchecked((uint) Environment.TickCount) - session.LastReturnedTicks) / 1000 >= ConnectionSettings.ConnectionIdleTimeout, true, cancellationToken).ConfigureAwait(false);
+		await CleanPoolAsync(ioBehavior, session => Utility.GetElapsedMilliseconds(session.LastReturnedTimestamp) / 1000 >= ConnectionSettings.ConnectionIdleTimeout, true, cancellationToken).ConfigureAwait(false);
 	}
 
 	/// <summary>
@@ -403,7 +403,7 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh
 
 			try
 			{
-				var session = await ConnectSessionAsync(connection, s_createdToReachMinimumPoolSize, Environment.TickCount, null, ioBehavior, cancellationToken).ConfigureAwait(false);
+				var session = await ConnectSessionAsync(connection, s_createdToReachMinimumPoolSize, Stopwatch.GetTimestamp(), null, ioBehavior, cancellationToken).ConfigureAwait(false);
 				AdjustHostConnectionCount(session, 1);
 				lock (m_sessions)
 					m_sessions.AddFirst(session);
@@ -417,7 +417,7 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh
 		}
 	}
 
-	private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection connection, Action<ILogger, int, string, Exception?> logMessage, int startTickCount, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection connection, Action<ILogger, int, string, Exception?> logMessage, long startingTimestamp, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		var session = new ServerSession(m_connectionLogger, this, m_generation, Interlocked.Increment(ref m_lastSessionId));
 		if (m_logger.IsEnabled(LogLevel.Debug))
@@ -425,7 +425,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 		string? statusInfo;
 		try
 		{
-			statusInfo = await session.ConnectAsync(ConnectionSettings, connection, startTickCount, m_loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+			statusInfo = await session.ConnectAsync(ConnectionSettings, connection, startingTimestamp, m_loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
 		}
 		catch (Exception)
 		{
@@ -452,7 +452,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 					var redirectedSession = new ServerSession(m_connectionLogger, this, m_generation, Interlocked.Increment(ref m_lastSessionId));
 					try
 					{
-						await redirectedSession.ConnectAsync(redirectedSettings, connection, startTickCount, m_loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+						await redirectedSession.ConnectAsync(redirectedSettings, connection, startingTimestamp, m_loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
 					}
 					catch (Exception ex)
 					{
diff --git a/src/MySqlConnector/Core/MetricsReporter.cs b/src/MySqlConnector/Core/MetricsReporter.cs
index 0336b0f5c..34b7f10eb 100644
--- a/src/MySqlConnector/Core/MetricsReporter.cs
+++ b/src/MySqlConnector/Core/MetricsReporter.cs
@@ -9,9 +9,9 @@ internal static class MetricsReporter
 	public static void RemoveIdle(ConnectionPool pool) => s_connectionsUsageCounter.Add(-1, pool.IdleStateTagList);
 	public static void AddUsed(ConnectionPool pool) => s_connectionsUsageCounter.Add(1, pool.UsedStateTagList);
 	public static void RemoveUsed(ConnectionPool pool) => s_connectionsUsageCounter.Add(-1, pool.UsedStateTagList);
-	public static void RecordCreateTime(ConnectionPool pool, uint ticks) => s_createTimeHistory.Record(ticks, pool.PoolNameTagList);
-	public static void RecordUseTime(ConnectionPool pool, uint ticks) => s_useTimeHistory.Record(ticks, pool.PoolNameTagList);
-	public static void RecordWaitTime(ConnectionPool pool, uint ticks) => s_waitTimeHistory.Record(ticks, pool.PoolNameTagList);
+	public static void RecordCreateTime(ConnectionPool pool, float milliseconds) => s_createTimeHistory.Record(milliseconds, pool.PoolNameTagList);
+	public static void RecordUseTime(ConnectionPool pool, float milliseconds) => s_useTimeHistory.Record(milliseconds, pool.PoolNameTagList);
+	public static void RecordWaitTime(ConnectionPool pool, float milliseconds) => s_waitTimeHistory.Record(milliseconds, pool.PoolNameTagList);
 
 	public static void AddPendingRequest(ConnectionPool? pool)
 	{
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 097450b1b..35cbf6670 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -38,7 +38,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 		m_payloadCache = new();
 		Id = (pool?.Id ?? 0) + "." + id;
 		ServerVersion = ServerVersion.Empty;
-		CreatedTicks = unchecked((uint) Environment.TickCount);
+		CreatedTimestamp = Stopwatch.GetTimestamp();
 		Pool = pool;
 		PoolGeneration = poolGeneration;
 		HostName = "";
@@ -54,11 +54,11 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public int CancellationTimeout { get; private set; }
 	public int ConnectionId { get; set; }
 	public byte[]? AuthPluginData { get; set; }
-	public uint CreatedTicks { get; }
+	public long CreatedTimestamp { get; }
 	public ConnectionPool? Pool { get; }
 	public int PoolGeneration { get; }
-	public uint LastLeasedTicks { get; set; }
-	public uint LastReturnedTicks { get; private set; }
+	public long LastLeasedTimestamp { get; set; }
+	public long LastReturnedTimestamp { get; private set; }
 	public string? DatabaseOverride { get; set; }
 	public string HostName { get; private set; }
 	public IPEndPoint? IPEndPoint => m_tcpClient?.Client.RemoteEndPoint as IPEndPoint;
@@ -75,11 +75,11 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? owningConnection)
 	{
 		Log.ReturningToPool(m_logger, Id, Pool?.Id ?? 0);
-		LastReturnedTicks = unchecked((uint) Environment.TickCount);
+		LastReturnedTimestamp = Stopwatch.GetTimestamp();
 		if (Pool is null)
 			return default;
-		MetricsReporter.RecordUseTime(Pool, unchecked(LastReturnedTicks - LastLeasedTicks));
-		LastLeasedTicks = 0;
+		MetricsReporter.RecordUseTime(Pool, Utility.GetElapsedMilliseconds(LastLeasedTimestamp, LastReturnedTimestamp));
+		LastLeasedTimestamp = 0;
 		return Pool.ReturnAsync(ioBehavior, this);
 	}
 
@@ -392,7 +392,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			m_state = State.Closed;
 	}
 
-	public async Task<string?> ConnectAsync(ConnectionSettings cs, MySqlConnection connection, int startTickCount, ILoadBalancer? loadBalancer, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	public async Task<string?> ConnectAsync(ConnectionSettings cs, MySqlConnection connection, long startingTimestamp, ILoadBalancer? loadBalancer, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		string? statusInfo = null;
 
@@ -445,7 +445,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				else if (cs.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket)
 					connected = await OpenUnixSocketAsync(cs, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
 				else if (cs.ConnectionProtocol == MySqlConnectionProtocol.NamedPipe)
-					connected = await OpenNamedPipeAsync(cs, startTickCount, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+					connected = await OpenNamedPipeAsync(cs, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
 				if (!connected)
 				{
 					lock (m_lock)
@@ -456,7 +456,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 
 				var byteHandler = m_socket is null ? new StreamByteHandler(m_stream!) : (IByteHandler) new SocketByteHandler(m_socket);
 				if (cs.ConnectionTimeout != 0)
-					byteHandler.RemainingTimeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - unchecked(Environment.TickCount - startTickCount));
+					byteHandler.RemainingTimeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - (int) Utility.GetElapsedMilliseconds(startingTimestamp));
 				m_payloadHandler = new StandardPayloadHandler(byteHandler);
 
 				payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
@@ -1200,7 +1200,7 @@ private async Task<bool> OpenUnixSocketAsync(ConnectionSettings cs, Activity? ac
 		return false;
 	}
 
-	private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, int startTickCount, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, long startingTimestamp, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		Log.ConnectingToNamedPipe(m_logger, Id, cs.PipeName, cs.HostNames![0]);
 
@@ -1218,7 +1218,7 @@ private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, int startTick
 		}
 
 		var namedPipeStream = new NamedPipeClientStream(cs.HostNames![0], cs.PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
-		var timeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - unchecked(Environment.TickCount - startTickCount));
+		var timeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - (int) Utility.GetElapsedMilliseconds(startingTimestamp));
 		try
 		{
 			using (cancellationToken.Register(namedPipeStream.Dispose))
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index dfe3a0c8b..c5975e7ce 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -384,7 +384,7 @@ private async ValueTask<bool> PingAsync(IOBehavior ioBehavior, CancellationToken
 
 	internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancellationToken)
 	{
-		var openStartTickCount = Environment.TickCount;
+		var openStartingTimestamp = Stopwatch.GetTimestamp();
 
 		VerifyNotDisposed();
 		cancellationToken.ThrowIfCancellationRequested();
@@ -416,7 +416,7 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 
 			try
 			{
-				m_session = await CreateSessionAsync(pool, openStartTickCount, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+				m_session = await CreateSessionAsync(pool, openStartingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
 				m_hasBeenOpened = true;
 				SetState(ConnectionState.Open);
 			}
@@ -894,7 +894,7 @@ internal void FinishQuerying(bool hasWarnings)
 		}
 	}
 
-	private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool, int startTickCount, Activity? activity, IOBehavior? ioBehavior, CancellationToken cancellationToken)
+	private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool, long startingTimestamp, Activity? activity, IOBehavior? ioBehavior, CancellationToken cancellationToken)
 	{
 		MetricsReporter.AddPendingRequest(pool);
 		var connectionSettings = GetInitializedConnectionSettings();
@@ -907,7 +907,7 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 			// the cancellation token for connection is controlled by 'cancellationToken' (if it can be cancelled), ConnectionTimeout
 			// (from the connection string, if non-zero), or a combination of both
 			if (connectionSettings.ConnectionTimeout != 0)
-				timeoutSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(Math.Max(1, connectionSettings.ConnectionTimeoutMilliseconds - unchecked(Environment.TickCount - startTickCount))));
+				timeoutSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(Math.Max(1, connectionSettings.ConnectionTimeoutMilliseconds - (int) Utility.GetElapsedMilliseconds(startingTimestamp))));
 			if (cancellationToken.CanBeCanceled && timeoutSource is not null)
 				linkedSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutSource.Token);
 			var connectToken = linkedSource?.Token ?? timeoutSource?.Token ?? cancellationToken;
@@ -916,7 +916,7 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 			if (pool is not null)
 			{
 				// this returns an open session
-				return await pool.GetSessionAsync(this, startTickCount, connectionSettings.ConnectionTimeoutMilliseconds, activity, actualIOBehavior, connectToken).ConfigureAwait(false);
+				return await pool.GetSessionAsync(this, startingTimestamp, connectionSettings.ConnectionTimeoutMilliseconds, activity, actualIOBehavior, connectToken).ConfigureAwait(false);
 			}
 			else
 			{
@@ -929,7 +929,7 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 				Log.CreatedNonPooledSession(m_logger, session.Id);
 				try
 				{
-					await session.ConnectAsync(connectionSettings, this, startTickCount, loadBalancer, activity, actualIOBehavior, connectToken).ConfigureAwait(false);
+					await session.ConnectAsync(connectionSettings, this, startingTimestamp, loadBalancer, activity, actualIOBehavior, connectToken).ConfigureAwait(false);
 					return session;
 				}
 				catch (Exception)
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 651ef5bff..a3216e5cf 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -1,5 +1,6 @@
 using System.Buffers;
 using System.Buffers.Text;
+using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Net;
 using System.Reflection;
@@ -564,6 +565,26 @@ public static void GetOSDetails(out string? os, out string osDescription, out st
 	}
 #endif
 
+	/// <summary>
+	/// Gets the elapsed time (in milliseconds) since the specified <paramref name="startingTimestamp"/> (which must be a value returned from <see cref="Stopwatch.GetTimestamp"/>.
+	/// </summary>
+	public static float GetElapsedMilliseconds(long startingTimestamp) =>
+#if NET7_0_OR_GREATER
+		(float) Stopwatch.GetElapsedTime(startingTimestamp).TotalMilliseconds;
+#else
+		GetElapsedMilliseconds(startingTimestamp, Stopwatch.GetTimestamp());
+#endif
+
+	/// <summary>
+	/// Gets the elapsed time (in milliseconds) between the specified <paramref name="startingTimestamp"/> and <paramref name="endingTimestamp"/>. (These must be values returned from <see cref="Stopwatch.GetTimestamp"/>.)
+	/// </summary>
+	public static float GetElapsedMilliseconds(long startingTimestamp, long endingTimestamp) =>
+#if NET7_0_OR_GREATER
+		(float) Stopwatch.GetElapsedTime(startingTimestamp, endingTimestamp).TotalMilliseconds;
+#else
+		(endingTimestamp - startingTimestamp) * 1000.0f / Stopwatch.Frequency;
+#endif
+
 #if NET462
 	public static SslProtocols GetDefaultSslProtocols()
 	{

From c5ca41c29a9ea3d5315856003fc482270f6759b7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 15 Nov 2023 16:21:33 -0800
Subject: [PATCH 212/393] Change metrics duration units to seconds. Fixes #1396

---
 docs/content/diagnostics/metrics.md            |  6 +++---
 src/MySqlConnector/Core/ConnectionPool.cs      |  6 +++---
 src/MySqlConnector/Core/MetricsReporter.cs     | 18 +++++++++---------
 src/MySqlConnector/Core/ServerSession.cs       |  6 +++---
 src/MySqlConnector/MySqlConnection.cs          |  2 +-
 src/MySqlConnector/Utilities/Utility.cs        | 14 +++++++-------
 .../Metrics/ConnectionTimeTests.cs             | 12 ++++++------
 .../Metrics/MetricsTestsBase.cs                | 14 +++++++-------
 8 files changed, 39 insertions(+), 39 deletions(-)

diff --git a/docs/content/diagnostics/metrics.md b/docs/content/diagnostics/metrics.md
index 720615cf1..24ade9245 100644
--- a/docs/content/diagnostics/metrics.md
+++ b/docs/content/diagnostics/metrics.md
@@ -34,9 +34,9 @@ Name | Type | Unit | Description
 --- | --- | --- | ---
 `db.client.connections.usage` | UpDownCounter | `{connection}` | The number of connections that are currently in the state described by the state tag.
 `db.client.connections.pending_requests` | UpDownCounter | `{request}` | The number of pending requests for an open connection, cumulative for the entire pool.
-`db.client.connections.create_time` | Histogram | `ms` | The time it took to create a new connection.
-`db.client.connections.use_time` | Histogram | `ms` | The time between borrowing a connection and returning it to the pool.
-`db.client.connections.wait_time` | Histogram | `ms` | The time it took to obtain an open connection from the pool.
+`db.client.connections.create_time` | Histogram | `s` | The time it took to create a new connection.
+`db.client.connections.use_time` | Histogram | `s` | The time between borrowing a connection and returning it to the pool.
+`db.client.connections.wait_time` | Histogram | `s` | The time it took to obtain an open connection from the pool.
 `db.client.connections.idle.max` | UpDownCounter | `{connection}` | The maximum number of idle open connections allowed; this corresponds to `MaximumPoolSize` in the connection string.
 `db.client.connections.idle.min` | UpDownCounter | `{connection}` | The minimum number of idle open connections allowed; this corresponds to `MinimumPoolSize` in the connection string.
 `db.client.connections.max` | UpDownCounter | `{connection}` | The maximum number of open connections allowed; this corresponds to `MaximumPoolSize` in the connection string.
diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index b3be668d2..7f464b501 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -72,7 +72,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 					if (ConnectionSettings.ConnectionReset || session.DatabaseOverride is not null)
 					{
 						if (timeoutMilliseconds != 0)
-							session.SetTimeout(Math.Max(1, timeoutMilliseconds - (int) Utility.GetElapsedMilliseconds(startingTimestamp)));
+							session.SetTimeout(Math.Max(1, timeoutMilliseconds - Utility.GetElapsedMilliseconds(startingTimestamp)));
 						reuseSession = await session.TryResetConnectionAsync(ConnectionSettings, connection, ioBehavior, cancellationToken).ConfigureAwait(false);
 						session.SetTimeout(Constants.InfiniteTimeout);
 					}
@@ -104,7 +104,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 					Log.ReturningPooledSession(m_logger, Id, session.Id, leasedSessionsCountPooled);
 
 					session.LastLeasedTimestamp = Stopwatch.GetTimestamp();
-					MetricsReporter.RecordWaitTime(this, Utility.GetElapsedMilliseconds(startingTimestamp, session.LastLeasedTimestamp));
+					MetricsReporter.RecordWaitTime(this, Utility.GetElapsedSeconds(startingTimestamp, session.LastLeasedTimestamp));
 					return session;
 				}
 			}
@@ -123,7 +123,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			Log.ReturningNewSession(m_logger, Id, session.Id, leasedSessionsCountNew);
 
 			session.LastLeasedTimestamp = Stopwatch.GetTimestamp();
-			MetricsReporter.RecordCreateTime(this, Utility.GetElapsedMilliseconds(startingTimestamp, session.LastLeasedTimestamp));
+			MetricsReporter.RecordCreateTime(this, Utility.GetElapsedSeconds(startingTimestamp, session.LastLeasedTimestamp));
 			return session;
 		}
 		catch (Exception ex)
diff --git a/src/MySqlConnector/Core/MetricsReporter.cs b/src/MySqlConnector/Core/MetricsReporter.cs
index 34b7f10eb..86599d774 100644
--- a/src/MySqlConnector/Core/MetricsReporter.cs
+++ b/src/MySqlConnector/Core/MetricsReporter.cs
@@ -9,9 +9,9 @@ internal static class MetricsReporter
 	public static void RemoveIdle(ConnectionPool pool) => s_connectionsUsageCounter.Add(-1, pool.IdleStateTagList);
 	public static void AddUsed(ConnectionPool pool) => s_connectionsUsageCounter.Add(1, pool.UsedStateTagList);
 	public static void RemoveUsed(ConnectionPool pool) => s_connectionsUsageCounter.Add(-1, pool.UsedStateTagList);
-	public static void RecordCreateTime(ConnectionPool pool, float milliseconds) => s_createTimeHistory.Record(milliseconds, pool.PoolNameTagList);
-	public static void RecordUseTime(ConnectionPool pool, float milliseconds) => s_useTimeHistory.Record(milliseconds, pool.PoolNameTagList);
-	public static void RecordWaitTime(ConnectionPool pool, float milliseconds) => s_waitTimeHistory.Record(milliseconds, pool.PoolNameTagList);
+	public static void RecordCreateTime(ConnectionPool pool, double seconds) => s_createTimeHistory.Record(seconds, pool.PoolNameTagList);
+	public static void RecordUseTime(ConnectionPool pool, double seconds) => s_useTimeHistory.Record(seconds, pool.PoolNameTagList);
+	public static void RecordWaitTime(ConnectionPool pool, double seconds) => s_waitTimeHistory.Record(seconds, pool.PoolNameTagList);
 
 	public static void AddPendingRequest(ConnectionPool? pool)
 	{
@@ -48,10 +48,10 @@ static IEnumerable<Measurement<int>> GetMinimumConnections() =>
 			unit: "{connection}", description: "The number of connections that are currently in the state described by the state tag.");
 	private static readonly UpDownCounter<int> s_pendingRequestsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.pending_requests",
 			unit: "{request}", description: "The number of pending requests for an open connection, cumulative for the entire pool.");
-	private static readonly Histogram<float> s_createTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.create_time",
-			unit: "ms", description: "The time it took to create a new connection.");
-	private static readonly Histogram<float> s_useTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.use_time",
-		unit: "ms", description: "The time between borrowing a connection and returning it to the pool.");
-	private static readonly Histogram<float> s_waitTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<float>("db.client.connections.wait_time",
-			unit: "ms", description: "The time it took to obtain an open connection from the pool.");
+	private static readonly Histogram<double> s_createTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<double>("db.client.connections.create_time",
+			unit: "s", description: "The time it took to create a new connection.");
+	private static readonly Histogram<double> s_useTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<double>("db.client.connections.use_time",
+		unit: "s", description: "The time between borrowing a connection and returning it to the pool.");
+	private static readonly Histogram<double> s_waitTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<double>("db.client.connections.wait_time",
+			unit: "s", description: "The time it took to obtain an open connection from the pool.");
 }
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 35cbf6670..7f8e471a7 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -78,7 +78,7 @@ public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? ownin
 		LastReturnedTimestamp = Stopwatch.GetTimestamp();
 		if (Pool is null)
 			return default;
-		MetricsReporter.RecordUseTime(Pool, Utility.GetElapsedMilliseconds(LastLeasedTimestamp, LastReturnedTimestamp));
+		MetricsReporter.RecordUseTime(Pool, Utility.GetElapsedSeconds(LastLeasedTimestamp, LastReturnedTimestamp));
 		LastLeasedTimestamp = 0;
 		return Pool.ReturnAsync(ioBehavior, this);
 	}
@@ -456,7 +456,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 
 				var byteHandler = m_socket is null ? new StreamByteHandler(m_stream!) : (IByteHandler) new SocketByteHandler(m_socket);
 				if (cs.ConnectionTimeout != 0)
-					byteHandler.RemainingTimeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - (int) Utility.GetElapsedMilliseconds(startingTimestamp));
+					byteHandler.RemainingTimeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - Utility.GetElapsedMilliseconds(startingTimestamp));
 				m_payloadHandler = new StandardPayloadHandler(byteHandler);
 
 				payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
@@ -1218,7 +1218,7 @@ private async Task<bool> OpenNamedPipeAsync(ConnectionSettings cs, long starting
 		}
 
 		var namedPipeStream = new NamedPipeClientStream(cs.HostNames![0], cs.PipeName, PipeDirection.InOut, PipeOptions.Asynchronous);
-		var timeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - (int) Utility.GetElapsedMilliseconds(startingTimestamp));
+		var timeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - Utility.GetElapsedMilliseconds(startingTimestamp));
 		try
 		{
 			using (cancellationToken.Register(namedPipeStream.Dispose))
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index c5975e7ce..46c1668fe 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -907,7 +907,7 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 			// the cancellation token for connection is controlled by 'cancellationToken' (if it can be cancelled), ConnectionTimeout
 			// (from the connection string, if non-zero), or a combination of both
 			if (connectionSettings.ConnectionTimeout != 0)
-				timeoutSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(Math.Max(1, connectionSettings.ConnectionTimeoutMilliseconds - (int) Utility.GetElapsedMilliseconds(startingTimestamp))));
+				timeoutSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(Math.Max(1, connectionSettings.ConnectionTimeoutMilliseconds - Utility.GetElapsedMilliseconds(startingTimestamp))));
 			if (cancellationToken.CanBeCanceled && timeoutSource is not null)
 				linkedSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, timeoutSource.Token);
 			var connectToken = linkedSource?.Token ?? timeoutSource?.Token ?? cancellationToken;
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index a3216e5cf..2b2ce0332 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -568,21 +568,21 @@ public static void GetOSDetails(out string? os, out string osDescription, out st
 	/// <summary>
 	/// Gets the elapsed time (in milliseconds) since the specified <paramref name="startingTimestamp"/> (which must be a value returned from <see cref="Stopwatch.GetTimestamp"/>.
 	/// </summary>
-	public static float GetElapsedMilliseconds(long startingTimestamp) =>
+	public static int GetElapsedMilliseconds(long startingTimestamp) =>
 #if NET7_0_OR_GREATER
-		(float) Stopwatch.GetElapsedTime(startingTimestamp).TotalMilliseconds;
+		(int) Stopwatch.GetElapsedTime(startingTimestamp).TotalMilliseconds;
 #else
-		GetElapsedMilliseconds(startingTimestamp, Stopwatch.GetTimestamp());
+		(int) ((Stopwatch.GetTimestamp() - startingTimestamp) * 1000L / Stopwatch.Frequency);
 #endif
 
 	/// <summary>
-	/// Gets the elapsed time (in milliseconds) between the specified <paramref name="startingTimestamp"/> and <paramref name="endingTimestamp"/>. (These must be values returned from <see cref="Stopwatch.GetTimestamp"/>.)
+	/// Gets the elapsed time (in seconds) between the specified <paramref name="startingTimestamp"/> and <paramref name="endingTimestamp"/>. (These must be values returned from <see cref="Stopwatch.GetTimestamp"/>.)
 	/// </summary>
-	public static float GetElapsedMilliseconds(long startingTimestamp, long endingTimestamp) =>
+	public static double GetElapsedSeconds(long startingTimestamp, long endingTimestamp) =>
 #if NET7_0_OR_GREATER
-		(float) Stopwatch.GetElapsedTime(startingTimestamp, endingTimestamp).TotalMilliseconds;
+		Stopwatch.GetElapsedTime(startingTimestamp, endingTimestamp).TotalSeconds;
 #else
-		(endingTimestamp - startingTimestamp) * 1000.0f / Stopwatch.Frequency;
+		(endingTimestamp - startingTimestamp) / (double) Stopwatch.Frequency;
 #endif
 
 #if NET462
diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
index 98156fe21..4a919f8ee 100644
--- a/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionTimeTests.cs
@@ -11,7 +11,7 @@ public async Task ConnectionTime()
 		await connection.OpenAsync();
 		var measurements = GetAndClearMeasurements("db.client.connections.create_time");
 		var time = Assert.Single(measurements);
-		Assert.InRange(time, 0, 300);
+		Assert.InRange(time, 0, 0.3);
 	}
 
 	[Fact(Skip = MetricsSkip)]
@@ -25,7 +25,7 @@ public async Task ConnectionTimeWithDelay()
 		await connection.OpenAsync();
 		var measurements = GetAndClearMeasurements("db.client.connections.create_time");
 		var time = Assert.Single(measurements);
-		Assert.InRange(time, 1000, 1300);
+		Assert.InRange(time, 1.0, 1.3);
 	}
 
 	[Fact(Skip = MetricsSkip)]
@@ -41,7 +41,7 @@ public async Task OpenFromPoolTime()
 		await connection.OpenAsync();
 		var measurements = GetAndClearMeasurements("db.client.connections.wait_time");
 		var time = Assert.Single(measurements);
-		Assert.InRange(time, 0, 200);
+		Assert.InRange(time, 0, 0.2);
 	}
 
 	[Fact(Skip = MetricsSkip)]
@@ -58,7 +58,7 @@ public async Task OpenFromPoolTimeWithDelay()
 		await connection.OpenAsync();
 		var measurements = GetAndClearMeasurements("db.client.connections.wait_time");
 		var time = Assert.Single(measurements);
-		Assert.InRange(time, 1000, 1200);
+		Assert.InRange(time, 1.0, 1.2);
 	}
 
 	[Fact(Skip = MetricsSkip)]
@@ -72,7 +72,7 @@ public async Task UseTime()
 		connection.Close();
 
 		var time = Assert.Single(GetAndClearMeasurements("db.client.connections.use_time"));
-		Assert.InRange(time, 0, 100);
+		Assert.InRange(time, 0, 0.1);
 	}
 
 	[Fact(Skip = MetricsSkip)]
@@ -87,6 +87,6 @@ public async Task UseTimeWithDelay()
 		connection.Close();
 
 		var time = Assert.Single(GetAndClearMeasurements("db.client.connections.use_time"));
-		Assert.InRange(time, 500, 600);
+		Assert.InRange(time, 0.5, 0.6);
 	}
 }
diff --git a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
index 6751b7477..a40c4d425 100644
--- a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
+++ b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
@@ -23,7 +23,7 @@ public MetricsTestsBase()
 			}
 		};
 		m_meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
-		m_meterListener.SetMeasurementEventCallback<float>(OnMeasurementRecorded);
+		m_meterListener.SetMeasurementEventCallback<double>(OnMeasurementRecorded);
 		m_meterListener.Start();
 	}
 
@@ -68,11 +68,11 @@ protected void AssertMeasurement(string name, int expected)
 			Assert.Equal(expected, m_measurements.GetValueOrDefault(name));
 	}
 
-	protected List<float> GetAndClearMeasurements(string name)
+	protected List<double> GetAndClearMeasurements(string name)
 	{
 		if (!m_timeMeasurements.TryGetValue(name, out var list))
-			list = new List<float>();
-		m_timeMeasurements[name] = new List<float>();
+			list = new();
+		m_timeMeasurements[name] = new List<double>();
 		return list;
 	}
 
@@ -96,7 +96,7 @@ private void OnMeasurementRecorded(Instrument instrument, int measurement, ReadO
 		}
 	}
 
-	private void OnMeasurementRecorded(Instrument instrument, float measurement, ReadOnlySpan<KeyValuePair<string, object?>> tags, object? state)
+	private void OnMeasurementRecorded(Instrument instrument, double measurement, ReadOnlySpan<KeyValuePair<string, object?>> tags, object? state)
 	{
 		var (poolName, stateTag) = GetTags(tags);
 		if (poolName != PoolName)
@@ -105,7 +105,7 @@ private void OnMeasurementRecorded(Instrument instrument, float measurement, Rea
 		lock (m_timeMeasurements)
 		{
 			if (!m_timeMeasurements.TryGetValue(instrument.Name, out var list))
-				list = m_timeMeasurements[instrument.Name] = new List<float>();
+				list = m_timeMeasurements[instrument.Name] = new List<double>();
 			list.Add(measurement);
 		}
 	}
@@ -126,6 +126,6 @@ private void OnMeasurementRecorded(Instrument instrument, float measurement, Rea
 
 
 	private readonly Dictionary<string, int> m_measurements;
-	private readonly Dictionary<string, List<float>> m_timeMeasurements;
+	private readonly Dictionary<string, List<double>> m_timeMeasurements;
 	private readonly MeterListener m_meterListener;
 }

From cd76fd3c34d18ace5935d6db049c15ef805f586b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 16 Nov 2023 12:45:56 -0800
Subject: [PATCH 213/393] Support keyed services in dependency injection. Fixes
 #1391

Add tests for MySqlConnector.DependencyInjection project.
---
 .ci/build-steps.yml                           |  12 ++
 .ci/mysqlconnector-tests-steps.yml            |  10 +
 .ci/test.ps1                                  |   6 +
 Directory.Packages.props                      |   3 +-
 MySqlConnector.sln                            |   6 +
 ...SqlConnectorServiceCollectionExtensions.cs |  76 +++++++-
 .../docs/README.md                            |  30 +++
 src/MySqlConnector/MySqlConnector.csproj      |   1 +
 .../DependencyInjectionTests.cs               | 174 ++++++++++++++++++
 ...Connector.DependencyInjection.Tests.csproj |  33 ++++
 10 files changed, 348 insertions(+), 3 deletions(-)
 create mode 100644 tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs
 create mode 100644 tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj

diff --git a/.ci/build-steps.yml b/.ci/build-steps.yml
index c95f7acf3..e81044196 100644
--- a/.ci/build-steps.yml
+++ b/.ci/build-steps.yml
@@ -47,6 +47,18 @@ steps:
     artifactName: 'Conformance.Tests-8.0-$(Agent.OS)'
     targetPath: 'artifacts/publish/Conformance.Tests/release_net8.0'
 
+- task: DotNetCoreCLI@2
+  displayName: 'Publish MySqlConnector.DependencyInjection.Tests'
+  inputs:
+    command: 'publish'
+    arguments: '-c Release -f net8.0 --no-build tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj'
+    publishWebProjects: false
+    zipAfterPublish: false
+- task: PublishPipelineArtifact@0
+  inputs:
+    artifactName: 'MySqlConnector.DependencyInjection.Tests-8.0-$(Agent.OS)'
+    targetPath: 'artifacts/publish/MySqlConnector.DependencyInjection.Tests/release_net8.0'
+
 - task: DotNetCoreCLI@2
   displayName: 'Publish IntegrationTests (7.0)'
   inputs:
diff --git a/.ci/mysqlconnector-tests-steps.yml b/.ci/mysqlconnector-tests-steps.yml
index ca4b1ce8f..012b11a0b 100644
--- a/.ci/mysqlconnector-tests-steps.yml
+++ b/.ci/mysqlconnector-tests-steps.yml
@@ -14,6 +14,16 @@ steps:
     command: 'custom'
     custom: 'vstest'
     arguments: 'MySqlConnector.Tests.dll /logger:trx'
+- task: DownloadPipelineArtifact@0
+  inputs:
+    artifactName: 'MySqlConnector.DependencyInjection.Tests-8.0-$(Agent.OS)'
+    targetPath: $(System.DefaultWorkingDirectory)
+- task: DotNetCoreCLI@2
+  displayName: 'Run MySqlConnector.DependencyInjection.Tests'
+  inputs:
+    command: 'custom'
+    custom: 'vstest'
+    arguments: 'MySqlConnector.DependencyInjection.Tests.dll /logger:trx'
 - task: PublishTestResults@2
   inputs:
     testResultsFormat: VSTest
diff --git a/.ci/test.ps1 b/.ci/test.ps1
index 7ebe26842..a619c6402 100644
--- a/.ci/test.ps1
+++ b/.ci/test.ps1
@@ -23,6 +23,12 @@ if ($LASTEXITCODE -ne 0){
     exit $LASTEXITCODE;
 }
 popd
+pushd tests\MySqlConnector.DependencyInjection.Tests
+dotnet test -c Release
+if ($LASTEXITCODE -ne 0){
+    exit $LASTEXITCODE;
+}
+popd
 
 pushd .\tests\IntegrationTests
 
diff --git a/Directory.Packages.props b/Directory.Packages.props
index b319ba3da..d8225df5a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -12,7 +12,8 @@
     <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
-    <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="7.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
     <PackageVersion Include="MySql.Data" Version="8.2.0" />
diff --git a/MySqlConnector.sln b/MySqlConnector.sln
index 0d56e5fa4..c3646c415 100644
--- a/MySqlConnector.sln
+++ b/MySqlConnector.sln
@@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SchemaCollectionGenerator",
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.DependencyInjection", "src\MySqlConnector.DependencyInjection\MySqlConnector.DependencyInjection.csproj", "{D48B3619-7FE1-420C-A96C-B231B7EA73EA}"
 EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.DependencyInjection.Tests", "tests\MySqlConnector.DependencyInjection.Tests\MySqlConnector.DependencyInjection.Tests.csproj", "{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -76,6 +78,10 @@ Global
 		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Release|Any CPU.Build.0 = Release|Any CPU
+		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs b/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
index 782ac6183..f00ff40db 100644
--- a/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
+++ b/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
@@ -42,6 +42,45 @@ public static IServiceCollection AddMySqlDataSource(
 		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
 		DoAddMySqlDataSource(serviceCollection, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
 
+	/// <summary>
+	/// Registers a <see cref="MySqlDataSource" /> and a <see cref="MySqlConnection" /> in the <see cref="IServiceCollection" />.
+	/// </summary>
+	/// <param name="serviceCollection">The <see cref="IServiceCollection" /> to add services to.</param>
+	/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+	/// <param name="connectionString">A MySQL connection string.</param>
+	/// <param name="connectionLifetime">The lifetime with which to register the <see cref="MySqlConnection" /> in the container. Defaults to <see cref="ServiceLifetime.Transient" />.</param>
+	/// <param name="dataSourceLifetime">The lifetime with which to register the <see cref="MySqlDataSource" /> service in the container. Defaults to <see cref="ServiceLifetime.Singleton" />.</param>
+	/// <returns>The same service collection so that multiple calls can be chained.</returns>
+	/// <remarks>If the <paramref name="serviceKey"/> is a <see langword="string"/>, it will automatically be used to initialize the data source name.</remarks>
+	public static IServiceCollection AddKeyedMySqlDataSource(
+		this IServiceCollection serviceCollection,
+		object? serviceKey,
+		string connectionString,
+		ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
+		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
+		DoAddMySqlDataSource(serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
+
+	/// <summary>
+	/// Registers a <see cref="MySqlDataSource" /> and a <see cref="MySqlConnection" /> in the <see cref="IServiceCollection" />.
+	/// </summary>
+	/// <param name="serviceCollection">The <see cref="IServiceCollection" /> to add services to.</param>
+	/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+	/// <param name="connectionString">A MySQL connection string.</param>
+	/// <param name="dataSourceBuilderAction">An action to configure the <see cref="MySqlDataSourceBuilder" /> for further customizations of the <see cref="MySqlDataSource" />.</param>
+	/// <param name="connectionLifetime">The lifetime with which to register the <see cref="MySqlConnection" /> in the container. Defaults to <see cref="ServiceLifetime.Transient" />.</param>
+	/// <param name="dataSourceLifetime">The lifetime with which to register the <see cref="MySqlDataSource" /> service in the container. Defaults to <see cref="ServiceLifetime.Singleton" />.</param>
+	/// <returns>The same service collection so that multiple calls can be chained.</returns>
+	/// <remarks>If the <paramref name="serviceKey"/> is a <see langword="string"/>, it will automatically be used to initialize the data source name; this can
+	/// be overridden by the <paramref name="dataSourceBuilderAction"/> configuration action.</remarks>
+	public static IServiceCollection AddKeyedMySqlDataSource(
+		this IServiceCollection serviceCollection,
+		object? serviceKey,
+		string connectionString,
+		Action<MySqlDataSourceBuilder> dataSourceBuilderAction,
+		ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
+		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
+		DoAddMySqlDataSource(serviceCollection, serviceKey, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
+
 	private static IServiceCollection DoAddMySqlDataSource(
 		this IServiceCollection serviceCollection,
 		string connectionString,
@@ -52,10 +91,10 @@ private static IServiceCollection DoAddMySqlDataSource(
 		serviceCollection.TryAdd(
 			new ServiceDescriptor(
 				typeof(MySqlDataSource),
-				x =>
+				serviceProvider =>
 				{
 					var dataSourceBuilder = new MySqlDataSourceBuilder(connectionString)
-						.UseLoggerFactory(x.GetService<ILoggerFactory>());
+						.UseLoggerFactory(serviceProvider.GetService<ILoggerFactory>());
 					dataSourceBuilderAction?.Invoke(dataSourceBuilder);
 					return dataSourceBuilder.Build();
 				},
@@ -71,4 +110,37 @@ private static IServiceCollection DoAddMySqlDataSource(
 
 		return serviceCollection;
 	}
+
+	private static IServiceCollection DoAddMySqlDataSource(
+		this IServiceCollection serviceCollection,
+		object? serviceKey,
+		string connectionString,
+		Action<MySqlDataSourceBuilder>? dataSourceBuilderAction,
+		ServiceLifetime connectionLifetime,
+		ServiceLifetime dataSourceLifetime)
+	{
+		serviceCollection.TryAdd(
+			new ServiceDescriptor(
+				typeof(MySqlDataSource),
+				serviceKey,
+				(serviceProvider, serviceKey) =>
+				{
+					var dataSourceBuilder = new MySqlDataSourceBuilder(connectionString)
+						.UseLoggerFactory(serviceProvider.GetService<ILoggerFactory>())
+						.UseName(serviceKey as string);
+					dataSourceBuilderAction?.Invoke(dataSourceBuilder);
+					return dataSourceBuilder.Build();
+				},
+				dataSourceLifetime));
+
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(MySqlConnection), serviceKey, (sp, sk) => sp.GetRequiredKeyedService<MySqlDataSource>(sk).CreateConnection(), connectionLifetime));
+
+#if NET7_0_OR_GREATER
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbDataSource), serviceKey, (sp, sk) => sp.GetRequiredKeyedService<MySqlDataSource>(sk), dataSourceLifetime));
+#endif
+
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbConnection), serviceKey, (sp, sk) => sp.GetRequiredKeyedService<MySqlConnection>(sk), connectionLifetime));
+
+		return serviceCollection;
+	}
 }
diff --git a/src/MySqlConnector.DependencyInjection/docs/README.md b/src/MySqlConnector.DependencyInjection/docs/README.md
index d28d69389..a19a3f96b 100644
--- a/src/MySqlConnector.DependencyInjection/docs/README.md
+++ b/src/MySqlConnector.DependencyInjection/docs/README.md
@@ -48,3 +48,33 @@ builder.Services.AddMySqlDataSource("Server=server;User ID=test;Password=test;Da
 	x => x.UseRemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors) => { /* custom logic */ })
 );
 ```
+
+## Keyed Services
+
+Use the `AddKeyedMySqlDataSource` method to register a `MySqlDataSource` as a [keyed service](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#keyed-di-services).
+This is useful if you have multiple connection strings or need to connect to multiple databases.
+If the service key is a string, it will automatically be used as the `MySqlDataSource` name;
+to customize this, call the `AddKeyedMySqlDataSource(object?, string, Action<MySqlDataSourceBuilder>)` overload and call `MySqlDataSourceBuilder.UseName`.
+
+```csharp
+builder.Services.AddKeyedMySqlDataSource("users", builder.Configuration.GetConnectionString("Users"));
+builder.Services.AddKeyedMySqlDataSource("products", builder.Configuration.GetConnectionString("Products"));
+
+app.MapGet("/users/{userId}", async (int userId, [FromKeyedServices("users")] MySqlConnection connection) =>
+{
+    await connection.OpenAsync();
+    await using var command = connection.CreateCommand();
+    command.CommandText = "SELECT name FROM users WHERE user_id = @userId LIMIT 1";
+    command.Parameters.AddWithValue("@userId", userId);
+    return $"Hello, {await command.ExecuteScalarAsync()}";
+});
+
+app.MapGet("/products/{productId}", async (int productId, [FromKeyedServices("products")] MySqlConnection connection) =>
+{
+    await connection.OpenAsync();
+    await using var command = connection.CreateCommand();
+    command.CommandText = "SELECT name FROM products WHERE product_id = @productId LIMIT 1";
+    command.Parameters.AddWithValue("@productId", productId);
+    return await command.ExecuteScalarAsync();
+});
+```
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 5ce7293e0..536f437c3 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -34,6 +34,7 @@
   </ItemGroup>
 
   <ItemGroup>
+    <InternalsVisibleTo Include="MySqlConnector.DependencyInjection.Tests" Key="00240000048000001402000006020000002400005253413100100000010001000521c81bf0f0ec7b261bb89bb583611d3767205d542c16c9353e317455acf612d3ec3dd03b77e7e6fda1aa8f15c58576d90dae0fb9f4fd4bd48709ae199b8c771963fa67d70b35f7ed2fbb6c60423935adfae0606716ea6ce31a1fcd56fdb206fc0c3b1205ec6ba56fb20c14c42105a601ddd0bfaea7207d535b29a39ffe82f00880f4f64f86e6bcf26eb5242a133bad9d7a32e3126036b68b13b413ce4097dfc18d9a5b1e494f1aed54dc84d7089fd0d931a49e679fdc7c8f07a5121df38ec27c2c9993a8f8f136b2937849aed32aef7324a5b7e482dc2eb693c7988f6074e82e75a41dd001587be4d79108588b25d40ed9aeb30ff921edaf509c94f71428e48219ba940f5f10c061421dc0c006e09feadec30df20b2d13d02c3ce4ceb32b6fbefd254288d45f3bb2c425b197e19699d7efdfc7aba5dd45b727bc98abd866d2f6e69e33a64e4b5a5ab1e4d749266c7bf285550da9fb036f10eff76b697de9c5ed8de4a3cdbca1174543540bed6c3a95641cfdacbac834896639f8a75ed1fb9cfd9983d83d0b43b76bd3894bd2b3da0dd23d1e0362985217f087acce1a7f56546c214890acae8fc60e27890ff31c38578f85e220342061a1a5c867362a14aafdffa003dc13af064f5f860d1757883ea5237feed3a6228c86200062bd88f5592d5c399ef270a562d458ae8eac5eaa382b5bcc3f64298cc34b4598f0b33d7943b8" />
     <InternalsVisibleTo Include="MySqlConnector.Tests" Key="00240000048000001402000006020000002400005253413100100000010001000521c81bf0f0ec7b261bb89bb583611d3767205d542c16c9353e317455acf612d3ec3dd03b77e7e6fda1aa8f15c58576d90dae0fb9f4fd4bd48709ae199b8c771963fa67d70b35f7ed2fbb6c60423935adfae0606716ea6ce31a1fcd56fdb206fc0c3b1205ec6ba56fb20c14c42105a601ddd0bfaea7207d535b29a39ffe82f00880f4f64f86e6bcf26eb5242a133bad9d7a32e3126036b68b13b413ce4097dfc18d9a5b1e494f1aed54dc84d7089fd0d931a49e679fdc7c8f07a5121df38ec27c2c9993a8f8f136b2937849aed32aef7324a5b7e482dc2eb693c7988f6074e82e75a41dd001587be4d79108588b25d40ed9aeb30ff921edaf509c94f71428e48219ba940f5f10c061421dc0c006e09feadec30df20b2d13d02c3ce4ceb32b6fbefd254288d45f3bb2c425b197e19699d7efdfc7aba5dd45b727bc98abd866d2f6e69e33a64e4b5a5ab1e4d749266c7bf285550da9fb036f10eff76b697de9c5ed8de4a3cdbca1174543540bed6c3a95641cfdacbac834896639f8a75ed1fb9cfd9983d83d0b43b76bd3894bd2b3da0dd23d1e0362985217f087acce1a7f56546c214890acae8fc60e27890ff31c38578f85e220342061a1a5c867362a14aafdffa003dc13af064f5f860d1757883ea5237feed3a6228c86200062bd88f5592d5c399ef270a562d458ae8eac5eaa382b5bcc3f64298cc34b4598f0b33d7943b8" />
     <InternalsVisibleTo Include="IntegrationTests" Key="00240000048000001402000006020000002400005253413100100000010001000521c81bf0f0ec7b261bb89bb583611d3767205d542c16c9353e317455acf612d3ec3dd03b77e7e6fda1aa8f15c58576d90dae0fb9f4fd4bd48709ae199b8c771963fa67d70b35f7ed2fbb6c60423935adfae0606716ea6ce31a1fcd56fdb206fc0c3b1205ec6ba56fb20c14c42105a601ddd0bfaea7207d535b29a39ffe82f00880f4f64f86e6bcf26eb5242a133bad9d7a32e3126036b68b13b413ce4097dfc18d9a5b1e494f1aed54dc84d7089fd0d931a49e679fdc7c8f07a5121df38ec27c2c9993a8f8f136b2937849aed32aef7324a5b7e482dc2eb693c7988f6074e82e75a41dd001587be4d79108588b25d40ed9aeb30ff921edaf509c94f71428e48219ba940f5f10c061421dc0c006e09feadec30df20b2d13d02c3ce4ceb32b6fbefd254288d45f3bb2c425b197e19699d7efdfc7aba5dd45b727bc98abd866d2f6e69e33a64e4b5a5ab1e4d749266c7bf285550da9fb036f10eff76b697de9c5ed8de4a3cdbca1174543540bed6c3a95641cfdacbac834896639f8a75ed1fb9cfd9983d83d0b43b76bd3894bd2b3da0dd23d1e0362985217f087acce1a7f56546c214890acae8fc60e27890ff31c38578f85e220342061a1a5c867362a14aafdffa003dc13af064f5f860d1757883ea5237feed3a6228c86200062bd88f5592d5c399ef270a562d458ae8eac5eaa382b5bcc3f64298cc34b4598f0b33d7943b8" />
     <Using Include="System.Data" />
diff --git a/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs b/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs
new file mode 100644
index 000000000..6d9bf4775
--- /dev/null
+++ b/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs
@@ -0,0 +1,174 @@
+namespace MySqlConnector.DependencyInjection.Tests;
+
+public class DependencyInjectionTests
+{
+	[Fact]
+	public async Task MySqlDataSourceIsRegistered()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddMySqlDataSource(c_connectionString);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		var dataSource = serviceProvider.GetRequiredService<MySqlDataSource>();
+		await using var connection = dataSource.CreateConnection();
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	[Fact]
+	public async Task MySqlConnectionIsRegistered()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddMySqlDataSource(c_connectionString);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		await using var connection = serviceProvider.GetRequiredService<MySqlConnection>();
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	[Fact]
+	public async Task DbConnectionIsRegistered()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddMySqlDataSource(c_connectionString);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		await using var connection = serviceProvider.GetRequiredService<DbConnection>();
+		Assert.IsAssignableFrom<MySqlConnection>(connection);
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	[Fact]
+	public async Task DbDataSourceIsRegistered()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddMySqlDataSource(c_connectionString);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		await using var dataSource = serviceProvider.GetRequiredService<DbDataSource>();
+		Assert.IsAssignableFrom<MySqlDataSource>(dataSource);
+		await using var connection = dataSource.CreateConnection();
+		Assert.IsAssignableFrom<MySqlConnection>(connection);
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	[Fact]
+	public async Task MySqlDataSourceCanSetName()
+	{
+		var serviceCollection = new ServiceCollection();
+
+		serviceCollection.AddMySqlDataSource(c_connectionString, builder =>
+		{
+			builder.UseName("MyName");
+		});
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+		var dataSource = serviceProvider.GetRequiredService<MySqlDataSource>();
+		Assert.Equal("MyName", dataSource.Name);
+	}
+
+	[Fact]
+	public async Task KeyedMySqlDataSourceIsRegistered()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddKeyedMySqlDataSource(KeyedService.AnyKey, c_connectionString);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		var dataSource = serviceProvider.GetRequiredKeyedService<MySqlDataSource>(new object());
+		Assert.Null(dataSource.Name);
+		await using var connection = dataSource.CreateConnection();
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	[Fact]
+	public async Task StringKeyedMySqlDataSourceHasNameSet()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddKeyedMySqlDataSource("key", c_connectionString);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		var dataSource = serviceProvider.GetRequiredKeyedService<MySqlDataSource>("key");
+		Assert.Equal("key", dataSource.Name);
+		await using var connection = dataSource.CreateConnection();
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	[Fact]
+	public async Task KeyedMySqlDataSourceRetrievedWithStringKeyHasName()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddKeyedMySqlDataSource(KeyedService.AnyKey, c_connectionString);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		var dataSource = serviceProvider.GetRequiredKeyedService<MySqlDataSource>("key");
+		Assert.Equal("key", dataSource.Name);
+		await using var connection = dataSource.CreateConnection();
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	[Fact]
+	public async Task KeyedMySqlConnectionIsRegistered()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddKeyedMySqlDataSource("key", c_connectionString);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		await using var connection = serviceProvider.GetRequiredKeyedService<MySqlConnection>("key");
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	[Fact]
+	public async Task TwoKeyedMySqlDataConnectionsAreRegistered()
+	{
+		const string c_connectionString2 = c_connectionString + ";Database=test";
+
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddKeyedMySqlDataSource(KeyedService.AnyKey, c_connectionString);
+		serviceCollection.AddKeyedMySqlDataSource("key2", c_connectionString2);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		await using var connection1 = serviceProvider.GetRequiredKeyedService<MySqlConnection>("key");
+		Assert.Equal(c_connectionString, connection1.ConnectionString);
+
+		await using var connection2 = serviceProvider.GetRequiredKeyedService<MySqlConnection>("key2");
+		Assert.Equal(c_connectionString2, connection2.ConnectionString);
+	}
+
+	[Fact]
+	public async Task KeyedDbConnectionIsRegistered()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddKeyedMySqlDataSource("key", c_connectionString);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		await using var connection = serviceProvider.GetRequiredKeyedService<DbConnection>("key");
+		Assert.IsAssignableFrom<MySqlConnection>(connection);
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	[Fact]
+	public async Task KeyedDbDataSourceIsRegistered()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddKeyedMySqlDataSource("key", c_connectionString);
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		await using var dataSource = serviceProvider.GetRequiredKeyedService<DbDataSource>("key");
+		Assert.IsAssignableFrom<MySqlDataSource>(dataSource);
+		await using var connection = dataSource.CreateConnection();
+		Assert.IsAssignableFrom<MySqlConnection>(connection);
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	const string c_connectionString = "Server=localhost;User ID=root;Password=pass";
+}
diff --git a/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj b/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj
new file mode 100644
index 000000000..fac726a3a
--- /dev/null
+++ b/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj
@@ -0,0 +1,33 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <TargetFrameworks>net8.0</TargetFrameworks>
+    <SignAssembly>true</SignAssembly>
+    <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
+    <AssemblyOriginatorKeyFile>..\..\MySqlConnector.snk</AssemblyOriginatorKeyFile>
+    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <Nullable>enable</Nullable>
+    <ImplicitUsings>enable</ImplicitUsings>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" />
+    <PackageReference Include="xunit" />
+    <PackageReference Include="xunit.runner.visualstudio">
+      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
+      <PrivateAssets>all</PrivateAssets>
+    </PackageReference>
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="../../src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <Using Include="Microsoft.Extensions.DependencyInjection" />
+    <Using Include="System.Data.Common" />
+    <Using Include="Xunit" />
+  </ItemGroup>
+
+</Project>

From 115b8692568155d8edb69b18948ec384895b176e Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 16 Nov 2023 16:13:40 -0800
Subject: [PATCH 214/393] Add 'db.client.connections.timeouts' counter. Fixes
 #1392

---
 docs/content/diagnostics/metrics.md           |  1 +
 src/MySqlConnector/Core/MetricsReporter.cs    |  3 ++
 src/MySqlConnector/MySqlConnection.cs         |  7 +++
 .../Metrics/ConnectTimeoutTests.cs            | 50 +++++++++++++++++++
 4 files changed, 61 insertions(+)
 create mode 100644 tests/MySqlConnector.Tests/Metrics/ConnectTimeoutTests.cs

diff --git a/docs/content/diagnostics/metrics.md b/docs/content/diagnostics/metrics.md
index 24ade9245..46195d4c7 100644
--- a/docs/content/diagnostics/metrics.md
+++ b/docs/content/diagnostics/metrics.md
@@ -34,6 +34,7 @@ Name | Type | Unit | Description
 --- | --- | --- | ---
 `db.client.connections.usage` | UpDownCounter | `{connection}` | The number of connections that are currently in the state described by the state tag.
 `db.client.connections.pending_requests` | UpDownCounter | `{request}` | The number of pending requests for an open connection, cumulative for the entire pool.
+`db.client.connections.timeouts` | Counter | `{timeout}` | The number of connection timeouts that have occurred trying to obtain a connection from the pool.
 `db.client.connections.create_time` | Histogram | `s` | The time it took to create a new connection.
 `db.client.connections.use_time` | Histogram | `s` | The time between borrowing a connection and returning it to the pool.
 `db.client.connections.wait_time` | Histogram | `s` | The time it took to obtain an open connection from the pool.
diff --git a/src/MySqlConnector/Core/MetricsReporter.cs b/src/MySqlConnector/Core/MetricsReporter.cs
index 86599d774..22c4a9112 100644
--- a/src/MySqlConnector/Core/MetricsReporter.cs
+++ b/src/MySqlConnector/Core/MetricsReporter.cs
@@ -9,6 +9,7 @@ internal static class MetricsReporter
 	public static void RemoveIdle(ConnectionPool pool) => s_connectionsUsageCounter.Add(-1, pool.IdleStateTagList);
 	public static void AddUsed(ConnectionPool pool) => s_connectionsUsageCounter.Add(1, pool.UsedStateTagList);
 	public static void RemoveUsed(ConnectionPool pool) => s_connectionsUsageCounter.Add(-1, pool.UsedStateTagList);
+	public static void AddTimeout(ConnectionPool? pool, ConnectionSettings connectionSettings) => s_connectionTimeouts.Add(1, new KeyValuePair<string, object?>("pool.name", pool?.Name ?? connectionSettings.ConnectionStringBuilder.GetConnectionString(includePassword: false)));
 	public static void RecordCreateTime(ConnectionPool pool, double seconds) => s_createTimeHistory.Record(seconds, pool.PoolNameTagList);
 	public static void RecordUseTime(ConnectionPool pool, double seconds) => s_useTimeHistory.Record(seconds, pool.PoolNameTagList);
 	public static void RecordWaitTime(ConnectionPool pool, double seconds) => s_waitTimeHistory.Record(seconds, pool.PoolNameTagList);
@@ -48,6 +49,8 @@ static IEnumerable<Measurement<int>> GetMinimumConnections() =>
 			unit: "{connection}", description: "The number of connections that are currently in the state described by the state tag.");
 	private static readonly UpDownCounter<int> s_pendingRequestsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.pending_requests",
 			unit: "{request}", description: "The number of pending requests for an open connection, cumulative for the entire pool.");
+	private static readonly Counter<int> s_connectionTimeouts = ActivitySourceHelper.Meter.CreateCounter<int>("db.client.connections.timeouts",
+            unit: "{timeout}", description: "The number of connection timeouts that have occurred trying to obtain a connection from the pool.");
 	private static readonly Histogram<double> s_createTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<double>("db.client.connections.create_time",
 			unit: "s", description: "The time it took to create a new connection.");
 	private static readonly Histogram<double> s_useTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<double>("db.client.connections.use_time",
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 46c1668fe..346de9bc6 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -941,13 +941,20 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 		}
 		catch (OperationCanceledException) when (timeoutSource?.IsCancellationRequested is true)
 		{
+			MetricsReporter.AddTimeout(pool, connectionSettings);
 			var messageSuffix = (pool?.IsEmpty is true) ? " All pooled connections are in use." : "";
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired." + messageSuffix);
 		}
 		catch (MySqlException ex) when ((timeoutSource?.IsCancellationRequested is true) || (ex.ErrorCode == MySqlErrorCode.CommandTimeoutExpired))
 		{
+			MetricsReporter.AddTimeout(pool, connectionSettings);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Connect Timeout expired.", ex);
 		}
+		catch (MySqlException ex) when (ex.ErrorCode == MySqlErrorCode.UnableToConnectToHost && ex.Message == "Connect Timeout expired.")
+		{
+			MetricsReporter.AddTimeout(pool, connectionSettings);
+			throw;
+		}
 		finally
 		{
 			MetricsReporter.RemovePendingRequest(pool);
diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectTimeoutTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectTimeoutTests.cs
new file mode 100644
index 000000000..405ce03d9
--- /dev/null
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectTimeoutTests.cs
@@ -0,0 +1,50 @@
+namespace MySqlConnector.Tests.Metrics;
+
+public class ConnectTimeoutTests : MetricsTestsBase
+{
+	[Fact(Skip = MetricsSkip)]
+	public async Task ConnectTimeout()
+	{
+		var csb = CreateConnectionStringBuilder();
+		csb.ConnectionTimeout = 1;
+		csb.Server = "www.example.com";
+		PoolName = csb.GetConnectionString(includePassword: false);
+
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await Assert.ThrowsAsync<MySqlException>(connection.OpenAsync);
+
+		AssertMeasurement("db.client.connections.timeouts", 1);
+	}
+
+	[Fact(Skip = MetricsSkip)]
+	public async Task DataSourceConnectTimeout()
+	{
+		var csb = CreateConnectionStringBuilder();
+		csb.ConnectionTimeout = 1;
+		csb.Server = "www.example.com";
+
+		PoolName = "timeout";
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UseName(PoolName)
+			.Build();
+
+		await Assert.ThrowsAsync<MySqlException>(async () => await dataSource.OpenConnectionAsync());
+
+		AssertMeasurement("db.client.connections.timeouts", 1);
+	}
+
+	[Fact(Skip = MetricsSkip)]
+	public async Task NoPoolConnectTimeout()
+	{
+		var csb = CreateConnectionStringBuilder();
+		csb.ConnectionTimeout = 1;
+		csb.Server = "www.example.com";
+		csb.Pooling = false;
+		PoolName = csb.GetConnectionString(includePassword: false);
+
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await Assert.ThrowsAsync<MySqlException>(connection.OpenAsync);
+
+		AssertMeasurement("db.client.connections.timeouts", 1);
+	}
+}

From 413b4bb5e231e9115c1018bc5af85115c4940123 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 16 Nov 2023 16:37:05 -0800
Subject: [PATCH 215/393] Release 2.3.1.

---
 docs/content/overview/version-history.md | 15 ++++++++++++++-
 1 file changed, 14 insertions(+), 1 deletion(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 633ed7d0e..96bf06068 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-11-14
+lastmod: 2023-11-16
 date: 2017-03-27
 menu:
   main:
@@ -11,6 +11,19 @@ weight: 30
 
 # Version History
 
+### 2.3.1
+
+* Add `db.client.connections.timeouts` counter to metrics: [#1392](https://github.com/mysql-net/MySqlConnector/pull/1392)
+* Allow empty `schemaRestrictions` array to be passed to `GetSchema`: [#1394](https://github.com/mysql-net/MySqlConnector/pull/1394)
+* Use higher-resolution timestamps for duration metrics: [#1395](https://github.com/mysql-net/MySqlConnector/issues/1395).
+* **Possibly Breaking** Use seconds as the units for duration metrics; change type to `double`: [#1396](https://github.com/mysql-net/MySqlConnector/issues/1396).
+* Thanks to [Tuomas Hietanen](https://github.com/Thorium) for contributions to this release.
+
+#### MySqlConnector.DependencyInjection
+
+* Add `AddKeyedMySqlDataSource` to support keyed services: [#1391](https://github.com/mysql-net/MySqlConnector/pull/1391).
+* Update `Microsoft.Extensions.DependencyInjection.Abstractions` dependency to 8.0.0.
+
 ### 2.3.0
 
 * Support .NET 8.0.

From 868eacdb6a8a70d988c754f60f87bac7a982a286 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 19 Nov 2023 08:34:22 -0800
Subject: [PATCH 216/393] Fix some 404s in the documentation site.

---
 docs/content/connection-options.md |  2 +-
 docs/content/diagnostics/index.md  | 14 ++++++++++++++
 netlify.toml                       |  6 ++++++
 3 files changed, 21 insertions(+), 1 deletion(-)
 create mode 100644 docs/content/diagnostics/index.md

diff --git a/docs/content/connection-options.md b/docs/content/connection-options.md
index 466cb732c..9985b35c8 100644
--- a/docs/content/connection-options.md
+++ b/docs/content/connection-options.md
@@ -542,7 +542,7 @@ from your connection string when migrating from Connector/NET to MySqlConnector.
   <tr id="SshHostName">
     <td>SshHostName, SshPort, SshUserName, SshPassword, SshKeyFile, SshPassPhrase</td>
     <td></td>
-    <td>Connecting via SSH isn’t built into MySqlConnector, but can be set up easily by following <a href="../tutorials/connect-ssh/" title="Connecting to MySQL Server with SSH from C#">these instructions</a>.</td>
+    <td>Connecting via SSH isn’t built into MySqlConnector, but can be set up easily by following <a href="/tutorials/connect-ssh/" title="Connecting to MySQL Server with SSH from C#">these instructions</a>.</td>
   </tr>
   <tr id="SqlServerMode">
     <td>SqlServerMode, Sql Server Mode</td>
diff --git a/docs/content/diagnostics/index.md b/docs/content/diagnostics/index.md
new file mode 100644
index 000000000..bd65cb05d
--- /dev/null
+++ b/docs/content/diagnostics/index.md
@@ -0,0 +1,14 @@
+---
+date: 2023-11-19
+title: Diagnostics
+---
+
+# Diagnostics
+
+<blockquote class="note">Metrics are only supported in MySqlConnector 2.3.0 or later.</blockquote>
+
+MySqlConnector supports the following diagnostics:
+
+* [Logging](logging/) - logging via Microsoft.Extensions.Logging
+* [Tracing](tracing/) - OpenTelemetry-compatible tracing via `ActivitySource`
+* [Metrics](metrics/) - OpenTelemetry-compatible metrics via System.Diagnostics.Metrics
diff --git a/netlify.toml b/netlify.toml
index f2ee87083..b7f86ab51 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -112,3 +112,9 @@ from = "/tutorials/tracing/"
 to = "/diagnostics/tracing/"
 status = 302
 force = true
+
+[[redirects]]
+from = "/connection%E2%80%91options/"
+to = "/connection-options/"
+status = 301
+force = true

From d6867743ad425c4971256afcfb3556fc983eac4b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 19 Nov 2023 08:39:39 -0800
Subject: [PATCH 217/393] Make diagnostics page a redirect.

Having an actual page there broke the site hierarchy (with the current template).
---
 docs/content/diagnostics/index.md | 14 --------------
 netlify.toml                      |  6 ++++++
 2 files changed, 6 insertions(+), 14 deletions(-)
 delete mode 100644 docs/content/diagnostics/index.md

diff --git a/docs/content/diagnostics/index.md b/docs/content/diagnostics/index.md
deleted file mode 100644
index bd65cb05d..000000000
--- a/docs/content/diagnostics/index.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-date: 2023-11-19
-title: Diagnostics
----
-
-# Diagnostics
-
-<blockquote class="note">Metrics are only supported in MySqlConnector 2.3.0 or later.</blockquote>
-
-MySqlConnector supports the following diagnostics:
-
-* [Logging](logging/) - logging via Microsoft.Extensions.Logging
-* [Tracing](tracing/) - OpenTelemetry-compatible tracing via `ActivitySource`
-* [Metrics](metrics/) - OpenTelemetry-compatible metrics via System.Diagnostics.Metrics
diff --git a/netlify.toml b/netlify.toml
index b7f86ab51..e6a1ba6c1 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -71,6 +71,12 @@ to = "/api/MySqlConnector/MySqlTransactionType/"
 status = 301
 force = true
 
+[[redirects]]
+from = "/diagnostics/"
+to = "/"
+status = 302
+force = true
+
 [[redirects]]
 from = "/groups/"
 to = "/"

From 5a23e93ca66b895bf7c390117b0bd639ffba5c34 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 17 Nov 2023 12:42:35 -0800
Subject: [PATCH 218/393] Revert "Remove Azure MySQL integration tests."

This reverts commit c3183376325f5a4aaf9caa7a8e86649724f1ebd4.

Additional Azure Credits for Open Source have been applied to this project.
---
 .ci/config/config.ssl.json |  4 ++--
 README.md                  |  3 +++
 azure-pipelines.yml        | 41 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 46 insertions(+), 2 deletions(-)

diff --git a/.ci/config/config.ssl.json b/.ci/config/config.ssl.json
index ef4a5afb0..84261b1be 100644
--- a/.ci/config/config.ssl.json
+++ b/.ci/config/config.ssl.json
@@ -5,8 +5,8 @@
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
       "UnsupportedFeatures": "RsaEncryption,CachingSha2Password,Tls12,Tls13,UuidToBin",
-      "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
-      "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
+      "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
+      "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV",
       "CertificatesPath": "../../../../.ci/server/certs"
     }
 }
diff --git a/README.md b/README.md
index aecd0c19e..a79528558 100644
--- a/README.md
+++ b/README.md
@@ -104,3 +104,6 @@ Development of MySqlConnector is supported by:
 
 [Faithlife](https://faithlife.com/about) ([View jobs](https://faithlife.com/careers))
 
+[![Microsoft Azure](https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Microsoft_Azure.svg/75px-Microsoft_Azure.svg.png)](https://azure.microsoft.com/en-us/overview/open-source/)
+
+[Azure Credits for Open Source](https://opensource.microsoft.com/azure-credits)
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 5b69d7ce4..36b3a5b83 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -102,6 +102,47 @@ jobs:
       image: 'mysql:8.0'
       connectionString: 'server=localhost;port=3306;user id=mysqltest;password=test;database=conformance;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
 
+- job: azure_mysql_integration_tests
+  condition: ne('True', variables['System.PullRequest.IsFork'])
+  displayName: 'Azure MySQL Integration Tests'
+  pool:
+    vmimage: 'windows-latest'
+  steps:
+  - task: PowerShell@2
+    displayName: Create Database
+    inputs:
+      targetType: inline
+      script: "mysql -u$(Azure MySQL Root User) -p$(Azure MySQL Root Password) -h '$(Azure MySQL Host)' -e 'create schema mysqltest_$(Build.BuildId) collate utf8mb4_0900_ai_ci;'"
+  - task: UseDotNet@2
+    displayName: 'Install .NET'
+    inputs:
+      version: $(DotNetCoreSdkVersion)
+  - task: PowerShell@2
+    displayName: 'Copy Azure config'
+    inputs:
+      targetType: inline
+      script: Copy-Item -Path ".\.ci\config\config.ssl.json" -Destination ".\tests\IntegrationTests\config.json"
+  - task: DotNetCoreCLI@2
+    displayName: 'Restore packages'
+    inputs:
+      command: 'restore'
+  - task: DotNetCoreCLI@2
+    displayName: 'Integration tests (net8.0)'
+    inputs:
+      command: 'test'
+      projects: 'tests/IntegrationTests/IntegrationTests.csproj'
+      arguments: '-c Release -f net8.0 --no-restore'
+      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'Azure', 'net8.0', 'SSL') }}
+    env:
+      DATA__UNSUPPORTEDFEATURES: 'CachingSha2Password,Ed25519,GlobalLog,KnownCertificateAuthority,QueryAttributes,RsaEncryption,Sha256Password,StreamingResults,Timeout,Tls11,Tls13,UnixDomainSocket,ZeroDateTime'
+      DATA__CONNECTIONSTRING: "$(AzureConnectionString);database=mysqltest_$(Build.BuildId);ssl mode=Required;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True"
+  - task: PowerShell@2
+    displayName: Drop Database
+    condition: always()
+    inputs:
+      targetType: inline
+      script: "mysql -u$(Azure MySQL Root User) -p$(Azure MySQL Root Password) -h '$(Azure MySQL Host)' -e 'drop schema if exists mysqltest_$(Build.BuildId);'"
+
 - job: windows_integration_tests_1
   displayName: 'Windows Integration Tests (Part 1)'
   pool:

From 4553c8f536993a59700ff7189f6757f5d0827e07 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 19 Nov 2023 12:54:21 -0800
Subject: [PATCH 219/393] Update Benchmark to .NET 8.0.

---
 tests/Benchmark/Benchmark.csproj | 11 ++++++-----
 tests/Benchmark/Program.cs       |  2 +-
 2 files changed, 7 insertions(+), 6 deletions(-)

diff --git a/tests/Benchmark/Benchmark.csproj b/tests/Benchmark/Benchmark.csproj
index b668e841c..96ff1f704 100644
--- a/tests/Benchmark/Benchmark.csproj
+++ b/tests/Benchmark/Benchmark.csproj
@@ -1,13 +1,14 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net7.0</TargetFramework>
-    <LangVersion>11.0</LangVersion>
+    <TargetFramework>net8.0</TargetFramework>
+    <LangVersion>12.0</LangVersion>
+    <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
-    <PackageReference Include="MySqlConnector" Version="2.2.6" />
-    <PackageReference Include="MySql.Data" Version="8.0.32" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
+    <PackageReference Include="MySqlConnector" Version="2.3.1" />
+    <PackageReference Include="MySql.Data" Version="8.2.0" />
   </ItemGroup>
 
 <!--  <ItemGroup>-->
diff --git a/tests/Benchmark/Program.cs b/tests/Benchmark/Program.cs
index 47760b498..1b7863028 100644
--- a/tests/Benchmark/Program.cs
+++ b/tests/Benchmark/Program.cs
@@ -23,7 +23,7 @@ static void Main()
 			.AddValidator(JitOptimizationsValidator.FailOnError)
 			.AddDiagnoser(MemoryDiagnoser.Default)
 			.AddColumn(StatisticColumn.AllStatistics)
-			.AddJob(Job.Default.WithRuntime(CoreRuntime.Core70))
+			.AddJob(Job.Default.WithRuntime(CoreRuntime.Core80))
 			.AddExporter(DefaultExporters.Csv);
 
 		var summary = BenchmarkRunner.Run<MySqlClient>(customConfig);

From 4d7516a2832b838fb2cbd4dbe1a04b007f5a6a42 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 19 Nov 2023 13:56:13 -0800
Subject: [PATCH 220/393] Update benchmarks with latest versions.

---
 docs/content/home.md | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/docs/content/home.md b/docs/content/home.md
index f6dbc5e1e..0ba958a29 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-02-25
+lastmod: 2023-11-19
 date: 2016-10-16
 title: Home
 weight: 10
@@ -76,9 +76,9 @@ TiDB | |
 
 MySqlConnector outperforms Connector/NET (MySql.Data) on benchmarks:
 
-<p><img src="https://files.logoscdn.com/v1/files/63673908/assets/13928411/content.png?signature=MVHBZxDfB0J-0Pueja8NtvuLD9A" alt="Benchmark results for MySql.Data vs MySqlConnector" width="800" height="534"></p>
+<p><img src="https://files.logoscdn.com/v1/assets/15435916/optimized" alt="Benchmark results for MySql.Data vs MySqlConnector" width="736" height="454"></p>
 
-(Client: MySqlConnector 2.2.0, Ubuntu 20.04, .NET 7.0; Server: Azure Database for MySQL 8.0.28, TLS 1.2)
+(Client: MySqlConnector 2.3.1, MySql.Data 8.2.0, Ubuntu 23.04, .NET 8.0; Server: Azure Database for MySQL 8.0.34, TLS 1.2)
 
 ## Why use MySqlConnector over Oracle’s MySQL Connector/NET?
 

From 08b0b3d9a50695df4f311ff119246b598d879f85 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 19 Nov 2023 16:24:55 -0800
Subject: [PATCH 221/393] Update main README with new benchmarks.

---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index a79528558..b7ba89e0f 100644
--- a/README.md
+++ b/README.md
@@ -13,9 +13,9 @@ Complete documentation is available at the [MySqlConnector Documentation Website
 
 This library outperforms MySQL Connector/NET (`MySql.Data`) on benchmarks:
 
-![Benchmark](https://files.logoscdn.com/v1/files/63673908/assets/13928411/content.png?signature=MVHBZxDfB0J-0Pueja8NtvuLD9A)
+![Benchmark](https://files.logoscdn.com/v1/assets/15435916/optimized)
 
-(Client: MySqlConnector 2.2.0, Ubuntu 20.04, .NET 7.0; Server: Azure Database for MySQL 8.0.28, TLS 1.2)
+(Client: MySqlConnector 2.3.1, MySql.Data 8.2.0, Ubuntu 23.04, .NET 8.0; Server: Azure Database for MySQL 8.0.34, TLS 1.2)
 
 ### Server Compatibility
 

From 061215ab78690e56e7ed51dba6cbb71148d2af62 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 20 Nov 2023 05:25:53 -0800
Subject: [PATCH 222/393] Update xUnit and other packages.

---
 Directory.Packages.props                  | 14 +++++++-------
 tests/IntegrationTests/BulkLoaderAsync.cs | 12 ++++++------
 2 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index d8225df5a..2bf6e09f6 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,12 +6,12 @@
 
   <ItemGroup>
     <PackageVersion Include="AdoNet.Specification.Tests" Version="2.0.0-beta.2" />
-    <PackageVersion Include="Dapper.StrongName" Version="2.1.15" />
+    <PackageVersion Include="Dapper.StrongName" Version="2.1.21" />
     <PackageVersion Include="IndexRange" Version="1.0.2" />
     <PackageVersion Include="log4net" Version="2.0.10" />
-    <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="7.0.4" />
-    <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="7.0.0" />
-    <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="7.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
@@ -22,13 +22,13 @@
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageVersion Include="xunit" Version="2.6.1" />
-    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.3" />
+    <PackageVersion Include="xunit" Version="2.6.2" />
+    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.4" />
     <PackageVersion Include="YamlDotNet" Version="13.7.1" />
   </ItemGroup>
 
   <ItemGroup>
-    <GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" />
+    <GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
     <GlobalPackageReference Include="MinVer" Version="4.3.0" />
   </ItemGroup>
 
diff --git a/tests/IntegrationTests/BulkLoaderAsync.cs b/tests/IntegrationTests/BulkLoaderAsync.cs
index 04d4e7ec5..cc147866e 100644
--- a/tests/IntegrationTests/BulkLoaderAsync.cs
+++ b/tests/IntegrationTests/BulkLoaderAsync.cs
@@ -443,12 +443,12 @@ public async Task BulkCopyDataReader()
 	}
 
 	[Fact]
-	public void BulkCopyNullDataTable()
+	public async Task BulkCopyNullDataTable()
 	{
 		using var connection = new MySqlConnection(GetLocalConnectionString());
-		connection.Open();
+		await connection.OpenAsync();
 		var bulkCopy = new MySqlBulkCopy(connection);
-		Assert.ThrowsAsync<ArgumentNullException>(async () => await bulkCopy.WriteToServerAsync(default(DataTable)));
+		await Assert.ThrowsAsync<ArgumentNullException>(async () => await bulkCopy.WriteToServerAsync(default(DataTable)));
 	}
 
 	[Fact]
@@ -612,12 +612,12 @@ public async Task BulkCopyDataTableWithWarnings()
 	}
 
 	[Fact]
-	public void BulkCopyNullDataReader()
+	public async Task BulkCopyNullDataReader()
 	{
 		using var connection = new MySqlConnection(GetLocalConnectionString());
-		connection.Open();
+		await connection.OpenAsync();
 		var bulkCopy = new MySqlBulkCopy(connection);
-		Assert.ThrowsAsync<ArgumentNullException>(async () => await bulkCopy.WriteToServerAsync(default(DbDataReader)));
+		await Assert.ThrowsAsync<ArgumentNullException>(async () => await bulkCopy.WriteToServerAsync(default(DbDataReader)));
 	}
 #endif
 

From 44397548e4eb3086b71e41c0e0c6382e3e922f14 Mon Sep 17 00:00:00 2001
From: Jan Trejbal <jan@trejbal.land>
Date: Wed, 22 Nov 2023 05:33:30 +0100
Subject: [PATCH 223/393] Handle missing metadata (#1399)

Create ColumnCountPayload when metadata are missing.

Co-authored-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Protocol/Payloads/ColumnCountPayload.cs   |  2 +-
 .../ColumnCountPayloadTests.cs                | 21 +++++++++++++++++++
 .../MySqlConnector.Tests.csproj               |  2 +-
 3 files changed, 23 insertions(+), 2 deletions(-)
 create mode 100644 tests/MySqlConnector.Tests/ColumnCountPayloadTests.cs

diff --git a/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs b/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
index 551093d59..8e407ac89 100644
--- a/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/ColumnCountPayload.cs
@@ -22,7 +22,7 @@ public static ColumnCountPayload Create(ReadOnlySpan<byte> span, bool supportsOp
 	{
 		var reader = new ByteArrayReader(span);
 		var columnCount = (int) reader.ReadLengthEncodedInteger();
-		var metadataFollows = !supportsOptionalMetadata || reader.ReadByte() == 1;
+		var metadataFollows = !supportsOptionalMetadata || reader.BytesRemaining == 0 || reader.ReadByte() == 1;
 		return new ColumnCountPayload(columnCount, metadataFollows);
 	}
 }
diff --git a/tests/MySqlConnector.Tests/ColumnCountPayloadTests.cs b/tests/MySqlConnector.Tests/ColumnCountPayloadTests.cs
new file mode 100644
index 000000000..010d6793c
--- /dev/null
+++ b/tests/MySqlConnector.Tests/ColumnCountPayloadTests.cs
@@ -0,0 +1,21 @@
+﻿using MySqlConnector.Protocol.Payloads;
+
+namespace MySqlConnector.Tests;
+
+public class ColumnCountPayloadTests
+{
+	[Theory]
+	[InlineData(new byte[] { 1 }, false, 1, true)]
+	[InlineData(new byte[] { 1, 1 }, true, 1, true)]
+	[InlineData(new byte[] { 1, 0 }, true, 1, false)]
+	[InlineData(new byte[] { 2 }, false, 2, true)]
+	[InlineData(new byte[] { 2, 1 }, true, 2, true)]
+	[InlineData(new byte[] { 2, 0 }, true, 2, false)]
+	[InlineData(new byte[] { 2 }, true, 2, true)] // this seems like invalid data, but a server may return this
+	public void ParseResultSetHeader(byte[] span, bool supportsOptionalMetadata, int expectedColumnCount, bool expectedMetadataFollows)
+	{
+		var columnCountPayload = ColumnCountPayload.Create(span.AsSpan(), supportsOptionalMetadata);
+		Assert.Equal(expectedColumnCount, columnCountPayload.ColumnCount);
+		Assert.Equal(expectedMetadataFollows, columnCountPayload.MetadataFollows);
+	}
+}
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 473c72caa..8eddd9566 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -47,7 +47,7 @@
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
     <PackageReference Include="MySql.Data" />
-    <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ColumnReaderTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
+    <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ColumnCountPayloadTests.cs;ColumnReaderTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
     <Compile Remove="Metrics\*.cs" />
     <Using Include="MySql.Data.MySqlClient" />
     <Using Include="MySql.Data.Types" />

From 6a63fa49795b54318085938e4a09cda0bc0ab2cd Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 23 Nov 2023 08:28:32 -0800
Subject: [PATCH 224/393] Update API documentation.

---
 .../MySqlAttribute/MySqlAttribute.md          |  6 +++-
 .../api/MySqlConnector/MySqlAttributeType.md  |  2 +-
 .../MySqlBatchCommand/CanCreateParameter.md   | 17 ++++++++++
 .../MySqlBatchCommand/CreateParameter.md      | 17 ++++++++++
 .../MySqlConnector/MySqlBatchCommandType.md   |  2 ++
 .../MySqlBulkCopyColumnMapping.md             | 24 +++++++++++++-
 .../MySqlBulkCopyColumnMappingType.md         |  8 ++++-
 .../MySqlConnection/GetSchema.md              |  4 +--
 .../MySqlDataSource/Password.md               | 25 +++++++++++++++
 .../MySqlDataSourceBuilder/UseName.md         | 27 ++++++++++++++++
 .../UsePeriodicPasswordProvider.md            | 32 +++++++++++++++++++
 .../MySqlDataSourceBuilderType.md             |  2 ++
 .../api/MySqlConnector/MySqlDataSourceType.md |  3 +-
 .../MySqlDateTime/MySqlDateTime.md            |  2 +-
 .../api/MySqlConnector/MySqlDateTimeType.md   | 12 ++++++-
 .../ExpectedByteCount.md                      | 17 ++++++++++
 .../ReadByteCount.md                          | 17 ++++++++++
 .../MySqlEndOfStreamExceptionType.md          | 23 +++++++++++++
 .../MySqlException/GetObjectData.md           | 24 --------------
 .../api/MySqlConnector/MySqlExceptionType.md  |  1 -
 docs/content/api/MySqlConnectorAssembly.md    |  3 +-
 docs/content/api/MySqlConnectorNamespace.md   |  3 +-
 src/MySqlConnector/MySqlDataSource.cs         |  2 +-
 23 files changed, 236 insertions(+), 37 deletions(-)
 create mode 100644 docs/content/api/MySqlConnector/MySqlBatchCommand/CanCreateParameter.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlBatchCommand/CreateParameter.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSource/Password.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseName.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UsePeriodicPasswordProvider.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlEndOfStreamException/ExpectedByteCount.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlEndOfStreamException/ReadByteCount.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlEndOfStreamExceptionType.md
 delete mode 100644 docs/content/api/MySqlConnector/MySqlException/GetObjectData.md

diff --git a/docs/content/api/MySqlConnector/MySqlAttribute/MySqlAttribute.md b/docs/content/api/MySqlConnector/MySqlAttribute/MySqlAttribute.md
index ef2815a3b..06f9f3be9 100644
--- a/docs/content/api/MySqlConnector/MySqlAttribute/MySqlAttribute.md
+++ b/docs/content/api/MySqlConnector/MySqlAttribute/MySqlAttribute.md
@@ -20,12 +20,16 @@ public MySqlAttribute()
 
 # MySqlAttribute constructor (2 of 2)
 
-Initializes a new [`MySqlAttribute`](../../MySqlAttributeType/) with the specified attribute name and value.
+[`MySqlAttribute`](../../MySqlAttributeType/) represents an attribute that can be sent with a MySQL query.
 
 ```csharp
 public MySqlAttribute(string attributeName, object? value)
 ```
 
+## Remarks
+
+See [Query Attributes](https://dev.mysql.com/doc/refman/8.0/en/query-attributes.html) for information on using query attributes.
+
 ## See Also
 
 * class [MySqlAttribute](../../MySqlAttributeType/)
diff --git a/docs/content/api/MySqlConnector/MySqlAttributeType.md b/docs/content/api/MySqlConnector/MySqlAttributeType.md
index d389b9981..bcf4a8184 100644
--- a/docs/content/api/MySqlConnector/MySqlAttributeType.md
+++ b/docs/content/api/MySqlConnector/MySqlAttributeType.md
@@ -15,7 +15,7 @@ public sealed class MySqlAttribute : ICloneable
 | name | description |
 | --- | --- |
 | [MySqlAttribute](../MySqlAttribute/MySqlAttribute/)() | Initializes a new [`MySqlAttribute`](../MySqlAttributeType/). |
-| [MySqlAttribute](../MySqlAttribute/MySqlAttribute/)(…) | Initializes a new [`MySqlAttribute`](../MySqlAttributeType/) with the specified attribute name and value. |
+| [MySqlAttribute](../MySqlAttribute/MySqlAttribute/)(…) | [`MySqlAttribute`](../MySqlAttributeType/) represents an attribute that can be sent with a MySQL query. |
 | [AttributeName](../MySqlAttribute/AttributeName/) { get; set; } | Gets or sets the attribute name. |
 | [Value](../MySqlAttribute/Value/) { get; set; } | Gets or sets the attribute value. |
 | [Clone](../MySqlAttribute/Clone/)() | Returns a new [`MySqlAttribute`](../MySqlAttributeType/) with the same property values as this instance. |
diff --git a/docs/content/api/MySqlConnector/MySqlBatchCommand/CanCreateParameter.md b/docs/content/api/MySqlConnector/MySqlBatchCommand/CanCreateParameter.md
new file mode 100644
index 000000000..1714a54b6
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlBatchCommand/CanCreateParameter.md
@@ -0,0 +1,17 @@
+---
+title: MySqlBatchCommand.CanCreateParameter property
+---
+
+# MySqlBatchCommand.CanCreateParameter property
+
+```csharp
+public override bool CanCreateParameter { get; }
+```
+
+## See Also
+
+* class [MySqlBatchCommand](../../MySqlBatchCommandType/)
+* namespace [MySqlConnector](../../MySqlBatchCommandType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlBatchCommand/CreateParameter.md b/docs/content/api/MySqlConnector/MySqlBatchCommand/CreateParameter.md
new file mode 100644
index 000000000..722edbfbe
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlBatchCommand/CreateParameter.md
@@ -0,0 +1,17 @@
+---
+title: MySqlBatchCommand.CreateParameter method
+---
+
+# MySqlBatchCommand.CreateParameter method
+
+```csharp
+public override DbParameter CreateParameter()
+```
+
+## See Also
+
+* class [MySqlBatchCommand](../../MySqlBatchCommandType/)
+* namespace [MySqlConnector](../../MySqlBatchCommandType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlBatchCommandType.md b/docs/content/api/MySqlConnector/MySqlBatchCommandType.md
index 2eb882a9e..0bc897502 100644
--- a/docs/content/api/MySqlConnector/MySqlBatchCommandType.md
+++ b/docs/content/api/MySqlConnector/MySqlBatchCommandType.md
@@ -14,10 +14,12 @@ public sealed class MySqlBatchCommand : DbBatchCommand
 | --- | --- |
 | [MySqlBatchCommand](../MySqlBatchCommand/MySqlBatchCommand/)() | The default constructor. |
 | [MySqlBatchCommand](../MySqlBatchCommand/MySqlBatchCommand/)(…) |  |
+| override [CanCreateParameter](../MySqlBatchCommand/CanCreateParameter/) { get; } |  |
 | override [CommandText](../MySqlBatchCommand/CommandText/) { get; set; } |  |
 | override [CommandType](../MySqlBatchCommand/CommandType/) { get; set; } |  |
 | [Parameters](../MySqlBatchCommand/Parameters/) { get; } |  |
 | override [RecordsAffected](../MySqlBatchCommand/RecordsAffected/) { get; } |  |
+| override [CreateParameter](../MySqlBatchCommand/CreateParameter/)() |  |
 
 ## Protected Members
 
diff --git a/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping.md b/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping.md
index 974817a66..ae4bc3092 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping.md
@@ -20,7 +20,29 @@ public MySqlBulkCopyColumnMapping()
 
 # MySqlBulkCopyColumnMapping constructor (2 of 2)
 
-Initializes [`MySqlBulkCopyColumnMapping`](../../MySqlBulkCopyColumnMappingType/) to the specified values.
+Use [`MySqlBulkCopyColumnMapping`](../../MySqlBulkCopyColumnMappingType/) to specify how to map columns in the source data to columns in the destination table when using [`MySqlBulkCopy`](../../MySqlBulkCopyType/).
+
+Set [`SourceOrdinal`](../SourceOrdinal/) to the zero-based index of the source column to map. Set [`DestinationColumn`](../DestinationColumn/) to either the name of a column in the destination table, or the name of a user-defined variable. If a user-defined variable, you can use [`Expression`](../Expression/) to specify a MySQL expression that assigns its value to destination column.
+
+Source columns that don't have an entry in [`ColumnMappings`](../../MySqlBulkCopy/ColumnMappings/) will be ignored (unless the [`ColumnMappings`](../../MySqlBulkCopy/ColumnMappings/) collection is empty, in which case all columns will be mapped one-to-one).
+
+MySqlConnector will transmit all binary data as hex, so any expression that operates on it must decode it with the `UNHEX` function first. (This will be performed automatically if no [`Expression`](../Expression/) is specified, but will be necessary to specify manually for more complex expressions.)
+
+Example code:
+
+```csharp
+new MySqlBulkCopyColumnMapping
+{
+    SourceOrdinal = 2,
+    DestinationColumn = "user_name",
+},
+new MySqlBulkCopyColumnMapping
+{
+    SourceOrdinal = 0,
+    DestinationColumn = "@tmp",
+    Expression = "column_value = @tmp * 2",
+},
+```
 
 ```csharp
 public MySqlBulkCopyColumnMapping(int sourceOrdinal, string destinationColumn, 
diff --git a/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMappingType.md b/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMappingType.md
index cdcc716a1..2bce2888c 100644
--- a/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMappingType.md
+++ b/docs/content/api/MySqlConnector/MySqlBulkCopyColumnMappingType.md
@@ -32,12 +32,18 @@ new MySqlBulkCopyColumnMapping
 public sealed class MySqlBulkCopyColumnMapping
 ```
 
+| parameter | description |
+| --- | --- |
+| sourceOrdinal | The zero-based ordinal position of the source column. |
+| destinationColumn | The name of the destination column. |
+| expression | The optional expression to be used to set the destination column. |
+
 ## Public Members
 
 | name | description |
 | --- | --- |
 | [MySqlBulkCopyColumnMapping](../MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping/)() | Initializes [`MySqlBulkCopyColumnMapping`](../MySqlBulkCopyColumnMappingType/) with the default values. |
-| [MySqlBulkCopyColumnMapping](../MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping/)(…) | Initializes [`MySqlBulkCopyColumnMapping`](../MySqlBulkCopyColumnMappingType/) to the specified values. |
+| [MySqlBulkCopyColumnMapping](../MySqlBulkCopyColumnMapping/MySqlBulkCopyColumnMapping/)(…) | Use [`MySqlBulkCopyColumnMapping`](../MySqlBulkCopyColumnMappingType/) to specify how to map columns in the source data to columns in the destination table when using [`MySqlBulkCopy`](../MySqlBulkCopyType/). |
 | [DestinationColumn](../MySqlBulkCopyColumnMapping/DestinationColumn/) { get; set; } | The name of the destination column to copy to. To use an expression, this should be the name of a unique user-defined variable. |
 | [Expression](../MySqlBulkCopyColumnMapping/Expression/) { get; set; } | An optional expression for setting a destination column. To use an expression, the [`DestinationColumn`](../MySqlBulkCopyColumnMapping/DestinationColumn/) should be set to the name of a user-defined variable and this expression should set a column using that variable. |
 | [SourceOrdinal](../MySqlBulkCopyColumnMapping/SourceOrdinal/) { get; set; } | The zero-based ordinal position of the source column to map from. |
diff --git a/docs/content/api/MySqlConnector/MySqlConnection/GetSchema.md b/docs/content/api/MySqlConnector/MySqlConnection/GetSchema.md
index 42a6bf46e..c7f9b218e 100644
--- a/docs/content/api/MySqlConnector/MySqlConnection/GetSchema.md
+++ b/docs/content/api/MySqlConnector/MySqlConnection/GetSchema.md
@@ -32,7 +32,7 @@ public override DataTable GetSchema(string collectionName)
 
 | parameter | description |
 | --- | --- |
-| collectionName | The name of the schema to return. |
+| collectionName | The name of the schema to return. See [Supported Schema Collections](https://mysqlconnector.net/overview/schema-collections/) for the list of supported schema names. |
 
 ## Return Value
 
@@ -56,7 +56,7 @@ public override DataTable GetSchema(string collectionName, string?[] restriction
 
 | parameter | description |
 | --- | --- |
-| collectionName | The name of the schema to return. |
+| collectionName | The name of the schema to return. See [Supported Schema Collections](https://mysqlconnector.net/overview/schema-collections/) for the list of supported schema names. |
 | restrictionValues | The restrictions to apply to the schema. |
 
 ## Return Value
diff --git a/docs/content/api/MySqlConnector/MySqlDataSource/Password.md b/docs/content/api/MySqlConnector/MySqlDataSource/Password.md
new file mode 100644
index 000000000..159c461cf
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSource/Password.md
@@ -0,0 +1,25 @@
+---
+title: MySqlDataSource.Password property
+---
+
+# MySqlDataSource.Password property
+
+Sets the password that will be used by the next [`MySqlConnection`](../../MySqlConnectionType/) created from this [`MySqlDataSource`](../../MySqlDataSourceType/).
+
+```csharp
+public string Password { set; }
+```
+
+## Remarks
+
+This can be used to update the password for database servers that periodically rotate authentication tokens, without affecting connection pooling. The [`Password`](../../MySqlConnectionStringBuilder/Password/) property must not be specified in order for this field to be used.
+
+Consider using [`UsePeriodicPasswordProvider`](../../MySqlDataSourceBuilder/UsePeriodicPasswordProvider/) instead.
+
+## See Also
+
+* class [MySqlDataSource](../../MySqlDataSourceType/)
+* namespace [MySqlConnector](../../MySqlDataSourceType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseName.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseName.md
new file mode 100644
index 000000000..d9a6d4073
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseName.md
@@ -0,0 +1,27 @@
+---
+title: MySqlDataSourceBuilder.UseName method
+---
+
+# MySqlDataSourceBuilder.UseName method
+
+Sets the name of the [`MySqlDataSource`](../../MySqlDataSourceType/) that will be created.
+
+```csharp
+public MySqlDataSourceBuilder UseName(string? name)
+```
+
+| parameter | description |
+| --- | --- |
+| name | The data source name. |
+
+## Return Value
+
+This builder, so that method calls can be chained.
+
+## See Also
+
+* class [MySqlDataSourceBuilder](../../MySqlDataSourceBuilderType/)
+* namespace [MySqlConnector](../../MySqlDataSourceBuilderType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UsePeriodicPasswordProvider.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UsePeriodicPasswordProvider.md
new file mode 100644
index 000000000..028339557
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UsePeriodicPasswordProvider.md
@@ -0,0 +1,32 @@
+---
+title: MySqlDataSourceBuilder.UsePeriodicPasswordProvider method
+---
+
+# MySqlDataSourceBuilder.UsePeriodicPasswordProvider method
+
+Configures a periodic password provider, which is automatically called by the data source at some regular interval. This is the recommended way to fetch a rotating access token.
+
+```csharp
+public MySqlDataSourceBuilder UsePeriodicPasswordProvider(
+    Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? passwordProvider, 
+    TimeSpan successRefreshInterval, TimeSpan failureRefreshInterval)
+```
+
+| parameter | description |
+| --- | --- |
+| passwordProvider | A callback which returns the password to be used by any new MySQL connections that are made. |
+| successRefreshInterval | How long to cache the password before re-invoking the callback. |
+| failureRefreshInterval | How long to wait before re-invoking the callback on failure. This should typically be much shorter than *successRefreshInterval*. |
+
+## Return Value
+
+This builder, so that method calls can be chained.
+
+## See Also
+
+* class [MySqlProvidePasswordContext](../../MySqlProvidePasswordContextType/)
+* class [MySqlDataSourceBuilder](../../MySqlDataSourceBuilderType/)
+* namespace [MySqlConnector](../../MySqlDataSourceBuilderType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
index 2d639f166..6330bd5d0 100644
--- a/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
@@ -19,6 +19,8 @@ public sealed class MySqlDataSourceBuilder
 | [Build](../MySqlDataSourceBuilder/Build/)() | Builds a [`MySqlDataSource`](../MySqlDataSourceType/) which is ready for use. |
 | [UseClientCertificatesCallback](../MySqlDataSourceBuilder/UseClientCertificatesCallback/)(…) | Sets the callback used to provide client certificates for connecting to a server. |
 | [UseLoggerFactory](../MySqlDataSourceBuilder/UseLoggerFactory/)(…) | Sets the ILoggerFactory that will be used for logging. |
+| [UseName](../MySqlDataSourceBuilder/UseName/)(…) | Sets the name of the [`MySqlDataSource`](../MySqlDataSourceType/) that will be created. |
+| [UsePeriodicPasswordProvider](../MySqlDataSourceBuilder/UsePeriodicPasswordProvider/)(…) | Configures a periodic password provider, which is automatically called by the data source at some regular interval. This is the recommended way to fetch a rotating access token. |
 | [UseRemoteCertificateValidationCallback](../MySqlDataSourceBuilder/UseRemoteCertificateValidationCallback/)(…) | Sets the callback used to verify that the server's certificate is valid. |
 
 ## See Also
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceType.md b/docs/content/api/MySqlConnector/MySqlDataSourceType.md
index 29211c5f4..439631a43 100644
--- a/docs/content/api/MySqlConnector/MySqlDataSourceType.md
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceType.md
@@ -4,7 +4,7 @@ title: MySqlDataSource
 
 # MySqlDataSource class
 
-[`MySqlDataSource`](../MySqlDataSourceType/) implements a MySQL data source which can be used to obtain open connections, and against which commands can be executed directly.
+[`MySqlDataSource`](../MySqlDataSourceType/) implements a MySQL data source which can be used to obtain open connections.
 
 ```csharp
 public sealed class MySqlDataSource : DbDataSource
@@ -16,6 +16,7 @@ public sealed class MySqlDataSource : DbDataSource
 | --- | --- |
 | [MySqlDataSource](../MySqlDataSource/MySqlDataSource/)(…) | Initializes a new instance of the [`MySqlDataSource`](../MySqlDataSourceType/) class. |
 | override [ConnectionString](../MySqlDataSource/ConnectionString/) { get; } | Gets the connection string of the database represented by this [`MySqlDataSource`](../MySqlDataSourceType/). |
+| [Password](../MySqlDataSource/Password/) { set; } | Sets the password that will be used by the next [`MySqlConnection`](../MySqlConnectionType/) created from this [`MySqlDataSource`](../MySqlDataSourceType/). |
 | [CreateConnection](../MySqlDataSource/CreateConnection/)() | Creates a new [`MySqlConnection`](../MySqlConnectionType/) that can connect to the database represented by this [`MySqlDataSource`](../MySqlDataSourceType/). |
 | [OpenConnection](../MySqlDataSource/OpenConnection/)() | Returns a new, open [`MySqlConnection`](../MySqlConnectionType/) to the database represented by this [`MySqlDataSource`](../MySqlDataSourceType/). |
 | [OpenConnectionAsync](../MySqlDataSource/OpenConnectionAsync/)(…) | Asynchronously returns a new, open [`MySqlConnection`](../MySqlConnectionType/) to the database represented by this [`MySqlDataSource`](../MySqlDataSourceType/). |
diff --git a/docs/content/api/MySqlConnector/MySqlDateTime/MySqlDateTime.md b/docs/content/api/MySqlConnector/MySqlDateTime/MySqlDateTime.md
index ba8829993..a43698a6d 100644
--- a/docs/content/api/MySqlConnector/MySqlDateTime/MySqlDateTime.md
+++ b/docs/content/api/MySqlConnector/MySqlDateTime/MySqlDateTime.md
@@ -44,7 +44,7 @@ public MySqlDateTime(MySqlDateTime other)
 
 # MySqlDateTime constructor (3 of 3)
 
-Initializes a new instance of [`MySqlDateTime`](../../MySqlDateTimeType/).
+Represents a MySQL date/time value. This type can be used to store `DATETIME` values such as `0000-00-00` that can be stored in MySQL (when [`AllowZeroDateTime`](../../MySqlConnectionStringBuilder/AllowZeroDateTime/) is true) but can't be stored in a DateTime value.
 
 ```csharp
 public MySqlDateTime(int year, int month, int day, int hour, int minute, int second, 
diff --git a/docs/content/api/MySqlConnector/MySqlDateTimeType.md b/docs/content/api/MySqlConnector/MySqlDateTimeType.md
index 957ea7e5a..f6de9366f 100644
--- a/docs/content/api/MySqlConnector/MySqlDateTimeType.md
+++ b/docs/content/api/MySqlConnector/MySqlDateTimeType.md
@@ -11,11 +11,21 @@ public struct MySqlDateTime : IComparable, IComparable<MySqlDateTime>, IConverti
     IEquatable<MySqlDateTime>
 ```
 
+| parameter | description |
+| --- | --- |
+| year | The year. |
+| month | The (one-based) month. |
+| day | The (one-based) day of the month. |
+| hour | The hour. |
+| minute | The minute. |
+| second | The second. |
+| microsecond | The microsecond. |
+
 ## Public Members
 
 | name | description |
 | --- | --- |
-| [MySqlDateTime](../MySqlDateTime/MySqlDateTime/)(…) | Initializes a new instance of [`MySqlDateTime`](../MySqlDateTimeType/). (3 constructors) |
+| [MySqlDateTime](../MySqlDateTime/MySqlDateTime/)(…) | Represents a MySQL date/time value. This type can be used to store `DATETIME` values such as `0000-00-00` that can be stored in MySQL (when [`AllowZeroDateTime`](../MySqlConnectionStringBuilder/AllowZeroDateTime/) is true) but can't be stored in a DateTime value. (3 constructors) |
 | [Day](../MySqlDateTime/Day/) { get; set; } | Gets or sets the day of the month. |
 | [Hour](../MySqlDateTime/Hour/) { get; set; } | Gets or sets the hour. |
 | [IsValidDateTime](../MySqlDateTime/IsValidDateTime/) { get; } | Returns `true` if this value is a valid DateTime. |
diff --git a/docs/content/api/MySqlConnector/MySqlEndOfStreamException/ExpectedByteCount.md b/docs/content/api/MySqlConnector/MySqlEndOfStreamException/ExpectedByteCount.md
new file mode 100644
index 000000000..aaf2b111a
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlEndOfStreamException/ExpectedByteCount.md
@@ -0,0 +1,17 @@
+---
+title: MySqlEndOfStreamException.ExpectedByteCount property
+---
+
+# MySqlEndOfStreamException.ExpectedByteCount property
+
+```csharp
+public int ExpectedByteCount { get; }
+```
+
+## See Also
+
+* class [MySqlEndOfStreamException](../../MySqlEndOfStreamExceptionType/)
+* namespace [MySqlConnector](../../MySqlEndOfStreamExceptionType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlEndOfStreamException/ReadByteCount.md b/docs/content/api/MySqlConnector/MySqlEndOfStreamException/ReadByteCount.md
new file mode 100644
index 000000000..37b51437f
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlEndOfStreamException/ReadByteCount.md
@@ -0,0 +1,17 @@
+---
+title: MySqlEndOfStreamException.ReadByteCount property
+---
+
+# MySqlEndOfStreamException.ReadByteCount property
+
+```csharp
+public int ReadByteCount { get; }
+```
+
+## See Also
+
+* class [MySqlEndOfStreamException](../../MySqlEndOfStreamExceptionType/)
+* namespace [MySqlConnector](../../MySqlEndOfStreamExceptionType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlEndOfStreamExceptionType.md b/docs/content/api/MySqlConnector/MySqlEndOfStreamExceptionType.md
new file mode 100644
index 000000000..a73fc79c4
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlEndOfStreamExceptionType.md
@@ -0,0 +1,23 @@
+---
+title: MySqlEndOfStreamException
+---
+
+# MySqlEndOfStreamException class
+
+```csharp
+public sealed class MySqlEndOfStreamException : EndOfStreamException
+```
+
+## Public Members
+
+| name | description |
+| --- | --- |
+| [ExpectedByteCount](../MySqlEndOfStreamException/ExpectedByteCount/) { get; } |  |
+| [ReadByteCount](../MySqlEndOfStreamException/ReadByteCount/) { get; } |  |
+
+## See Also
+
+* namespace [MySqlConnector](../../MySqlConnectorNamespace/)
+* assembly [MySqlConnector](../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlException/GetObjectData.md b/docs/content/api/MySqlConnector/MySqlException/GetObjectData.md
deleted file mode 100644
index 0c7e55da9..000000000
--- a/docs/content/api/MySqlConnector/MySqlException/GetObjectData.md
+++ /dev/null
@@ -1,24 +0,0 @@
----
-title: MySqlException.GetObjectData method
----
-
-# MySqlException.GetObjectData method
-
-Sets the SerializationInfo with information about the exception.
-
-```csharp
-public override void GetObjectData(SerializationInfo info, StreamingContext context)
-```
-
-| parameter | description |
-| --- | --- |
-| info | The SerializationInfo that will be set. |
-| context | The context. |
-
-## See Also
-
-* class [MySqlException](../../MySqlExceptionType/)
-* namespace [MySqlConnector](../../MySqlExceptionType/)
-* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
-
-<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlExceptionType.md b/docs/content/api/MySqlConnector/MySqlExceptionType.md
index bf1fab6ba..a079e5179 100644
--- a/docs/content/api/MySqlConnector/MySqlExceptionType.md
+++ b/docs/content/api/MySqlConnector/MySqlExceptionType.md
@@ -19,7 +19,6 @@ public sealed class MySqlException : DbException
 | override [IsTransient](../MySqlException/IsTransient/) { get; } | Returns `true` if this exception could indicate a transient error condition (that could succeed if retried); otherwise, `false`. |
 | [Number](../MySqlException/Number/) { get; } | A [`MySqlErrorCode`](../MySqlErrorCodeType/) value identifying the kind of error. Prefer to use the [`ErrorCode`](../MySqlException/ErrorCode/) property. |
 | override [SqlState](../MySqlException/SqlState/) { get; } | A `SQLSTATE` code identifying the kind of error. |
-| override [GetObjectData](../MySqlException/GetObjectData/)(…) | Sets the SerializationInfo with information about the exception. |
 
 ## See Also
 
diff --git a/docs/content/api/MySqlConnectorAssembly.md b/docs/content/api/MySqlConnectorAssembly.md
index 7d739a64f..2700276c9 100644
--- a/docs/content/api/MySqlConnectorAssembly.md
+++ b/docs/content/api/MySqlConnectorAssembly.md
@@ -29,13 +29,14 @@ title: MySqlConnector
 | class [MySqlConversionException](../MySqlConnector/MySqlConversionExceptionType/) | [`MySqlConversionException`](../MySqlConnector/MySqlConversionExceptionType/) is thrown when a MySQL value can't be converted to another type. |
 | class [MySqlDataAdapter](../MySqlConnector/MySqlDataAdapterType/) |  |
 | class [MySqlDataReader](../MySqlConnector/MySqlDataReaderType/) |  |
-| class [MySqlDataSource](../MySqlConnector/MySqlDataSourceType/) | [`MySqlDataSource`](../MySqlConnector/MySqlDataSourceType/) implements a MySQL data source which can be used to obtain open connections, and against which commands can be executed directly. |
+| class [MySqlDataSource](../MySqlConnector/MySqlDataSourceType/) | [`MySqlDataSource`](../MySqlConnector/MySqlDataSourceType/) implements a MySQL data source which can be used to obtain open connections. |
 | class [MySqlDataSourceBuilder](../MySqlConnector/MySqlDataSourceBuilderType/) | [`MySqlDataSourceBuilder`](../MySqlConnector/MySqlDataSourceBuilderType/) provides an API for configuring and creating a [`MySqlDataSource`](../MySqlConnector/MySqlDataSourceType/), from which [`MySqlConnection`](../MySqlConnector/MySqlConnectionType/) objects can be obtained. |
 | struct [MySqlDateTime](../MySqlConnector/MySqlDateTimeType/) | Represents a MySQL date/time value. This type can be used to store `DATETIME` values such as `0000-00-00` that can be stored in MySQL (when [`AllowZeroDateTime`](../MySqlConnector/MySqlConnectionStringBuilder/AllowZeroDateTime/) is true) but can't be stored in a DateTime value. |
 | enum [MySqlDateTimeKind](../MySqlConnector/MySqlDateTimeKindType/) | The DateTimeKind used when reading DateTime from the database. |
 | class [MySqlDbColumn](../MySqlConnector/MySqlDbColumnType/) |  |
 | enum [MySqlDbType](../MySqlConnector/MySqlDbTypeType/) |  |
 | struct [MySqlDecimal](../MySqlConnector/MySqlDecimalType/) | [`MySqlDecimal`](../MySqlConnector/MySqlDecimalType/) represents a MySQL `DECIMAL` value that is too large to fit in a .NET Decimal. |
+| class [MySqlEndOfStreamException](../MySqlConnector/MySqlEndOfStreamExceptionType/) |  |
 | class [MySqlError](../MySqlConnector/MySqlErrorType/) | [`MySqlError`](../MySqlConnector/MySqlErrorType/) represents an error or warning that occurred during the execution of a SQL statement. |
 | enum [MySqlErrorCode](../MySqlConnector/MySqlErrorCodeType/) | MySQL Server error codes. Taken from [Server Error Codes and Messages](https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html). |
 | class [MySqlException](../MySqlConnector/MySqlExceptionType/) | [`MySqlException`](../MySqlConnector/MySqlExceptionType/) is thrown when MySQL Server returns an error code, or there is a communication error with the server. |
diff --git a/docs/content/api/MySqlConnectorNamespace.md b/docs/content/api/MySqlConnectorNamespace.md
index 740a3c32a..0204e78d5 100644
--- a/docs/content/api/MySqlConnectorNamespace.md
+++ b/docs/content/api/MySqlConnectorNamespace.md
@@ -27,13 +27,14 @@ title: MySqlConnector
 | class [MySqlConversionException](../MySqlConnector/MySqlConversionExceptionType/) | [`MySqlConversionException`](../MySqlConnector/MySqlConversionExceptionType/) is thrown when a MySQL value can't be converted to another type. |
 | class [MySqlDataAdapter](../MySqlConnector/MySqlDataAdapterType/) |  |
 | class [MySqlDataReader](../MySqlConnector/MySqlDataReaderType/) |  |
-| class [MySqlDataSource](../MySqlConnector/MySqlDataSourceType/) | [`MySqlDataSource`](../MySqlConnector/MySqlDataSourceType/) implements a MySQL data source which can be used to obtain open connections, and against which commands can be executed directly. |
+| class [MySqlDataSource](../MySqlConnector/MySqlDataSourceType/) | [`MySqlDataSource`](../MySqlConnector/MySqlDataSourceType/) implements a MySQL data source which can be used to obtain open connections. |
 | class [MySqlDataSourceBuilder](../MySqlConnector/MySqlDataSourceBuilderType/) | [`MySqlDataSourceBuilder`](../MySqlConnector/MySqlDataSourceBuilderType/) provides an API for configuring and creating a [`MySqlDataSource`](../MySqlConnector/MySqlDataSourceType/), from which [`MySqlConnection`](../MySqlConnector/MySqlConnectionType/) objects can be obtained. |
 | struct [MySqlDateTime](../MySqlConnector/MySqlDateTimeType/) | Represents a MySQL date/time value. This type can be used to store `DATETIME` values such as `0000-00-00` that can be stored in MySQL (when [`AllowZeroDateTime`](../MySqlConnector/MySqlConnectionStringBuilder/AllowZeroDateTime/) is true) but can't be stored in a DateTime value. |
 | enum [MySqlDateTimeKind](../MySqlConnector/MySqlDateTimeKindType/) | The DateTimeKind used when reading DateTime from the database. |
 | class [MySqlDbColumn](../MySqlConnector/MySqlDbColumnType/) |  |
 | enum [MySqlDbType](../MySqlConnector/MySqlDbTypeType/) |  |
 | struct [MySqlDecimal](../MySqlConnector/MySqlDecimalType/) | [`MySqlDecimal`](../MySqlConnector/MySqlDecimalType/) represents a MySQL `DECIMAL` value that is too large to fit in a .NET Decimal. |
+| class [MySqlEndOfStreamException](../MySqlConnector/MySqlEndOfStreamExceptionType/) |  |
 | class [MySqlError](../MySqlConnector/MySqlErrorType/) | [`MySqlError`](../MySqlConnector/MySqlErrorType/) represents an error or warning that occurred during the execution of a SQL statement. |
 | enum [MySqlErrorCode](../MySqlConnector/MySqlErrorCodeType/) | MySQL Server error codes. Taken from [Server Error Codes and Messages](https://dev.mysql.com/doc/mysql-errors/8.0/en/server-error-reference.html). |
 | class [MySqlException](../MySqlConnector/MySqlExceptionType/) | [`MySqlException`](../MySqlConnector/MySqlExceptionType/) is thrown when MySQL Server returns an error code, or there is a communication error with the server. |
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index 25a34d431..986ad011e 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -8,7 +8,7 @@
 namespace MySqlConnector;
 
 /// <summary>
-/// <see cref="MySqlDataSource"/> implements a MySQL data source which can be used to obtain open connections, and against which commands can be executed directly.
+/// <see cref="MySqlDataSource"/> implements a MySQL data source which can be used to obtain open connections.
 /// </summary>
 public sealed class MySqlDataSource : DbDataSource
 {

From e39d3b894c408f55820fcee96d7c5e1d5c8e7c29 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 1 Dec 2023 15:35:41 -0800
Subject: [PATCH 225/393] Clear activity tags when retrying TLS. Fixes #1405

---
 src/MySqlConnector/Core/ServerSession.cs | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 7f8e471a7..9cc297617 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -561,6 +561,14 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				{
 					shouldRetrySsl = false;
 				}
+
+				if (shouldRetrySsl)
+				{
+					// avoid "The collection already contains item with same key 'net.transport'" exception when retrying SSL
+					m_activityTags.Remove(ActivitySourceHelper.NetTransportTagName);
+					m_activityTags.Remove(ActivitySourceHelper.NetPeerNameTagName);
+					m_activityTags.Remove(ActivitySourceHelper.NetPeerPortTagName);
+				}
 			} while (shouldRetrySsl);
 
 			if (m_supportsConnectionAttributes && cs.ConnectionAttributes is null)

From d89918c01c30a7f0136be636d888cc4580d1428d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 1 Dec 2023 15:50:13 -0800
Subject: [PATCH 226/393] Avoid retrying TLS when a non-recoverable exception
 occurs.

Certain types of IOException will not be resolved by retrying the connection with a different TLS version so we can avoid the loop and throw the exception immediately.
---
 src/MySqlConnector/Core/ServerSession.cs | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 9cc297617..10e8526a4 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -549,13 +549,16 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 						Log.SessionDoesNotSupportSslProtocolsNone(m_logger, ex, Id);
 						sslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
 					}
-					catch (Exception ex) when (shouldRetrySsl && ((ex is MySqlException && ex.InnerException is AuthenticationException or IOException) || ex is AuthenticationException or IOException))
+					catch (Exception ex) when (shouldRetrySsl && IsRetryableException(ex))
 					{
 						// negotiating TLS 1.2 with a yaSSL-based server throws an exception on Windows, see comment at top of method
 						Log.FailedNegotiatingTls(m_logger, ex, Id);
 						sslProtocols = sslProtocols == SslProtocols.None ? SslProtocols.Tls | SslProtocols.Tls11 : (SslProtocols.Tls | SslProtocols.Tls11) & sslProtocols;
 						shouldUpdatePoolSslProtocols = true;
 					}
+
+					static bool IsRetryableException(Exception? ex) => (ex is MySqlException && IsRetryableException(ex.InnerException)) ||
+						(ex is AuthenticationException or (IOException and not FileNotFoundException and not DirectoryNotFoundException and not DriveNotFoundException and not PathTooLongException));
 				}
 				else
 				{

From 83403d37d305ae704769bf72982f6f5bc64d6234 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 4 Dec 2023 09:29:37 -0800
Subject: [PATCH 227/393] Update Appveyor build to MySQL 8.0.

This mirrors changes other projects have made in response to this issue: https://github.com/appveyor/ci/issues/3894
---
 .ci/config/config.compression.json          |  4 ++--
 .ci/config/config.json                      |  4 ++--
 appveyor.yml                                | 13 ++++++++-----
 tests/Conformance.Tests/DbFactoryFixture.cs |  2 +-
 4 files changed, 13 insertions(+), 10 deletions(-)

diff --git a/.ci/config/config.compression.json b/.ci/config/config.compression.json
index ca3199da0..f42f53ab6 100644
--- a/.ci/config/config.compression.json
+++ b/.ci/config/config.compression.json
@@ -1,10 +1,10 @@
 {
     "Data": {
-      "ConnectionString": "server=127.0.0.1;user id=mysqltest;password=test;port=3306;database=mysqltest;ssl mode=none;UseCompression=true;DefaultCommandTimeout=3600",
+      "ConnectionString": "server=127.0.0.1;user id=mysqltest;password=test;port=3306;database=mysqltest;ssl mode=none;UseCompression=true;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=true",
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "Ed25519,RsaEncryption,CachingSha2Password,QueryAttributes,Tls12,Tls13,UnixDomainSocket,UuidToBin",
+      "UnsupportedFeatures": "Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket,ZeroDateTime",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
diff --git a/.ci/config/config.json b/.ci/config/config.json
index fa5908d80..183b2299c 100644
--- a/.ci/config/config.json
+++ b/.ci/config/config.json
@@ -1,10 +1,10 @@
 {
     "Data": {
-      "ConnectionString": "server=127.0.0.1;user id=mysqltest;password=test;port=3306;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600",
+      "ConnectionString": "server=127.0.0.1;user id=mysqltest;password=test;port=3306;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=true",
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "Ed25519,RsaEncryption,CachingSha2Password,QueryAttributes,Tls12,Tls13,UnixDomainSocket,UuidToBin",
+      "UnsupportedFeatures": "Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket,ZeroDateTime",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
diff --git a/appveyor.yml b/appveyor.yml
index 7e9cc531f..df9959cd6 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,6 +1,4 @@
-image: Previous Visual Studio 2022
-services:
-- mysql
+image: Visual Studio 2022
 environment:
   global:
     DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true
@@ -15,9 +13,14 @@ install:
 build_script:
 - dotnet --info
 before_test:
+- ps: Start-Service MySQL80
 - cmd: |-
-    "C:\Program Files\MySQL\MySQL Server 5.7\bin\mysql.exe" -u root --password=Password12! < .ci\server\init.sql
-    "C:\Program Files\MySQL\MySQL Server 5.7\bin\mysql.exe" -u root --password=Password12! < .ci\server\init_sha256.sql
+    "C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe" -u root --password=Password12! -e "SET GLOBAL log_bin_trust_function_creators = 1;"
+    "C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe" -u root --password=Password12! -e "SET GLOBAL local_infile = 1;"
+    "C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe" -u root --password=Password12! -e "SET GLOBAL max_connections = 250;"
+    "C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe" -u root --password=Password12! < .ci\server\init.sql
+    "C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe" -u root --password=Password12! < .ci\server\init_sha256.sql
+    "C:\Program Files\MySQL\MySQL Server 8.0\bin\mysql.exe" -u root --password=Password12! < .ci\server\init_caching_sha2.sql
 test_script:
 - ps: .\.ci\test.ps1
 after_test:
diff --git a/tests/Conformance.Tests/DbFactoryFixture.cs b/tests/Conformance.Tests/DbFactoryFixture.cs
index 358226069..d52ea741b 100644
--- a/tests/Conformance.Tests/DbFactoryFixture.cs
+++ b/tests/Conformance.Tests/DbFactoryFixture.cs
@@ -9,7 +9,7 @@ public class DbFactoryFixture : IDbFactoryFixture
 	{
 		public DbFactoryFixture()
 		{
-			ConnectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING") ?? "Server=localhost;User Id=mysqltest;Password=test;SSL Mode=None";
+			ConnectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING") ?? "Server=localhost;User Id=mysqltest;Password=test;SSL Mode=None;AllowPublicKeyRetrieval=true";
 		}
 
 		public string ConnectionString { get; }

From ab05700f9ee851888201ede992fbdfb4ed97b053 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 5 Dec 2023 20:27:51 +0000
Subject: [PATCH 228/393] Bump actions/setup-dotnet from 3 to 4

Bumps [actions/setup-dotnet](https://github.com/actions/setup-dotnet) from 3 to 4.
- [Release notes](https://github.com/actions/setup-dotnet/releases)
- [Commits](https://github.com/actions/setup-dotnet/compare/v3...v4)

---
updated-dependencies:
- dependency-name: actions/setup-dotnet
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/ci.yml              | 2 +-
 .github/workflows/codeql-analysis.yml | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 0d9342836..4cc77bfca 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -16,7 +16,7 @@ jobs:
       uses: actions/checkout@v4
 
     - name: Set up .NET 8.0
-      uses: actions/setup-dotnet@v3
+      uses: actions/setup-dotnet@v4
       with:
         dotnet-version: 8.x
 
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index d9c51efda..617a65fcb 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -27,7 +27,7 @@ jobs:
       uses: actions/checkout@v4
 
     - name: Set up .NET 8.0
-      uses: actions/setup-dotnet@v3
+      uses: actions/setup-dotnet@v4
       with:
         dotnet-version: 8.x
 

From c0a4522c0b76ef917fdbe3651264dc0b18583f9a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 13 Dec 2023 16:58:12 +0000
Subject: [PATCH 229/393] Bump github/codeql-action from 2 to 3

Bumps [github/codeql-action](https://github.com/github/codeql-action) from 2 to 3.
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/v2...v3)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/codeql-analysis.yml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 617a65fcb..676bb5d55 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -32,13 +32,13 @@ jobs:
         dotnet-version: 8.x
 
     - name: Initialize CodeQL
-      uses: github/codeql-action/init@v2
+      uses: github/codeql-action/init@v3
       with:
         languages: ${{ matrix.language }}
         queries: security-extended
 
     - name: Build
-      uses: github/codeql-action/autobuild@v2
+      uses: github/codeql-action/autobuild@v3
 
     - name: Perform CodeQL Analysis
-      uses: github/codeql-action/analyze@v2
+      uses: github/codeql-action/analyze@v3

From 9992b5def49e2c47ee29506a715cae180d68e646 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 15 Dec 2023 16:07:29 -0800
Subject: [PATCH 230/393] Add `Foreign Keys`, `Indexes`, `IndexColumns` schema
 collections (#1419)

Signed-off-by: Evgeni Alesinskyy <ealesinskyy@exocad.com>
Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
Co-authored-by: Evgeni Alesinskyy <ealesinskyy@exocad.com>
---
 docs/content/overview/schema-collections.md   |   5 +-
 docs/content/overview/schema/foreign keys.md  |  34 +++++
 docs/content/overview/schema/indexcolumns.md  |  30 ++++
 docs/content/overview/schema/indexes.md       |  30 ++++
 src/MySqlConnector/Core/SchemaProvider.cs     | 132 ++++++++++++++++--
 src/MySqlConnector/Core/SchemaProvider.g.cs   |  88 ++++++++++++
 .../IntegrationTests/SchemaProviderFixture.cs |  32 +++++
 tests/IntegrationTests/SchemaProviderTests.cs |  71 +++++++++-
 .../SchemaCollectionGenerator.cs              |  29 ++--
 .../SchemaCollections.yaml                    |  98 +++++++++++++
 10 files changed, 528 insertions(+), 21 deletions(-)
 create mode 100644 docs/content/overview/schema/foreign keys.md
 create mode 100644 docs/content/overview/schema/indexcolumns.md
 create mode 100644 docs/content/overview/schema/indexes.md
 create mode 100644 tests/IntegrationTests/SchemaProviderFixture.cs

diff --git a/docs/content/overview/schema-collections.md b/docs/content/overview/schema-collections.md
index b9ef01ddd..d8a4178b4 100644
--- a/docs/content/overview/schema-collections.md
+++ b/docs/content/overview/schema-collections.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-01-28
+lastmod: 2023-12-15
 date: 2021-04-24
 title: Schema Collections
 customtitle: "Supported Schema Collections"
@@ -45,3 +45,6 @@ weight: 80
 * `Triggers`
 * `UserPrivileges`
 * `Views`
+* `Foreign Keys`—[information about foreign keys in the server's SQL syntax](../schema/foreign keys/)
+* `Indexes`—[information about indexes in the server's SQL syntax](../schema/indexes/)
+* `IndexColumns`—[information about indexes in the server's SQL syntax](../schema/indexcolumns/)
diff --git a/docs/content/overview/schema/foreign keys.md b/docs/content/overview/schema/foreign keys.md
new file mode 100644
index 000000000..3fea8ece8
--- /dev/null
+++ b/docs/content/overview/schema/foreign keys.md	
@@ -0,0 +1,34 @@
+---
+date: 2022-07-10
+lastmod: 2023-12-15
+title: Foreign Keys Schema
+---
+
+# Foreign Keys Schema
+
+The `Foreign Keys` schema provides information about foreign keys in the server's SQL syntax.
+
+Column Name | Data Type | Description
+--- | --- | ---
+CONSTRAINT_CATALOG | string | 
+CONSTRAINT_SCHEMA | string | 
+CONSTRAINT_NAME | string | 
+TABLE_CATALOG | string | 
+TABLE_SCHEMA | string | 
+TABLE_NAME | string | 
+MATCH_OPTION | string | 
+UPDATE_RULE | string | 
+DELETE_RULE | string | 
+REFERENCED_TABLE_CATALOG | string | 
+REFERENCED_TABLE_SCHEMA | string | 
+REFERENCED_TABLE_NAME | string | 
+
+The following restrictions are supported:
+
+Restriction Name | Restriction Default | Restriction Number
+--- | --- | --:
+Catalog | TABLE_CATALOG | 1
+Schema | TABLE_SCHEMA | 2
+Table | TABLE_NAME | 3
+Column | COLUMN_NAME | 4
+
diff --git a/docs/content/overview/schema/indexcolumns.md b/docs/content/overview/schema/indexcolumns.md
new file mode 100644
index 000000000..e184e00d0
--- /dev/null
+++ b/docs/content/overview/schema/indexcolumns.md
@@ -0,0 +1,30 @@
+---
+date: 2022-07-10
+lastmod: 2023-12-15
+title: IndexColumns Schema
+---
+
+# IndexColumns Schema
+
+The `IndexColumns` schema provides information about indexes in the server's SQL syntax.
+
+Column Name | Data Type | Description
+--- | --- | ---
+INDEX_CATALOG | string | 
+INDEX_SCHEMA | string | 
+INDEX_NAME | string | 
+TABLE_NAME | string | 
+COLUMN_NAME | string | 
+ORDINAL_POSITION | int | 
+SORT_ORDER | string | 
+
+The following restrictions are supported:
+
+Restriction Name | Restriction Default | Restriction Number
+--- | --- | --:
+Catalog | TABLE_CATALOG | 1
+Schema | TABLE_SCHEMA | 2
+Table | TABLE_NAME | 3
+Constraint | CONSTRAINT_NAME | 4
+Column | COLUMN_NAME | 5
+
diff --git a/docs/content/overview/schema/indexes.md b/docs/content/overview/schema/indexes.md
new file mode 100644
index 000000000..2cce279ba
--- /dev/null
+++ b/docs/content/overview/schema/indexes.md
@@ -0,0 +1,30 @@
+---
+date: 2022-07-10
+lastmod: 2023-12-15
+title: Indexes Schema
+---
+
+# Indexes Schema
+
+The `Indexes` schema provides information about indexes in the server's SQL syntax.
+
+Column Name | Data Type | Description
+--- | --- | ---
+INDEX_CATALOG | string | 
+INDEX_SCHEMA | string | 
+INDEX_NAME | string | 
+TABLE_NAME | string | 
+UNIQUE | bool | 
+PRIMARY | bool | 
+TYPE | string | 
+COMMENT | string | 
+
+The following restrictions are supported:
+
+Restriction Name | Restriction Default | Restriction Number
+--- | --- | --:
+Catalog | TABLE_CATALOG | 1
+Schema | TABLE_SCHEMA | 2
+Table | TABLE_NAME | 3
+Column | COLUMN_NAME | 4
+
diff --git a/src/MySqlConnector/Core/SchemaProvider.cs b/src/MySqlConnector/Core/SchemaProvider.cs
index 1ca8b015e..f854fe650 100644
--- a/src/MySqlConnector/Core/SchemaProvider.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.cs
@@ -1,4 +1,5 @@
 using System.Globalization;
+using System.Text;
 using MySqlConnector.Protocol.Serialization;
 
 namespace MySqlConnector.Core;
@@ -382,6 +383,22 @@ private static void DoFillReservedWords(DataTable dataTable)
 	}
 
 	private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, List<KeyValuePair<string, string>>? columns, CancellationToken cancellationToken)
+	{
+		await FillDataTableAsync(ioBehavior, dataTable, command =>
+		{
+#pragma warning disable CA2100
+			command.CommandText = "SELECT " + string.Join(", ", dataTable.Columns.Cast<DataColumn>().Select(static x => x!.ColumnName)) + " FROM INFORMATION_SCHEMA." + tableName;
+#pragma warning restore CA2100
+			if (columns is { Count: > 0 })
+			{
+				command.CommandText += " WHERE " + string.Join(" AND ", columns.Select(x => $@"{x.Key} = @{x.Key}"));
+				foreach (var column in columns)
+					command.Parameters.AddWithValue("@" + column.Key, column.Value);
+			}
+		}, cancellationToken).ConfigureAwait(false);
+	}
+
+	private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable, Action<MySqlCommand> configureCommand, CancellationToken cancellationToken)
 	{
 		Action? close = null;
 		if (connection.State != ConnectionState.Open)
@@ -408,15 +425,7 @@ private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable
 
 		using (var command = connection.CreateCommand())
 		{
-#pragma warning disable CA2100
-			command.CommandText = "SELECT " + string.Join(", ", dataTable.Columns.Cast<DataColumn>().Select(static x => x!.ColumnName)) + " FROM INFORMATION_SCHEMA." + tableName;
-#pragma warning restore CA2100
-			if (columns is { Count: > 0 })
-			{
-				command.CommandText += " WHERE " + string.Join(" AND ", columns.Select(x => $@"{x.Key} = @{x.Key}"));
-				foreach (var column in columns)
-					command.Parameters.AddWithValue("@" + column.Key, column.Value);
-			}
+			configureCommand(command);
 
 			using var reader = await command.ExecuteReaderAsync(default, ioBehavior, cancellationToken).ConfigureAwait(false);
 			while (await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false))
@@ -429,4 +438,109 @@ private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable
 
 		close?.Invoke();
 	}
+
+	private Task DoFillForeignKeysAsync(IOBehavior ioBehavior, DataTable dataTable, string?[]? restrictionValues, CancellationToken cancellationToken) =>
+		FillDataTableAsync(IOBehavior.Synchronous, dataTable, command =>
+		{
+			command.CommandText = """
+				SELECT rc.constraint_catalog, rc.constraint_schema,
+					rc.constraint_name, kcu.table_catalog, kcu.table_schema, rc.table_name,
+					rc.match_option, rc.update_rule, rc.delete_rule, 
+					NULL as referenced_table_catalog,
+					kcu.referenced_table_schema, rc.referenced_table_name 
+				FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
+					LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu ON 
+					kcu.constraint_catalog <=> rc.constraint_catalog AND
+					kcu.constraint_schema <=> rc.constraint_schema AND 
+					kcu.constraint_name <=> rc.constraint_name 
+				WHERE 1=1 AND kcu.ORDINAL_POSITION=1
+				""";
+
+			if (restrictionValues is not null)
+			{
+				var where = "";
+				if (restrictionValues.Length >= 2 && !string.IsNullOrEmpty(restrictionValues[1]))
+				{
+					where += " AND rc.constraint_schema LIKE @schema";
+					command.Parameters.AddWithValue("@schema", restrictionValues[1]);
+				}
+				if (restrictionValues.Length >= 3 && !string.IsNullOrEmpty(restrictionValues[2]))
+				{
+					where += " AND rc.table_name LIKE @table";
+					command.Parameters.AddWithValue("@table", restrictionValues[2]);
+				}
+				if (restrictionValues.Length >= 4 && !string.IsNullOrEmpty(restrictionValues[3]))
+				{
+					where += " AND rc.constraint_name LIKE @constraint";
+					command.Parameters.AddWithValue("@constraint", restrictionValues[3]);
+				}
+				command.CommandText += where;
+			}
+		}, cancellationToken);
+
+	private Task DoFillIndexesAsync(IOBehavior ioBehavior, DataTable dataTable, string?[]? restrictionValues, CancellationToken cancellationToken) =>
+		FillDataTableAsync(ioBehavior, dataTable, command =>
+		{
+			command.CommandText = """
+				SELECT DISTINCT null AS INDEX_CATALOG, INDEX_SCHEMA,
+					INDEX_NAME, TABLE_NAME,
+					!NON_UNIQUE as `UNIQUE`, 
+					INDEX_NAME='PRIMARY' as `PRIMARY`,
+					INDEX_TYPE as TYPE, COMMENT 
+				FROM INFORMATION_SCHEMA.STATISTICS
+				WHERE 1=1
+				""";
+
+			if (restrictionValues is not null)
+			{
+				var where = "";
+				if (restrictionValues.Length >= 2 && !string.IsNullOrEmpty(restrictionValues[1]))
+				{
+					where += " AND INDEX_SCHEMA LIKE @schema";
+					command.Parameters.AddWithValue("@schema", restrictionValues[1]);
+				}
+				if (restrictionValues.Length >= 3 && !string.IsNullOrEmpty(restrictionValues[2]))
+				{
+					where += " AND TABLE_NAME LIKE @table";
+					command.Parameters.AddWithValue("@table", restrictionValues[2]);
+				}
+
+				command.CommandText += where;
+			}
+		}, cancellationToken);
+
+	private Task DoFillIndexColumnsAsync(IOBehavior ioBehavior, DataTable dataTable, string?[]? restrictionValues, CancellationToken cancellationToken) =>
+		FillDataTableAsync(ioBehavior, dataTable, command =>
+		{
+			command.CommandText = """
+				SELECT null AS INDEX_CATALOG, INDEX_SCHEMA,
+					INDEX_NAME, TABLE_NAME,
+					COLUMN_NAME,
+					SEQ_IN_INDEX as `ORDINAL_POSITION`,
+					COLLATION as SORT_ORDER
+				FROM INFORMATION_SCHEMA.STATISTICS
+				WHERE 1=1
+				""";
+
+			if (restrictionValues is not null)
+			{
+				var where = "";
+				if (restrictionValues.Length >= 2 && !string.IsNullOrEmpty(restrictionValues[1]))
+				{
+					where += " AND INDEX_SCHEMA LIKE @schema";
+					command.Parameters.AddWithValue("@schema", restrictionValues[1]);
+				}
+				if (restrictionValues.Length >= 3 && !string.IsNullOrEmpty(restrictionValues[2]))
+				{
+					where += " AND TABLE_NAME LIKE @table";
+					command.Parameters.AddWithValue("@table", restrictionValues[2]);
+				}
+				if (restrictionValues.Length >= 4 && !string.IsNullOrEmpty(restrictionValues[3]))
+				{
+					where += " AND INDEX_NAME LIKE @index";
+					command.Parameters.AddWithValue("@index", restrictionValues[3]);
+				}
+				command.CommandText += where;
+			}
+		}, cancellationToken);
 }
diff --git a/src/MySqlConnector/Core/SchemaProvider.g.cs b/src/MySqlConnector/Core/SchemaProvider.g.cs
index 98471c6b5..03e942960 100644
--- a/src/MySqlConnector/Core/SchemaProvider.g.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.g.cs
@@ -75,6 +75,12 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 			await FillUserPrivilegesAsync(ioBehavior, dataTable, "UserPrivileges", restrictionValues, cancellationToken).ConfigureAwait(false);
 		else if (string.Equals(collectionName, "Views", StringComparison.OrdinalIgnoreCase))
 			await FillViewsAsync(ioBehavior, dataTable, "Views", restrictionValues, cancellationToken).ConfigureAwait(false);
+		else if (string.Equals(collectionName, "Foreign Keys", StringComparison.OrdinalIgnoreCase))
+			await FillForeignKeysAsync(ioBehavior, dataTable, "Foreign Keys", restrictionValues, cancellationToken).ConfigureAwait(false);
+		else if (string.Equals(collectionName, "Indexes", StringComparison.OrdinalIgnoreCase))
+			await FillIndexesAsync(ioBehavior, dataTable, "Indexes", restrictionValues, cancellationToken).ConfigureAwait(false);
+		else if (string.Equals(collectionName, "IndexColumns", StringComparison.OrdinalIgnoreCase))
+			await FillIndexColumnsAsync(ioBehavior, dataTable, "IndexColumns", restrictionValues, cancellationToken).ConfigureAwait(false);
 		else
 			throw new ArgumentException($"Invalid collection name: '{collectionName}'.", nameof(collectionName));
 
@@ -123,6 +129,9 @@ private Task FillMetaDataCollectionsAsync(IOBehavior ioBehavior, DataTable dataT
 		dataTable.Rows.Add("Triggers", 0, 3);
 		dataTable.Rows.Add("UserPrivileges", 0, 0);
 		dataTable.Rows.Add("Views", 0, 3);
+		dataTable.Rows.Add("Foreign Keys", 4, 0);
+		dataTable.Rows.Add("Indexes", 4, 0);
+		dataTable.Rows.Add("IndexColumns", 5, 0);
 
 		return Task.CompletedTask;
 	}
@@ -628,6 +637,19 @@ private Task FillRestrictionsAsync(IOBehavior ioBehavior, DataTable dataTable, s
 		dataTable.Rows.Add("Tables", "Schema", "TABLE_SCHEMA", 2);
 		dataTable.Rows.Add("Tables", "Table", "TABLE_NAME", 3);
 		dataTable.Rows.Add("Tables", "TableType", "TABLE_TYPE", 4);
+		dataTable.Rows.Add("Foreign Keys", "Catalog", "TABLE_CATALOG", 1);
+		dataTable.Rows.Add("Foreign Keys", "Schema", "TABLE_SCHEMA", 2);
+		dataTable.Rows.Add("Foreign Keys", "Table", "TABLE_NAME", 3);
+		dataTable.Rows.Add("Foreign Keys", "Column", "COLUMN_NAME", 4);
+		dataTable.Rows.Add("Indexes", "Catalog", "TABLE_CATALOG", 1);
+		dataTable.Rows.Add("Indexes", "Schema", "TABLE_SCHEMA", 2);
+		dataTable.Rows.Add("Indexes", "Table", "TABLE_NAME", 3);
+		dataTable.Rows.Add("Indexes", "Column", "COLUMN_NAME", 4);
+		dataTable.Rows.Add("IndexColumns", "Catalog", "TABLE_CATALOG", 1);
+		dataTable.Rows.Add("IndexColumns", "Schema", "TABLE_SCHEMA", 2);
+		dataTable.Rows.Add("IndexColumns", "Table", "TABLE_NAME", 3);
+		dataTable.Rows.Add("IndexColumns", "Constraint", "CONSTRAINT_NAME", 4);
+		dataTable.Rows.Add("IndexColumns", "Column", "COLUMN_NAME", 5);
 
 		return Task.CompletedTask;
 	}
@@ -832,4 +854,70 @@ private async Task FillViewsAsync(IOBehavior ioBehavior, DataTable dataTable, st
 		await FillDataTableAsync(ioBehavior, dataTable, "VIEWS", null, cancellationToken).ConfigureAwait(false);
 	}
 
+	private async Task FillForeignKeysAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
+	{
+		if (restrictionValues is { Length: > 4 })
+			throw new ArgumentException("More than 4 restrictionValues are not supported for schema 'Foreign Keys'.", nameof(restrictionValues));
+
+		dataTable.TableName = tableName;
+		dataTable.Columns.AddRange(
+		[
+			new("CONSTRAINT_CATALOG", typeof(string)),
+			new("CONSTRAINT_SCHEMA", typeof(string)),
+			new("CONSTRAINT_NAME", typeof(string)),
+			new("TABLE_CATALOG", typeof(string)),
+			new("TABLE_SCHEMA", typeof(string)),
+			new("TABLE_NAME", typeof(string)),
+			new("MATCH_OPTION", typeof(string)),
+			new("UPDATE_RULE", typeof(string)),
+			new("DELETE_RULE", typeof(string)),
+			new("REFERENCED_TABLE_CATALOG", typeof(string)),
+			new("REFERENCED_TABLE_SCHEMA", typeof(string)),
+			new("REFERENCED_TABLE_NAME", typeof(string)),
+		]);
+
+		await DoFillForeignKeysAsync(ioBehavior, dataTable, restrictionValues, cancellationToken).ConfigureAwait(false);
+	}
+
+	private async Task FillIndexesAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
+	{
+		if (restrictionValues is { Length: > 4 })
+			throw new ArgumentException("More than 4 restrictionValues are not supported for schema 'Indexes'.", nameof(restrictionValues));
+
+		dataTable.TableName = tableName;
+		dataTable.Columns.AddRange(
+		[
+			new("INDEX_CATALOG", typeof(string)),
+			new("INDEX_SCHEMA", typeof(string)),
+			new("INDEX_NAME", typeof(string)),
+			new("TABLE_NAME", typeof(string)),
+			new("UNIQUE", typeof(bool)),
+			new("PRIMARY", typeof(bool)),
+			new("TYPE", typeof(string)),
+			new("COMMENT", typeof(string)),
+		]);
+
+		await DoFillIndexesAsync(ioBehavior, dataTable, restrictionValues, cancellationToken).ConfigureAwait(false);
+	}
+
+	private async Task FillIndexColumnsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
+	{
+		if (restrictionValues is { Length: > 5 })
+			throw new ArgumentException("More than 5 restrictionValues are not supported for schema 'IndexColumns'.", nameof(restrictionValues));
+
+		dataTable.TableName = tableName;
+		dataTable.Columns.AddRange(
+		[
+			new("INDEX_CATALOG", typeof(string)),
+			new("INDEX_SCHEMA", typeof(string)),
+			new("INDEX_NAME", typeof(string)),
+			new("TABLE_NAME", typeof(string)),
+			new("COLUMN_NAME", typeof(string)),
+			new("ORDINAL_POSITION", typeof(int)),
+			new("SORT_ORDER", typeof(string)),
+		]);
+
+		await DoFillIndexColumnsAsync(ioBehavior, dataTable, restrictionValues, cancellationToken).ConfigureAwait(false);
+	}
+
 }
diff --git a/tests/IntegrationTests/SchemaProviderFixture.cs b/tests/IntegrationTests/SchemaProviderFixture.cs
new file mode 100644
index 000000000..85eba866f
--- /dev/null
+++ b/tests/IntegrationTests/SchemaProviderFixture.cs
@@ -0,0 +1,32 @@
+namespace IntegrationTests;
+
+public class SchemaProviderFixture : DatabaseFixture
+{
+	public SchemaProviderFixture()
+	{
+		Connection.Execute("""
+			DROP TABLE IF EXISTS fk_test;
+			DROP TABLE IF EXISTS pk_test;
+
+			CREATE TABLE pk_test
+			(
+				a INT NOT NULL,
+				b INT NOT NULL,
+				c INT NOT NULL,
+				d INT NOT NULL,
+				e INT NOT NULL,
+				CONSTRAINT pk_test_pk PRIMARY KEY (a, b),
+				CONSTRAINT pk_test_uq UNIQUE INDEX (c, d, e),
+				INDEX pk_test_ix (c, d)
+			);
+
+			CREATE TABLE fk_test
+			(
+				g INT NOT NULL PRIMARY KEY AUTO_INCREMENT,
+				h INT NOT NULL,
+				i INT NOT NULL,
+				CONSTRAINT fk_test_fk FOREIGN KEY (h, i) REFERENCES pk_test(a, b)
+			);
+			""");
+	}
+}
diff --git a/tests/IntegrationTests/SchemaProviderTests.cs b/tests/IntegrationTests/SchemaProviderTests.cs
index aba11872d..8d56cb491 100644
--- a/tests/IntegrationTests/SchemaProviderTests.cs
+++ b/tests/IntegrationTests/SchemaProviderTests.cs
@@ -1,8 +1,9 @@
+#nullable enable
 namespace IntegrationTests;
 
-public class SchemaProviderTests : IClassFixture<DatabaseFixture>, IDisposable
+public class SchemaProviderTests : IClassFixture<SchemaProviderFixture>, IDisposable
 {
-	public SchemaProviderTests(DatabaseFixture database)
+	public SchemaProviderTests(SchemaProviderFixture database)
 	{
 		m_database = database;
 		m_database.Connection.Open();
@@ -118,6 +119,8 @@ public void MetaDataCollectionsRestriction() =>
 	[InlineData("DATABASES", "Databases")]
 	[InlineData("DataTypes")]
 	[InlineData("datatypes", "DataTypes")]
+	[InlineData("Indexes")]
+	[InlineData("IndexColumns")]
 	// only in 8.0 - [InlineData("KeyWords")]
 	[InlineData("MetaDataCollections")]
 	[InlineData("Procedures")]
@@ -130,6 +133,7 @@ public void MetaDataCollectionsRestriction() =>
 	[InlineData("CollationCharacterSetApplicability")]
 	[InlineData("Collations")]
 	[InlineData("Engines")]
+	[InlineData("Foreign Keys")]
 	[InlineData("KeyColumnUsage")]
 	[InlineData("Parameters")]
 	[InlineData("Partitions")]
@@ -143,7 +147,7 @@ public void MetaDataCollectionsRestriction() =>
 	[InlineData("TableSpaces")]
 	[InlineData("UserPrivileges")]
 #endif
-	public void GetSchema(string schemaName, string expectedSchemaName = null)
+	public void GetSchema(string schemaName, string? expectedSchemaName = null)
 	{
 		var table = m_database.Connection.GetSchema(schemaName);
 		Assert.NotNull(table);
@@ -177,5 +181,66 @@ public void GetCollationsSchema()
 	}
 #endif
 
+	[Fact]
+	public void ForeignKeys()
+    {
+        var schemaName = m_database.Connection.Database;
+        var table = m_database.Connection.GetSchema("Foreign Keys", new[] { null, schemaName, "fk_test" });
+        var row = table.Rows.Cast<DataRow>().Single();
+        foreach (var (column, value) in new[]
+        {
+			("CONSTRAINT_CATALOG", "def"),
+			("CONSTRAINT_SCHEMA", schemaName),
+			("CONSTRAINT_NAME", "fk_test_fk"),
+			("TABLE_CATALOG", "def"),
+			("TABLE_SCHEMA", schemaName),
+			("TABLE_NAME", "fk_test"),
+			("MATCH_OPTION", "NONE"),
+			("REFERENCED_TABLE_CATALOG", null),
+			("REFERENCED_TABLE_SCHEMA", schemaName),
+			("REFERENCED_TABLE_NAME", "pk_test"),
+		})
+        {
+			Assert.Equal(value, row[column] is DBNull ? null : (string?) row[column]);
+        }
+    }
+
+	[Fact]
+	public void Indexes()
+	{
+		var schemaName = m_database.Connection.Database;
+		var table = m_database.Connection.GetSchema("Indexes", new[] { null, schemaName, "pk_test" });
+		var actual = table.Rows
+			.Cast<DataRow>()
+			.OrderBy(x => (string) x["INDEX_NAME"])
+			.Select(x => ((string) x["INDEX_SCHEMA"], (string) x["INDEX_NAME"], (string) x["TABLE_NAME"], (bool) x["UNIQUE"], (bool) x["PRIMARY"], (string) x["TYPE"]));
+		var expected = new[]
+		{
+			(schemaName, "pk_test_ix", "pk_test", false, false, "BTREE"),
+			(schemaName, "pk_test_uq", "pk_test", true, false, "BTREE"),
+			(schemaName, "PRIMARY", "pk_test", true, true, "BTREE"),
+		};
+		Assert.Equal(expected, actual);
+	}
+
+	[Fact]
+	public void IndexColumns()
+	{
+		var schemaName = m_database.Connection.Database;
+		var table = m_database.Connection.GetSchema("IndexColumns", new[] { null, schemaName, "pk_test", "pk_test_uq" });
+		var actual = table.Rows
+			.Cast<DataRow>()
+			.OrderBy(x => (string) x["INDEX_NAME"])
+			.ThenBy(x => (int) x["ORDINAL_POSITION"])
+			.Select(x => ((string) x["INDEX_SCHEMA"], (string) x["INDEX_NAME"], (string) x["TABLE_NAME"], (string) x["COLUMN_NAME"], (int) x["ORDINAL_POSITION"]));
+		var expected = new[]
+		{
+			(schemaName, "pk_test_uq", "pk_test", "c", 1),
+			(schemaName, "pk_test_uq", "pk_test", "d", 2),
+			(schemaName, "pk_test_uq", "pk_test", "e", 3),
+		};
+		Assert.Equal(expected, actual);
+	}
+
 	readonly DatabaseFixture m_database;
 }
diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
index 347c2bf6a..549267aa4 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
+++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.cs
@@ -1,5 +1,6 @@
 using System.Globalization;
 using System.Reflection;
+using System.Text.RegularExpressions;
 using YamlDotNet.Serialization;
 using YamlDotNet.Serialization.NamingConventions;
 
@@ -11,7 +12,7 @@
 	.Build();
 var schemaCollections = deserializer.Deserialize<List<Schema>>(reader);
 
-using var codeWriter = new StreamWriter(@"..\..\..\..\..\src\MySqlConnector\Core\SchemaProvider.g.cs");
+using var codeWriter = new StreamWriter(@"..\..\src\MySqlConnector\Core\SchemaProvider.g.cs");
 codeWriter.WriteLine("""
 	// DO NOT EDIT - generated by SchemaCollectionGenerator.cs
 	#nullable enable
@@ -36,9 +37,10 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 var elseIf = "if";
 foreach (var schema in schemaCollections)
 {
+	string methodSchemaName = Regexes.NonAsciiAlphaNumeric().Replace(schema.Name, "");
 	codeWriter.WriteLine($"""
 				{elseIf} (string.Equals(collectionName, "{schema.Name}", StringComparison.OrdinalIgnoreCase))
-					await Fill{schema.Name}Async(ioBehavior, dataTable, "{schema.Name}", restrictionValues, cancellationToken).ConfigureAwait(false);
+					await Fill{methodSchemaName}Async(ioBehavior, dataTable, "{schema.Name}", restrictionValues, cancellationToken).ConfigureAwait(false);
 		""");
 	elseIf = "else if";
 }
@@ -53,10 +55,11 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 
 foreach (var schema in schemaCollections)
 {
-	var isAsync = schema.Table is not null;
+	var isAsync = schema.Table is not null || schema.Custom?.EndsWith("Async") is true;
+	string methodSchemaName = Regexes.NonAsciiAlphaNumeric().Replace(schema.Name, "");
 	var supportsRestrictions = schema.Restrictions is { Count: > 0 };
 	codeWriter.WriteLine($$"""
-			private {{(isAsync ? "async " : "")}}Task Fill{{schema.Name}}Async(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
+			private {{(isAsync ? "async " : "")}}Task Fill{{methodSchemaName}}Async(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 			{
 		""");
 	if (!supportsRestrictions)
@@ -145,9 +148,13 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 	}
 	else
 	{
-		codeWriter.WriteLine($"""
-					{schema.Custom}(dataTable);
+		// custom function will be called here
+		string restrictions = (schema.Restrictions?.Count > 0) ? ", restrictionValues" : "";
+		codeWriter.Write($"""
+					{(isAsync ? "await " : "")}{schema.Custom}({(isAsync ? "ioBehavior, " : "")}dataTable{restrictions}{(isAsync ? ", cancellationToken" : "")}){(isAsync ? ".ConfigureAwait(false)" : "")};
 			""");
+		if (!isAsync)
+			codeWriter.WriteLine();
 	}
 
 	if (!isAsync)
@@ -169,7 +176,7 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 	}
 	""");
 
-using var docWriter = new StreamWriter(@"..\..\..\..\..\docs\content\overview\schema-collections.md");
+using var docWriter = new StreamWriter(@"..\..\docs\content\overview\schema-collections.md");
 docWriter.Write($"""
 	---
 	date: 2021-04-24
@@ -197,7 +204,7 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 
 foreach (var schema in schemaCollections.Where(x => x.Description is not null))
 {
-	using var schemaDocWriter = new StreamWriter($@"..\..\..\..\..\docs\content\overview\schema\{schema.Name.ToLowerInvariant()}.md");
+	using var schemaDocWriter = new StreamWriter($@"..\..\docs\content\overview\schema\{schema.Name.ToLowerInvariant()}.md");
 #pragma warning restore CA1308 // Normalize strings to uppercase
 	schemaDocWriter.Write($"""
 		---
@@ -258,3 +265,9 @@ internal sealed class Restriction
 	public required string Name { get; init; }
 	public required string Default { get; init; }
 }
+
+internal sealed partial class Regexes
+{
+	[GeneratedRegex("[^a-z0-9]", RegexOptions.IgnoreCase | RegexOptions.Compiled, "en-US")]
+	public static partial Regex NonAsciiAlphaNumeric();
+}
diff --git a/tools/SchemaCollectionGenerator/SchemaCollections.yaml b/tools/SchemaCollectionGenerator/SchemaCollections.yaml
index af385739e..376660540 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollections.yaml
+++ b/tools/SchemaCollectionGenerator/SchemaCollections.yaml
@@ -759,3 +759,101 @@
     type: string
   - name: COLLATION_CONNECTION
     type: string
+
+- name: Foreign Keys
+  description: information about foreign keys in the server's SQL syntax
+  custom: DoFillForeignKeysAsync
+  restrictions:
+  - name: Catalog
+    default: TABLE_CATALOG
+  - name: Schema
+    default: TABLE_SCHEMA
+  - name: Table
+    default: TABLE_NAME
+  - name: Column
+    default: COLUMN_NAME
+  columns:
+  - name: CONSTRAINT_CATALOG
+    type: string
+  - name: CONSTRAINT_SCHEMA
+    type: string
+  - name: CONSTRAINT_NAME
+    type: string
+  - name: TABLE_CATALOG
+    type: string
+  - name: TABLE_SCHEMA
+    type: string
+  - name: TABLE_NAME
+    type: string
+  - name: MATCH_OPTION
+    type: string
+  - name: UPDATE_RULE
+    type: string
+  - name: DELETE_RULE
+    type: string
+  - name: REFERENCED_TABLE_CATALOG
+    type: string
+  - name: REFERENCED_TABLE_SCHEMA
+    type: string
+  - name: REFERENCED_TABLE_NAME
+    type: string
+
+- name: Indexes
+  description: information about indexes in the server's SQL syntax
+  custom: DoFillIndexesAsync
+  restrictions:
+  - name: Catalog
+    default: TABLE_CATALOG
+  - name: Schema
+    default: TABLE_SCHEMA
+  - name: Table
+    default: TABLE_NAME
+  - name: Column # it is only a guess - might need to be changed, but NHibernate passes 4 values, with last one is null
+    default: COLUMN_NAME
+  columns:
+  - name: INDEX_CATALOG # placeholder  - will be filled by SQL query
+    type: string
+  - name: INDEX_SCHEMA # same as TABLE_SCHEMA, see https://mariadb.com/kb/en/information-schema-statistics-table/
+    type: string
+  - name: INDEX_NAME
+    type: string
+  - name: TABLE_NAME
+    type: string
+  - name: UNIQUE
+    type: bool
+  - name: PRIMARY
+    type: bool
+  - name: TYPE
+    type: string
+  - name: COMMENT
+    type: string
+
+- name: IndexColumns
+  description: information about indexes in the server's SQL syntax
+  custom: DoFillIndexColumnsAsync
+  restrictions:
+  - name: Catalog
+    default: TABLE_CATALOG
+  - name: Schema
+    default: TABLE_SCHEMA
+  - name: Table
+    default: TABLE_NAME
+  - name: Constraint
+    default: CONSTRAINT_NAME
+  - name: Column # it comes fro
+    default: COLUMN_NAME
+  columns:
+  - name: INDEX_CATALOG # placeholder  - will be filled by SQL query
+    type: string
+  - name: INDEX_SCHEMA # same as TABLE_SCHEMA, see https://mariadb.com/kb/en/information-schema-statistics-table/
+    type: string
+  - name: INDEX_NAME
+    type: string
+  - name: TABLE_NAME
+    type: string
+  - name: COLUMN_NAME
+    type: string
+  - name: ORDINAL_POSITION
+    type: int
+  - name: SORT_ORDER
+    type: string

From 5aa36b2cd43a71ba188c47812e4eee01b287963c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 15 Dec 2023 16:12:53 -0800
Subject: [PATCH 231/393] Update test packages.

---
 Directory.Packages.props | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2bf6e09f6..68b02336b 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,7 +6,7 @@
 
   <ItemGroup>
     <PackageVersion Include="AdoNet.Specification.Tests" Version="2.0.0-beta.2" />
-    <PackageVersion Include="Dapper.StrongName" Version="2.1.21" />
+    <PackageVersion Include="Dapper.StrongName" Version="2.1.24" />
     <PackageVersion Include="IndexRange" Version="1.0.2" />
     <PackageVersion Include="log4net" Version="2.0.10" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
@@ -22,8 +22,8 @@
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageVersion Include="xunit" Version="2.6.2" />
-    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.4" />
+    <PackageVersion Include="xunit" Version="2.6.3" />
+    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.5" />
     <PackageVersion Include="YamlDotNet" Version="13.7.1" />
   </ItemGroup>
 

From dbccb331439ab1c7cefa25f3af1f258d0e1e4862 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 15 Dec 2023 20:18:30 -0800
Subject: [PATCH 232/393] Move NuGet badge to top of README.

---
 README.md | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index b7ba89e0f..a7a4b50b8 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
 # MySQL Connector for .NET and .NET Core
 
+[![NuGet](https://img.shields.io/nuget/vpre/MySqlConnector.svg)](https://www.nuget.org/packages/MySqlConnector/)
+
 This is an [ADO.NET](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/) data
 provider for [MySQL](https://www.mysql.com/). It provides implementations of
 `DbConnection`, `DbCommand`, `DbDataReader`, `DbTransaction`—the classes
@@ -56,13 +58,12 @@ For Entity Framework support, use:
 
 ## Build Status
 
-Appveyor | Azure Pipelines | NuGet
---- | --- | ---
-[![AppVeyor](https://img.shields.io/appveyor/ci/mysqlnet/mysqlconnector/master.svg)](https://ci.appveyor.com/project/mysqlnet/mysqlconnector) | [![Azure Pipelines](https://dev.azure.com/mysqlnet/MySqlConnector/_apis/build/status/mysql-net.MySqlConnector?branchName=master)](https://dev.azure.com/mysqlnet/MySqlConnector/_build/latest?definitionId=2&branchName=master) | [![NuGet](https://img.shields.io/nuget/vpre/MySqlConnector.svg)](https://www.nuget.org/packages/MySqlConnector/)
+[![AppVeyor](https://img.shields.io/appveyor/ci/mysqlnet/mysqlconnector/master.svg)](https://ci.appveyor.com/project/mysqlnet/mysqlconnector)
+[![Azure Pipelines](https://dev.azure.com/mysqlnet/MySqlConnector/_apis/build/status/mysql-net.MySqlConnector?branchName=master)](https://dev.azure.com/mysqlnet/MySqlConnector/_build/latest?definitionId=2&branchName=master)
 
 ## Building
 
-Install the latest [.NET Core](https://www.microsoft.com/net/core).
+Install the latest [.NET](https://dotnet.microsoft.com/en-us/download).
 
 To build and run the tests, clone the repo and execute:
 

From 8748a725e6de168c01094f10b6206a8d6a6fae70 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 15 Dec 2023 20:45:10 -0800
Subject: [PATCH 233/393] Update constraints for recently-added schemas.

---
 docs/content/overview/schema/foreign keys.md  |   4 +-
 docs/content/overview/schema/indexcolumns.md  |   5 +-
 docs/content/overview/schema/indexes.md       |   4 +-
 src/MySqlConnector/Core/SchemaProvider.cs     | 112 ++++++++----------
 src/MySqlConnector/Core/SchemaProvider.g.cs   |  13 +-
 .../SchemaCollections.yaml                    |  18 ++-
 6 files changed, 71 insertions(+), 85 deletions(-)

diff --git a/docs/content/overview/schema/foreign keys.md b/docs/content/overview/schema/foreign keys.md
index 3fea8ece8..7abcc899f 100644
--- a/docs/content/overview/schema/foreign keys.md	
+++ b/docs/content/overview/schema/foreign keys.md	
@@ -1,6 +1,6 @@
 ---
 date: 2022-07-10
-lastmod: 2023-12-15
+lastmod: 2023-12-16
 title: Foreign Keys Schema
 ---
 
@@ -30,5 +30,5 @@ Restriction Name | Restriction Default | Restriction Number
 Catalog | TABLE_CATALOG | 1
 Schema | TABLE_SCHEMA | 2
 Table | TABLE_NAME | 3
-Column | COLUMN_NAME | 4
+Constraint Name | CONSTRAINT_NAME | 4
 
diff --git a/docs/content/overview/schema/indexcolumns.md b/docs/content/overview/schema/indexcolumns.md
index e184e00d0..440f89250 100644
--- a/docs/content/overview/schema/indexcolumns.md
+++ b/docs/content/overview/schema/indexcolumns.md
@@ -1,6 +1,6 @@
 ---
 date: 2022-07-10
-lastmod: 2023-12-15
+lastmod: 2023-12-16
 title: IndexColumns Schema
 ---
 
@@ -25,6 +25,5 @@ Restriction Name | Restriction Default | Restriction Number
 Catalog | TABLE_CATALOG | 1
 Schema | TABLE_SCHEMA | 2
 Table | TABLE_NAME | 3
-Constraint | CONSTRAINT_NAME | 4
-Column | COLUMN_NAME | 5
+Name | INDEX_NAME | 4
 
diff --git a/docs/content/overview/schema/indexes.md b/docs/content/overview/schema/indexes.md
index 2cce279ba..251118081 100644
--- a/docs/content/overview/schema/indexes.md
+++ b/docs/content/overview/schema/indexes.md
@@ -1,6 +1,6 @@
 ---
 date: 2022-07-10
-lastmod: 2023-12-15
+lastmod: 2023-12-16
 title: Indexes Schema
 ---
 
@@ -26,5 +26,5 @@ Restriction Name | Restriction Default | Restriction Number
 Catalog | TABLE_CATALOG | 1
 Schema | TABLE_SCHEMA | 2
 Table | TABLE_NAME | 3
-Column | COLUMN_NAME | 4
+Name | INDEX_NAME | 4
 
diff --git a/src/MySqlConnector/Core/SchemaProvider.cs b/src/MySqlConnector/Core/SchemaProvider.cs
index f854fe650..5a97bb7c3 100644
--- a/src/MySqlConnector/Core/SchemaProvider.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.cs
@@ -443,38 +443,34 @@ private Task DoFillForeignKeysAsync(IOBehavior ioBehavior, DataTable dataTable,
 		FillDataTableAsync(IOBehavior.Synchronous, dataTable, command =>
 		{
 			command.CommandText = """
-				SELECT rc.constraint_catalog, rc.constraint_schema,
-					rc.constraint_name, kcu.table_catalog, kcu.table_schema, rc.table_name,
-					rc.match_option, rc.update_rule, rc.delete_rule, 
-					NULL as referenced_table_catalog,
-					kcu.referenced_table_schema, rc.referenced_table_name 
+				SELECT rc.constraint_catalog, rc.constraint_schema, rc.constraint_name,
+					kcu.table_catalog, kcu.table_schema,
+					rc.table_name, rc.match_option, rc.update_rule, rc.delete_rule, 
+					NULL as referenced_table_catalog, kcu.referenced_table_schema, rc.referenced_table_name 
 				FROM INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc
 					LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE kcu ON 
-					kcu.constraint_catalog <=> rc.constraint_catalog AND
-					kcu.constraint_schema <=> rc.constraint_schema AND 
-					kcu.constraint_name <=> rc.constraint_name 
-				WHERE 1=1 AND kcu.ORDINAL_POSITION=1
+					(
+						(kcu.constraint_catalog = rc.constraint_catalog OR (kcu.constraint_catalog IS NULL AND rc.constraint_catalog IS NULL)) AND
+						(kcu.constraint_schema = rc.constraint_schema OR (kcu.constraint_schema IS NULL AND rc.constraint_schema IS NULL)) AND
+						(kcu.constraint_name = rc.constraint_name OR (kcu.constraint_name IS NULL AND rc.constraint_name IS NULL))
+					)
+				WHERE kcu.ORDINAL_POSITION = 1
 				""";
 
-			if (restrictionValues is not null)
+			if (restrictionValues is [_, { Length: > 0 } schema, ..])
 			{
-				var where = "";
-				if (restrictionValues.Length >= 2 && !string.IsNullOrEmpty(restrictionValues[1]))
-				{
-					where += " AND rc.constraint_schema LIKE @schema";
-					command.Parameters.AddWithValue("@schema", restrictionValues[1]);
-				}
-				if (restrictionValues.Length >= 3 && !string.IsNullOrEmpty(restrictionValues[2]))
-				{
-					where += " AND rc.table_name LIKE @table";
-					command.Parameters.AddWithValue("@table", restrictionValues[2]);
-				}
-				if (restrictionValues.Length >= 4 && !string.IsNullOrEmpty(restrictionValues[3]))
-				{
-					where += " AND rc.constraint_name LIKE @constraint";
-					command.Parameters.AddWithValue("@constraint", restrictionValues[3]);
-				}
-				command.CommandText += where;
+				command.CommandText += " AND rc.constraint_schema LIKE @schema";
+				command.Parameters.AddWithValue("@schema", schema);
+			}
+			if (restrictionValues is [_, _, { Length: > 0 } table, ..])
+			{
+				command.CommandText += " AND rc.table_name LIKE @table";
+				command.Parameters.AddWithValue("@table", table);
+			}
+			if (restrictionValues is [_, _, _, { Length: > 0 } constraint, ..])
+			{
+				command.CommandText += " AND rc.constraint_name LIKE @constraint";
+				command.Parameters.AddWithValue("@constraint", constraint);
 			}
 		}, cancellationToken);
 
@@ -482,30 +478,29 @@ private Task DoFillIndexesAsync(IOBehavior ioBehavior, DataTable dataTable, stri
 		FillDataTableAsync(ioBehavior, dataTable, command =>
 		{
 			command.CommandText = """
-				SELECT DISTINCT null AS INDEX_CATALOG, INDEX_SCHEMA,
+				SELECT null AS INDEX_CATALOG, INDEX_SCHEMA,
 					INDEX_NAME, TABLE_NAME,
 					!NON_UNIQUE as `UNIQUE`, 
 					INDEX_NAME='PRIMARY' as `PRIMARY`,
 					INDEX_TYPE as TYPE, COMMENT 
 				FROM INFORMATION_SCHEMA.STATISTICS
-				WHERE 1=1
+				WHERE SEQ_IN_INDEX=1
 				""";
 
-			if (restrictionValues is not null)
+			if (restrictionValues is [_, { Length: > 0 } schema, ..])
 			{
-				var where = "";
-				if (restrictionValues.Length >= 2 && !string.IsNullOrEmpty(restrictionValues[1]))
-				{
-					where += " AND INDEX_SCHEMA LIKE @schema";
-					command.Parameters.AddWithValue("@schema", restrictionValues[1]);
-				}
-				if (restrictionValues.Length >= 3 && !string.IsNullOrEmpty(restrictionValues[2]))
-				{
-					where += " AND TABLE_NAME LIKE @table";
-					command.Parameters.AddWithValue("@table", restrictionValues[2]);
-				}
-
-				command.CommandText += where;
+				command.CommandText += " AND INDEX_SCHEMA LIKE @schema";
+				command.Parameters.AddWithValue("@schema", schema);
+			}
+			if (restrictionValues is [_, _, { Length: > 0 } table, ..])
+			{
+				command.CommandText += " AND TABLE_NAME LIKE @table";
+				command.Parameters.AddWithValue("@table", table);
+			}
+			if (restrictionValues is [_, _, _, { Length: > 0 } index, ..])
+			{
+				command.CommandText += " AND INDEX_NAME LIKE @index";
+				command.Parameters.AddWithValue("@index", index);
 			}
 		}, cancellationToken);
 
@@ -522,25 +517,20 @@ FROM INFORMATION_SCHEMA.STATISTICS
 				WHERE 1=1
 				""";
 
-			if (restrictionValues is not null)
+			if (restrictionValues is [_, { Length: > 0 } schema, ..])
+			{
+				command.CommandText += " AND INDEX_SCHEMA LIKE @schema";
+				command.Parameters.AddWithValue("@schema", schema);
+			}
+			if (restrictionValues is [_, _, { Length: > 0 } table, ..])
+			{
+				command.CommandText += " AND TABLE_NAME LIKE @table";
+				command.Parameters.AddWithValue("@table", table);
+			}
+			if (restrictionValues is [_, _, _, { Length: > 0 } index, ..])
 			{
-				var where = "";
-				if (restrictionValues.Length >= 2 && !string.IsNullOrEmpty(restrictionValues[1]))
-				{
-					where += " AND INDEX_SCHEMA LIKE @schema";
-					command.Parameters.AddWithValue("@schema", restrictionValues[1]);
-				}
-				if (restrictionValues.Length >= 3 && !string.IsNullOrEmpty(restrictionValues[2]))
-				{
-					where += " AND TABLE_NAME LIKE @table";
-					command.Parameters.AddWithValue("@table", restrictionValues[2]);
-				}
-				if (restrictionValues.Length >= 4 && !string.IsNullOrEmpty(restrictionValues[3]))
-				{
-					where += " AND INDEX_NAME LIKE @index";
-					command.Parameters.AddWithValue("@index", restrictionValues[3]);
-				}
-				command.CommandText += where;
+				command.CommandText += " AND INDEX_NAME LIKE @index";
+				command.Parameters.AddWithValue("@index", index);
 			}
 		}, cancellationToken);
 }
diff --git a/src/MySqlConnector/Core/SchemaProvider.g.cs b/src/MySqlConnector/Core/SchemaProvider.g.cs
index 03e942960..1e97a5699 100644
--- a/src/MySqlConnector/Core/SchemaProvider.g.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.g.cs
@@ -131,7 +131,7 @@ private Task FillMetaDataCollectionsAsync(IOBehavior ioBehavior, DataTable dataT
 		dataTable.Rows.Add("Views", 0, 3);
 		dataTable.Rows.Add("Foreign Keys", 4, 0);
 		dataTable.Rows.Add("Indexes", 4, 0);
-		dataTable.Rows.Add("IndexColumns", 5, 0);
+		dataTable.Rows.Add("IndexColumns", 4, 0);
 
 		return Task.CompletedTask;
 	}
@@ -640,16 +640,15 @@ private Task FillRestrictionsAsync(IOBehavior ioBehavior, DataTable dataTable, s
 		dataTable.Rows.Add("Foreign Keys", "Catalog", "TABLE_CATALOG", 1);
 		dataTable.Rows.Add("Foreign Keys", "Schema", "TABLE_SCHEMA", 2);
 		dataTable.Rows.Add("Foreign Keys", "Table", "TABLE_NAME", 3);
-		dataTable.Rows.Add("Foreign Keys", "Column", "COLUMN_NAME", 4);
+		dataTable.Rows.Add("Foreign Keys", "Constraint Name", "CONSTRAINT_NAME", 4);
 		dataTable.Rows.Add("Indexes", "Catalog", "TABLE_CATALOG", 1);
 		dataTable.Rows.Add("Indexes", "Schema", "TABLE_SCHEMA", 2);
 		dataTable.Rows.Add("Indexes", "Table", "TABLE_NAME", 3);
-		dataTable.Rows.Add("Indexes", "Column", "COLUMN_NAME", 4);
+		dataTable.Rows.Add("Indexes", "Name", "INDEX_NAME", 4);
 		dataTable.Rows.Add("IndexColumns", "Catalog", "TABLE_CATALOG", 1);
 		dataTable.Rows.Add("IndexColumns", "Schema", "TABLE_SCHEMA", 2);
 		dataTable.Rows.Add("IndexColumns", "Table", "TABLE_NAME", 3);
-		dataTable.Rows.Add("IndexColumns", "Constraint", "CONSTRAINT_NAME", 4);
-		dataTable.Rows.Add("IndexColumns", "Column", "COLUMN_NAME", 5);
+		dataTable.Rows.Add("IndexColumns", "Name", "INDEX_NAME", 4);
 
 		return Task.CompletedTask;
 	}
@@ -902,8 +901,8 @@ private async Task FillIndexesAsync(IOBehavior ioBehavior, DataTable dataTable,
 
 	private async Task FillIndexColumnsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is { Length: > 5 })
-			throw new ArgumentException("More than 5 restrictionValues are not supported for schema 'IndexColumns'.", nameof(restrictionValues));
+		if (restrictionValues is { Length: > 4 })
+			throw new ArgumentException("More than 4 restrictionValues are not supported for schema 'IndexColumns'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
 		dataTable.Columns.AddRange(
diff --git a/tools/SchemaCollectionGenerator/SchemaCollections.yaml b/tools/SchemaCollectionGenerator/SchemaCollections.yaml
index 376660540..f6b58cbe4 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollections.yaml
+++ b/tools/SchemaCollectionGenerator/SchemaCollections.yaml
@@ -770,8 +770,8 @@
     default: TABLE_SCHEMA
   - name: Table
     default: TABLE_NAME
-  - name: Column
-    default: COLUMN_NAME
+  - name: Constraint Name
+    default: CONSTRAINT_NAME
   columns:
   - name: CONSTRAINT_CATALOG
     type: string
@@ -808,10 +808,10 @@
     default: TABLE_SCHEMA
   - name: Table
     default: TABLE_NAME
-  - name: Column # it is only a guess - might need to be changed, but NHibernate passes 4 values, with last one is null
-    default: COLUMN_NAME
+  - name: Name
+    default: INDEX_NAME
   columns:
-  - name: INDEX_CATALOG # placeholder  - will be filled by SQL query
+  - name: INDEX_CATALOG
     type: string
   - name: INDEX_SCHEMA # same as TABLE_SCHEMA, see https://mariadb.com/kb/en/information-schema-statistics-table/
     type: string
@@ -838,12 +838,10 @@
     default: TABLE_SCHEMA
   - name: Table
     default: TABLE_NAME
-  - name: Constraint
-    default: CONSTRAINT_NAME
-  - name: Column # it comes fro
-    default: COLUMN_NAME
+  - name: Name
+    default: INDEX_NAME
   columns:
-  - name: INDEX_CATALOG # placeholder  - will be filled by SQL query
+  - name: INDEX_CATALOG
     type: string
   - name: INDEX_SCHEMA # same as TABLE_SCHEMA, see https://mariadb.com/kb/en/information-schema-statistics-table/
     type: string

From 9352d4867843a84f584f4527acde216431823499 Mon Sep 17 00:00:00 2001
From: Alisha Kim <pril@pril.cc>
Date: Sun, 17 Dec 2023 20:03:18 +0900
Subject: [PATCH 234/393] Fixed packet corruption issue when enum was not 4
 bytes.

Signed-off-by: Alisha Kim <pril@pril.cc>
---
 src/MySqlConnector/MySqlParameter.cs          | 40 ++++++++++++++-
 tests/MySqlConnector.Tests/DummyEnum.cs       | 50 ++++++++++++++++++-
 .../MySqlConnector.Tests.csproj               |  2 +-
 .../MySqlParameterAppendBinaryTests.cs        | 27 ++++++++++
 4 files changed, 116 insertions(+), 3 deletions(-)
 create mode 100644 tests/MySqlConnector.Tests/MySqlParameterAppendBinaryTests.cs

diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 841051780..34100b5bd 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -835,7 +835,45 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
 		}
 		else if (Value is Enum)
 		{
-			writer.Write(Convert.ToInt32(Value, CultureInfo.InvariantCulture));
+			var enumType = Value.GetType();
+			var enumUnderlyingType = enumType.GetEnumUnderlyingType();
+
+			if (enumUnderlyingType == typeof(byte))
+			{
+				writer.Write((byte) Value);
+			}
+			else if (enumUnderlyingType == typeof(sbyte))
+			{
+				writer.Write(unchecked((byte) (sbyte) Value));
+			}
+			else if (enumUnderlyingType == typeof(short))
+			{
+				writer.Write(unchecked((ushort) (short) Value));
+			}
+			else if (enumUnderlyingType == typeof(ushort))
+			{
+				writer.Write((ushort) Value);
+			}
+			else if (enumUnderlyingType == typeof(int))
+			{
+				writer.Write((int) Value);
+			}
+			else if (enumUnderlyingType == typeof(uint))
+			{
+				writer.Write((uint) Value);
+			}
+			else if (enumUnderlyingType == typeof(long))
+			{
+				writer.Write(unchecked((ulong) (long) Value));
+			}
+			else if (enumUnderlyingType == typeof(ulong))
+			{
+				writer.Write((ulong) Value);
+			}
+			else
+			{
+				throw new NotSupportedException($"Parameter type {Value.GetType().Name} is not supported; see https://fl.vu/mysql-param-type. Value: {Value}");
+			}
 		}
 		else if (MySqlDbType == MySqlDbType.Int16)
 		{
diff --git a/tests/MySqlConnector.Tests/DummyEnum.cs b/tests/MySqlConnector.Tests/DummyEnum.cs
index 70b580905..627e5e0c1 100644
--- a/tests/MySqlConnector.Tests/DummyEnum.cs
+++ b/tests/MySqlConnector.Tests/DummyEnum.cs
@@ -4,4 +4,52 @@ internal enum DummyEnum
 {
 	FirstValue,
 	SecondValue
-} 
+}
+
+internal enum DummyByteEnum : byte
+{
+	FirstValue,
+	SecondValue = 0x11,
+}
+
+internal enum DummySByteEnum : sbyte
+{
+	FirstValue,
+	SecondValue = 0x11,
+}
+
+internal enum DummyShortEnum : short
+{
+	FirstValue,
+	SecondValue = 0x1122,
+}
+
+internal enum DummyUShortEnum : ushort
+{
+	FirstValue,
+	SecondValue = 0x1122,
+}
+
+internal enum DummyIntEnum : int
+{
+	FirstValue,
+	SecondValue = 0x11223344,
+}
+
+internal enum DummyUIntEnum : uint
+{
+	FirstValue,
+	SecondValue = 0x11223344,
+}
+
+internal enum DummyLongEnum : long
+{
+	FirstValue,
+	SecondValue = 0x11223344_55667788,
+}
+
+internal enum DummyULongEnum : ulong
+{
+	FirstValue,
+	SecondValue = 0x11223344_55667788,
+}
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 8eddd9566..fed6d961c 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -47,7 +47,7 @@
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
     <PackageReference Include="MySql.Data" />
-    <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ColumnCountPayloadTests.cs;ColumnReaderTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
+    <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ColumnCountPayloadTests.cs;ColumnReaderTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterAppendBinaryTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
     <Compile Remove="Metrics\*.cs" />
     <Using Include="MySql.Data.MySqlClient" />
     <Using Include="MySql.Data.Types" />
diff --git a/tests/MySqlConnector.Tests/MySqlParameterAppendBinaryTests.cs b/tests/MySqlConnector.Tests/MySqlParameterAppendBinaryTests.cs
new file mode 100644
index 000000000..948332773
--- /dev/null
+++ b/tests/MySqlConnector.Tests/MySqlParameterAppendBinaryTests.cs
@@ -0,0 +1,27 @@
+using MySqlConnector.Protocol.Serialization;
+
+namespace MySqlConnector.Tests;
+
+public class MySqlParameterAppendBinaryTests
+{
+	[Theory]
+	[InlineData(DummySByteEnum.SecondValue, MySqlDbType.Byte, new byte[] { 0x11 })]
+	[InlineData(DummyByteEnum.SecondValue, MySqlDbType.UByte, new byte[] { 0x11 })]
+	[InlineData(DummyShortEnum.SecondValue, MySqlDbType.Int16, new byte[] { 0x22, 0x11 })]
+	[InlineData(DummyUShortEnum.SecondValue, MySqlDbType.UInt16, new byte[] { 0x22, 0x11 })]
+	[InlineData(DummyIntEnum.SecondValue, MySqlDbType.Int32, new byte[] { 0x44, 0x33, 0x22, 0x11 })]
+	[InlineData(DummyUIntEnum.SecondValue, MySqlDbType.UInt32, new byte[] { 0x44, 0x33, 0x22, 0x11 })]
+	[InlineData(DummyEnum.SecondValue, MySqlDbType.Int32, new byte[] { 0x01, 0x00, 0x00, 0x00 })]
+	[InlineData(DummyLongEnum.SecondValue, MySqlDbType.Int64, new byte[] { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 })]
+	[InlineData(DummyULongEnum.SecondValue, MySqlDbType.UInt64, new byte[] { 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11 })]
+	public void WriteBinaryEnumType(object value, MySqlDbType expectedMySqlDbType, byte[] expectedBinary)
+	{
+		var parameter = new MySqlParameter { Value = value };
+		var writer = new ByteBufferWriter();
+		parameter.AppendBinary(writer, StatementPreparerOptions.None);
+
+		Assert.Equal(parameter.MySqlDbType, expectedMySqlDbType);
+		Assert.Equal(writer.Position, expectedBinary.Length);
+		Assert.Equal(writer.ArraySegment, expectedBinary);
+	}
+}

From 2d44a008a7dcecc69985cf467cbe7dd07528da06 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 18 Dec 2023 11:13:12 -0800
Subject: [PATCH 235/393] Use underlying enum type when writing binary
 parameters. Fixes #1421

---
 src/MySqlConnector/MySqlParameter.cs          | 94 ++++++++++---------
 .../IntegrationTests/PreparedCommandTests.cs  | 57 +++++++++++
 2 files changed, 109 insertions(+), 42 deletions(-)

diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 841051780..3b4761c45 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -637,116 +637,124 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
 		{
 			// stored in "null bitmap" only
 		}
-		else if (Value is string stringValue)
+		else
+		{
+			AppendBinary(writer, Value, options);
+		}
+	}
+
+	private void AppendBinary(ByteBufferWriter writer, object value, StatementPreparerOptions options)
+	{
+		if (value is string stringValue)
 		{
 			writer.WriteLengthEncodedString(stringValue);
 		}
-		else if (Value is char charValue)
+		else if (value is char charValue)
 		{
 			writer.WriteLengthEncodedString(charValue.ToString());
 		}
-		else if (Value is sbyte sbyteValue)
+		else if (value is sbyte sbyteValue)
 		{
 			writer.Write(unchecked((byte) sbyteValue));
 		}
-		else if (Value is byte byteValue)
+		else if (value is byte byteValue)
 		{
 			writer.Write(byteValue);
 		}
-		else if (Value is bool boolValue)
+		else if (value is bool boolValue)
 		{
 			writer.Write((byte) (boolValue ? 1 : 0));
 		}
-		else if (Value is short shortValue)
+		else if (value is short shortValue)
 		{
 			writer.Write(unchecked((ushort) shortValue));
 		}
-		else if (Value is ushort ushortValue)
+		else if (value is ushort ushortValue)
 		{
 			writer.Write(ushortValue);
 		}
-		else if (Value is int intValue)
+		else if (value is int intValue)
 		{
 			writer.Write(intValue);
 		}
-		else if (Value is uint uintValue)
+		else if (value is uint uintValue)
 		{
 			writer.Write(uintValue);
 		}
-		else if (Value is long longValue)
+		else if (value is long longValue)
 		{
 			writer.Write(unchecked((ulong) longValue));
 		}
-		else if (Value is ulong ulongValue)
+		else if (value is ulong ulongValue)
 		{
 			writer.Write(ulongValue);
 		}
-		else if (Value is byte[] byteArrayValue)
+		else if (value is byte[] byteArrayValue)
 		{
 			writer.WriteLengthEncodedInteger(unchecked((ulong) byteArrayValue.Length));
 			writer.Write(byteArrayValue);
 		}
-		else if (Value is ReadOnlyMemory<byte> readOnlyMemoryValue)
+		else if (value is ReadOnlyMemory<byte> readOnlyMemoryValue)
 		{
 			writer.WriteLengthEncodedInteger(unchecked((ulong) readOnlyMemoryValue.Length));
 			writer.Write(readOnlyMemoryValue.Span);
 		}
-		else if (Value is Memory<byte> memoryValue)
+		else if (value is Memory<byte> memoryValue)
 		{
 			writer.WriteLengthEncodedInteger(unchecked((ulong) memoryValue.Length));
 			writer.Write(memoryValue.Span);
 		}
-		else if (Value is ArraySegment<byte> arraySegmentValue)
+		else if (value is ArraySegment<byte> arraySegmentValue)
 		{
 			writer.WriteLengthEncodedInteger(unchecked((ulong) arraySegmentValue.Count));
 			writer.Write(arraySegmentValue);
 		}
-		else if (Value is MySqlGeometry geometry)
+		else if (value is MySqlGeometry geometry)
 		{
 			writer.WriteLengthEncodedInteger(unchecked((ulong) geometry.ValueSpan.Length));
 			writer.Write(geometry.ValueSpan);
 		}
-		else if (Value is MemoryStream memoryStream)
+		else if (value is MemoryStream memoryStream)
 		{
 			if (!memoryStream.TryGetBuffer(out var streamBuffer))
 				streamBuffer = new ArraySegment<byte>(memoryStream.ToArray());
 			writer.WriteLengthEncodedInteger(unchecked((ulong) streamBuffer.Count));
 			writer.Write(streamBuffer);
 		}
-		else if (Value is float floatValue)
+		else if (value is float floatValue)
 		{
 			writer.Write(BitConverter.GetBytes(floatValue));
 		}
-		else if (Value is double doubleValue)
+		else if (value is double doubleValue)
 		{
 			writer.Write(unchecked((ulong) BitConverter.DoubleToInt64Bits(doubleValue)));
 		}
-		else if (Value is decimal decimalValue)
+		else if (value is decimal decimalValue)
 		{
 			writer.WriteLengthEncodedAsciiString(decimalValue.ToString(CultureInfo.InvariantCulture));
 		}
-		else if (Value is BigInteger bigInteger)
+		else if (value is BigInteger bigInteger)
 		{
 			writer.WriteLengthEncodedAsciiString(bigInteger.ToString(CultureInfo.InvariantCulture));
 		}
-		else if (Value is MySqlDateTime mySqlDateTimeValue)
+		else if (value is MySqlDateTime mySqlDateTimeValue)
 		{
 			if (mySqlDateTimeValue.IsValidDateTime)
 				WriteDateTime(writer, mySqlDateTimeValue.GetDateTime());
 			else
 				writer.Write((byte) 0);
 		}
-		else if (Value is MySqlDecimal mySqlDecimal)
+		else if (value is MySqlDecimal mySqlDecimal)
 		{
 			writer.WriteLengthEncodedAsciiString(mySqlDecimal.ToString());
 		}
 #if NET6_0_OR_GREATER
-		else if (Value is DateOnly dateOnlyValue)
+		else if (value is DateOnly dateOnlyValue)
 		{
 			WriteDateOnly(writer, dateOnlyValue);
 		}
 #endif
-		else if (Value is DateTime dateTimeValue)
+		else if (value is DateTime dateTimeValue)
 		{
 			if ((options & StatementPreparerOptions.DateTimeUtc) != 0 && dateTimeValue.Kind == DateTimeKind.Local)
 				throw new MySqlException($"DateTime.Kind must not be Local when DateTimeKind setting is Utc (parameter name: {ParameterName})");
@@ -755,22 +763,22 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
 
 			WriteDateTime(writer, dateTimeValue);
 		}
-		else if (Value is DateTimeOffset dateTimeOffsetValue)
+		else if (value is DateTimeOffset dateTimeOffsetValue)
 		{
 			// store as UTC as it will be read as such when deserialized from a timespan column
 			WriteDateTime(writer, dateTimeOffsetValue.UtcDateTime);
 		}
 #if NET6_0_OR_GREATER
-		else if (Value is TimeOnly timeOnlyValue)
+		else if (value is TimeOnly timeOnlyValue)
 		{
 			WriteTime(writer, timeOnlyValue.ToTimeSpan());
 		}
 #endif
-		else if (Value is TimeSpan ts)
+		else if (value is TimeSpan ts)
 		{
 			WriteTime(writer, ts);
 		}
-		else if (Value is Guid guidValue)
+		else if (value is Guid guidValue)
 		{
 			StatementPreparerOptions guidOptions = options & StatementPreparerOptions.GuidFormatMask;
 			if (guidOptions is StatementPreparerOptions.GuidFormatBinary16 or StatementPreparerOptions.GuidFormatTimeSwapBinary16 or StatementPreparerOptions.GuidFormatLittleEndianBinary16)
@@ -817,53 +825,55 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
 				writer.Advance(guidLength);
 			}
 		}
-		else if (Value is ReadOnlyMemory<char> readOnlyMemoryChar)
+		else if (value is ReadOnlyMemory<char> readOnlyMemoryChar)
 		{
 			writer.WriteLengthEncodedString(readOnlyMemoryChar.Span);
 		}
-		else if (Value is Memory<char> memoryChar)
+		else if (value is Memory<char> memoryChar)
 		{
 			writer.WriteLengthEncodedString(memoryChar.Span);
 		}
-		else if (Value is StringBuilder stringBuilder)
+		else if (value is StringBuilder stringBuilder)
 		{
 			writer.WriteLengthEncodedString(stringBuilder);
 		}
-		else if ((MySqlDbType is MySqlDbType.String or MySqlDbType.VarChar) && HasSetDbType && Value is Enum stringEnumValue)
+		else if ((MySqlDbType is MySqlDbType.String or MySqlDbType.VarChar) && HasSetDbType && value is Enum stringEnumValue)
 		{
 			writer.WriteLengthEncodedString(stringEnumValue.ToString("G"));
 		}
-		else if (Value is Enum)
+		else if (value is Enum)
 		{
-			writer.Write(Convert.ToInt32(Value, CultureInfo.InvariantCulture));
+			// using the underlying type matches the log in TypeMapper.GetDbTypeMapping, which controls the column type value that was sent to the server
+			var underlyingValue = Convert.ChangeType(value, Enum.GetUnderlyingType(value.GetType()), CultureInfo.InvariantCulture);
+			AppendBinary(writer, underlyingValue, options);
 		}
 		else if (MySqlDbType == MySqlDbType.Int16)
 		{
-			writer.Write((ushort) (short) Value);
+			writer.Write((ushort) (short) value);
 		}
 		else if (MySqlDbType == MySqlDbType.UInt16)
 		{
-			writer.Write((ushort) Value);
+			writer.Write((ushort) value);
 		}
 		else if (MySqlDbType == MySqlDbType.Int32)
 		{
-			writer.Write((int) Value);
+			writer.Write((int) value);
 		}
 		else if (MySqlDbType == MySqlDbType.UInt32)
 		{
-			writer.Write((uint) Value);
+			writer.Write((uint) value);
 		}
 		else if (MySqlDbType == MySqlDbType.Int64)
 		{
-			writer.Write((ulong) (long) Value);
+			writer.Write((ulong) (long) value);
 		}
 		else if (MySqlDbType == MySqlDbType.UInt64)
 		{
-			writer.Write((ulong) Value);
+			writer.Write((ulong) value);
 		}
 		else
 		{
-			throw new NotSupportedException($"Parameter type {Value.GetType().Name} is not supported; see https://fl.vu/mysql-param-type. Value: {Value}");
+			throw new NotSupportedException($"Parameter type {value.GetType().Name} is not supported; see https://fl.vu/mysql-param-type. Value: {value}");
 		}
 	}
 
diff --git a/tests/IntegrationTests/PreparedCommandTests.cs b/tests/IntegrationTests/PreparedCommandTests.cs
index b17ca26e4..0cdbee401 100644
--- a/tests/IntegrationTests/PreparedCommandTests.cs
+++ b/tests/IntegrationTests/PreparedCommandTests.cs
@@ -433,6 +433,63 @@ public static IEnumerable<object[]> GetDifferentTypeInsertAndQueryData()
 		}
 	}
 
+	[Theory, MemberData(nameof(GetDifferentWidthData))]
+	public void InsertDifferentWidthParameters(string dataType1, object value1, string dataType2, object value2)
+	{
+		using var connection = CreateConnection();
+		using var command = connection.CreateCommand();
+		command.CommandText = $"""
+			drop table if exists parameter_width;
+			create table parameter_width(value1 {dataType1} not null, value2 {dataType2} not null);
+			""";
+		command.ExecuteNonQuery();
+
+		command.CommandText = "insert into parameter_width(value1, value2) values(@value1, @value2);";
+		command.Parameters.AddWithValue("@value1", value1);
+		command.Parameters.AddWithValue("@value2", value2);
+		command.Prepare();
+		command.ExecuteNonQuery();
+
+		using var queryCommand = connection.CreateCommand();
+		queryCommand.CommandText = "select value1, value2 from parameter_width;";
+		using var reader = queryCommand.ExecuteReader();
+		Assert.True(reader.Read());
+		Assert.Equal(Convert.ToInt32(value1), reader.GetInt32(0));
+		Assert.Equal(Convert.ToInt32(value2), reader.GetInt32(1));
+		Assert.False(reader.Read());
+	}
+
+	public static IEnumerable<object[]> GetDifferentWidthData()
+	{
+		var dataTypes = new string[] { "TINYINT", "SMALLINT", "INT" };
+		var values = new object[] { (sbyte) 100, (short) 110, 120, OneByteEnum.Value, TwoByteEnum.Value };
+
+		foreach (var dataType1 in dataTypes)
+		{
+			foreach (var value1 in values)
+			{
+				foreach (var dataType2 in dataTypes)
+				{
+					foreach (var value2 in values)
+					{
+						yield return new object[] { dataType1, value1, dataType2, value2 };
+					}
+				}
+			}
+		}
+	}
+
+	private enum OneByteEnum : sbyte
+	{
+		Value = 101,
+	}
+
+	private enum TwoByteEnum : short
+	{
+		Value = 111,
+	}
+		
+
 	private static MySqlConnection CreateConnection()
 	{
 		var connection = new MySqlConnection(AppConfig.ConnectionString);

From 65a73fc60ebf437d2f1bba0e9d1d0e2ccfabd101 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 18 Dec 2023 11:44:04 -0800
Subject: [PATCH 236/393] Release 2.3.2.

---
 docs/content/overview/version-history.md | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 96bf06068..dd00e6617 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-11-16
+lastmod: 2023-12-18
 date: 2017-03-27
 menu:
   main:
@@ -11,6 +11,15 @@ weight: 30
 
 # Version History
 
+### 2.3.2
+
+* Add `Foreign Keys`, `Indexes`, `Index Columns` schemas: [#1419](https://github.com/mysql-net/MySqlConnector/pull/1419).
+* Fix exception when server doesn't send expected metadata: [#1398](https://github.com/mysql-net/MySqlConnector/issues/1398).
+* Clear activity tags when retrying TLS: [#1405](https://github.com/mysql-net/MySqlConnector/issues/1405).
+* Use underlying enum type when writing binary parameters: [#1421](https://github.com/mysql-net/MySqlConnector/pull/1421).
+* Avoid retrying TLS negotiation when a non-recoverable exception occurs.
+* Thanks to [Alisha Kim](https://github.com/daebo01), [Evgeni Alesinskyy](https://github.com/eaexo), and [Jan Trejbal](https://github.com/trejjam) for contributions to this release.
+
 ### 2.3.1
 
 * Add `db.client.connections.timeouts` counter to metrics: [#1392](https://github.com/mysql-net/MySqlConnector/pull/1392)

From d3ad6d1acf8f70e0e693ac845e3ed342222396c1 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 19 Dec 2023 06:19:11 -0800
Subject: [PATCH 237/393] Add COLUMN_NAME as fifth restriction for IndexColumns
 schema.

---
 docs/content/overview/schema/indexcolumns.md    |  3 ++-
 src/MySqlConnector/Core/SchemaProvider.cs       |  5 +++++
 src/MySqlConnector/Core/SchemaProvider.g.cs     |  7 ++++---
 tests/IntegrationTests/SchemaProviderTests.cs   | 17 +++++++++++++++++
 .../SchemaCollections.yaml                      |  2 ++
 5 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/docs/content/overview/schema/indexcolumns.md b/docs/content/overview/schema/indexcolumns.md
index 440f89250..e36acc429 100644
--- a/docs/content/overview/schema/indexcolumns.md
+++ b/docs/content/overview/schema/indexcolumns.md
@@ -1,6 +1,6 @@
 ---
 date: 2022-07-10
-lastmod: 2023-12-16
+lastmod: 2023-12-19
 title: IndexColumns Schema
 ---
 
@@ -26,4 +26,5 @@ Catalog | TABLE_CATALOG | 1
 Schema | TABLE_SCHEMA | 2
 Table | TABLE_NAME | 3
 Name | INDEX_NAME | 4
+Column | COLUMN_NAME | 5
 
diff --git a/src/MySqlConnector/Core/SchemaProvider.cs b/src/MySqlConnector/Core/SchemaProvider.cs
index 5a97bb7c3..e3f843269 100644
--- a/src/MySqlConnector/Core/SchemaProvider.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.cs
@@ -532,5 +532,10 @@ FROM INFORMATION_SCHEMA.STATISTICS
 				command.CommandText += " AND INDEX_NAME LIKE @index";
 				command.Parameters.AddWithValue("@index", index);
 			}
+			if (restrictionValues is [_, _, _, _, { Length: > 0 } column, ..])
+			{
+				command.CommandText += " AND COLUMN_NAME LIKE @column";
+				command.Parameters.AddWithValue("@column", column);
+			}
 		}, cancellationToken);
 }
diff --git a/src/MySqlConnector/Core/SchemaProvider.g.cs b/src/MySqlConnector/Core/SchemaProvider.g.cs
index 1e97a5699..b3575bc83 100644
--- a/src/MySqlConnector/Core/SchemaProvider.g.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.g.cs
@@ -131,7 +131,7 @@ private Task FillMetaDataCollectionsAsync(IOBehavior ioBehavior, DataTable dataT
 		dataTable.Rows.Add("Views", 0, 3);
 		dataTable.Rows.Add("Foreign Keys", 4, 0);
 		dataTable.Rows.Add("Indexes", 4, 0);
-		dataTable.Rows.Add("IndexColumns", 4, 0);
+		dataTable.Rows.Add("IndexColumns", 5, 0);
 
 		return Task.CompletedTask;
 	}
@@ -649,6 +649,7 @@ private Task FillRestrictionsAsync(IOBehavior ioBehavior, DataTable dataTable, s
 		dataTable.Rows.Add("IndexColumns", "Schema", "TABLE_SCHEMA", 2);
 		dataTable.Rows.Add("IndexColumns", "Table", "TABLE_NAME", 3);
 		dataTable.Rows.Add("IndexColumns", "Name", "INDEX_NAME", 4);
+		dataTable.Rows.Add("IndexColumns", "Column", "COLUMN_NAME", 5);
 
 		return Task.CompletedTask;
 	}
@@ -901,8 +902,8 @@ private async Task FillIndexesAsync(IOBehavior ioBehavior, DataTable dataTable,
 
 	private async Task FillIndexColumnsAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
-		if (restrictionValues is { Length: > 4 })
-			throw new ArgumentException("More than 4 restrictionValues are not supported for schema 'IndexColumns'.", nameof(restrictionValues));
+		if (restrictionValues is { Length: > 5 })
+			throw new ArgumentException("More than 5 restrictionValues are not supported for schema 'IndexColumns'.", nameof(restrictionValues));
 
 		dataTable.TableName = tableName;
 		dataTable.Columns.AddRange(
diff --git a/tests/IntegrationTests/SchemaProviderTests.cs b/tests/IntegrationTests/SchemaProviderTests.cs
index 8d56cb491..f17687585 100644
--- a/tests/IntegrationTests/SchemaProviderTests.cs
+++ b/tests/IntegrationTests/SchemaProviderTests.cs
@@ -242,5 +242,22 @@ public void IndexColumns()
 		Assert.Equal(expected, actual);
 	}
 
+	[Fact]
+	public void IndexColumnsWithColumnName()
+	{
+		var schemaName = m_database.Connection.Database;
+		var table = m_database.Connection.GetSchema("IndexColumns", new[] { null, schemaName, "pk_test", "pk_test_uq", "d" });
+		var actual = table.Rows
+			.Cast<DataRow>()
+			.OrderBy(x => (string) x["INDEX_NAME"])
+			.ThenBy(x => (int) x["ORDINAL_POSITION"])
+			.Select(x => ((string) x["INDEX_SCHEMA"], (string) x["INDEX_NAME"], (string) x["TABLE_NAME"], (string) x["COLUMN_NAME"], (int) x["ORDINAL_POSITION"]));
+		var expected = new[]
+		{
+			(schemaName, "pk_test_uq", "pk_test", "d", 2),
+		};
+		Assert.Equal(expected, actual);
+	}
+
 	readonly DatabaseFixture m_database;
 }
diff --git a/tools/SchemaCollectionGenerator/SchemaCollections.yaml b/tools/SchemaCollectionGenerator/SchemaCollections.yaml
index f6b58cbe4..770166cae 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollections.yaml
+++ b/tools/SchemaCollectionGenerator/SchemaCollections.yaml
@@ -840,6 +840,8 @@
     default: TABLE_NAME
   - name: Name
     default: INDEX_NAME
+  - name: Column
+    default: COLUMN_NAME
   columns:
   - name: INDEX_CATALOG
     type: string

From 5a05ef29c0b368a4e7e529b96f006ebd273bed7f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 19 Dec 2023 06:21:54 -0800
Subject: [PATCH 238/393] Release 2.3.3.

---
 docs/content/overview/version-history.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index dd00e6617..07cffb6bb 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-12-18
+lastmod: 2023-12-19
 date: 2017-03-27
 menu:
   main:
@@ -11,6 +11,10 @@ weight: 30
 
 # Version History
 
+### 2.3.3
+
+* Support `COLUMN_NAME` restriction for `IndexColumns` schema; this improves NHibernate compatibility.
+
 ### 2.3.2
 
 * Add `Foreign Keys`, `Indexes`, `Index Columns` schemas: [#1419](https://github.com/mysql-net/MySqlConnector/pull/1419).

From 561192007a838634347607d17f2229b87d9eb3d8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 25 Dec 2023 11:18:47 -0800
Subject: [PATCH 239/393] Update xunit and StyleCop.

---
 .editorconfig            | 2 --
 Directory.Packages.props | 6 +++---
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index 1765a3ee5..117e019fe 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -88,8 +88,6 @@ dotnet_diagnostic.IDE0065.severity = error # Using directives must be placed out
 dotnet_diagnostic.SA1003.severity = none # Operator must not be followed by whitespace.
 dotnet_diagnostic.SA1008.severity = none # Opening parenthesis must not be preceded by a space.
 dotnet_diagnostic.SA1009.severity = none # Closing parenthesis must not be followed by a space.
-dotnet_diagnostic.SA1010.severity = silent # https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3503
-dotnet_diagnostic.SA1011.severity = silent # https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3503
 dotnet_diagnostic.SA1027.severity = none # Tabs must not be used.
 dotnet_diagnostic.SA1101.severity = none # Prefix local calls with this.
 dotnet_diagnostic.SA1116.severity = none # Split parameters should start on line after declaration.
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 68b02336b..0db881d7f 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -19,11 +19,11 @@
     <PackageVersion Include="MySql.Data" Version="8.2.0" />
     <PackageVersion Include="NLog" Version="4.5.0" />
     <PackageVersion Include="Serilog" Version="2.6.0" />
-    <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.507" />
+    <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageVersion Include="xunit" Version="2.6.3" />
-    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.5" />
+    <PackageVersion Include="xunit" Version="2.6.4" />
+    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6" />
     <PackageVersion Include="YamlDotNet" Version="13.7.1" />
   </ItemGroup>
 

From 77479f01dc23bf12154d287628eea2725a92e35e Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 28 Dec 2023 16:21:57 -0800
Subject: [PATCH 240/393] Disallow negative RemainingTimeout. Fixes #1424

---
 src/MySqlConnector/Protocol/Serialization/StreamByteHandler.cs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Protocol/Serialization/StreamByteHandler.cs b/src/MySqlConnector/Protocol/Serialization/StreamByteHandler.cs
index 9dc68a06e..7bf4e0998 100644
--- a/src/MySqlConnector/Protocol/Serialization/StreamByteHandler.cs
+++ b/src/MySqlConnector/Protocol/Serialization/StreamByteHandler.cs
@@ -18,8 +18,9 @@ public StreamByteHandler(Stream stream)
 
 	public ValueTask<int> ReadBytesAsync(Memory<byte> buffer, IOBehavior ioBehavior)
 	{
-		return ioBehavior == IOBehavior.Asynchronous ? new ValueTask<int>(DoReadBytesAsync(buffer)) :
+		return
 			RemainingTimeout <= 0 ? ValueTaskExtensions.FromException<int>(MySqlException.CreateForTimeout()) :
+			ioBehavior == IOBehavior.Asynchronous ? new ValueTask<int>(DoReadBytesAsync(buffer)) :
 			m_stream.CanTimeout ? DoReadBytesSync(buffer) :
 			DoReadBytesSyncOverAsync(buffer);
 

From 8fffc068247dbc304a371b7531241bc900fbf3a3 Mon Sep 17 00:00:00 2001
From: SolacomYongYao <68119348+SolacomYongYao@users.noreply.github.com>
Date: Mon, 8 Jan 2024 12:32:35 -0500
Subject: [PATCH 241/393] Update CachedProcedure.cs (#1432)

Fix InvalidCastException when using StoredProcedure with MySQL 5.0

Signed-off-by: Yong Yao <yong.yao@comtechtel.com>
Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
Co-authored-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/CachedProcedure.cs | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index c0f788649..995edb8ec 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -30,12 +30,15 @@ internal sealed class CachedProcedure
 				if (!exists)
 					return null;
 
-				var parametersSqlBytes = (byte[]) reader.GetValue(0);
-				var returnsSqlBytes = (byte[]) reader.GetValue(1);
-
 				// ASSUME this is UTF-8 encoded; it's possible that the `character_set_client` column would need to be used?
+				var parametersSqlBytes = (byte[]) reader.GetValue(0);
 				var parametersSql = Encoding.UTF8.GetString(parametersSqlBytes);
-				var returnsSql = Encoding.UTF8.GetString(returnsSqlBytes);
+
+				var returnsSql = reader.GetValue(1) switch
+				{
+					string s => s,
+					object o => Encoding.UTF8.GetString((byte[]) o),
+				};
 
 				var parsedParameters = ParseParameters(parametersSql);
 				if (returnsSql.Length != 0)

From 19fb8e55cf7acd9c4cd2c16fcd64818c41c20a89 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 11 Jan 2024 14:45:15 -0800
Subject: [PATCH 242/393] Parameterize almost-identical metrics tests.

---
 .../Metrics/ConnectionsUsageTests.cs          | 143 +++---------------
 .../Metrics/IConnectionCreator.cs             |  47 ++++++
 .../MySqlConnector.Tests.csproj               |   2 +-
 3 files changed, 66 insertions(+), 126 deletions(-)
 create mode 100644 tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs

diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
index 09e9440e8..74ef83d68 100644
--- a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
@@ -4,13 +4,12 @@ namespace MySqlConnector.Tests.Metrics;
 
 public class ConnectionsUsageTests : MetricsTestsBase
 {
-    [Fact(Skip = MetricsSkip)]
-	public void NamedDataSource()
+    [Theory(Skip = MetricsSkip)]
+	[MemberData(nameof(GetPoolCreators))]
+	public void PoolCreators(IConnectionCreator connectionCreator)
     {
-		PoolName = "metrics-test";
-		using var dataSource = new MySqlDataSourceBuilder(CreateConnectionStringBuilder().ConnectionString)
-			.UseName(PoolName)
-			.Build();
+		connectionCreator.SetConnectionStringBuilder(CreateConnectionStringBuilder());
+		PoolName = connectionCreator.PoolName;
 
 		// no connections at beginning of test
 		AssertMeasurement("db.client.connections.usage", 0);
@@ -19,7 +18,7 @@ public void NamedDataSource()
 		Assert.Equal(0, Server.ActiveConnections);
 
 		// opening a connection creates a 'used' connection
-		using (var connection = dataSource.OpenConnection())
+		using (var connection = connectionCreator.OpenConnection())
 		{
 			AssertMeasurement("db.client.connections.usage", 1);
 			AssertMeasurement("db.client.connections.usage|idle", 0);
@@ -34,7 +33,7 @@ public void NamedDataSource()
 		Assert.Equal(1, Server.ActiveConnections);
 
 		// reopening the connection transitions it back to 'used'
-		using (var connection = dataSource.OpenConnection())
+		using (var connection = connectionCreator.OpenConnection())
 		{
 			AssertMeasurement("db.client.connections.usage", 1);
 			AssertMeasurement("db.client.connections.usage|idle", 0);
@@ -43,8 +42,8 @@ public void NamedDataSource()
 		Assert.Equal(1, Server.ActiveConnections);
 
 		// opening a second connection creates a net new 'used' connection
-		using (var connection = dataSource.OpenConnection())
-		using (var connection2 = dataSource.OpenConnection())
+		using (var connection = connectionCreator.OpenConnection())
+		using (var connection2 = connectionCreator.OpenConnection())
 		{
 			AssertMeasurement("db.client.connections.usage", 2);
 			AssertMeasurement("db.client.connections.usage|idle", 0);
@@ -56,6 +55,15 @@ public void NamedDataSource()
 		AssertMeasurement("db.client.connections.usage|idle", 2);
 		AssertMeasurement("db.client.connections.usage|used", 0);
 		Assert.Equal(2, Server.ActiveConnections);
+
+		connectionCreator.Dispose();
+	}
+
+	public static IEnumerable<object[]> GetPoolCreators()
+	{
+		yield return new object[] { new DataSourceConnectionCreator("metrics-test") };
+		yield return new object[] { new DataSourceConnectionCreator(null) };
+		yield return new object[] { new PlainConnectionCreator() };
 	}
 
 	[Fact(Skip = MetricsSkip)]
@@ -91,121 +99,6 @@ public void NamedDataSourceWithMinPoolSize()
 		Assert.Equal(3, Server.ActiveConnections);
 	}
 
-	[Fact(Skip = MetricsSkip)]
-	public void UnnamedDataSource()
-	{
-		var csb = CreateConnectionStringBuilder();
-
-		// NOTE: pool "name" is connection string (without password)
-		PoolName = csb.GetConnectionString(includePassword: false);
-
-		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
-			.Build();
-
-		// no connections at beginning of test
-		AssertMeasurement("db.client.connections.usage", 0);
-		AssertMeasurement("db.client.connections.usage|idle", 0);
-		AssertMeasurement("db.client.connections.usage|used", 0);
-		Assert.Equal(0, Server.ActiveConnections);
-
-		// opening a connection creates a 'used' connection
-		using (var connection = dataSource.OpenConnection())
-		{
-			AssertMeasurement("db.client.connections.usage", 1);
-			AssertMeasurement("db.client.connections.usage|idle", 0);
-			AssertMeasurement("db.client.connections.usage|used", 1);
-			Assert.Equal(1, Server.ActiveConnections);
-		}
-
-		// closing it creates an 'idle' connection
-		AssertMeasurement("db.client.connections.usage", 1);
-		AssertMeasurement("db.client.connections.usage|idle", 1);
-		AssertMeasurement("db.client.connections.usage|used", 0);
-		Assert.Equal(1, Server.ActiveConnections);
-
-		// reopening the connection transitions it back to 'used'
-		using (var connection = dataSource.OpenConnection())
-		{
-			AssertMeasurement("db.client.connections.usage", 1);
-			AssertMeasurement("db.client.connections.usage|idle", 0);
-			AssertMeasurement("db.client.connections.usage|used", 1);
-		}
-		Assert.Equal(1, Server.ActiveConnections);
-
-		// opening a second connection creates a net new 'used' connection
-		using (var connection = dataSource.OpenConnection())
-		using (var connection2 = dataSource.OpenConnection())
-		{
-			AssertMeasurement("db.client.connections.usage", 2);
-			AssertMeasurement("db.client.connections.usage|idle", 0);
-			AssertMeasurement("db.client.connections.usage|used", 2);
-			Assert.Equal(2, Server.ActiveConnections);
-		}
-
-		AssertMeasurement("db.client.connections.usage", 2);
-		AssertMeasurement("db.client.connections.usage|idle", 2);
-		AssertMeasurement("db.client.connections.usage|used", 0);
-		Assert.Equal(2, Server.ActiveConnections);
-	}
-
-	[Fact(Skip = MetricsSkip)]
-	public void NoDataSource()
-	{
-		var csb = CreateConnectionStringBuilder();
-
-		// NOTE: pool "name" is connection string (without password)
-		PoolName = csb.GetConnectionString(includePassword: false);
-
-		// no connections at beginning of test
-		AssertMeasurement("db.client.connections.usage", 0);
-		AssertMeasurement("db.client.connections.usage|idle", 0);
-		AssertMeasurement("db.client.connections.usage|used", 0);
-		Assert.Equal(0, Server.ActiveConnections);
-
-		// opening a connection creates a 'used' connection
-		using (var connection = new MySqlConnection(csb.ConnectionString))
-		{
-			connection.Open();
-			AssertMeasurement("db.client.connections.usage", 1);
-			AssertMeasurement("db.client.connections.usage|idle", 0);
-			AssertMeasurement("db.client.connections.usage|used", 1);
-			Assert.Equal(1, Server.ActiveConnections);
-		}
-
-		// closing it creates an 'idle' connection
-		AssertMeasurement("db.client.connections.usage", 1);
-		AssertMeasurement("db.client.connections.usage|idle", 1);
-		AssertMeasurement("db.client.connections.usage|used", 0);
-		Assert.Equal(1, Server.ActiveConnections);
-
-		// reopening the connection transitions it back to 'used'
-		using (var connection = new MySqlConnection(csb.ConnectionString))
-		{
-			connection.Open();
-			AssertMeasurement("db.client.connections.usage", 1);
-			AssertMeasurement("db.client.connections.usage|idle", 0);
-			AssertMeasurement("db.client.connections.usage|used", 1);
-		}
-		Assert.Equal(1, Server.ActiveConnections);
-
-		// opening a second connection creates a net new 'used' connection
-		using (var connection = new MySqlConnection(csb.ConnectionString))
-		using (var connection2 = new MySqlConnection(csb.ConnectionString))
-		{
-			connection.Open();
-			connection2.Open();
-			AssertMeasurement("db.client.connections.usage", 2);
-			AssertMeasurement("db.client.connections.usage|idle", 0);
-			AssertMeasurement("db.client.connections.usage|used", 2);
-			Assert.Equal(2, Server.ActiveConnections);
-		}
-
-		AssertMeasurement("db.client.connections.usage", 2);
-		AssertMeasurement("db.client.connections.usage|idle", 2);
-		AssertMeasurement("db.client.connections.usage|used", 0);
-		Assert.Equal(2, Server.ActiveConnections);
-	}
-
 	[Fact(Skip = MetricsSkip)]
 	public async Task PendingRequestForCreation()
 	{
diff --git a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
new file mode 100644
index 000000000..be1b85cd2
--- /dev/null
+++ b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
@@ -0,0 +1,47 @@
+#nullable enable
+
+namespace MySqlConnector.Tests.Metrics;
+
+public interface IConnectionCreator : IDisposable
+{
+	void SetConnectionStringBuilder(MySqlConnectionStringBuilder connectionStringBuilder);
+	string PoolName { get; }
+	MySqlConnection OpenConnection();
+}
+
+internal sealed class DataSourceConnectionCreator(string? poolName) : IConnectionCreator
+{
+	public void SetConnectionStringBuilder(MySqlConnectionStringBuilder connectionStringBuilder)
+	{
+		m_connectionStringBuilder = connectionStringBuilder;
+		m_dataSource = new MySqlDataSourceBuilder(connectionStringBuilder.ConnectionString)
+			.UseName(m_poolName)
+			.Build();
+	}
+
+	public MySqlConnection OpenConnection() => m_dataSource!.OpenConnection();
+	public string PoolName => m_poolName ?? m_connectionStringBuilder!.GetConnectionString(includePassword: false);
+	public override string ToString() => $"DataSource: {m_poolName ?? "(unnamed)"}";
+	public void Dispose() => m_dataSource!.Dispose();
+
+	private readonly string? m_poolName = poolName;
+	private MySqlConnectionStringBuilder? m_connectionStringBuilder;
+	private MySqlDataSource? m_dataSource;
+}
+
+internal sealed class PlainConnectionCreator : IConnectionCreator
+{
+	public MySqlConnection OpenConnection()
+	{
+		var connection = new MySqlConnection(m_connectionStringBuilder!.ConnectionString);
+		connection.Open();
+		return connection;
+	}
+
+	public void SetConnectionStringBuilder(MySqlConnectionStringBuilder connectionStringBuilder) => m_connectionStringBuilder = connectionStringBuilder;
+	public string PoolName => m_connectionStringBuilder!.GetConnectionString(includePassword: false);
+	public override string ToString() => "Plain";
+	public void Dispose() { }
+
+	private MySqlConnectionStringBuilder? m_connectionStringBuilder;
+}
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index fed6d961c..4992175b1 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -15,7 +15,7 @@
     <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
     <AssemblyOriginatorKeyFile>..\..\MySqlConnector.snk</AssemblyOriginatorKeyFile>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
-    <LangVersion>11.0</LangVersion>
+    <LangVersion>12.0</LangVersion>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(GITHUB_ACTIONS)' == 'true' OR '$(APPVEYOR)' == 'True' OR '$(TF_BUILD)' == 'True' ">

From ebcf3cf0c71cd1536369328fe840178bb7dac6e2 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 11 Jan 2024 15:07:59 -0800
Subject: [PATCH 243/393] Test for absence of metrics from connections without
 pools.

Refactor tests to take string arguments rather than being parameterized with objects. This makes it easier to view/run the tests in Tests Explorer.
---
 .../Metrics/ConnectionsUsageTests.cs          | 60 ++++++++++++++++---
 .../Metrics/IConnectionCreator.cs             | 36 +++++------
 2 files changed, 70 insertions(+), 26 deletions(-)

diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
index 74ef83d68..685bce31f 100644
--- a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
@@ -5,10 +5,12 @@ namespace MySqlConnector.Tests.Metrics;
 public class ConnectionsUsageTests : MetricsTestsBase
 {
     [Theory(Skip = MetricsSkip)]
-	[MemberData(nameof(GetPoolCreators))]
-	public void PoolCreators(IConnectionCreator connectionCreator)
+	[InlineData("DataSource|true|")]
+	[InlineData("DataSource|true|metrics-test")]
+	[InlineData("Plain|true")]
+	public void ConnectionsWithPoolsHaveMetrics(string connectionCreatorSpec)
     {
-		connectionCreator.SetConnectionStringBuilder(CreateConnectionStringBuilder());
+		using var connectionCreator = CreateConnectionCreator(connectionCreatorSpec, CreateConnectionStringBuilder());
 		PoolName = connectionCreator.PoolName;
 
 		// no connections at beginning of test
@@ -55,15 +57,44 @@ public void PoolCreators(IConnectionCreator connectionCreator)
 		AssertMeasurement("db.client.connections.usage|idle", 2);
 		AssertMeasurement("db.client.connections.usage|used", 0);
 		Assert.Equal(2, Server.ActiveConnections);
-
-		connectionCreator.Dispose();
 	}
 
-	public static IEnumerable<object[]> GetPoolCreators()
+	[Theory(Skip = MetricsSkip)]
+    [InlineData("DataSource|false|")]
+    [InlineData("DataSource|false|metrics-test")]
+    [InlineData("Plain|false")]
+    public void ConnectionsWithoutPoolsHaveNoMetrics(string connectionCreatorSpec)
 	{
-		yield return new object[] { new DataSourceConnectionCreator("metrics-test") };
-		yield return new object[] { new DataSourceConnectionCreator(null) };
-		yield return new object[] { new PlainConnectionCreator() };
+		using var connectionCreator = CreateConnectionCreator(connectionCreatorSpec, CreateConnectionStringBuilder());
+		PoolName = connectionCreator.PoolName;
+
+		// no connections at beginning of test
+		AssertMeasurement("db.client.connections.usage", 0);
+		AssertMeasurement("db.client.connections.usage|idle", 0);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+		Assert.Equal(0, Server.ActiveConnections);
+
+		// opening a connection doesn't change connection counts
+		using (var connection = connectionCreator.OpenConnection())
+		{
+			AssertMeasurement("db.client.connections.usage", 0);
+			AssertMeasurement("db.client.connections.usage|idle", 0);
+			AssertMeasurement("db.client.connections.usage|used", 0);
+			Assert.Equal(1, Server.ActiveConnections);
+		}
+
+		// closing it doesn't create an idle connection but closes it immediately
+		AssertMeasurement("db.client.connections.usage", 0);
+		AssertMeasurement("db.client.connections.usage|idle", 0);
+		AssertMeasurement("db.client.connections.usage|used", 0);
+
+		// disposing the connection sends a COM_QUIT packet and immediately returns; give the in-proc server a chance to process it
+		for (var retry = 0; retry < 20; retry++)
+		{
+			if (Server.ActiveConnections != 0)
+				Thread.Sleep(1);
+		}
+		Assert.Equal(0, Server.ActiveConnections);
 	}
 
 	[Fact(Skip = MetricsSkip)]
@@ -135,4 +166,15 @@ public async Task PendingRequestForOpenFromPool()
 
 		AssertMeasurement("db.client.connections.pending_requests", 0);
 	}
+
+	private IConnectionCreator CreateConnectionCreator(string spec, MySqlConnectionStringBuilder connectionStringBuilder)
+	{
+		var parts = spec.Split('|');
+		return parts[0] switch
+		{
+			"DataSource" => new DataSourceConnectionCreator(bool.Parse(parts[1]), parts[2] == "" ? null : parts[2], connectionStringBuilder),
+			"Plain" => new PlainConnectionCreator(bool.Parse(parts[1]), connectionStringBuilder),
+			_ => throw new NotSupportedException(),
+		};
+	}
 }
diff --git a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
index be1b85cd2..9285c0922 100644
--- a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
+++ b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
@@ -4,44 +4,46 @@ namespace MySqlConnector.Tests.Metrics;
 
 public interface IConnectionCreator : IDisposable
 {
-	void SetConnectionStringBuilder(MySqlConnectionStringBuilder connectionStringBuilder);
 	string PoolName { get; }
 	MySqlConnection OpenConnection();
 }
 
-internal sealed class DataSourceConnectionCreator(string? poolName) : IConnectionCreator
+internal sealed class DataSourceConnectionCreator : IConnectionCreator
 {
-	public void SetConnectionStringBuilder(MySqlConnectionStringBuilder connectionStringBuilder)
+	public DataSourceConnectionCreator(bool usePooling, string? poolName, MySqlConnectionStringBuilder connectionStringBuilder)
 	{
-		m_connectionStringBuilder = connectionStringBuilder;
+        connectionStringBuilder.Pooling =	usePooling;
 		m_dataSource = new MySqlDataSourceBuilder(connectionStringBuilder.ConnectionString)
-			.UseName(m_poolName)
+			.UseName(poolName)
 			.Build();
-	}
+        PoolName = poolName ?? connectionStringBuilder!.GetConnectionString(includePassword: false);
+    }
 
-	public MySqlConnection OpenConnection() => m_dataSource!.OpenConnection();
-	public string PoolName => m_poolName ?? m_connectionStringBuilder!.GetConnectionString(includePassword: false);
-	public override string ToString() => $"DataSource: {m_poolName ?? "(unnamed)"}";
+    public MySqlConnection OpenConnection() => m_dataSource!.OpenConnection();
+	public string PoolName { get; }
 	public void Dispose() => m_dataSource!.Dispose();
 
-	private readonly string? m_poolName = poolName;
-	private MySqlConnectionStringBuilder? m_connectionStringBuilder;
-	private MySqlDataSource? m_dataSource;
+	private readonly MySqlDataSource m_dataSource;
 }
 
 internal sealed class PlainConnectionCreator : IConnectionCreator
 {
+	public PlainConnectionCreator(bool usePooling, MySqlConnectionStringBuilder connectionStringBuilder)
+	{
+		connectionStringBuilder.Pooling = usePooling;
+		m_connectionString = connectionStringBuilder.ConnectionString;
+		PoolName = connectionStringBuilder.GetConnectionString(includePassword: false);
+    }
+
 	public MySqlConnection OpenConnection()
 	{
-		var connection = new MySqlConnection(m_connectionStringBuilder!.ConnectionString);
+		var connection = new MySqlConnection(m_connectionString);
 		connection.Open();
 		return connection;
 	}
 
-	public void SetConnectionStringBuilder(MySqlConnectionStringBuilder connectionStringBuilder) => m_connectionStringBuilder = connectionStringBuilder;
-	public string PoolName => m_connectionStringBuilder!.GetConnectionString(includePassword: false);
-	public override string ToString() => "Plain";
+	public string PoolName { get; }
 	public void Dispose() { }
 
-	private MySqlConnectionStringBuilder? m_connectionStringBuilder;
+	private readonly string m_connectionString;
 }

From 2f95bb2612e513c8b2e8779b6f5764bab18254cd Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 11 Jan 2024 16:13:54 -0800
Subject: [PATCH 244/393] Fix hang when disposing MySqlDataSource twice on .NET
 Framework.

---
 src/MySqlConnector/Core/ConnectionPool.cs      |  2 ++
 tests/IntegrationTests/MySqlDataSourceTests.cs | 11 +++++++++++
 2 files changed, 13 insertions(+)

diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 7f464b501..06eb38fe2 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -250,12 +250,14 @@ public void Dispose()
 			using var dnsCheckWaitHandle = new ManualResetEvent(false);
 			m_dnsCheckTimer.Dispose(dnsCheckWaitHandle);
 			dnsCheckWaitHandle.WaitOne();
+			m_dnsCheckTimer = null;
 		}
 		if (m_reaperTimer is not null)
 		{
 			using var reaperWaitHandle = new ManualResetEvent(false);
 			m_reaperTimer.Dispose(reaperWaitHandle);
 			reaperWaitHandle.WaitOne();
+			m_reaperTimer = null;
 		}
 #endif
 	}
diff --git a/tests/IntegrationTests/MySqlDataSourceTests.cs b/tests/IntegrationTests/MySqlDataSourceTests.cs
index 5e6804b5a..af881033b 100644
--- a/tests/IntegrationTests/MySqlDataSourceTests.cs
+++ b/tests/IntegrationTests/MySqlDataSourceTests.cs
@@ -25,6 +25,17 @@ public void OpenConnection()
 		Assert.Equal(ConnectionState.Open, connection.State);
 	}
 
+    [Fact]
+    public void DoubleDispose()
+    {
+        using var dbSource = new MySqlDataSource(AppConfig.ConnectionString);
+		using (var connection = dbSource.OpenConnection())
+		{
+            Assert.Equal(ConnectionState.Open, connection.State);
+        }
+		dbSource.Dispose();
+    }
+    
 	[Fact]
 	public async Task OpenConnectionAsync()
 	{

From 64e4fc5393f29c0fa94954c455b9e853ffdfc82c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 11 Jan 2024 22:29:00 -0800
Subject: [PATCH 245/393] Add 'Application Name' connection string option to
 the tests.

---
 .../Metrics/ConnectionsUsageTests.cs          | 22 ++++++++++++-------
 .../Metrics/IConnectionCreator.cs             |  6 +++--
 2 files changed, 18 insertions(+), 10 deletions(-)

diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
index 685bce31f..f42020906 100644
--- a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
@@ -5,9 +5,12 @@ namespace MySqlConnector.Tests.Metrics;
 public class ConnectionsUsageTests : MetricsTestsBase
 {
     [Theory(Skip = MetricsSkip)]
-	[InlineData("DataSource|true|")]
-	[InlineData("DataSource|true|metrics-test")]
-	[InlineData("Plain|true")]
+	[InlineData("DataSource|true||")]
+	[InlineData("DataSource|true||app-name")]
+    [InlineData("DataSource|true|pool-name|")]
+    [InlineData("DataSource|true|pool-name|app-name")]
+	[InlineData("Plain|true|")]
+	[InlineData("Plain|true|app-name")]
 	public void ConnectionsWithPoolsHaveMetrics(string connectionCreatorSpec)
     {
 		using var connectionCreator = CreateConnectionCreator(connectionCreatorSpec, CreateConnectionStringBuilder());
@@ -60,9 +63,12 @@ public void ConnectionsWithPoolsHaveMetrics(string connectionCreatorSpec)
 	}
 
 	[Theory(Skip = MetricsSkip)]
-    [InlineData("DataSource|false|")]
-    [InlineData("DataSource|false|metrics-test")]
-    [InlineData("Plain|false")]
+    [InlineData("DataSource|false||")]
+    [InlineData("DataSource|false||app-name")]
+    [InlineData("DataSource|false|pool-name|")]
+    [InlineData("DataSource|false|pool-name|app-name")]
+    [InlineData("Plain|false|")]
+    [InlineData("Plain|false|app-name")]
     public void ConnectionsWithoutPoolsHaveNoMetrics(string connectionCreatorSpec)
 	{
 		using var connectionCreator = CreateConnectionCreator(connectionCreatorSpec, CreateConnectionStringBuilder());
@@ -172,8 +178,8 @@ private IConnectionCreator CreateConnectionCreator(string spec, MySqlConnectionS
 		var parts = spec.Split('|');
 		return parts[0] switch
 		{
-			"DataSource" => new DataSourceConnectionCreator(bool.Parse(parts[1]), parts[2] == "" ? null : parts[2], connectionStringBuilder),
-			"Plain" => new PlainConnectionCreator(bool.Parse(parts[1]), connectionStringBuilder),
+			"DataSource" => new DataSourceConnectionCreator(bool.Parse(parts[1]), parts[2] == "" ? null : parts[2], parts[3] == "" ? null : parts[3], connectionStringBuilder),
+			"Plain" => new PlainConnectionCreator(bool.Parse(parts[1]), parts[2] == "" ? null : parts[2], connectionStringBuilder),
 			_ => throw new NotSupportedException(),
 		};
 	}
diff --git a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
index 9285c0922..4bc3e4af8 100644
--- a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
+++ b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
@@ -10,9 +10,10 @@ public interface IConnectionCreator : IDisposable
 
 internal sealed class DataSourceConnectionCreator : IConnectionCreator
 {
-	public DataSourceConnectionCreator(bool usePooling, string? poolName, MySqlConnectionStringBuilder connectionStringBuilder)
+	public DataSourceConnectionCreator(bool usePooling, string? poolName, string? applicationName, MySqlConnectionStringBuilder connectionStringBuilder)
 	{
         connectionStringBuilder.Pooling =	usePooling;
+		connectionStringBuilder.ApplicationName = applicationName;
 		m_dataSource = new MySqlDataSourceBuilder(connectionStringBuilder.ConnectionString)
 			.UseName(poolName)
 			.Build();
@@ -28,9 +29,10 @@ public DataSourceConnectionCreator(bool usePooling, string? poolName, MySqlConne
 
 internal sealed class PlainConnectionCreator : IConnectionCreator
 {
-	public PlainConnectionCreator(bool usePooling, MySqlConnectionStringBuilder connectionStringBuilder)
+	public PlainConnectionCreator(bool usePooling, string? applicationName, MySqlConnectionStringBuilder connectionStringBuilder)
 	{
 		connectionStringBuilder.Pooling = usePooling;
+		connectionStringBuilder.ApplicationName = applicationName;
 		m_connectionString = connectionStringBuilder.ConnectionString;
 		PoolName = connectionStringBuilder.GetConnectionString(includePassword: false);
     }

From c9d2217de0146913beb10323166e0debf537bb12 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 13 Jan 2024 19:36:53 -0800
Subject: [PATCH 246/393] Unify Application Name and UseName. Fixes #1434

---
 .../MySqlDataSourceBuilder/UseName.md         |  4 ++
 docs/content/connection-options.md            |  9 +++-
 docs/content/diagnostics/metrics.md           | 52 +++++++++++++++++++
 src/MySqlConnector/Core/ConnectionPool.cs     | 11 ++--
 src/MySqlConnector/Core/ConnectionSettings.cs |  4 +-
 src/MySqlConnector/Core/MetricsReporter.cs    |  2 +-
 src/MySqlConnector/Core/ServerSession.cs      |  4 +-
 src/MySqlConnector/MySqlDataSourceBuilder.cs  |  3 ++
 .../Metrics/IConnectionCreator.cs             |  4 +-
 9 files changed, 80 insertions(+), 13 deletions(-)

diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseName.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseName.md
index d9a6d4073..0ffc7716b 100644
--- a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseName.md
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseName.md
@@ -18,6 +18,10 @@ public MySqlDataSourceBuilder UseName(string? name)
 
 This builder, so that method calls can be chained.
 
+## Remarks
+
+The connection pool name is used to set the `program_name` connection attribute (which is visible to some diagnostic tools) and the `pool.name` tag supplied with [connection pool metrics](https://mysqlconnector.net/diagnostics/metrics/).
+
 ## See Also
 
 * class [MySqlDataSourceBuilder](../../MySqlDataSourceBuilderType/)
diff --git a/docs/content/connection-options.md b/docs/content/connection-options.md
index 9985b35c8..96f53260d 100644
--- a/docs/content/connection-options.md
+++ b/docs/content/connection-options.md
@@ -286,8 +286,13 @@ These are the other options that MySqlConnector supports. They are set to sensib
   <tr id="ApplicationName">
     <td>Application Name, ApplicationName</td>
     <td>null</td>
-    <td>Sets the <c>program_name</c> connection attribute passed to MySQL Server. This value may be displayed by diagnostic tools,
-    e.g., as the “Program” column in “Client Connections” in <a href="https://www.mysql.com/products/workbench/">MySQL Workbench</a>.</td>
+    <td><p>Sets the <c>program_name</c> connection attribute passed to MySQL Server. This value may be displayed by diagnostic tools,
+    e.g., as the “Program” column in “Client Connections” in <a href="https://www.mysql.com/products/workbench/">MySQL Workbench</a>.
+    It also sets the connection pool name reported by the <code>pool.name</code> tag associated with connection pool metrics.</p>
+    <p>This connection string option is deprecated and is provided for backwards compatibility. Newer applications should
+    use <a href="/api/MySqlConnector/MySqlDataSourceBuilder/UseName/"><code>MySqlDataSourceBuilder.UseName</code></a> instead.
+    </p></td>
+    </td>
   </tr>
   <tr id="AutoEnlist">
     <td>Auto Enlist, AutoEnlist</td>
diff --git a/docs/content/diagnostics/metrics.md b/docs/content/diagnostics/metrics.md
index 46195d4c7..c4fcbcb6a 100644
--- a/docs/content/diagnostics/metrics.md
+++ b/docs/content/diagnostics/metrics.md
@@ -41,3 +41,55 @@ Name | Type | Unit | Description
 `db.client.connections.idle.max` | UpDownCounter | `{connection}` | The maximum number of idle open connections allowed; this corresponds to `MaximumPoolSize` in the connection string.
 `db.client.connections.idle.min` | UpDownCounter | `{connection}` | The minimum number of idle open connections allowed; this corresponds to `MinimumPoolSize` in the connection string.
 `db.client.connections.max` | UpDownCounter | `{connection}` | The maximum number of open connections allowed; this corresponds to `MaximumPoolSize` in the connection string.
+
+## Supported Tags
+
+Name | Description
+--- | ---
+`pool.name` | The name of the connection pool (see below).
+`state` | `idle` or `used`; this tag is used with the `db.client.connections.usage` instrument.
+
+## Connection Pool Name
+
+A connection pool name can be specified by one of the following means:
+
+### UseName
+
+The `MySqlDataSourceBuilder.UseName` method can be used to specify a name for the connection pool:
+
+```csharp
+using var dataSource = new MySqlDataSourceBuilder("...connection string...")
+    .UseName("MyPoolName")
+    .Build();
+```
+
+This can also be used with dependency injection:
+
+```csharp
+builder.Services.AddMySqlDataSource(builder.Configuration.GetConnectionString("Default"),
+    x => x.UseName("MyPoolName"));
+```
+
+### Keyed Services
+
+Use the `AddKeyedMySqlDataSource` method to register a `MySqlDataSource` as a [keyed service](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#keyed-di-services).
+If the service key is a string, it will automatically be used as the `MySqlDataSource` name.
+
+```csharp
+builder.Services.AddKeyedMySqlDataSource("MyPoolName",
+    builder.Configuration.GetConnectionString("MyConnectionString"));
+```
+
+### Application Name
+
+Finally, the connection pool name can be specified by setting the `Application Name` connection string option:
+
+```csharp
+using var connection = new MySqlConnection("server=dbserver;...;Application Name=MyPoolName");
+```
+
+If `UseName` is used, it will override the `Application Name` connection string option.
+
+### Default
+
+For metrics, if no pool name is specified, the connection string (without a password) will be used as the pool name.
diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 06eb38fe2..26a49b71a 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -501,9 +501,12 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 		if (!connectionStringBuilder.Pooling)
 			return null;
 
+		if (name is not null)
+			connectionStringBuilder.ApplicationName = name;
+
 		// force a new pool to be created, ignoring the cache
 		var connectionSettings = new ConnectionSettings(connectionStringBuilder);
-		var pool = new ConnectionPool(loggingConfiguration, connectionSettings, name);
+		var pool = new ConnectionPool(loggingConfiguration, connectionSettings);
 		pool.StartReaperTask();
 		pool.StartDnsCheckTimer();
 		return pool;
@@ -551,7 +554,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 
 		// create a new pool and attempt to insert it; if someone else beats us to it, just use their value
 		var connectionSettings = new ConnectionSettings(connectionStringBuilder);
-		var newPool = new ConnectionPool(loggingConfiguration!, connectionSettings, name: null);
+		var newPool = new ConnectionPool(loggingConfiguration!, connectionSettings);
 		pool = s_pools.GetOrAdd(normalizedConnectionString, newPool);
 
 		if (pool == newPool)
@@ -591,12 +594,12 @@ static List<ConnectionPool> GetCachedPools()
 		}
 	}
 
-	private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration, ConnectionSettings cs, string? name)
+	private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration, ConnectionSettings cs)
 	{
 		m_logger = loggingConfiguration.PoolLogger;
 		m_connectionLogger = loggingConfiguration.ConnectionLogger;
 		ConnectionSettings = cs;
-		Name = name;
+		Name = cs.ApplicationName;
 		SslProtocols = cs.TlsVersions;
 		m_generation = 0;
 		m_cleanSemaphore = new(1);
diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index b4e9c87be..17194e297 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -121,7 +121,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
 		AllowPublicKeyRetrieval = csb.AllowPublicKeyRetrieval;
 		AllowUserVariables = csb.AllowUserVariables;
 		AllowZeroDateTime = csb.AllowZeroDateTime;
-		ApplicationName = csb.ApplicationName;
+		ApplicationName = csb.ApplicationName is { Length: 0 } ? null : csb.ApplicationName;
 		AutoEnlist = csb.AutoEnlist;
 		CancellationTimeout = csb.CancellationTimeout;
 		ConnectionTimeout = ToSigned(csb.ConnectionTimeout);
@@ -216,7 +216,7 @@ private static MySqlGuidFormat GetEffectiveGuidFormat(MySqlGuidFormat guidFormat
 	public bool AllowPublicKeyRetrieval { get; }
 	public bool AllowUserVariables { get; }
 	public bool AllowZeroDateTime { get; }
-	public string ApplicationName { get; }
+	public string? ApplicationName { get; }
 	public bool AutoEnlist { get; }
 	public int CancellationTimeout { get; }
 	public int ConnectionTimeout { get; }
diff --git a/src/MySqlConnector/Core/MetricsReporter.cs b/src/MySqlConnector/Core/MetricsReporter.cs
index 22c4a9112..800b43a2e 100644
--- a/src/MySqlConnector/Core/MetricsReporter.cs
+++ b/src/MySqlConnector/Core/MetricsReporter.cs
@@ -9,7 +9,7 @@ internal static class MetricsReporter
 	public static void RemoveIdle(ConnectionPool pool) => s_connectionsUsageCounter.Add(-1, pool.IdleStateTagList);
 	public static void AddUsed(ConnectionPool pool) => s_connectionsUsageCounter.Add(1, pool.UsedStateTagList);
 	public static void RemoveUsed(ConnectionPool pool) => s_connectionsUsageCounter.Add(-1, pool.UsedStateTagList);
-	public static void AddTimeout(ConnectionPool? pool, ConnectionSettings connectionSettings) => s_connectionTimeouts.Add(1, new KeyValuePair<string, object?>("pool.name", pool?.Name ?? connectionSettings.ConnectionStringBuilder.GetConnectionString(includePassword: false)));
+	public static void AddTimeout(ConnectionPool? pool, ConnectionSettings connectionSettings) => s_connectionTimeouts.Add(1, new KeyValuePair<string, object?>("pool.name", pool?.Name ?? connectionSettings.ApplicationName ?? connectionSettings.ConnectionStringBuilder.GetConnectionString(includePassword: false)));
 	public static void RecordCreateTime(ConnectionPool pool, double seconds) => s_createTimeHistory.Record(seconds, pool.PoolNameTagList);
 	public static void RecordUseTime(ConnectionPool pool, double seconds) => s_useTimeHistory.Record(seconds, pool.PoolNameTagList);
 	public static void RecordWaitTime(ConnectionPool pool, double seconds) => s_waitTimeHistory.Record(seconds, pool.PoolNameTagList);
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 10e8526a4..00e067344 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1777,7 +1777,7 @@ private void VerifyState(State state1, State state2, State state3, State state4,
 
 	internal SslProtocols SslProtocol => m_sslStream?.SslProtocol ?? SslProtocols.None;
 
-	private byte[] CreateConnectionAttributes(string programName)
+	private byte[] CreateConnectionAttributes(string? programName)
 	{
 		Log.CreatingConnectionAttributes(m_logger, Id);
 		var attributesWriter = new ByteBufferWriter();
@@ -1815,7 +1815,7 @@ private byte[] CreateConnectionAttributes(string programName)
 #endif
 		attributesWriter.WriteLengthEncodedString("_pid");
 		attributesWriter.WriteLengthEncodedString(processId.ToString(CultureInfo.InvariantCulture));
-		if (programName.Length != 0)
+		if (!string.IsNullOrEmpty(programName))
 		{
 			attributesWriter.WriteLengthEncodedString("program_name");
 			attributesWriter.WriteLengthEncodedString(programName!);
diff --git a/src/MySqlConnector/MySqlDataSourceBuilder.cs b/src/MySqlConnector/MySqlDataSourceBuilder.cs
index dcdb05edd..fbe902b40 100644
--- a/src/MySqlConnector/MySqlDataSourceBuilder.cs
+++ b/src/MySqlConnector/MySqlDataSourceBuilder.cs
@@ -36,6 +36,9 @@ public MySqlDataSourceBuilder UseLoggerFactory(ILoggerFactory? loggerFactory)
 	/// </summary>
 	/// <param name="name">The data source name.</param>
 	/// <returns>This builder, so that method calls can be chained.</returns>
+	/// <remarks>The connection pool name is used to set the <c>program_name</c> connection attribute
+	/// (which is visible to some diagnostic tools) and the <c>pool.name</c> tag supplied with
+	/// <a href="https://mysqlconnector.net/diagnostics/metrics/">connection pool metrics</a>.</remarks>
 	public MySqlDataSourceBuilder UseName(string? name)
 	{
 		m_name = name;
diff --git a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
index 4bc3e4af8..56d941650 100644
--- a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
+++ b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
@@ -17,7 +17,7 @@ public DataSourceConnectionCreator(bool usePooling, string? poolName, string? ap
 		m_dataSource = new MySqlDataSourceBuilder(connectionStringBuilder.ConnectionString)
 			.UseName(poolName)
 			.Build();
-        PoolName = poolName ?? connectionStringBuilder!.GetConnectionString(includePassword: false);
+        PoolName = poolName ?? applicationName ?? connectionStringBuilder!.GetConnectionString(includePassword: false);
     }
 
     public MySqlConnection OpenConnection() => m_dataSource!.OpenConnection();
@@ -34,7 +34,7 @@ public PlainConnectionCreator(bool usePooling, string? applicationName, MySqlCon
 		connectionStringBuilder.Pooling = usePooling;
 		connectionStringBuilder.ApplicationName = applicationName;
 		m_connectionString = connectionStringBuilder.ConnectionString;
-		PoolName = connectionStringBuilder.GetConnectionString(includePassword: false);
+		PoolName = applicationName ?? connectionStringBuilder.GetConnectionString(includePassword: false);
     }
 
 	public MySqlConnection OpenConnection()

From dcf6856461c9dbdcd2118e144921f52894b2a0b6 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 13 Jan 2024 21:29:07 -0800
Subject: [PATCH 247/393] Remove TLS version fallback code. Fixes #1409

---
 src/MySqlConnector/Core/ConnectionPool.cs |   3 -
 src/MySqlConnector/Core/ServerSession.cs  | 219 +++++++++-------------
 src/MySqlConnector/Logging/EventIds.cs    |   2 -
 src/MySqlConnector/Logging/Log.cs         |   6 -
 4 files changed, 84 insertions(+), 146 deletions(-)

diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 26a49b71a..763056ed8 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -18,8 +18,6 @@ internal sealed class ConnectionPool : IDisposable
 
 	public ConnectionSettings ConnectionSettings { get; }
 
-	public SslProtocols SslProtocols { get; set; }
-
 	public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection, long startingTimestamp, int timeoutMilliseconds, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		cancellationToken.ThrowIfCancellationRequested();
@@ -600,7 +598,6 @@ private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration,
 		m_connectionLogger = loggingConfiguration.ConnectionLogger;
 		ConnectionSettings = cs;
 		Name = cs.ApplicationName;
-		SslProtocols = cs.TlsVersions;
 		m_generation = 0;
 		m_cleanSemaphore = new(1);
 		m_sessionSemaphore = new(cs.MaximumPoolSize);
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 00e067344..f5d4b57b3 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -422,157 +422,106 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				}
 			}
 
-			// TLS negotiation should automatically fall back to the best version supported by client and server. However,
-			// Windows Schannel clients will fail to connect to a yaSSL-based MySQL Server if TLS 1.2 is requested and
-			// have to use only TLS 1.1: https://github.com/mysql-net/MySqlConnector/pull/101
-			// In order to use the best protocol possible (i.e., not always default to TLS 1.1), we try the OS-default protocol
-			// (which is SslProtocols.None; see https://docs.microsoft.com/en-us/dotnet/framework/network-programming/tls),
-			// then fall back to SslProtocols.Tls11 if that fails and it's possible that the cause is a yaSSL server.
-			bool shouldRetrySsl;
-			var shouldUpdatePoolSslProtocols = false;
-			var sslProtocols = Pool?.SslProtocols ?? cs.TlsVersions;
-			PayloadData payload;
-			InitialHandshakePayload initialHandshake;
-			do
+			var connected = false;
+			if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets)
+				connected = await OpenTcpSocketAsync(cs, loadBalancer ?? throw new ArgumentNullException(nameof(loadBalancer)), activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+			else if (cs.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket)
+				connected = await OpenUnixSocketAsync(cs, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+			else if (cs.ConnectionProtocol == MySqlConnectionProtocol.NamedPipe)
+				connected = await OpenNamedPipeAsync(cs, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+			if (!connected)
 			{
-				var isTls11or10Supported = (sslProtocols & (SslProtocols.Tls | SslProtocols.Tls11)) != SslProtocols.None;
-				var isTls12Supported = (sslProtocols & SslProtocols.Tls12) == SslProtocols.Tls12;
-				shouldRetrySsl = (sslProtocols == SslProtocols.None || (isTls12Supported && isTls11or10Supported)) && Utility.IsWindows();
-
-				var connected = false;
-				if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets)
-					connected = await OpenTcpSocketAsync(cs, loadBalancer ?? throw new ArgumentNullException(nameof(loadBalancer)), activity, ioBehavior, cancellationToken).ConfigureAwait(false);
-				else if (cs.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket)
-					connected = await OpenUnixSocketAsync(cs, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
-				else if (cs.ConnectionProtocol == MySqlConnectionProtocol.NamedPipe)
-					connected = await OpenNamedPipeAsync(cs, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
-				if (!connected)
-				{
-					lock (m_lock)
-						m_state = State.Failed;
-					Log.ConnectingFailed(m_logger, Id);
-					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Unable to connect to any of the specified MySQL hosts.");
-				}
+				lock (m_lock)
+					m_state = State.Failed;
+				Log.ConnectingFailed(m_logger, Id);
+				throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Unable to connect to any of the specified MySQL hosts.");
+			}
 
-				var byteHandler = m_socket is null ? new StreamByteHandler(m_stream!) : (IByteHandler) new SocketByteHandler(m_socket);
-				if (cs.ConnectionTimeout != 0)
-					byteHandler.RemainingTimeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - Utility.GetElapsedMilliseconds(startingTimestamp));
-				m_payloadHandler = new StandardPayloadHandler(byteHandler);
+			var byteHandler = m_socket is null ? new StreamByteHandler(m_stream!) : (IByteHandler) new SocketByteHandler(m_socket);
+			if (cs.ConnectionTimeout != 0)
+				byteHandler.RemainingTimeout = Math.Max(1, cs.ConnectionTimeoutMilliseconds - Utility.GetElapsedMilliseconds(startingTimestamp));
+			m_payloadHandler = new StandardPayloadHandler(byteHandler);
 
-				payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-				initialHandshake = InitialHandshakePayload.Create(payload.Span);
+			var payload = await ReceiveAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			var initialHandshake = InitialHandshakePayload.Create(payload.Span);
 
-				// if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
-				string authPluginName;
-				if ((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
-					authPluginName = initialHandshake.AuthPluginName!;
-				else
-					authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password";
-				Log.ServerSentAuthPluginName(m_logger, Id, authPluginName);
-				if (authPluginName != "mysql_native_password" && authPluginName != "sha256_password" && authPluginName != "caching_sha2_password")
-				{
-					Log.UnsupportedAuthenticationMethod(m_logger, Id, authPluginName);
-					throw new NotSupportedException($"Authentication method '{initialHandshake.AuthPluginName}' is not supported.");
-				}
+			// if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
+			string authPluginName;
+			if ((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
+				authPluginName = initialHandshake.AuthPluginName!;
+			else
+				authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password";
+			Log.ServerSentAuthPluginName(m_logger, Id, authPluginName);
+			if (authPluginName != "mysql_native_password" && authPluginName != "sha256_password" && authPluginName != "caching_sha2_password")
+			{
+				Log.UnsupportedAuthenticationMethod(m_logger, Id, authPluginName);
+				throw new NotSupportedException($"Authentication method '{initialHandshake.AuthPluginName}' is not supported.");
+			}
 
-				ServerVersion = new(initialHandshake.ServerVersion);
-				ConnectionId = initialHandshake.ConnectionId;
-				AuthPluginData = initialHandshake.AuthPluginData;
-				m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0;
-				CancellationTimeout = cs.CancellationTimeout;
-				UserID = cs.UserID;
+			ServerVersion = new(initialHandshake.ServerVersion);
+			ConnectionId = initialHandshake.ConnectionId;
+			AuthPluginData = initialHandshake.AuthPluginData;
+			m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0;
+			CancellationTimeout = cs.CancellationTimeout;
+			UserID = cs.UserID;
 
-				// set activity tags
-				{
-					var connectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
-					m_activityTags[ActivitySourceHelper.DatabaseConnectionIdTagName] = connectionId;
-					if (activity is { IsAllDataRequested: true })
-						activity.SetTag(ActivitySourceHelper.DatabaseConnectionIdTagName, connectionId);
-				}
+			// set activity tags
+			{
+				var connectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
+				m_activityTags[ActivitySourceHelper.DatabaseConnectionIdTagName] = connectionId;
+				if (activity is { IsAllDataRequested: true })
+					activity.SetTag(ActivitySourceHelper.DatabaseConnectionIdTagName, connectionId);
+			}
 
-				m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
-				m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
-				SupportsCachedPreparedMetadata = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
-				SupportsQueryAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
-				m_supportsSessionTrack = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
-				var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0;
-				m_characterSet = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4GeneralCaseInsensitive : CharacterSet.Utf8Mb3GeneralCaseInsensitive;
-				m_setNamesPayload = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ?
-					(SupportsQueryAttributes ? s_setNamesUtf8mb4WithAttributesPayload : s_setNamesUtf8mb4NoAttributesPayload) :
-					(SupportsQueryAttributes ? s_setNamesUtf8WithAttributesPayload : s_setNamesUtf8NoAttributesPayload);
-
-				// disable pipelining for RDS MySQL 5.7 (assuming Aurora); otherwise take it from the connection string or default to true
-				if (!cs.Pipelining.HasValue && ServerVersion.Version.Major == 5 && ServerVersion.Version.Minor == 7 && HostName.EndsWith(".rds.amazonaws.com", StringComparison.OrdinalIgnoreCase))
-				{
-					Log.AutoDetectedAurora57(m_logger, Id, HostName);
-					m_supportsPipelining = false;
-				}
-				else
-				{
-					// pipelining is not currently compatible with compression
-					m_supportsPipelining = !cs.UseCompression && cs.Pipelining is not false;
+			m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
+			m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
+			SupportsCachedPreparedMetadata = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
+			SupportsQueryAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
+			m_supportsSessionTrack = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
+			var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0;
+			m_characterSet = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4GeneralCaseInsensitive : CharacterSet.Utf8Mb3GeneralCaseInsensitive;
+			m_setNamesPayload = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ?
+				(SupportsQueryAttributes ? s_setNamesUtf8mb4WithAttributesPayload : s_setNamesUtf8mb4NoAttributesPayload) :
+				(SupportsQueryAttributes ? s_setNamesUtf8WithAttributesPayload : s_setNamesUtf8NoAttributesPayload);
+
+			// disable pipelining for RDS MySQL 5.7 (assuming Aurora); otherwise take it from the connection string or default to true
+			if (!cs.Pipelining.HasValue && ServerVersion.Version.Major == 5 && ServerVersion.Version.Minor == 7 && HostName.EndsWith(".rds.amazonaws.com", StringComparison.OrdinalIgnoreCase))
+			{
+				Log.AutoDetectedAurora57(m_logger, Id, HostName);
+				m_supportsPipelining = false;
+			}
+			else
+			{
+				// pipelining is not currently compatible with compression
+				m_supportsPipelining = !cs.UseCompression && cs.Pipelining is not false;
 
-					// for pipelining, concatenate reset connection and SET NAMES query into one buffer
-					if (m_supportsPipelining)
-					{
-						m_pipelinedResetConnectionBytes = new byte[m_setNamesPayload.Span.Length + 9];
+				// for pipelining, concatenate reset connection and SET NAMES query into one buffer
+				if (m_supportsPipelining)
+				{
+					m_pipelinedResetConnectionBytes = new byte[m_setNamesPayload.Span.Length + 9];
 
-						// first packet: reset connection
-						m_pipelinedResetConnectionBytes[0] = 1;
-						m_pipelinedResetConnectionBytes[4] = (byte) CommandKind.ResetConnection;
+					// first packet: reset connection
+					m_pipelinedResetConnectionBytes[0] = 1;
+					m_pipelinedResetConnectionBytes[4] = (byte) CommandKind.ResetConnection;
 
-						// second packet: SET NAMES query
-						m_pipelinedResetConnectionBytes[5] = (byte) m_setNamesPayload.Span.Length;
-						m_setNamesPayload.Span.CopyTo(m_pipelinedResetConnectionBytes.AsSpan()[9..]);
-					}
+					// second packet: SET NAMES query
+					m_pipelinedResetConnectionBytes[5] = (byte) m_setNamesPayload.Span.Length;
+					m_setNamesPayload.Span.CopyTo(m_pipelinedResetConnectionBytes.AsSpan()[9..]);
 				}
+			}
 
-				Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
-
-				if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
-				{
-					if (!serverSupportsSsl)
-					{
-						Log.ServerDoesNotSupportSsl(m_logger, Id);
-						throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support SSL");
-					}
-
-					try
-					{
-						await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, connection, sslProtocols, ioBehavior, cancellationToken).ConfigureAwait(false);
-						shouldRetrySsl = false;
-						if (shouldUpdatePoolSslProtocols && Pool is not null)
-							Pool.SslProtocols = sslProtocols;
-					}
-					catch (ArgumentException ex) when (ex.ParamName == "sslProtocolType" && sslProtocols == SslProtocols.None)
-					{
-						Log.SessionDoesNotSupportSslProtocolsNone(m_logger, ex, Id);
-						sslProtocols = SslProtocols.Tls | SslProtocols.Tls11 | SslProtocols.Tls12;
-					}
-					catch (Exception ex) when (shouldRetrySsl && IsRetryableException(ex))
-					{
-						// negotiating TLS 1.2 with a yaSSL-based server throws an exception on Windows, see comment at top of method
-						Log.FailedNegotiatingTls(m_logger, ex, Id);
-						sslProtocols = sslProtocols == SslProtocols.None ? SslProtocols.Tls | SslProtocols.Tls11 : (SslProtocols.Tls | SslProtocols.Tls11) & sslProtocols;
-						shouldUpdatePoolSslProtocols = true;
-					}
+			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
 
-					static bool IsRetryableException(Exception? ex) => (ex is MySqlException && IsRetryableException(ex.InnerException)) ||
-						(ex is AuthenticationException or (IOException and not FileNotFoundException and not DirectoryNotFoundException and not DriveNotFoundException and not PathTooLongException));
-				}
-				else
+			if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
+			{
+				if (!serverSupportsSsl)
 				{
-					shouldRetrySsl = false;
+					Log.ServerDoesNotSupportSsl(m_logger, Id);
+					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support SSL");
 				}
 
-				if (shouldRetrySsl)
-				{
-					// avoid "The collection already contains item with same key 'net.transport'" exception when retrying SSL
-					m_activityTags.Remove(ActivitySourceHelper.NetTransportTagName);
-					m_activityTags.Remove(ActivitySourceHelper.NetPeerNameTagName);
-					m_activityTags.Remove(ActivitySourceHelper.NetPeerPortTagName);
-				}
-			} while (shouldRetrySsl);
+				await InitSslAsync(initialHandshake.ProtocolCapabilities, cs, connection, cs.TlsVersions, ioBehavior, cancellationToken).ConfigureAwait(false);
+			}
 
 			if (m_supportsConnectionAttributes && cs.ConnectionAttributes is null)
 				cs.ConnectionAttributes = CreateConnectionAttributes(cs.ApplicationName);
diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index 02fe0b91a..ddb99123d 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -33,8 +33,6 @@ internal static class EventIds
 	public const int AutoDetectedAurora57 = 2103;
 	public const int SessionMadeConnection = 2104;
 	public const int ServerDoesNotSupportSsl = 2105;
-	public const int SessionDoesNotSupportSslProtocolsNone = 2106;
-	public const int FailedNegotiatingTls = 2107;
 	public const int CouldNotConnectToServer = 2108;
 	public const int SendingPipelinedResetConnectionRequest = 2109;
 	public const int SendingResetConnectionRequest = 2110;
diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index bcad0be27..e9c0bccc1 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -52,12 +52,6 @@ internal static partial class Log
 	[LoggerMessage(EventIds.ServerDoesNotSupportSsl, LogLevel.Error, "Session {SessionId} requires SSL but server doesn't support it")]
 	public static partial void ServerDoesNotSupportSsl(ILogger logger, string sessionId);
 
-	[LoggerMessage(EventIds.SessionDoesNotSupportSslProtocolsNone, LogLevel.Debug, "Session {SessionId} doesn't support SslProtocols.None; falling back to explicitly specifying SslProtocols")]
-	public static partial void SessionDoesNotSupportSslProtocolsNone(ILogger logger, Exception exception, string sessionId);
-
-	[LoggerMessage(EventIds.FailedNegotiatingTls, LogLevel.Warning, "Session {SessionId} failed negotiating TLS; falling back to TLS 1.1")]
-	public static partial void FailedNegotiatingTls(ILogger logger, Exception exception, string sessionId);
-
 	[LoggerMessage(EventIds.CouldNotConnectToServer, LogLevel.Error, "Session {SessionId} couldn't connect to server")]
 	public static partial void CouldNotConnectToServer(ILogger logger, Exception exception, string sessionId);
 

From 71ef33a4511ba5c73096457e678b1995492ffd7c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 13 Jan 2024 21:45:14 -0800
Subject: [PATCH 248/393] Improve code style.

---
 src/MySqlConnector/Core/ServerSession.cs | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index f5d4b57b3..b32896d34 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -422,13 +422,13 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				}
 			}
 
-			var connected = false;
-			if (cs.ConnectionProtocol == MySqlConnectionProtocol.Sockets)
-				connected = await OpenTcpSocketAsync(cs, loadBalancer ?? throw new ArgumentNullException(nameof(loadBalancer)), activity, ioBehavior, cancellationToken).ConfigureAwait(false);
-			else if (cs.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket)
-				connected = await OpenUnixSocketAsync(cs, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
-			else if (cs.ConnectionProtocol == MySqlConnectionProtocol.NamedPipe)
-				connected = await OpenNamedPipeAsync(cs, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+			var connected = cs.ConnectionProtocol switch
+			{
+				MySqlConnectionProtocol.Sockets => await OpenTcpSocketAsync(cs, loadBalancer ?? throw new ArgumentNullException(nameof(loadBalancer)), activity, ioBehavior, cancellationToken).ConfigureAwait(false),
+				MySqlConnectionProtocol.UnixSocket => await OpenUnixSocketAsync(cs, activity, ioBehavior, cancellationToken).ConfigureAwait(false),
+				MySqlConnectionProtocol.NamedPipe => await OpenNamedPipeAsync(cs, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false),
+				_ => false,
+			};
 			if (!connected)
 			{
 				lock (m_lock)
@@ -686,12 +686,13 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			case "caching_sha2_password":
+				// see https://dev.mysql.com/doc/dev/mysql-server/latest/page_caching_sha2_authentication_exchanges.html
 				var scrambleBytes = AuthenticationUtility.CreateScrambleResponse(Utility.TrimZeroByte(switchRequest.Data.AsSpan()), password);
 				payload = new(scrambleBytes);
 				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
-				// OK payload can be sent immediately (e.g., if password is empty( (short-circuiting the )
+				// OK payload can be sent immediately (e.g., if password is empty) bypassing even the fast authentication path
 				if (OkPayload.IsOk(payload.Span, SupportsDeprecateEof))
 					return payload;
 
@@ -814,7 +815,7 @@ private async Task<string> GetRsaPublicKeyAsync(string switchRequestName, Connec
 		{
 			// request the RSA public key
 			var payloadContent = switchRequestName == "caching_sha2_password" ? (byte) 0x02 : (byte) 0x01;
-			await SendReplyAsync(new PayloadData([ payloadContent ]), ioBehavior, cancellationToken).ConfigureAwait(false);
+			await SendReplyAsync(new PayloadData([payloadContent]), ioBehavior, cancellationToken).ConfigureAwait(false);
 			var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 			var publicKeyPayload = AuthenticationMoreDataPayload.Create(payload.Span);
 			return Encoding.ASCII.GetString(publicKeyPayload.Data);

From db551d4cb2ee7b8120f848beeb69eaccbe2caa40 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 13 Jan 2024 22:21:27 -0800
Subject: [PATCH 249/393] Eliminate temporary array.

---
 src/MySqlConnector/Core/ServerSession.cs              |  9 +++------
 .../Protocol/Serialization/AuthenticationUtility.cs   | 11 +++++++++++
 2 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index b32896d34..290c8b4c9 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -679,8 +679,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 				}
 
 				// send the password as a NULL-terminated UTF-8 string
-				var passwordBytes = Encoding.UTF8.GetBytes(password);
-				Array.Resize(ref passwordBytes, passwordBytes.Length + 1);
+				var passwordBytes = AuthenticationUtility.GetNullTerminatedPasswordBytes(password);
 				payload = new(passwordBytes);
 				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
@@ -736,8 +735,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 	private async Task<PayloadData> SendClearPasswordAsync(string password, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		// add NUL terminator to password
-		var passwordBytes = Encoding.UTF8.GetBytes(password);
-		Array.Resize(ref passwordBytes, passwordBytes.Length + 1);
+		var passwordBytes = AuthenticationUtility.GetNullTerminatedPasswordBytes(password);
 
 		// send plaintext password
 		var payload = new PayloadData(passwordBytes);
@@ -780,8 +778,7 @@ private async Task<PayloadData> SendEncryptedPasswordAsync(
 #endif
 
 		// add NUL terminator to password
-		var passwordBytes = Encoding.UTF8.GetBytes(password);
-		Array.Resize(ref passwordBytes, passwordBytes.Length + 1);
+		var passwordBytes = AuthenticationUtility.GetNullTerminatedPasswordBytes(password);
 
 		// XOR the password bytes with the challenge
 		AuthPluginData = Utility.TrimZeroByte(switchRequestData);
diff --git a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
index 5fca2ba4f..4cbdfc6ea 100644
--- a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
@@ -9,6 +9,17 @@ namespace MySqlConnector.Protocol.Serialization;
 
 internal static class AuthenticationUtility
 {
+	/// <summary>
+	/// Returns the UTF-8 bytes for <paramref name="password"/>, followed by a null byte.
+	/// </summary>
+	public static byte[] GetNullTerminatedPasswordBytes(string password)
+	{
+		var passwordByteCount = Encoding.UTF8.GetByteCount(password);
+		var passwordBytes = new byte[passwordByteCount + 1];
+		Encoding.UTF8.GetBytes(password.AsSpan(), passwordBytes);
+		return passwordBytes;
+	}
+
 	public static byte[] CreateAuthenticationResponse(ReadOnlySpan<byte> challenge, string password) =>
 		string.IsNullOrEmpty(password) ? [] : HashPassword(challenge, password);
 

From 43a25e00d630e87fb2088e0931ed8f7a4051c8e8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 14 Jan 2024 06:07:11 -0800
Subject: [PATCH 250/393] Fix spacing around square brackets.

---
 src/MySqlConnector/Core/ConnectionSettings.cs |   4 +-
 src/MySqlConnector/Core/ServerVersion.cs      |   4 +-
 src/MySqlConnector/Core/TypeMapper.cs         |  46 +++----
 src/MySqlConnector/MySqlBulkCopy.cs           |   2 +-
 src/MySqlConnector/MySqlCommandBuilder.cs     |   2 +-
 .../MySqlConnectionStringBuilder.cs           | 112 +++++++++---------
 .../Payloads/HandshakeResponse41Payload.cs    |   2 +-
 .../Protocol/Payloads/PingPayload.cs          |   2 +-
 .../Protocol/Payloads/QuitPayload.cs          |   2 +-
 .../Payloads/ResetConnectionPayload.cs        |   2 +-
 .../Protocol/Serialization/ProtocolUtility.cs |   2 +-
 src/MySqlConnector/Utilities/Utility.cs       |  12 +-
 12 files changed, 96 insertions(+), 96 deletions(-)

diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index 17194e297..f0c5e6cee 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -26,7 +26,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
 			if (csb.LoadBalance != MySqlLoadBalance.RoundRobin)
 				throw new NotSupportedException("LoadBalance not supported when ConnectionProtocol=NamedPipe");
 			ConnectionProtocol = MySqlConnectionProtocol.NamedPipe;
-			HostNames = (csb.Server == "." || string.Equals(csb.Server, "localhost", StringComparison.OrdinalIgnoreCase)) ? s_localhostPipeServer : [ csb.Server ];
+			HostNames = (csb.Server == "." || string.Equals(csb.Server, "localhost", StringComparison.OrdinalIgnoreCase)) ? s_localhostPipeServer : [csb.Server];
 			PipeName = csb.PipeName;
 		}
 		else if (csb.ConnectionProtocol == MySqlConnectionProtocol.SharedMemory)
@@ -272,7 +272,7 @@ private ConnectionSettings(ConnectionSettings other, string host, int port, stri
 		ConnectionString = other.ConnectionString;
 
 		ConnectionProtocol = MySqlConnectionProtocol.Sockets;
-		HostNames = [ host ];
+		HostNames = [host];
 		LoadBalance = other.LoadBalance;
 		Port = port;
 		PipeName = other.PipeName;
diff --git a/src/MySqlConnector/Core/ServerVersion.cs b/src/MySqlConnector/Core/ServerVersion.cs
index 2feac84fc..f6822faf1 100644
--- a/src/MySqlConnector/Core/ServerVersion.cs
+++ b/src/MySqlConnector/Core/ServerVersion.cs
@@ -25,13 +25,13 @@ public ServerVersion(ReadOnlySpan<byte> versionString)
 		if (Utf8Parser.TryParse(versionString, out int major, out var bytesConsumed))
 		{
 			versionString = versionString[bytesConsumed..];
-			if (versionString is [ 0x2E, .. ])
+			if (versionString is [0x2E, ..])
 			{
 				versionString = versionString[1..];
 				if (Utf8Parser.TryParse(versionString, out minor, out bytesConsumed))
 				{
 					versionString = versionString[bytesConsumed..];
-					if (versionString is [ 0x2E, .. ])
+					if (versionString is [0x2E, ..])
 					{
 						versionString = versionString[1..];
 						if (Utf8Parser.TryParse(versionString, out build, out bytesConsumed))
diff --git a/src/MySqlConnector/Core/TypeMapper.cs b/src/MySqlConnector/Core/TypeMapper.cs
index 6d46c4822..5ba28e210 100644
--- a/src/MySqlConnector/Core/TypeMapper.cs
+++ b/src/MySqlConnector/Core/TypeMapper.cs
@@ -20,18 +20,18 @@ private TypeMapper()
 		m_mySqlDbTypeToColumnTypeMetadata = [];
 
 		// boolean
-		var typeBoolean = AddDbTypeMapping(new(typeof(bool), [ DbType.Boolean ], convert: static o => Convert.ToBoolean(o, CultureInfo.InvariantCulture)));
+		var typeBoolean = AddDbTypeMapping(new(typeof(bool), [DbType.Boolean], convert: static o => Convert.ToBoolean(o, CultureInfo.InvariantCulture)));
 		AddColumnTypeMetadata(new("TINYINT", typeBoolean, MySqlDbType.Bool, isUnsigned: false, length: 1, columnSize: 1, simpleDataTypeName: "BOOL", createFormat: "BOOL"));
 
 		// integers
-		var typeSbyte = AddDbTypeMapping(new(typeof(sbyte), [ DbType.SByte ], convert: static o => Convert.ToSByte(o, CultureInfo.InvariantCulture)));
-		var typeByte = AddDbTypeMapping(new(typeof(byte), [ DbType.Byte ], convert: static o => Convert.ToByte(o, CultureInfo.InvariantCulture)));
-		var typeShort = AddDbTypeMapping(new(typeof(short), [ DbType.Int16 ], convert: static o => Convert.ToInt16(o, CultureInfo.InvariantCulture)));
-		var typeUshort = AddDbTypeMapping(new(typeof(ushort), [ DbType.UInt16 ], convert: static o => Convert.ToUInt16(o, CultureInfo.InvariantCulture)));
-		var typeInt = AddDbTypeMapping(new(typeof(int), [ DbType.Int32 ], convert: static o => Convert.ToInt32(o, CultureInfo.InvariantCulture)));
-		var typeUint = AddDbTypeMapping(new(typeof(uint), [ DbType.UInt32 ], convert: static o => Convert.ToUInt32(o, CultureInfo.InvariantCulture)));
-		var typeLong = AddDbTypeMapping(new(typeof(long), [ DbType.Int64 ], convert: static o => Convert.ToInt64(o, CultureInfo.InvariantCulture)));
-		var typeUlong = AddDbTypeMapping(new(typeof(ulong), [ DbType.UInt64 ], convert: static o => Convert.ToUInt64(o, CultureInfo.InvariantCulture)));
+		var typeSbyte = AddDbTypeMapping(new(typeof(sbyte), [DbType.SByte], convert: static o => Convert.ToSByte(o, CultureInfo.InvariantCulture)));
+		var typeByte = AddDbTypeMapping(new(typeof(byte), [DbType.Byte], convert: static o => Convert.ToByte(o, CultureInfo.InvariantCulture)));
+		var typeShort = AddDbTypeMapping(new(typeof(short), [DbType.Int16], convert: static o => Convert.ToInt16(o, CultureInfo.InvariantCulture)));
+		var typeUshort = AddDbTypeMapping(new(typeof(ushort), [DbType.UInt16], convert: static o => Convert.ToUInt16(o, CultureInfo.InvariantCulture)));
+		var typeInt = AddDbTypeMapping(new(typeof(int), [DbType.Int32], convert: static o => Convert.ToInt32(o, CultureInfo.InvariantCulture)));
+		var typeUint = AddDbTypeMapping(new(typeof(uint), [DbType.UInt32], convert: static o => Convert.ToUInt32(o, CultureInfo.InvariantCulture)));
+		var typeLong = AddDbTypeMapping(new(typeof(long), [DbType.Int64], convert: static o => Convert.ToInt64(o, CultureInfo.InvariantCulture)));
+		var typeUlong = AddDbTypeMapping(new(typeof(ulong), [DbType.UInt64], convert: static o => Convert.ToUInt64(o, CultureInfo.InvariantCulture)));
 		AddColumnTypeMetadata(new("TINYINT", typeSbyte, MySqlDbType.Byte, isUnsigned: false));
 		AddColumnTypeMetadata(new("TINYINT", typeByte, MySqlDbType.UByte, isUnsigned: true, length: 1));
 		AddColumnTypeMetadata(new("TINYINT", typeByte, MySqlDbType.UByte, isUnsigned: true));
@@ -46,9 +46,9 @@ private TypeMapper()
 		AddColumnTypeMetadata(new("BIT", typeUlong, MySqlDbType.Bit));
 
 		// decimals
-		var typeDecimal = AddDbTypeMapping(new(typeof(decimal), [ DbType.Decimal, DbType.Currency, DbType.VarNumeric ], convert: static o => Convert.ToDecimal(o, CultureInfo.InvariantCulture)));
-		var typeDouble = AddDbTypeMapping(new(typeof(double), [ DbType.Double ], convert: static o => Convert.ToDouble(o, CultureInfo.InvariantCulture)));
-		var typeFloat = AddDbTypeMapping(new(typeof(float), [ DbType.Single ], convert: static o => Convert.ToSingle(o, CultureInfo.InvariantCulture)));
+		var typeDecimal = AddDbTypeMapping(new(typeof(decimal), [DbType.Decimal, DbType.Currency, DbType.VarNumeric], convert: static o => Convert.ToDecimal(o, CultureInfo.InvariantCulture)));
+		var typeDouble = AddDbTypeMapping(new(typeof(double), [DbType.Double], convert: static o => Convert.ToDouble(o, CultureInfo.InvariantCulture)));
+		var typeFloat = AddDbTypeMapping(new(typeof(float), [DbType.Single], convert: static o => Convert.ToSingle(o, CultureInfo.InvariantCulture)));
 		AddColumnTypeMetadata(new("DECIMAL", typeDecimal, MySqlDbType.NewDecimal, createFormat: "DECIMAL({0},{1});precision,scale"));
 		AddColumnTypeMetadata(new("DECIMAL", typeDecimal, MySqlDbType.NewDecimal, isUnsigned: true, createFormat: "DECIMAL({0},{1}) UNSIGNED;precision,scale"));
 		AddColumnTypeMetadata(new("DECIMAL", typeDecimal, MySqlDbType.Decimal));
@@ -56,8 +56,8 @@ private TypeMapper()
 		AddColumnTypeMetadata(new("FLOAT", typeFloat, MySqlDbType.Float));
 
 		// string
-		var typeFixedString = AddDbTypeMapping(new(typeof(string), [ DbType.StringFixedLength, DbType.AnsiStringFixedLength ], convert: Convert.ToString!));
-		var typeString = AddDbTypeMapping(new(typeof(string), [ DbType.String, DbType.AnsiString, DbType.Xml ], convert: Convert.ToString!));
+		var typeFixedString = AddDbTypeMapping(new(typeof(string), [DbType.StringFixedLength, DbType.AnsiStringFixedLength], convert: Convert.ToString!));
+		var typeString = AddDbTypeMapping(new(typeof(string), [DbType.String, DbType.AnsiString, DbType.Xml], convert: Convert.ToString!));
 		AddColumnTypeMetadata(new("VARCHAR", typeString, MySqlDbType.VarChar, createFormat: "VARCHAR({0});size"));
 		AddColumnTypeMetadata(new("VARCHAR", typeString, MySqlDbType.VarString));
 		AddColumnTypeMetadata(new("CHAR", typeFixedString, MySqlDbType.String, createFormat: "CHAR({0});size"));
@@ -70,7 +70,7 @@ private TypeMapper()
 		AddColumnTypeMetadata(new("JSON", typeString, MySqlDbType.JSON));
 
 		// binary
-		var typeBinary = AddDbTypeMapping(new(typeof(byte[]), [ DbType.Binary ]));
+		var typeBinary = AddDbTypeMapping(new(typeof(byte[]), [DbType.Binary]));
 		AddColumnTypeMetadata(new("BLOB", typeBinary, MySqlDbType.Blob, binary: true, columnSize: ushort.MaxValue, simpleDataTypeName: "BLOB"));
 		AddColumnTypeMetadata(new("BINARY", typeBinary, MySqlDbType.Binary, binary: true, simpleDataTypeName: "BLOB", createFormat: "BINARY({0});length"));
 		AddColumnTypeMetadata(new("VARBINARY", typeBinary, MySqlDbType.VarBinary, binary: true, simpleDataTypeName: "BLOB", createFormat: "VARBINARY({0});length"));
@@ -91,15 +91,15 @@ private TypeMapper()
 
 		// date/time
 #if NET6_0_OR_GREATER
-		AddDbTypeMapping(new(typeof(DateOnly), [ DbType.Date ]));
+		AddDbTypeMapping(new(typeof(DateOnly), [DbType.Date]));
 #endif
-		var typeDate = AddDbTypeMapping(new(typeof(DateTime), [ DbType.Date ]));
-		var typeDateTime = AddDbTypeMapping(new(typeof(DateTime), [ DbType.DateTime, DbType.DateTime2, DbType.DateTimeOffset ]));
-		AddDbTypeMapping(new(typeof(DateTimeOffset), [ DbType.DateTimeOffset ]));
+		var typeDate = AddDbTypeMapping(new(typeof(DateTime), [DbType.Date]));
+		var typeDateTime = AddDbTypeMapping(new(typeof(DateTime), [DbType.DateTime, DbType.DateTime2, DbType.DateTimeOffset]));
+		AddDbTypeMapping(new(typeof(DateTimeOffset), [DbType.DateTimeOffset]));
 #if NET6_0_OR_GREATER
-		AddDbTypeMapping(new(typeof(TimeOnly), [ DbType.Time ]));
+		AddDbTypeMapping(new(typeof(TimeOnly), [DbType.Time]));
 #endif
-		var typeTime = AddDbTypeMapping(new(typeof(TimeSpan), [ DbType.Time ], convert: static o => o is string s ? Utility.ParseTimeSpan(Encoding.UTF8.GetBytes(s)) : Convert.ChangeType(o, typeof(TimeSpan), CultureInfo.InvariantCulture)));
+		var typeTime = AddDbTypeMapping(new(typeof(TimeSpan), [DbType.Time], convert: static o => o is string s ? Utility.ParseTimeSpan(Encoding.UTF8.GetBytes(s)) : Convert.ChangeType(o, typeof(TimeSpan), CultureInfo.InvariantCulture)));
 		AddColumnTypeMetadata(new("DATETIME", typeDateTime, MySqlDbType.DateTime));
 		AddColumnTypeMetadata(new("DATE", typeDate, MySqlDbType.Date));
 		AddColumnTypeMetadata(new("DATE", typeDate, MySqlDbType.Newdate));
@@ -113,11 +113,11 @@ private TypeMapper()
 #else
 		Func<object, object> convertGuid = static o => Guid.Parse(Convert.ToString(o, CultureInfo.InvariantCulture)!);
 #endif
-		var typeGuid = AddDbTypeMapping(new(typeof(Guid), [ DbType.Guid ], convert: convertGuid));
+		var typeGuid = AddDbTypeMapping(new(typeof(Guid), [DbType.Guid], convert: convertGuid));
 		AddColumnTypeMetadata(new("CHAR", typeGuid, MySqlDbType.Guid, length: 36, simpleDataTypeName: "CHAR(36)", createFormat: "CHAR(36)"));
 
 		// null
-		var typeNull = AddDbTypeMapping(new(typeof(object), [ DbType.Object ]));
+		var typeNull = AddDbTypeMapping(new(typeof(object), [DbType.Object]));
 		AddColumnTypeMetadata(new("NULL", typeNull, MySqlDbType.Null));
 	}
 
diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs
index 88170b243..1d2510a2e 100644
--- a/src/MySqlConnector/MySqlBulkCopy.cs
+++ b/src/MySqlConnector/MySqlBulkCopy.cs
@@ -700,7 +700,7 @@ static bool WriteBytes(ReadOnlySpan<byte> value, ref int inputIndex, Span<byte>
 		}
 	}
 
-	private static readonly char[] s_specialCharacters = [ '\t', '\\', '\n' ];
+	private static readonly char[] s_specialCharacters = ['\t', '\\', '\n'];
 	private static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
 
 	private readonly MySqlConnection m_connection;
diff --git a/src/MySqlConnector/MySqlCommandBuilder.cs b/src/MySqlConnector/MySqlCommandBuilder.cs
index b69b9444d..828f973fb 100644
--- a/src/MySqlConnector/MySqlCommandBuilder.cs
+++ b/src/MySqlConnector/MySqlCommandBuilder.cs
@@ -94,7 +94,7 @@ protected override void SetRowUpdatingHandler(DbDataAdapter adapter)
 
 	public override string UnquoteIdentifier(string quotedIdentifier)
 	{
-		if (quotedIdentifier is [ '`', .., '`' ])
+		if (quotedIdentifier is ['`', .., '`'])
 			quotedIdentifier = quotedIdentifier[1..^1];
 		return quotedIdentifier.Replace("``", "`");
 	}
diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index b82352596..416470666 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -989,72 +989,72 @@ static MySqlConnectionStringOption()
 		// Base Options
 #pragma warning disable SA1118 // Parameter should not span multiple lines
 		AddOption(options, Server = new(
-			keys: [ "Server", "Host", "Data Source", "DataSource", "Address", "Addr", "Network Address" ],
+			keys: ["Server", "Host", "Data Source", "DataSource", "Address", "Addr", "Network Address"],
 			defaultValue: ""));
 
 		AddOption(options, Port = new(
-			keys: [ "Port" ],
+			keys: ["Port"],
 			defaultValue: 3306u));
 
 		AddOption(options, UserID = new(
-			keys: [ "User ID", "UserID", "Username", "Uid", "User name", "User" ],
+			keys: ["User ID", "UserID", "Username", "Uid", "User name", "User"],
 			defaultValue: ""));
 
 		AddOption(options, Password = new(
-			keys: [ "Password", "pwd" ],
+			keys: ["Password", "pwd"],
 			defaultValue: ""));
 
 		AddOption(options, Database = new(
-			keys: [ "Database", "Initial Catalog" ],
+			keys: ["Database", "Initial Catalog"],
 			defaultValue: ""));
 
 		AddOption(options, LoadBalance = new(
-			keys: [ "Load Balance", "LoadBalance" ],
+			keys: ["Load Balance", "LoadBalance"],
 			defaultValue: MySqlLoadBalance.RoundRobin));
 
 		AddOption(options, ConnectionProtocol = new(
-			keys: [ "Connection Protocol", "ConnectionProtocol", "Protocol" ],
+			keys: ["Connection Protocol", "ConnectionProtocol", "Protocol"],
 			defaultValue: MySqlConnectionProtocol.Socket));
 
 		AddOption(options, PipeName = new(
-			keys: [ "Pipe Name", "PipeName", "Pipe" ],
+			keys: ["Pipe Name", "PipeName", "Pipe"],
 			defaultValue: "MYSQL"));
 
 		// SSL/TLS Options
 		AddOption(options, SslMode = new(
-			keys: [ "SSL Mode", "SslMode" ],
+			keys: ["SSL Mode", "SslMode"],
 			defaultValue: MySqlSslMode.Preferred));
 
 		AddOption(options, CertificateFile = new(
-			keys: [ "Certificate File", "CertificateFile" ],
+			keys: ["Certificate File", "CertificateFile"],
 			defaultValue: ""));
 
 		AddOption(options, CertificatePassword = new(
-			keys: [ "Certificate Password", "CertificatePassword" ],
+			keys: ["Certificate Password", "CertificatePassword"],
 			defaultValue: ""));
 
 		AddOption(options, CertificateStoreLocation = new(
-			keys: [ "Certificate Store Location", "CertificateStoreLocation" ],
+			keys: ["Certificate Store Location", "CertificateStoreLocation"],
 			defaultValue: MySqlCertificateStoreLocation.None));
 
 		AddOption(options, CertificateThumbprint = new(
-			keys: [ "Certificate Thumbprint", "CertificateThumbprint", "Certificate Thumb Print" ],
+			keys: ["Certificate Thumbprint", "CertificateThumbprint", "Certificate Thumb Print"],
 			defaultValue: ""));
 
 		AddOption(options, SslCert = new(
-			keys: [ "SSL Cert", "SslCert", "Ssl-Cert" ],
+			keys: ["SSL Cert", "SslCert", "Ssl-Cert"],
 			defaultValue: ""));
 
 		AddOption(options, SslKey = new(
-			keys: [ "SSL Key", "SslKey", "Ssl-Key" ],
+			keys: ["SSL Key", "SslKey", "Ssl-Key"],
 			defaultValue: ""));
 
 		AddOption(options, SslCa = new(
-			keys: [ "SSL CA", "CACertificateFile", "CA Certificate File", "SslCa", "Ssl-Ca" ],
+			keys: ["SSL CA", "CACertificateFile", "CA Certificate File", "SslCa", "Ssl-Ca"],
 			defaultValue: ""));
 
 		AddOption(options, TlsVersion = new(
-			keys: [ "TLS Version", "TlsVersion", "Tls-Version" ],
+			keys: ["TLS Version", "TlsVersion", "Tls-Version"],
 			defaultValue: "",
 			coerce: value =>
 			{
@@ -1097,73 +1097,73 @@ static MySqlConnectionStringOption()
 			}));
 
 		AddOption(options, TlsCipherSuites = new(
-			keys: [ "TLS Cipher Suites", "TlsCipherSuites" ],
+			keys: ["TLS Cipher Suites", "TlsCipherSuites"],
 			defaultValue: ""));
 
 		// Connection Pooling Options
 		AddOption(options, Pooling = new(
-			keys: [ "Pooling" ],
+			keys: ["Pooling"],
 			defaultValue: true));
 
 		AddOption(options, ConnectionLifeTime = new(
-			keys: [ "Connection Lifetime", "ConnectionLifeTime" ],
+			keys: ["Connection Lifetime", "ConnectionLifeTime"],
 			defaultValue: 0u));
 
 		AddOption(options, ConnectionReset = new(
-			keys: [ "Connection Reset", "ConnectionReset" ],
+			keys: ["Connection Reset", "ConnectionReset"],
 			defaultValue: true));
 
 		AddOption(options, DeferConnectionReset = new(
-			keys: [ "Defer Connection Reset", "DeferConnectionReset" ],
+			keys: ["Defer Connection Reset", "DeferConnectionReset"],
 			defaultValue: true));
 
 		AddOption(options, ConnectionIdlePingTime = new(
-			keys: [ "Connection Idle Ping Time", "ConnectionIdlePingTime" ],
+			keys: ["Connection Idle Ping Time", "ConnectionIdlePingTime"],
 			defaultValue: 0u));
 
 		AddOption(options, ConnectionIdleTimeout = new(
-			keys: [ "Connection Idle Timeout", "ConnectionIdleTimeout" ],
+			keys: ["Connection Idle Timeout", "ConnectionIdleTimeout"],
 			defaultValue: 180u));
 
 		AddOption(options, MinimumPoolSize = new(
-			keys: [ "Minimum Pool Size", "Min Pool Size", "MinimumPoolSize", "minpoolsize" ],
+			keys: ["Minimum Pool Size", "Min Pool Size", "MinimumPoolSize", "minpoolsize"],
 			defaultValue: 0u));
 
 		AddOption(options, MaximumPoolSize = new(
-			keys: [ "Maximum Pool Size", "Max Pool Size", "MaximumPoolSize", "maxpoolsize" ],
+			keys: ["Maximum Pool Size", "Max Pool Size", "MaximumPoolSize", "maxpoolsize"],
 			defaultValue: 100u));
 
 		AddOption(options, DnsCheckInterval = new(
-			keys: [ "DNS Check Interval", "DnsCheckInterval" ],
+			keys: ["DNS Check Interval", "DnsCheckInterval"],
 			defaultValue: 0u));
 
 		// Other Options
 		AddOption(options, AllowLoadLocalInfile = new(
-			keys: [ "Allow Load Local Infile", "AllowLoadLocalInfile" ],
+			keys: ["Allow Load Local Infile", "AllowLoadLocalInfile"],
 			defaultValue: false));
 
 		AddOption(options, AllowPublicKeyRetrieval = new(
-			keys: [ "Allow Public Key Retrieval", "AllowPublicKeyRetrieval" ],
+			keys: ["Allow Public Key Retrieval", "AllowPublicKeyRetrieval"],
 			defaultValue: false));
 
 		AddOption(options, AllowUserVariables = new(
-			keys: [ "Allow User Variables", "AllowUserVariables" ],
+			keys: ["Allow User Variables", "AllowUserVariables"],
 			defaultValue: false));
 
 		AddOption(options, AllowZeroDateTime = new(
-			keys: [ "Allow Zero DateTime", "AllowZeroDateTime" ],
+			keys: ["Allow Zero DateTime", "AllowZeroDateTime"],
 			defaultValue: false));
 
 		AddOption(options, ApplicationName = new(
-			keys: [ "Application Name", "ApplicationName" ],
+			keys: ["Application Name", "ApplicationName"],
 			defaultValue: ""));
 
 		AddOption(options, AutoEnlist = new(
-			keys: [ "Auto Enlist", "AutoEnlist" ],
+			keys: ["Auto Enlist", "AutoEnlist"],
 			defaultValue: true));
 
 		AddOption(options, CancellationTimeout = new(
-			keys: [ "Cancellation Timeout", "CancellationTimeout" ],
+			keys: ["Cancellation Timeout", "CancellationTimeout"],
 			defaultValue: 2,
 			coerce: x =>
 			{
@@ -1173,91 +1173,91 @@ static MySqlConnectionStringOption()
 			}));
 
 		AddOption(options, CharacterSet = new(
-			keys: [ "Character Set", "CharSet", "CharacterSet" ],
+			keys: ["Character Set", "CharSet", "CharacterSet"],
 			defaultValue: ""));
 
 		AddOption(options, ConnectionTimeout = new(
-			keys: [ "Connection Timeout", "ConnectionTimeout", "Connect Timeout" ],
+			keys: ["Connection Timeout", "ConnectionTimeout", "Connect Timeout"],
 			defaultValue: 15u));
 
 		AddOption(options, ConvertZeroDateTime = new(
-			keys: [ "Convert Zero DateTime", "ConvertZeroDateTime" ],
+			keys: ["Convert Zero DateTime", "ConvertZeroDateTime"],
 			defaultValue: false));
 
 		AddOption(options, DateTimeKind = new(
-			keys: [ "DateTime Kind", "DateTimeKind" ],
+			keys: ["DateTime Kind", "DateTimeKind"],
 			defaultValue: MySqlDateTimeKind.Unspecified));
 
 		AddOption(options, DefaultCommandTimeout = new(
-			keys: [ "Default Command Timeout", "DefaultCommandTimeout", "Command Timeout" ],
+			keys: ["Default Command Timeout", "DefaultCommandTimeout", "Command Timeout"],
 			defaultValue: 30u));
 
 		AddOption(options, ForceSynchronous = new(
-			keys: [ "Force Synchronous", "ForceSynchronous" ],
+			keys: ["Force Synchronous", "ForceSynchronous"],
 			defaultValue: false));
 
 		AddOption(options, GuidFormat = new(
-			keys: [ "GUID Format", "GuidFormat" ],
+			keys: ["GUID Format", "GuidFormat"],
 			defaultValue: MySqlGuidFormat.Default));
 
 		AddOption(options, IgnoreCommandTransaction = new(
-			keys: [ "Ignore Command Transaction", "IgnoreCommandTransaction" ],
+			keys: ["Ignore Command Transaction", "IgnoreCommandTransaction"],
 			defaultValue: false));
 
 		AddOption(options, IgnorePrepare = new(
-			keys: [ "Ignore Prepare", "IgnorePrepare" ],
+			keys: ["Ignore Prepare", "IgnorePrepare"],
 			defaultValue: false));
 
 		AddOption(options, InteractiveSession = new(
-			keys: [ "Interactive Session", "InteractiveSession", "Interactive" ],
+			keys: ["Interactive Session", "InteractiveSession", "Interactive"],
 			defaultValue: false));
 
 		AddOption(options, Keepalive = new(
-			keys: [ "Keep Alive", "Keepalive" ],
+			keys: ["Keep Alive", "Keepalive"],
 			defaultValue: 0u));
 
 		AddOption(options, NoBackslashEscapes = new(
-			keys: [ "No Backslash Escapes", "NoBackslashEscapes" ],
+			keys: ["No Backslash Escapes", "NoBackslashEscapes"],
 			defaultValue: false));
 
 		AddOption(options, OldGuids = new(
-			keys: [ "Old Guids", "OldGuids" ],
+			keys: ["Old Guids", "OldGuids"],
 			defaultValue: false));
 
 		AddOption(options, PersistSecurityInfo = new(
-			keys: [ "Persist Security Info", "PersistSecurityInfo" ],
+			keys: ["Persist Security Info", "PersistSecurityInfo"],
 			defaultValue: false));
 
 		AddOption(options, Pipelining = new(
-			keys: [ "Pipelining" ],
+			keys: ["Pipelining"],
 			defaultValue: true));
 
 		AddOption(options, ServerRedirectionMode = new(
-			keys: [ "Server Redirection Mode", "ServerRedirectionMode" ],
+			keys: ["Server Redirection Mode", "ServerRedirectionMode"],
 			defaultValue: MySqlServerRedirectionMode.Disabled));
 
 		AddOption(options, ServerRsaPublicKeyFile = new(
-			keys: [ "Server RSA Public Key File", "ServerRsaPublicKeyFile" ],
+			keys: ["Server RSA Public Key File", "ServerRsaPublicKeyFile"],
 			defaultValue: ""));
 
 		AddOption(options, ServerSPN = new(
-			keys: [ "Server SPN", "ServerSPN" ],
+			keys: ["Server SPN", "ServerSPN"],
 			defaultValue: ""));
 
 		AddOption(options, TreatTinyAsBoolean = new(
-			keys: [ "Treat Tiny As Boolean", "TreatTinyAsBoolean" ],
+			keys: ["Treat Tiny As Boolean", "TreatTinyAsBoolean"],
 			defaultValue: true));
 
 		AddOption(options, UseAffectedRows = new(
-			keys: [ "Use Affected Rows", "UseAffectedRows" ],
+			keys: ["Use Affected Rows", "UseAffectedRows"],
 			defaultValue: false));
 
 		AddOption(options, UseCompression = new(
-			keys: [ "Use Compression", "Compress", "UseCompression" ],
+			keys: ["Use Compression", "Compress", "UseCompression"],
 			defaultValue: false));
 
 		AddOption(options, UseXaTransactions = new(
-			keys: [ "Use XA Transactions", "UseXaTransactions" ],
+			keys: ["Use XA Transactions", "UseXaTransactions"],
 			defaultValue: true));
 #pragma warning restore SA1118 // Parameter should not span multiple lines
 
diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
index bd048f0ee..ba0e22f37 100644
--- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
@@ -36,7 +36,7 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 		writer.Write((byte) characterSet);
 
 		// NOTE: not new byte[19]; see https://github.com/dotnet/roslyn/issues/33088
-		ReadOnlySpan<byte> padding = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ];
+		ReadOnlySpan<byte> padding = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
 		writer.Write(padding);
 
 		if ((serverCapabilities & ProtocolCapabilities.LongPassword) == 0)
diff --git a/src/MySqlConnector/Protocol/Payloads/PingPayload.cs b/src/MySqlConnector/Protocol/Payloads/PingPayload.cs
index d6b02f768..a03ed5597 100644
--- a/src/MySqlConnector/Protocol/Payloads/PingPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/PingPayload.cs
@@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads;
 
 internal static class PingPayload
 {
-	public static PayloadData Instance { get; } = new([ (byte) CommandKind.Ping ]);
+	public static PayloadData Instance { get; } = new([(byte) CommandKind.Ping]);
 }
diff --git a/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs b/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs
index 1d416eddd..ce10d022f 100644
--- a/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/QuitPayload.cs
@@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads;
 
 internal static class QuitPayload
 {
-	public static PayloadData Instance { get; } = new([ (byte) CommandKind.Quit ]);
+	public static PayloadData Instance { get; } = new([(byte) CommandKind.Quit]);
 }
diff --git a/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs b/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs
index fe1ab10a2..bd9deb61a 100644
--- a/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/ResetConnectionPayload.cs
@@ -2,5 +2,5 @@ namespace MySqlConnector.Protocol.Payloads;
 
 internal static class ResetConnectionPayload
 {
-	public static PayloadData Instance { get; } = new([ (byte) CommandKind.ResetConnection ]);
+	public static PayloadData Instance { get; } = new([(byte) CommandKind.ResetConnection]);
 }
diff --git a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
index d3c1fc0ff..77e20d081 100644
--- a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
@@ -439,7 +439,7 @@ public static async ValueTask<ArraySegment<byte>> ReadPayloadAsync(BufferedByteR
 				if (protocolErrorBehavior == ProtocolErrorBehavior.Ignore)
 					packet = default;
 #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
-				else if (payloadBytes is [ ErrorPayload.Signature, .. ])
+				else if (payloadBytes is [ErrorPayload.Signature, ..])
 #else
 				else if (payloadBytes.Count > 0 && payloadBytes.AsSpan()[0] == ErrorPayload.Signature)
 #endif
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 2b2ce0332..1909c49fc 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -181,7 +181,7 @@ private static RSAParameters GetRsaParameters(ReadOnlySpan<byte> data, bool isPr
 		if (!isPrivate)
 		{
 			// encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
-			ReadOnlySpan<byte> rsaOid = [ 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00 ];
+			ReadOnlySpan<byte> rsaOid = [0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00];
 			if (!data.Slice(0, rsaOid.Length).SequenceEqual(rsaOid))
 				throw new FormatException($"Expected RSA OID but read {BitConverter.ToString(data.Slice(0, 15).ToArray())}");
 			data = data.Slice(rsaOid.Length);
@@ -404,7 +404,7 @@ public static TimeSpan ParseTimeSpan(ReadOnlySpan<byte> value)
 
 		// parse (optional) leading minus sign
 		var isNegative = false;
-		if (value is [ 0x2D, .. ])
+		if (value is [0x2D, ..])
 		{
 			isNegative = true;
 			value = value[1..];
@@ -474,13 +474,13 @@ public static bool TryComputeHash(this HashAlgorithm hashAlgorithm, ReadOnlySpan
 
 	public static byte[] TrimZeroByte(byte[] value)
 	{
-		if (value is [ .., 0 ])
+		if (value is [.., 0])
 			Array.Resize(ref value, value.Length - 1);
 		return value;
 	}
 
 	public static ReadOnlySpan<byte> TrimZeroByte(ReadOnlySpan<byte> value) =>
-		value is [ .., 0 ] ? value[..^1] : value;
+		value is [.., 0] ? value[..^1] : value;
 
 #if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
 	public static int Read(this Stream stream, Memory<byte> buffer)
@@ -656,7 +656,7 @@ private static bool TryReadAsnLength(ReadOnlySpan<byte> data, out int length, ou
 	private static bool TryReadAsnInteger(ReadOnlySpan<byte> data, out ReadOnlySpan<byte> number, out int bytesConsumed)
 	{
 		// integer tag is 2
-		if (data is not [ 0x02, .. ])
+		if (data is not [0x02, ..])
 		{
 			number = default;
 			bytesConsumed = 0;
@@ -677,7 +677,7 @@ private static bool TryReadAsnInteger(ReadOnlySpan<byte> data, out ReadOnlySpan<
 		bytesConsumed = lengthBytesConsumed + length + 1;
 
 		// trim leading zero bytes
-		while (number is [ 0, _, .. ])
+		while (number is [0, _, ..])
 			number = number[1..];
 
 		return true;

From babef3bfdf7527a35c7ca86596fca35a2b171400 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 14 Jan 2024 06:08:48 -0800
Subject: [PATCH 251/393] Fix whitespace and indentation formatting errors.

---
 src/MySqlConnector/Core/MetricsReporter.cs    |   2 +-
 .../Protocol/Serialization/ByteArrayReader.cs |   2 +-
 tests/Conformance.Tests/DbFactoryFixture.cs   |  14 +-
 tests/IntegrationTests/BulkLoaderSync.cs      |  42 ++--
 tests/IntegrationTests/CommandTests.cs        |   2 +-
 tests/IntegrationTests/DataTypes.cs           |  26 +--
 tests/IntegrationTests/InsertTests.cs         |   2 +-
 tests/IntegrationTests/LoadDataInfileAsync.cs |   3 +-
 tests/IntegrationTests/LoadDataInfileSync.cs  |   6 +-
 .../IntegrationTests/MySqlDataSourceTests.cs  |  18 +-
 .../IntegrationTests/PreparedCommandTests.cs  |   2 +-
 tests/IntegrationTests/SchemaProviderTests.cs |  18 +-
 .../IntegrationTests/StoredProcedureTests.cs  |  22 +-
 tests/IntegrationTests/TestUtilities.cs       |  18 +-
 tests/IntegrationTests/Transaction.cs         |   2 +-
 .../MySqlConnector.Tests/CancellationTests.cs |   2 +-
 .../FakeMySqlServerConnection.cs              | 194 +++++++++---------
 .../Metrics/ConnectionsUsageTests.cs          |  22 +-
 .../Metrics/IConnectionCreator.cs             |  10 +-
 .../MySqlConnector.Tests/MySqlDecimalTests.cs |   2 +-
 tests/MySqlConnector.Tests/TypeMapperTests.cs |  24 +--
 21 files changed, 218 insertions(+), 215 deletions(-)

diff --git a/src/MySqlConnector/Core/MetricsReporter.cs b/src/MySqlConnector/Core/MetricsReporter.cs
index 800b43a2e..f5556b614 100644
--- a/src/MySqlConnector/Core/MetricsReporter.cs
+++ b/src/MySqlConnector/Core/MetricsReporter.cs
@@ -50,7 +50,7 @@ static IEnumerable<Measurement<int>> GetMinimumConnections() =>
 	private static readonly UpDownCounter<int> s_pendingRequestsCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.pending_requests",
 			unit: "{request}", description: "The number of pending requests for an open connection, cumulative for the entire pool.");
 	private static readonly Counter<int> s_connectionTimeouts = ActivitySourceHelper.Meter.CreateCounter<int>("db.client.connections.timeouts",
-            unit: "{timeout}", description: "The number of connection timeouts that have occurred trying to obtain a connection from the pool.");
+			unit: "{timeout}", description: "The number of connection timeouts that have occurred trying to obtain a connection from the pool.");
 	private static readonly Histogram<double> s_createTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<double>("db.client.connections.create_time",
 			unit: "s", description: "The time it took to create a new connection.");
 	private static readonly Histogram<double> s_useTimeHistory = ActivitySourceHelper.Meter.CreateHistogram<double>("db.client.connections.use_time",
diff --git a/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs b/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs
index 5d15150ec..080cb8555 100644
--- a/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ByteArrayReader.cs
@@ -23,7 +23,7 @@ public int Offset
 			if (value < 0 || value > m_maxOffset)
 				throw new ArgumentOutOfRangeException(nameof(value), $"value must be between 0 and {m_maxOffset:d}");
 #endif
-			m_offset = value; 
+			m_offset = value;
 		}
 	}
 
diff --git a/tests/Conformance.Tests/DbFactoryFixture.cs b/tests/Conformance.Tests/DbFactoryFixture.cs
index d52ea741b..4ecaaa8af 100644
--- a/tests/Conformance.Tests/DbFactoryFixture.cs
+++ b/tests/Conformance.Tests/DbFactoryFixture.cs
@@ -6,12 +6,12 @@
 namespace Conformance.Tests;
 
 public class DbFactoryFixture : IDbFactoryFixture
+{
+	public DbFactoryFixture()
 	{
-		public DbFactoryFixture()
-		{
-			ConnectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING") ?? "Server=localhost;User Id=mysqltest;Password=test;SSL Mode=None;AllowPublicKeyRetrieval=true";
-		}
-
-		public string ConnectionString { get; }
-		public DbProviderFactory Factory => MySqlConnectorFactory.Instance;
+		ConnectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING") ?? "Server=localhost;User Id=mysqltest;Password=test;SSL Mode=None;AllowPublicKeyRetrieval=true";
 	}
+
+	public string ConnectionString { get; }
+	public DbProviderFactory Factory => MySqlConnectorFactory.Instance;
+}
diff --git a/tests/IntegrationTests/BulkLoaderSync.cs b/tests/IntegrationTests/BulkLoaderSync.cs
index 7c40a36bc..73b81bce7 100644
--- a/tests/IntegrationTests/BulkLoaderSync.cs
+++ b/tests/IntegrationTests/BulkLoaderSync.cs
@@ -1243,26 +1243,26 @@ public void BulkCopyDataTableConflictOption(MySqlBulkLoaderConflictOption confli
 
 		switch (conflictOption)
 		{
-		case MySqlBulkLoaderConflictOption.None:
-			var exception = Assert.Throws<MySqlException>(() => bulkCopy.WriteToServer(dataTable));
-			Assert.Equal(MySqlErrorCode.BulkCopyFailed, exception.ErrorCode);
-			break;
-
-		case MySqlBulkLoaderConflictOption.Replace:
-			var replaceResult = bulkCopy.WriteToServer(dataTable);
-			Assert.Equal(expectedRowsInserted, replaceResult.RowsInserted);
-			Assert.Empty(replaceResult.Warnings);
-			break;
-
-		case MySqlBulkLoaderConflictOption.Ignore:
-			var ignoreResult = bulkCopy.WriteToServer(dataTable);
-			Assert.Equal(expectedRowsInserted, ignoreResult.RowsInserted);
-			if (!connection.ServerVersion.StartsWith("5.6.", StringComparison.Ordinal))
-			{
-				var error = Assert.Single(ignoreResult.Warnings);
-				Assert.Equal(MySqlErrorCode.DuplicateKeyEntry, error.ErrorCode);
-			}
-			break;
+			case MySqlBulkLoaderConflictOption.None:
+				var exception = Assert.Throws<MySqlException>(() => bulkCopy.WriteToServer(dataTable));
+				Assert.Equal(MySqlErrorCode.BulkCopyFailed, exception.ErrorCode);
+				break;
+
+			case MySqlBulkLoaderConflictOption.Replace:
+				var replaceResult = bulkCopy.WriteToServer(dataTable);
+				Assert.Equal(expectedRowsInserted, replaceResult.RowsInserted);
+				Assert.Empty(replaceResult.Warnings);
+				break;
+
+			case MySqlBulkLoaderConflictOption.Ignore:
+				var ignoreResult = bulkCopy.WriteToServer(dataTable);
+				Assert.Equal(expectedRowsInserted, ignoreResult.RowsInserted);
+				if (!connection.ServerVersion.StartsWith("5.6.", StringComparison.Ordinal))
+				{
+					var error = Assert.Single(ignoreResult.Warnings);
+					Assert.Equal(MySqlErrorCode.DuplicateKeyEntry, error.ErrorCode);
+				}
+				break;
 		}
 
 		using (var cmd = new MySqlCommand("select b from bulk_load_data_table;", connection))
@@ -1270,7 +1270,7 @@ public void BulkCopyDataTableConflictOption(MySqlBulkLoaderConflictOption confli
 	}
 #endif
 
-		internal static string GetConnectionString() => AppConfig.ConnectionString;
+	internal static string GetConnectionString() => AppConfig.ConnectionString;
 
 	internal static string GetLocalConnectionString()
 	{
diff --git a/tests/IntegrationTests/CommandTests.cs b/tests/IntegrationTests/CommandTests.cs
index bec7385d9..c3f0556d3 100644
--- a/tests/IntegrationTests/CommandTests.cs
+++ b/tests/IntegrationTests/CommandTests.cs
@@ -226,7 +226,7 @@ public void ExecuteNonQueryForSelectReturnsNegativeOne()
 		Assert.Equal(-1, command.ExecuteNonQuery());
 	}
 
-[Fact]
+	[Fact]
 	public async Task ExecuteNonQueryReturnValue()
 	{
 		using var connection = new MySqlConnection(m_database.Connection.ConnectionString);
diff --git a/tests/IntegrationTests/DataTypes.cs b/tests/IntegrationTests/DataTypes.cs
index 9569d1b7e..a25cdb9a6 100644
--- a/tests/IntegrationTests/DataTypes.cs
+++ b/tests/IntegrationTests/DataTypes.cs
@@ -155,19 +155,19 @@ private async Task DoGetValue<T>(string column, Func<MySqlDataReader, int, T> ge
 			Assert.True(await reader.ReadAsync().ConfigureAwait(false));
 			switch (flags[i])
 			{
-			case 0: // normal
-				Assert.Equal(values[i], getInt(reader, 0));
-				Assert.Equal(values[i], getIntByName(reader, column));
-				break;
-
-			case 1: // null
-				Assert.True(await reader.IsDBNullAsync(0).ConfigureAwait(false));
-				break;
-
-			case 2: // overflow
-				Assert.Throws<OverflowException>(() => getInt(reader, 0));
-				Assert.Throws<OverflowException>(() => getIntByName(reader, column));
-				break;
+				case 0: // normal
+					Assert.Equal(values[i], getInt(reader, 0));
+					Assert.Equal(values[i], getIntByName(reader, column));
+					break;
+
+				case 1: // null
+					Assert.True(await reader.IsDBNullAsync(0).ConfigureAwait(false));
+					break;
+
+				case 2: // overflow
+					Assert.Throws<OverflowException>(() => getInt(reader, 0));
+					Assert.Throws<OverflowException>(() => getIntByName(reader, column));
+					break;
 			}
 		}
 		Assert.False(await reader.ReadAsync().ConfigureAwait(false));
diff --git a/tests/IntegrationTests/InsertTests.cs b/tests/IntegrationTests/InsertTests.cs
index 573d63962..8669836db 100644
--- a/tests/IntegrationTests/InsertTests.cs
+++ b/tests/IntegrationTests/InsertTests.cs
@@ -778,7 +778,7 @@ value mediumblob null
 				cmd.Prepare();
 			cmd.ExecuteNonQuery();
 		}
-		Assert.Equal(new byte[] { 1, 0, 2, 39, 3, 92, 4, 34, 5, 6  }, connection.Query<byte[]>(@"select value from insert_mysql_blob;").Single());
+		Assert.Equal(new byte[] { 1, 0, 2, 39, 3, 92, 4, 34, 5, 6 }, connection.Query<byte[]>(@"select value from insert_mysql_blob;").Single());
 	}
 
 	public static IEnumerable<object[]> GetBlobs()
diff --git a/tests/IntegrationTests/LoadDataInfileAsync.cs b/tests/IntegrationTests/LoadDataInfileAsync.cs
index 52bd089f0..5d88d032b 100644
--- a/tests/IntegrationTests/LoadDataInfileAsync.cs
+++ b/tests/IntegrationTests/LoadDataInfileAsync.cs
@@ -30,7 +30,8 @@ public async Task CommandLoadCsvFile()
 	{
 		var insertInlineCommand = string.Format(m_loadDataInfileCommand, "", AppConfig.MySqlBulkLoaderCsvFile.Replace("\\", "\\\\"));
 		using var command = new MySqlCommand(insertInlineCommand, m_database.Connection);
-		if (m_database.Connection.State != ConnectionState.Open) await m_database.Connection.OpenAsync();
+		if (m_database.Connection.State != ConnectionState.Open)
+			await m_database.Connection.OpenAsync();
 		var rowCount = await command.ExecuteNonQueryAsync();
 		m_database.Connection.Close();
 		Assert.Equal(20, rowCount);
diff --git a/tests/IntegrationTests/LoadDataInfileSync.cs b/tests/IntegrationTests/LoadDataInfileSync.cs
index 2eee12ffe..e59e7b1d1 100644
--- a/tests/IntegrationTests/LoadDataInfileSync.cs
+++ b/tests/IntegrationTests/LoadDataInfileSync.cs
@@ -30,7 +30,8 @@ public void CommandLoadCsvFile()
 	{
 		var insertInlineCommand = string.Format(m_loadDataInfileCommand, "", AppConfig.MySqlBulkLoaderCsvFile.Replace("\\", "\\\\"));
 		using var command = new MySqlCommand(insertInlineCommand, m_database.Connection);
-		if (m_database.Connection.State != ConnectionState.Open) m_database.Connection.Open();
+		if (m_database.Connection.State != ConnectionState.Open)
+			m_database.Connection.Open();
 		var rowCount = command.ExecuteNonQuery();
 		m_database.Connection.Close();
 		Assert.Equal(20, rowCount);
@@ -41,7 +42,8 @@ public void CommandLoadLocalCsvFile()
 	{
 		var insertInlineCommand = string.Format(m_loadDataInfileCommand, " LOCAL", AppConfig.MySqlBulkLoaderLocalCsvFile.Replace("\\", "\\\\"));
 		using var command = new MySqlCommand(insertInlineCommand, m_database.Connection);
-		if (m_database.Connection.State != ConnectionState.Open) m_database.Connection.Open();
+		if (m_database.Connection.State != ConnectionState.Open)
+			m_database.Connection.Open();
 		var rowCount = command.ExecuteNonQuery();
 		m_database.Connection.Close();
 		Assert.Equal(20, rowCount);
diff --git a/tests/IntegrationTests/MySqlDataSourceTests.cs b/tests/IntegrationTests/MySqlDataSourceTests.cs
index af881033b..dcd8d7f2e 100644
--- a/tests/IntegrationTests/MySqlDataSourceTests.cs
+++ b/tests/IntegrationTests/MySqlDataSourceTests.cs
@@ -25,17 +25,17 @@ public void OpenConnection()
 		Assert.Equal(ConnectionState.Open, connection.State);
 	}
 
-    [Fact]
-    public void DoubleDispose()
-    {
-        using var dbSource = new MySqlDataSource(AppConfig.ConnectionString);
+	[Fact]
+	public void DoubleDispose()
+	{
+		using var dbSource = new MySqlDataSource(AppConfig.ConnectionString);
 		using (var connection = dbSource.OpenConnection())
 		{
-            Assert.Equal(ConnectionState.Open, connection.State);
-        }
+			Assert.Equal(ConnectionState.Open, connection.State);
+		}
 		dbSource.Dispose();
-    }
-    
+	}
+
 	[Fact]
 	public async Task OpenConnectionAsync()
 	{
@@ -224,7 +224,7 @@ public void PasswordProviderFailureIsRetried()
 		var exception = Assert.Throws<MySqlException>(dataSource.OpenConnection); // Failed to obtain password via ProvidePasswordCallback
 		Assert.IsType<MySqlException>(exception.InnerException); // The periodic password provider failed
 		Assert.IsType<ApplicationException>(exception.InnerException.InnerException); // First-time failure
-			
+
 		// succeeds after failure retry
 		barrier.SignalAndWait();
 		Thread.Sleep(10);
diff --git a/tests/IntegrationTests/PreparedCommandTests.cs b/tests/IntegrationTests/PreparedCommandTests.cs
index 0cdbee401..d087072ad 100644
--- a/tests/IntegrationTests/PreparedCommandTests.cs
+++ b/tests/IntegrationTests/PreparedCommandTests.cs
@@ -488,7 +488,7 @@ private enum TwoByteEnum : short
 	{
 		Value = 111,
 	}
-		
+
 
 	private static MySqlConnection CreateConnection()
 	{
diff --git a/tests/IntegrationTests/SchemaProviderTests.cs b/tests/IntegrationTests/SchemaProviderTests.cs
index f17687585..023dcd363 100644
--- a/tests/IntegrationTests/SchemaProviderTests.cs
+++ b/tests/IntegrationTests/SchemaProviderTests.cs
@@ -183,12 +183,12 @@ public void GetCollationsSchema()
 
 	[Fact]
 	public void ForeignKeys()
-    {
-        var schemaName = m_database.Connection.Database;
-        var table = m_database.Connection.GetSchema("Foreign Keys", new[] { null, schemaName, "fk_test" });
-        var row = table.Rows.Cast<DataRow>().Single();
-        foreach (var (column, value) in new[]
-        {
+	{
+		var schemaName = m_database.Connection.Database;
+		var table = m_database.Connection.GetSchema("Foreign Keys", new[] { null, schemaName, "fk_test" });
+		var row = table.Rows.Cast<DataRow>().Single();
+		foreach (var (column, value) in new[]
+		{
 			("CONSTRAINT_CATALOG", "def"),
 			("CONSTRAINT_SCHEMA", schemaName),
 			("CONSTRAINT_NAME", "fk_test_fk"),
@@ -200,10 +200,10 @@ public void ForeignKeys()
 			("REFERENCED_TABLE_SCHEMA", schemaName),
 			("REFERENCED_TABLE_NAME", "pk_test"),
 		})
-        {
+		{
 			Assert.Equal(value, row[column] is DBNull ? null : (string?) row[column]);
-        }
-    }
+		}
+	}
 
 	[Fact]
 	public void Indexes()
diff --git a/tests/IntegrationTests/StoredProcedureTests.cs b/tests/IntegrationTests/StoredProcedureTests.cs
index e74e1defb..68b7f4b1f 100644
--- a/tests/IntegrationTests/StoredProcedureTests.cs
+++ b/tests/IntegrationTests/StoredProcedureTests.cs
@@ -443,18 +443,18 @@ private async Task<object> ExecuteCommandAsync(DbCommand cmd, string executorTyp
 	{
 		switch (executorType)
 		{
-		case "NonQuery":
-			await cmd.ExecuteNonQueryAsync();
-			return null;
-		case "Scalar":
-			return await cmd.ExecuteScalarAsync();
-		default:
-			using (var reader = await cmd.ExecuteReaderAsync())
-			{
-				if (await reader.ReadAsync())
-					return reader.GetValue(0);
+			case "NonQuery":
+				await cmd.ExecuteNonQueryAsync();
 				return null;
-			}
+			case "Scalar":
+				return await cmd.ExecuteScalarAsync();
+			default:
+				using (var reader = await cmd.ExecuteReaderAsync())
+				{
+					if (await reader.ReadAsync())
+						return reader.GetValue(0);
+					return null;
+				}
 		}
 	}
 
diff --git a/tests/IntegrationTests/TestUtilities.cs b/tests/IntegrationTests/TestUtilities.cs
index 91bf89c9b..737c9a9a4 100644
--- a/tests/IntegrationTests/TestUtilities.cs
+++ b/tests/IntegrationTests/TestUtilities.cs
@@ -24,17 +24,17 @@ public static void AssertIsOne(object value)
 	{
 		switch (value)
 		{
-		case int intValue:
-			Assert.Equal(1, intValue);
-			break;
+			case int intValue:
+				Assert.Equal(1, intValue);
+				break;
 
-		case long longValue:
-			Assert.Equal(1L, longValue);
-			break;
+			case long longValue:
+				Assert.Equal(1L, longValue);
+				break;
 
-		default:
-			Assert.Equal(1L, value);
-			break;
+			default:
+				Assert.Equal(1L, value);
+				break;
 		}
 	}
 
diff --git a/tests/IntegrationTests/Transaction.cs b/tests/IntegrationTests/Transaction.cs
index f1f2abdcf..7a22b96f5 100644
--- a/tests/IntegrationTests/Transaction.cs
+++ b/tests/IntegrationTests/Transaction.cs
@@ -399,7 +399,7 @@ public void TransactionHoldsLocks()
 				""";
 			command.ExecuteNonQuery();
 		}
-		
+
 		using var barrier = new Barrier(2);
 		using var barrier2 = new Barrier(2);
 
diff --git a/tests/MySqlConnector.Tests/CancellationTests.cs b/tests/MySqlConnector.Tests/CancellationTests.cs
index aa5bc8af2..3e2c16ed7 100644
--- a/tests/MySqlConnector.Tests/CancellationTests.cs
+++ b/tests/MySqlConnector.Tests/CancellationTests.cs
@@ -400,7 +400,7 @@ public async Task ExecuteAsync(int step, int method)
 
 	public static IEnumerable<object[]> GetSyncMethodSteps()
 	{
-		for (var step = 1; step <=  12; step++)
+		for (var step = 1; step <= 12; step++)
 		{
 			for (var method = 0; method < s_executeMethods.Length; method++)
 			{
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
index cf7441dd0..cb57cfb3d 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
@@ -54,59 +54,59 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 
 					switch ((CommandKind) bytes[0])
 					{
-					case CommandKind.Quit:
-						await SendAsync(stream, 1, WriteOk);
-						keepRunning = false;
-						break;
-
-					case CommandKind.Ping:
-						await SendAsync(stream, 1, WriteOk);
-						break;
+						case CommandKind.Quit:
+							await SendAsync(stream, 1, WriteOk);
+							keepRunning = false;
+							break;
 
-					case CommandKind.ResetConnection:
-						if (m_server.ResetDelay is { } resetDelay)
-							await Task.Delay(resetDelay);
-						await SendAsync(stream, 1, WriteOk);
-						break;
+						case CommandKind.Ping:
+							await SendAsync(stream, 1, WriteOk);
+							break;
 
-					case CommandKind.Query:
-						var query = Encoding.UTF8.GetString(bytes, 1, bytes.Length - 1);
-						Match match;
-						if (query == "SET NAMES utf8mb4;")
-						{
+						case CommandKind.ResetConnection:
+							if (m_server.ResetDelay is { } resetDelay)
+								await Task.Delay(resetDelay);
 							await SendAsync(stream, 1, WriteOk);
-						}
-						else if ((match = Regex.Match(query, @"^SELECT ([0-9])(;|$)")).Success)
-						{
-							var number = match.Groups[1].Value;
-							var data = new byte[number.Length + 1];
-							data[0] = (byte) number.Length;
-							Encoding.UTF8.GetBytes(number, 0, number.Length, data, 1);
+							break;
 
-							await SendAsync(stream, 1, x => x.Write((byte) 1)); // one column
-							await SendAsync(stream, 2, x => x.Write(new byte[] { 3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0 })); // column definition
-							await SendAsync(stream, 3, x => x.Write(new byte[] { 0xFE, 0, 0, 2, 0 })); // EOF
-							await SendAsync(stream, 4, x => x.Write(data));
-							await SendAsync(stream, 5, x => x.Write(new byte[] { 0xFE, 0, 0, 2, 0 })); // EOF
-						}
-						else if ((match = Regex.Match(query, @"^SELECT ([0-9]+), ([0-9]+), ([0-9-]+), ([0-9]+)(;|$)")).Success)
-						{
-							// command is "SELECT {value}, {delay}, {pauseStep}, {flags}"
-							var number = match.Groups[1].Value;
-							var value = int.Parse(number);
-							var delay = int.Parse(match.Groups[2].Value);
-							var pauseStep = int.Parse(match.Groups[3].Value);
-							var flags = int.Parse(match.Groups[4].Value);
-							var ignoreCancellation = (flags & 1) == 1;
-							var bufferOutput = (flags & 2) == 2;
+						case CommandKind.Query:
+							var query = Encoding.UTF8.GetString(bytes, 1, bytes.Length - 1);
+							Match match;
+							if (query == "SET NAMES utf8mb4;")
+							{
+								await SendAsync(stream, 1, WriteOk);
+							}
+							else if ((match = Regex.Match(query, @"^SELECT ([0-9])(;|$)")).Success)
+							{
+								var number = match.Groups[1].Value;
+								var data = new byte[number.Length + 1];
+								data[0] = (byte) number.Length;
+								Encoding.UTF8.GetBytes(number, 0, number.Length, data, 1);
 
-							var data = new byte[number.Length + 1];
-							data[0] = (byte) number.Length;
-							Encoding.UTF8.GetBytes(number, 0, number.Length, data, 1);
-							
-							var negativeOne = new byte[] { 2, 0x2D, 0x31 };
-							var packets = new[]
+								await SendAsync(stream, 1, x => x.Write((byte) 1)); // one column
+								await SendAsync(stream, 2, x => x.Write(new byte[] { 3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0 })); // column definition
+								await SendAsync(stream, 3, x => x.Write(new byte[] { 0xFE, 0, 0, 2, 0 })); // EOF
+								await SendAsync(stream, 4, x => x.Write(data));
+								await SendAsync(stream, 5, x => x.Write(new byte[] { 0xFE, 0, 0, 2, 0 })); // EOF
+							}
+							else if ((match = Regex.Match(query, @"^SELECT ([0-9]+), ([0-9]+), ([0-9-]+), ([0-9]+)(;|$)")).Success)
 							{
+								// command is "SELECT {value}, {delay}, {pauseStep}, {flags}"
+								var number = match.Groups[1].Value;
+								var value = int.Parse(number);
+								var delay = int.Parse(match.Groups[2].Value);
+								var pauseStep = int.Parse(match.Groups[3].Value);
+								var flags = int.Parse(match.Groups[4].Value);
+								var ignoreCancellation = (flags & 1) == 1;
+								var bufferOutput = (flags & 2) == 2;
+
+								var data = new byte[number.Length + 1];
+								data[0] = (byte) number.Length;
+								Encoding.UTF8.GetBytes(number, 0, number.Length, data, 1);
+
+								var negativeOne = new byte[] { 2, 0x2D, 0x31 };
+								var packets = new[]
+								{
 								new byte[] { 0xFF, 0x25, 0x05, 0x23, 0x37, 0x30, 0x31, 0x30, 0x30 }.Concat(Encoding.ASCII.GetBytes("Query execution was interrupted")).ToArray(), // error
 								new byte[] { 1 }, // one column
 								new byte[] { 3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0 }, // column definition
@@ -122,51 +122,51 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 								new byte[] { 0xFE, 0, 0, 2, 0 }, // EOF
 							};
 
-							if (bufferOutput)
-							{
-								// if 'bufferOutput' is set, perform the delay immediately then send all the output afterwards, as though it were buffered on the server
-								var queryInterrupted = false;
-								if (ignoreCancellation)
-									await Task.Delay(delay, token);
-								else
-									queryInterrupted = CancelQueryEvent.Wait(delay, token);
+								if (bufferOutput)
+								{
+									// if 'bufferOutput' is set, perform the delay immediately then send all the output afterwards, as though it were buffered on the server
+									var queryInterrupted = false;
+									if (ignoreCancellation)
+										await Task.Delay(delay, token);
+									else
+										queryInterrupted = CancelQueryEvent.Wait(delay, token);
 
-								for (var step = 1; step < pauseStep; step++)
-									await SendAsync(stream, step, x => x.Write(packets[step]));
-								await SendAsync(stream, pauseStep, x => x.Write(packets[queryInterrupted ? 0 : pauseStep]));
-							}
-							else
-							{
-								var queryInterrupted = false;
-								for (var step = 1; step < packets.Length && !queryInterrupted; step++)
+									for (var step = 1; step < pauseStep; step++)
+										await SendAsync(stream, step, x => x.Write(packets[step]));
+									await SendAsync(stream, pauseStep, x => x.Write(packets[queryInterrupted ? 0 : pauseStep]));
+								}
+								else
 								{
-									if (pauseStep == step || pauseStep == -1)
+									var queryInterrupted = false;
+									for (var step = 1; step < packets.Length && !queryInterrupted; step++)
 									{
-										if (ignoreCancellation)
-											await Task.Delay(delay, token);
-										else
-											queryInterrupted = CancelQueryEvent.Wait(delay, token);
-									}
+										if (pauseStep == step || pauseStep == -1)
+										{
+											if (ignoreCancellation)
+												await Task.Delay(delay, token);
+											else
+												queryInterrupted = CancelQueryEvent.Wait(delay, token);
+										}
 
-									await SendAsync(stream, step, x => x.Write(packets[queryInterrupted ? 0 : step]));
+										await SendAsync(stream, step, x => x.Write(packets[queryInterrupted ? 0 : step]));
+									}
 								}
 							}
-						}
-						else if ((match = Regex.Match(query, @"^KILL QUERY ([0-9]+)(;|$)", RegexOptions.IgnoreCase)).Success)
-						{
-							var connectionId = int.Parse(match.Groups[1].Value);
-							m_server.CancelQuery(connectionId);
-							await SendAsync(stream, 1, WriteOk);
-						}
-						else if (query == "SELECT SLEEP(0) INTO @\uE001MySqlConnector\uE001Sleep;")
-						{
-							var wasSet = CancelQueryEvent.Wait(0, token);
-							await SendAsync(stream, 1, WriteOk);
-						}
-						else if (query == "select infinity")
-						{
-							var packets = new[]
+							else if ((match = Regex.Match(query, @"^KILL QUERY ([0-9]+)(;|$)", RegexOptions.IgnoreCase)).Success)
+							{
+								var connectionId = int.Parse(match.Groups[1].Value);
+								m_server.CancelQuery(connectionId);
+								await SendAsync(stream, 1, WriteOk);
+							}
+							else if (query == "SELECT SLEEP(0) INTO @\uE001MySqlConnector\uE001Sleep;")
 							{
+								var wasSet = CancelQueryEvent.Wait(0, token);
+								await SendAsync(stream, 1, WriteOk);
+							}
+							else if (query == "select infinity")
+							{
+								var packets = new[]
+								{
 								new byte[] { 2 }, // two columns
 								new byte[] { 3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x46, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 4, 0x01, 0, 0x1F, 0, 0 }, // column definition (float)
 								new byte[] { 3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x44, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 5, 0x01, 0, 0x1F, 0, 0 }, // column definition (double)
@@ -176,19 +176,19 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 								new byte[] { 4, 0x2d, 0x69, 0x6e, 0x66, 4, 0x2d, 0x69, 0x6e, 0x66 }, // -inf
 								new byte[] { 0xFE, 0, 0, 2, 0 }, // EOF
 							};
-							for (var packetIndex = 0; packetIndex < packets.Length; packetIndex++)
-								await SendAsync(stream, packetIndex + 1, x => x.Write(packets[packetIndex]));
-						}
-						else
-						{
-							await SendAsync(stream, 1, x => WriteError(x, "Unhandled query: " + query));
-						}
-						break;
+								for (var packetIndex = 0; packetIndex < packets.Length; packetIndex++)
+									await SendAsync(stream, packetIndex + 1, x => x.Write(packets[packetIndex]));
+							}
+							else
+							{
+								await SendAsync(stream, 1, x => WriteError(x, "Unhandled query: " + query));
+							}
+							break;
 
-					default:
-						Console.WriteLine("** UNHANDLED ** {0}", (CommandKind) bytes[0]);
-						await SendAsync(stream, 1, x => WriteError(x));
-						break;
+						default:
+							Console.WriteLine("** UNHANDLED ** {0}", (CommandKind) bytes[0]);
+							await SendAsync(stream, 1, x => WriteError(x));
+							break;
 					}
 				}
 			}
diff --git a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
index f42020906..a0e6bd8f2 100644
--- a/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
+++ b/tests/MySqlConnector.Tests/Metrics/ConnectionsUsageTests.cs
@@ -4,15 +4,15 @@ namespace MySqlConnector.Tests.Metrics;
 
 public class ConnectionsUsageTests : MetricsTestsBase
 {
-    [Theory(Skip = MetricsSkip)]
+	[Theory(Skip = MetricsSkip)]
 	[InlineData("DataSource|true||")]
 	[InlineData("DataSource|true||app-name")]
-    [InlineData("DataSource|true|pool-name|")]
-    [InlineData("DataSource|true|pool-name|app-name")]
+	[InlineData("DataSource|true|pool-name|")]
+	[InlineData("DataSource|true|pool-name|app-name")]
 	[InlineData("Plain|true|")]
 	[InlineData("Plain|true|app-name")]
 	public void ConnectionsWithPoolsHaveMetrics(string connectionCreatorSpec)
-    {
+	{
 		using var connectionCreator = CreateConnectionCreator(connectionCreatorSpec, CreateConnectionStringBuilder());
 		PoolName = connectionCreator.PoolName;
 
@@ -63,13 +63,13 @@ public void ConnectionsWithPoolsHaveMetrics(string connectionCreatorSpec)
 	}
 
 	[Theory(Skip = MetricsSkip)]
-    [InlineData("DataSource|false||")]
-    [InlineData("DataSource|false||app-name")]
-    [InlineData("DataSource|false|pool-name|")]
-    [InlineData("DataSource|false|pool-name|app-name")]
-    [InlineData("Plain|false|")]
-    [InlineData("Plain|false|app-name")]
-    public void ConnectionsWithoutPoolsHaveNoMetrics(string connectionCreatorSpec)
+	[InlineData("DataSource|false||")]
+	[InlineData("DataSource|false||app-name")]
+	[InlineData("DataSource|false|pool-name|")]
+	[InlineData("DataSource|false|pool-name|app-name")]
+	[InlineData("Plain|false|")]
+	[InlineData("Plain|false|app-name")]
+	public void ConnectionsWithoutPoolsHaveNoMetrics(string connectionCreatorSpec)
 	{
 		using var connectionCreator = CreateConnectionCreator(connectionCreatorSpec, CreateConnectionStringBuilder());
 		PoolName = connectionCreator.PoolName;
diff --git a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
index 56d941650..43874f90c 100644
--- a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
+++ b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
@@ -12,15 +12,15 @@ internal sealed class DataSourceConnectionCreator : IConnectionCreator
 {
 	public DataSourceConnectionCreator(bool usePooling, string? poolName, string? applicationName, MySqlConnectionStringBuilder connectionStringBuilder)
 	{
-        connectionStringBuilder.Pooling =	usePooling;
+		connectionStringBuilder.Pooling = usePooling;
 		connectionStringBuilder.ApplicationName = applicationName;
 		m_dataSource = new MySqlDataSourceBuilder(connectionStringBuilder.ConnectionString)
 			.UseName(poolName)
 			.Build();
-        PoolName = poolName ?? applicationName ?? connectionStringBuilder!.GetConnectionString(includePassword: false);
-    }
+		PoolName = poolName ?? applicationName ?? connectionStringBuilder!.GetConnectionString(includePassword: false);
+	}
 
-    public MySqlConnection OpenConnection() => m_dataSource!.OpenConnection();
+	public MySqlConnection OpenConnection() => m_dataSource!.OpenConnection();
 	public string PoolName { get; }
 	public void Dispose() => m_dataSource!.Dispose();
 
@@ -35,7 +35,7 @@ public PlainConnectionCreator(bool usePooling, string? applicationName, MySqlCon
 		connectionStringBuilder.ApplicationName = applicationName;
 		m_connectionString = connectionStringBuilder.ConnectionString;
 		PoolName = applicationName ?? connectionStringBuilder.GetConnectionString(includePassword: false);
-    }
+	}
 
 	public MySqlConnection OpenConnection()
 	{
diff --git a/tests/MySqlConnector.Tests/MySqlDecimalTests.cs b/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
index 2df97f8a7..2d4bdc093 100644
--- a/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlDecimalTests.cs
@@ -24,7 +24,7 @@ public void TestToDouble()
 	[Fact]
 	public void TestToDecimal()
 	{
-		decimal doubleVal = 1.2M ;
+		decimal doubleVal = 1.2M;
 		string stringVal = "1.2";
 		MySqlDecimal mySqlDecimal = new MySqlDecimal(stringVal);
 		Assert.Equal(doubleVal, mySqlDecimal.Value);
diff --git a/tests/MySqlConnector.Tests/TypeMapperTests.cs b/tests/MySqlConnector.Tests/TypeMapperTests.cs
index 02713033e..6aa1ad6b9 100644
--- a/tests/MySqlConnector.Tests/TypeMapperTests.cs
+++ b/tests/MySqlConnector.Tests/TypeMapperTests.cs
@@ -31,18 +31,18 @@ public void DbTypeMappingTest(Type clrType, DbType dbType)
 
 	[Theory]
 	[InlineData(1, DbType.Boolean, true)]
-	[InlineData(true, DbType.SByte, (sbyte)1)]
-	[InlineData((sbyte)1, DbType.Byte, (byte)1)]
-	[InlineData((byte)1, DbType.Int16, (short)1)]
-	[InlineData((short)1, DbType.UInt16, (ushort)1)]
-	[InlineData((ushort)1, DbType.Int32, 1)]
-	[InlineData(1, DbType.UInt32, (uint)1)]
-	[InlineData((uint)1, DbType.Int64, (long)1)]
-	[InlineData((long)1, DbType.UInt64, (ulong)1)]
-	[InlineData((ulong)1, DbType.String, "1")]
-	[InlineData((ulong)1, DbType.StringFixedLength, "1")]
-	[InlineData((ulong)1, DbType.AnsiString, "1")]
-	[InlineData((ulong)1, DbType.AnsiStringFixedLength, "1")]
+	[InlineData(true, DbType.SByte, (sbyte) 1)]
+	[InlineData((sbyte) 1, DbType.Byte, (byte) 1)]
+	[InlineData((byte) 1, DbType.Int16, (short) 1)]
+	[InlineData((short) 1, DbType.UInt16, (ushort) 1)]
+	[InlineData((ushort) 1, DbType.Int32, 1)]
+	[InlineData(1, DbType.UInt32, (uint) 1)]
+	[InlineData((uint) 1, DbType.Int64, (long) 1)]
+	[InlineData((long) 1, DbType.UInt64, (ulong) 1)]
+	[InlineData((ulong) 1, DbType.String, "1")]
+	[InlineData((ulong) 1, DbType.StringFixedLength, "1")]
+	[InlineData((ulong) 1, DbType.AnsiString, "1")]
+	[InlineData((ulong) 1, DbType.AnsiStringFixedLength, "1")]
 	public void ConversionTest(object original, DbType dbType, object expected)
 	{
 		Assert.Equal(expected, TypeMapper.Instance.GetDbTypeMapping(dbType).DoConversion(original));

From 9adbea28362d8f707f0a533e92577fa17f46d58b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 14 Jan 2024 07:21:57 -0800
Subject: [PATCH 252/393] Update test packages.

---
 Directory.Packages.props | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0db881d7f..c857d3db5 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,7 +9,7 @@
     <PackageVersion Include="Dapper.StrongName" Version="2.1.24" />
     <PackageVersion Include="IndexRange" Version="1.0.2" />
     <PackageVersion Include="log4net" Version="2.0.10" />
-    <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
@@ -22,7 +22,7 @@
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageVersion Include="xunit" Version="2.6.4" />
+    <PackageVersion Include="xunit" Version="2.6.5" />
     <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6" />
     <PackageVersion Include="YamlDotNet" Version="13.7.1" />
   </ItemGroup>

From 0cad51cbd5a4b684c247e6189d06f445c1554e99 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 14 Jan 2024 07:28:08 -0800
Subject: [PATCH 253/393] Release 2.3.4.

---
 docs/content/connection-options.md       |  2 +-
 docs/content/diagnostics/metrics.md      |  1 +
 docs/content/overview/version-history.md | 10 +++++++++-
 3 files changed, 11 insertions(+), 2 deletions(-)

diff --git a/docs/content/connection-options.md b/docs/content/connection-options.md
index 96f53260d..2961f5da4 100644
--- a/docs/content/connection-options.md
+++ b/docs/content/connection-options.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-04-15
+lastmod: 2024-01-14
 date: 2016-10-16
 title: Connection Options
 customtitle: MySQL Connection String Options for .NET/C#
diff --git a/docs/content/diagnostics/metrics.md b/docs/content/diagnostics/metrics.md
index c4fcbcb6a..b916991ed 100644
--- a/docs/content/diagnostics/metrics.md
+++ b/docs/content/diagnostics/metrics.md
@@ -1,4 +1,5 @@
 ---
+lastmod: 2024-01-14
 date: 2023-11-11
 menu:
   main:
diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 07cffb6bb..143ee6ad7 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-12-19
+lastmod: 2024-01-14
 date: 2017-03-27
 menu:
   main:
@@ -11,6 +11,14 @@ weight: 30
 
 # Version History
 
+### 2.3.4
+
+* Unify `Application Name` connection string option and `MySqlDataSourceBuilder.UseName`: [#1434](https://github.com/mysql-net/MySqlConnector/issues/1434).
+* Remove TLS version fallback code: [#1409](https://github.com/mysql-net/MySqlConnector/issues/1409).
+* Fix `ArgumentOutOfRangeException` in `TimerQueue.Add`: [#1424](https://github.com/mysql-net/MySqlConnector/issues/1424).
+* Fix `InvalidCastException` when using stored procedures with MySQL 5.0: [#1431](https://github.com/mysql-net/MySqlConnector/issues/1431).
+* Fix hang when disposing `MySqlDataSource` twice on .NET Framework.
+
 ### 2.3.3
 
 * Support `COLUMN_NAME` restriction for `IndexColumns` schema; this improves NHibernate compatibility.

From d466ca6033dde6dd83235f9ed7f5b7586741988b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 14 Jan 2024 07:37:02 -0800
Subject: [PATCH 254/393] Thank contributor in v2.3.4 release notes.

---
 docs/content/overview/version-history.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 143ee6ad7..2e00a8e8d 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -18,6 +18,7 @@ weight: 30
 * Fix `ArgumentOutOfRangeException` in `TimerQueue.Add`: [#1424](https://github.com/mysql-net/MySqlConnector/issues/1424).
 * Fix `InvalidCastException` when using stored procedures with MySQL 5.0: [#1431](https://github.com/mysql-net/MySqlConnector/issues/1431).
 * Fix hang when disposing `MySqlDataSource` twice on .NET Framework.
+* Thanks to [Yong Yao](https://github.com/SolacomYongYao) for contributions to this release.
 
 ### 2.3.3
 

From 569524889203494dd43ad495d4fe6f137cda84ef Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 14 Jan 2024 16:03:39 -0800
Subject: [PATCH 255/393] Remove unnecessary null-forgiving operator in tests.

---
 tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
index 43874f90c..8bc870e27 100644
--- a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
+++ b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
@@ -17,12 +17,12 @@ public DataSourceConnectionCreator(bool usePooling, string? poolName, string? ap
 		m_dataSource = new MySqlDataSourceBuilder(connectionStringBuilder.ConnectionString)
 			.UseName(poolName)
 			.Build();
-		PoolName = poolName ?? applicationName ?? connectionStringBuilder!.GetConnectionString(includePassword: false);
+		PoolName = poolName ?? applicationName ?? connectionStringBuilder.GetConnectionString(includePassword: false);
 	}
 
-	public MySqlConnection OpenConnection() => m_dataSource!.OpenConnection();
+	public MySqlConnection OpenConnection() => m_dataSource.OpenConnection();
 	public string PoolName { get; }
-	public void Dispose() => m_dataSource!.Dispose();
+	public void Dispose() => m_dataSource.Dispose();
 
 	private readonly MySqlDataSource m_dataSource;
 }

From d4a5939c84abd61ffde92e8929083bc5ae250ef8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 14 Jan 2024 16:49:31 -0800
Subject: [PATCH 256/393] Use more static lambdas.

---
 .../MySqlConnectorServiceCollectionExtensions.cs     | 12 ++++++------
 src/MySqlConnector/Core/MetricsReporter.cs           |  4 ++--
 src/MySqlConnector/Core/SchemaProvider.cs            |  2 +-
 .../Core/SingleCommandPayloadCreator.cs              |  2 +-
 src/MySqlConnector/MySqlConnectionStringBuilder.cs   |  4 ++--
 5 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs b/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
index f00ff40db..9509902f2 100644
--- a/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
+++ b/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
@@ -100,13 +100,13 @@ private static IServiceCollection DoAddMySqlDataSource(
 				},
 				dataSourceLifetime));
 
-		serviceCollection.TryAdd(new ServiceDescriptor(typeof(MySqlConnection), x => x.GetRequiredService<MySqlDataSource>().CreateConnection(), connectionLifetime));
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(MySqlConnection), static x => x.GetRequiredService<MySqlDataSource>().CreateConnection(), connectionLifetime));
 
 #if NET7_0_OR_GREATER
-		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbDataSource), x => x.GetRequiredService<MySqlDataSource>(), dataSourceLifetime));
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbDataSource), static x => x.GetRequiredService<MySqlDataSource>(), dataSourceLifetime));
 #endif
 
-		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbConnection), x => x.GetRequiredService<MySqlConnection>(), connectionLifetime));
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbConnection), static x => x.GetRequiredService<MySqlConnection>(), connectionLifetime));
 
 		return serviceCollection;
 	}
@@ -133,13 +133,13 @@ private static IServiceCollection DoAddMySqlDataSource(
 				},
 				dataSourceLifetime));
 
-		serviceCollection.TryAdd(new ServiceDescriptor(typeof(MySqlConnection), serviceKey, (sp, sk) => sp.GetRequiredKeyedService<MySqlDataSource>(sk).CreateConnection(), connectionLifetime));
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(MySqlConnection), serviceKey, static (sp, sk) => sp.GetRequiredKeyedService<MySqlDataSource>(sk).CreateConnection(), connectionLifetime));
 
 #if NET7_0_OR_GREATER
-		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbDataSource), serviceKey, (sp, sk) => sp.GetRequiredKeyedService<MySqlDataSource>(sk), dataSourceLifetime));
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbDataSource), serviceKey, static (sp, sk) => sp.GetRequiredKeyedService<MySqlDataSource>(sk), dataSourceLifetime));
 #endif
 
-		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbConnection), serviceKey, (sp, sk) => sp.GetRequiredKeyedService<MySqlConnection>(sk), connectionLifetime));
+		serviceCollection.TryAdd(new ServiceDescriptor(typeof(DbConnection), serviceKey, static (sp, sk) => sp.GetRequiredKeyedService<MySqlConnection>(sk), connectionLifetime));
 
 		return serviceCollection;
 	}
diff --git a/src/MySqlConnector/Core/MetricsReporter.cs b/src/MySqlConnector/Core/MetricsReporter.cs
index f5556b614..e4720f8d5 100644
--- a/src/MySqlConnector/Core/MetricsReporter.cs
+++ b/src/MySqlConnector/Core/MetricsReporter.cs
@@ -39,10 +39,10 @@ static MetricsReporter()
 			description: "The maximum number of open connections allowed; this corresponds to MaximumPoolSize in the connection string.");
 
 		static IEnumerable<Measurement<int>> GetMaximumConnections() =>
-			ConnectionPool.GetAllPools().Select(x => new Measurement<int>(x.ConnectionSettings.MaximumPoolSize, x.PoolNameTagList));
+			ConnectionPool.GetAllPools().Select(static x => new Measurement<int>(x.ConnectionSettings.MaximumPoolSize, x.PoolNameTagList));
 
 		static IEnumerable<Measurement<int>> GetMinimumConnections() =>
-			ConnectionPool.GetAllPools().Select(x => new Measurement<int>(x.ConnectionSettings.MinimumPoolSize, x.PoolNameTagList));
+			ConnectionPool.GetAllPools().Select(static x => new Measurement<int>(x.ConnectionSettings.MinimumPoolSize, x.PoolNameTagList));
 	}
 
 	private static readonly UpDownCounter<int> s_connectionsUsageCounter = ActivitySourceHelper.Meter.CreateUpDownCounter<int>("db.client.connections.usage",
diff --git a/src/MySqlConnector/Core/SchemaProvider.cs b/src/MySqlConnector/Core/SchemaProvider.cs
index e3f843269..a9c2fc7e6 100644
--- a/src/MySqlConnector/Core/SchemaProvider.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.cs
@@ -391,7 +391,7 @@ await FillDataTableAsync(ioBehavior, dataTable, command =>
 #pragma warning restore CA2100
 			if (columns is { Count: > 0 })
 			{
-				command.CommandText += " WHERE " + string.Join(" AND ", columns.Select(x => $@"{x.Key} = @{x.Key}"));
+				command.CommandText += " WHERE " + string.Join(" AND ", columns.Select(static x => $@"{x.Key} = @{x.Key}"));
 				foreach (var column in columns)
 					command.Parameters.AddWithValue("@" + column.Key, column.Value);
 			}
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index 0cd528877..e6c2c641d 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -35,7 +35,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 				writer.Write((byte) 1);
 
 				if (attributes?.Count > 0)
-					WriteBinaryParameters(writer, attributes.Select(x => x.ToParameter()).ToArray(), command, true, 0);
+					WriteBinaryParameters(writer, attributes.Select(static x => x.ToParameter()).ToArray(), command, true, 0);
 			}
 			else if (command.RawAttributes?.Count > 0)
 			{
diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index 416470666..352508326 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -805,7 +805,7 @@ public bool UseXaTransactions
 	/// <summary>
 	/// Returns an <see cref="ICollection"/> that contains the keys in the <see cref="MySqlConnectionStringBuilder"/>.
 	/// </summary>
-	public override ICollection Keys => base.Keys.Cast<string>().OrderBy(x => MySqlConnectionStringOption.OptionNames.IndexOf(x)).ToList();
+	public override ICollection Keys => base.Keys.Cast<string>().OrderBy(static x => MySqlConnectionStringOption.OptionNames.IndexOf(x)).ToList();
 
 	/// <summary>
 	/// Whether this <see cref="MySqlConnectionStringBuilder"/> contains a set option with the specified name.
@@ -1165,7 +1165,7 @@ static MySqlConnectionStringOption()
 		AddOption(options, CancellationTimeout = new(
 			keys: ["Cancellation Timeout", "CancellationTimeout"],
 			defaultValue: 2,
-			coerce: x =>
+			coerce: static x =>
 			{
 				if (x < -1)
 					throw new ArgumentOutOfRangeException(nameof(CancellationTimeout), "CancellationTimeout must be greater than or equal to -1");

From a7d85de14d4fa4d1dcfc1ec567859d25da164c77 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 16 Jan 2024 06:28:36 -0800
Subject: [PATCH 257/393] Update test packages.

---
 Directory.Packages.props | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index c857d3db5..2222551b3 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,7 +6,7 @@
 
   <ItemGroup>
     <PackageVersion Include="AdoNet.Specification.Tests" Version="2.0.0-beta.2" />
-    <PackageVersion Include="Dapper.StrongName" Version="2.1.24" />
+    <PackageVersion Include="Dapper.StrongName" Version="2.1.28" />
     <PackageVersion Include="IndexRange" Version="1.0.2" />
     <PackageVersion Include="log4net" Version="2.0.10" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
@@ -22,7 +22,7 @@
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageVersion Include="xunit" Version="2.6.5" />
+    <PackageVersion Include="xunit" Version="2.6.6" />
     <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6" />
     <PackageVersion Include="YamlDotNet" Version="13.7.1" />
   </ItemGroup>

From 2c58800e1822c30f09f2f9111a57d5762e43572f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 20 Jan 2024 11:59:50 -0800
Subject: [PATCH 258/393] Stop automatically publishing to NuGet.

We will want to code-sign the DLLs first before publishing the NuGet package.
---
 appveyor.yml | 8 --------
 1 file changed, 8 deletions(-)

diff --git a/appveyor.yml b/appveyor.yml
index df9959cd6..c4f4f64c4 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -32,11 +32,3 @@ notifications:
     secure: SRJ5fYQE4tvelyqeX3Lkv0gXta3O2pl4/+wNaqmqmcFkmYBe+U31T5YCGhipBOVhHBIZLevihOJZ78sFVJrmAFV5bHgtX/VPIKEXN+7ytis=
 artifacts:
 - path: artifacts\package\release\*.nupkg
-deploy:
-- provider: NuGet
-  api_key:
-    secure: SCNhz02UXcSKv6UVgAlhbzGb0u7PAQF4w1kx+cCGw1tm95QNhy2yiKLsLjjQ0j7l
-  artifact: /.*MySqlConnector[0-9.]+(-(alpha|beta|rc)\.?[0-9.]+)?nupkg/
-  skip_symbols: true
-  on:
-    appveyor_repo_tag: true

From 5f835348b64b6c19f0818a308b9404c38aa6177f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 20 Jan 2024 12:03:52 -0800
Subject: [PATCH 259/393] Release 2.3.5.

---
 docs/content/overview/version-history.md | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 2e00a8e8d..735323b3e 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2024-01-14
+lastmod: 2024-01-20
 date: 2017-03-27
 menu:
   main:
@@ -11,6 +11,11 @@ weight: 30
 
 # Version History
 
+### 2.3.5
+
+* DLL files within the NuGet packages are now digitally signed.
+* Thanks to [Devolutions](https://devolutions.net/) for sponsoring this change.
+
 ### 2.3.4
 
 * Unify `Application Name` connection string option and `MySqlDataSourceBuilder.UseName`: [#1434](https://github.com/mysql-net/MySqlConnector/issues/1434).

From 5e5bc1340f178286e88bb243cab1cdc196645479 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 20 Jan 2024 13:03:45 -0800
Subject: [PATCH 260/393] Add Devolutions to acknowledgements in README.

---
 README.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/README.md b/README.md
index a7a4b50b8..7984ea06d 100644
--- a/README.md
+++ b/README.md
@@ -101,6 +101,10 @@ If you'd like to contribute to MySqlConnector, please read our [contributing gui
 
 Development of MySqlConnector is supported by:
 
+[![Devolutions](https://webdevolutions.blob.core.windows.net/images/projects/devolutions/logos/devolutions-icon-shadow.svg)](https://devolutions.net/)
+
+[Devolutions](https://devolutions.net/)
+
 [![Faithlife](https://files.logoscdn.com/v1/files/4319104/content.svg?signature=3szVb3XmOfYMAxIv-LmuNYL_290)](https://faithlife.com/about)
 
 [Faithlife](https://faithlife.com/about) ([View jobs](https://faithlife.com/careers))

From d6ca0a0b4d8aa5c02498cf5ebb26c48c1ef76f04 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 20 Jan 2024 13:05:06 -0800
Subject: [PATCH 261/393] Update various copyright dates to 2024.

---
 LICENSE                                                         | 2 +-
 .../MySqlConnector.Authentication.Ed25519.csproj                | 2 +-
 .../MySqlConnector.DependencyInjection.csproj                   | 2 +-
 .../MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj  | 2 +-
 .../MySqlConnector.Logging.NLog.csproj                          | 2 +-
 .../MySqlConnector.Logging.Serilog.csproj                       | 2 +-
 .../MySqlConnector.Logging.log4net.csproj                       | 2 +-
 src/MySqlConnector/MySqlConnector.csproj                        | 2 +-
 8 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/LICENSE b/LICENSE
index 11f3e7c69..7fad5192d 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
 MIT License
 
-Copyright (c) 2016-2023 Bradley Grainger
+Copyright (c) 2016-2024 Bradley Grainger
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal
diff --git a/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj b/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj
index c968f2571..bd2122bb3 100644
--- a/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj
+++ b/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj
@@ -4,7 +4,7 @@
     <TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
     <Title>MySqlConnector Ed25519 Authentication Plugin</Title>
     <Description>Implements the client_ed25519 authentication plugin for MariaDB.</Description>
-    <Copyright>Copyright 2019–2023 Bradley Grainger</Copyright>
+    <Copyright>Copyright 2019–2024 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
     <PackageReadmeFile>README.md</PackageReadmeFile>
     <PackageTags>mariadb;mysqlconnector;authentication;ed25519</PackageTags>
diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
index d1e793b74..69cd4cea2 100644
--- a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
+++ b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
@@ -7,7 +7,7 @@
     <EnablePackageValidation>false</EnablePackageValidation>
     <Title>MySqlConnector Dependency Injection Helpers</Title>
     <Description>Provides extension methods for integrating MySqlConnector with .NET dependency injection.</Description>
-    <Copyright>Copyright 2022 Bradley Grainger</Copyright>
+    <Copyright>Copyright 2024 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
     <PackageReadmeFile>README.md</PackageReadmeFile>
     <PackageTags>mysqlconnector;dependency injection;iservicecollection</PackageTags>
diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
index 92ba235e8..ba6a73e1c 100644
--- a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
+++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj
@@ -4,7 +4,7 @@
     <TargetFrameworks>netstandard2.0</TargetFrameworks>
     <Title>MySqlConnector Logging Adapter for Microsoft.Extensions.Logging</Title>
     <Description>Writes MySqlConnector logging output to Microsoft.Extensions.Logging with one line of code.</Description>
-    <Copyright>Copyright 2017–2023 Bradley Grainger</Copyright>
+    <Copyright>Copyright 2017–2024 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
     <PackageReadmeFile>README.md</PackageReadmeFile>
     <PackageTags>mysql;mysqlconnector;async;ado.net;database;netcore;logging</PackageTags>
diff --git a/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj b/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj
index b0cb9e194..bf85d7097 100644
--- a/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj
+++ b/src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj
@@ -4,7 +4,7 @@
     <TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
     <Title>MySqlConnector Logging Adapter for NLog</Title>
     <Description>Writes lightly-structured MySqlConnector logging output to NLog.</Description>
-    <Copyright>Copyright 2018–2023 Bradley Grainger</Copyright>
+    <Copyright>Copyright 2018–2024 Bradley Grainger</Copyright>
     <Authors>Rolf Kristensen</Authors>
     <PackageReadmeFile>README.md</PackageReadmeFile>
     <PackageTags>mysql;mysqlconnector;async;ado.net;database;netcore;NLog;logging</PackageTags>
diff --git a/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj b/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj
index 316d42263..8470a3926 100644
--- a/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj
+++ b/src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj
@@ -4,7 +4,7 @@
     <TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
     <Title>MySqlConnector Logging Adapter for Serilog</Title>
     <Description>Writes lightly-structured MySqlConnector logging output to Serilog.</Description>
-    <Copyright>Copyright 2017–2023 Bradley Grainger</Copyright>
+    <Copyright>Copyright 2017–2024 Bradley Grainger</Copyright>
     <Authors>Marc Lewandowski</Authors>
     <PackageReadmeFile>README.md</PackageReadmeFile>
     <PackageTags>mysql;mysqlconnector;async;ado.net;database;netcore;serilog;logging</PackageTags>
diff --git a/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj b/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj
index 6e655a9e4..175af91d4 100644
--- a/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj
+++ b/src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj
@@ -4,7 +4,7 @@
     <TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
     <Title>MySqlConnector Logging Adapter for log4net</Title>
     <Description>Writes MySqlConnector logging output to log4net with one line of code.</Description>
-    <Copyright>Copyright 2017–2023 Bradley Grainger</Copyright>
+    <Copyright>Copyright 2017–2024 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
     <PackageReadmeFile>README.md</PackageReadmeFile>
     <PackageTags>mysql;mysqlconnector;async;ado.net;database;netcore;log4net;logging</PackageTags>
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 536f437c3..4cb3425f5 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -3,7 +3,7 @@
   <PropertyGroup>
     <TargetFrameworks>net462;net471;net48;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0</TargetFrameworks>
     <Description>A truly async MySQL ADO.NET provider, supporting MySQL Server, MariaDB, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL, and more.</Description>
-    <Copyright>Copyright 2016–2023 Bradley Grainger</Copyright>
+    <Copyright>Copyright 2016–2024 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
     <AssemblyName>MySqlConnector</AssemblyName>
     <PackageId>MySqlConnector</PackageId>

From 999a3ab913a0112b5819f57f603b109e9c435e6f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 21 Jan 2024 14:22:35 -0800
Subject: [PATCH 262/393] Add link to AspNetCore.HealthChecks.MySql. Fixes
 #1368

---
 README.md | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 7984ea06d..f9b0150b6 100644
--- a/README.md
+++ b/README.md
@@ -38,7 +38,7 @@ This library is [MIT-licensed](LICENSE) and may be freely distributed with comme
 Commercial software that uses Connector/NET may have to purchase a [commercial license](https://www.mysql.com/about/legal/licensing/oem/)
 from Oracle.
 
-## ORMs
+## Related Projects
 
 This library is compatible with popular .NET ORMs including:
 
@@ -56,6 +56,10 @@ For Entity Framework support, use:
 
 * Pomelo.EntityFrameworkCore.MySql ([GitHub](https://github.com/PomeloFoundation/Pomelo.EntityFrameworkCore.MySql), [NuGet](https://www.nuget.org/packages/Pomelo.EntityFrameworkCore.MySql))
 
+For ASP.NET Core health checks, use:
+
+* AspNetCore.HealthChecks.MySql ([GitHub](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks), [NuGet](https://www.nuget.org/packages/AspNetCore.HealthChecks.MySql))
+
 ## Build Status
 
 [![AppVeyor](https://img.shields.io/appveyor/ci/mysqlnet/mysqlconnector/master.svg)](https://ci.appveyor.com/project/mysqlnet/mysqlconnector)

From 73031182d2048af4b40bc4fd492b42633e3e893b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 21 Jan 2024 19:29:04 -0800
Subject: [PATCH 263/393] Create and cache START TRANSACTION payloads.

---
 src/MySqlConnector/MySqlConnection.cs         | 158 +++++++++++++++---
 .../MySqlConnectionTests.cs                   |  43 +++++
 .../MySqlConnector.Tests.csproj               |   2 +-
 3 files changed, 182 insertions(+), 21 deletions(-)
 create mode 100644 tests/MySqlConnector.Tests/MySqlConnectionTests.cs

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 346de9bc6..d9b658c23 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -8,6 +8,7 @@
 using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
 using MySqlConnector.Logging;
+using MySqlConnector.Protocol;
 using MySqlConnector.Protocol.Payloads;
 using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
@@ -135,6 +136,7 @@ protected override async ValueTask<DbTransaction> BeginDbTransactionAsync(Isolat
 	/// <remarks>Transactions may not be nested.</remarks>
 	public ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel isolationLevel, bool isReadOnly, CancellationToken cancellationToken = default) => BeginTransactionAsync(isolationLevel, isReadOnly, AsyncIOBehavior, cancellationToken);
 
+	// set session transaction isolation level read uncommitted;start transaction with consistent snapshot,read write;
 	private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel isolationLevel, bool? isReadOnly, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		if (State != ConnectionState.Open)
@@ -144,39 +146,154 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
 		if (m_enlistedTransaction is not null)
 			throw new InvalidOperationException("Cannot begin a transaction when already enlisted in a transaction.");
 
-		var isolationLevelValue = isolationLevel switch
+		// get the bytes for both payloads concatenated together (suitable for pipelining)
+		var startTransactionPayload = GetStartTransactionPayload(isolationLevel, isReadOnly, m_session!.SupportsQueryAttributes);
+
+		// send the two packets separately
+		await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(4, startTransactionPayload.Span[0])), ioBehavior, cancellationToken).ConfigureAwait(false);
+
+		var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+		OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+
+		await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(8 + startTransactionPayload.Span[0], startTransactionPayload.Span[startTransactionPayload.Span[0] + 4])), ioBehavior, cancellationToken).ConfigureAwait(false);
+
+		payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+		OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+
+		var transaction = new MySqlTransaction(this, isolationLevel);
+		CurrentTransaction = transaction;
+		return transaction;
+	}
+
+	/// <summary>
+	/// Returns a <see cref="ReadOnlyMemory{Byte}"/> containing two payloads to set the isolation level and start a transaction.
+	/// </summary>
+	internal static ReadOnlyMemory<byte> GetStartTransactionPayload(IsolationLevel isolationLevel, bool? isReadOnly, bool supportsQueryAttributes)
+	{
+		var isolationLevelIndex = isolationLevel switch
 		{
-			IsolationLevel.ReadUncommitted => "read uncommitted",
-			IsolationLevel.ReadCommitted => "read committed",
-			IsolationLevel.RepeatableRead => "repeatable read",
-			IsolationLevel.Serializable => "serializable",
-			IsolationLevel.Snapshot => "repeatable read",
+			IsolationLevel.ReadUncommitted => 0,
+			IsolationLevel.ReadCommitted => 1,
+			IsolationLevel.Serializable => 2,
+			IsolationLevel.RepeatableRead => 3,
+			IsolationLevel.Snapshot => 3,
 
 			// "In terms of the SQL:1992 transaction isolation levels, the default InnoDB level is REPEATABLE READ." - http://dev.mysql.com/doc/refman/5.7/en/innodb-transaction-model.html
-			IsolationLevel.Unspecified => "repeatable read",
+			IsolationLevel.Unspecified => 3,
 
 			_ => throw new NotSupportedException($"IsolationLevel.{isolationLevel} is not supported."),
 		};
 
-		using (var cmd = new MySqlCommand($"set session transaction isolation level {isolationLevelValue};", this) { NoActivity = true })
+		var consistentSnapshotIndex = isolationLevel == IsolationLevel.Snapshot ? 1 : 0;
+
+		var readOnlyIndex = isReadOnly switch
+		{
+			null => 0,
+			false => 1,
+			true => 2,
+		};
+
+		var index = ((supportsQueryAttributes ? 1 : 0) * 5 + isolationLevelIndex + consistentSnapshotIndex) * 3 + readOnlyIndex;
+
+		if (s_startTransactionPayloads[index].IsEmpty)
 		{
-			await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			// create payload that's (four-byte header)(query)(set isolation level X)(four-byte header)(query)(start transaction)
+			var payload = new byte[125];
+			var startIndex = 4;
+			var length = 0;
+
+			payload[startIndex] = (byte) CommandKind.Query;
+			startIndex++;
+			length++;
 
-			var consistentSnapshotText = isolationLevel == IsolationLevel.Snapshot ? " with consistent snapshot" : "";
-			var readOnlyText = isReadOnly switch
+			if (supportsQueryAttributes)
 			{
-				true => " read only",
-				false => " read write",
-				null => "",
+				payload[startIndex + 1] = 1;
+				startIndex += 2;
+				length += 2;
+			}
+
+			// copy in 'set isolation level' prefix
+			ReadOnlySpan<byte> setIsolationLevelText = "set session transaction isolation level "u8;
+			setIsolationLevelText.CopyTo(payload.AsSpan(startIndex));
+			length += setIsolationLevelText.Length;
+			startIndex += setIsolationLevelText.Length;
+
+			// copy in isolation level
+			ReadOnlySpan<byte> isolationLevelText = isolationLevelIndex switch
+			{
+				0 => "read uncommitted"u8,
+				1 => "read committed"u8,
+				2 => "serializable"u8,
+				_ => "repeatable read"u8,
+			};
+			isolationLevelText.CopyTo(payload.AsSpan(startIndex));
+			startIndex += isolationLevelText.Length;
+			length += isolationLevelText.Length;
+
+			payload[startIndex] = (byte) ';';
+			startIndex++;
+			length++;
+
+			// set the length of the first payload
+			payload[0] = (byte) length;
+
+			startIndex += 4;
+
+			payload[startIndex] = (byte) CommandKind.Query;
+			startIndex++;
+			length = 1;
+
+			if (supportsQueryAttributes)
+			{
+				payload[startIndex + 1] = 1;
+				startIndex += 2;
+				length += 2;
+			}
+
+			// copy in 'start transaction' prefix
+			ReadOnlySpan<byte> startTransactionText = "start transaction"u8;
+			startTransactionText.CopyTo(payload.AsSpan(startIndex));
+			length += startTransactionText.Length;
+			startIndex += startTransactionText.Length;
+
+			// copy in 'with consistent snapshot'
+			if (consistentSnapshotIndex == 1)
+			{
+				ReadOnlySpan<byte> consistentSnapshotText = " with consistent snapshot"u8;
+				consistentSnapshotText.CopyTo(payload.AsSpan(startIndex));
+				length += consistentSnapshotText.Length;
+				startIndex += consistentSnapshotText.Length;
+			}
+
+			if (consistentSnapshotIndex > 0 && readOnlyIndex > 0)
+			{
+				payload[startIndex] = (byte) ',';
+				startIndex++;
+				length++;
+			}
+
+			ReadOnlySpan<byte> readOnlyText = readOnlyIndex switch
+			{
+				1 => " read write"u8,
+				2 => " read only"u8,
+				_ => ""u8,
 			};
-			var separatorText = (consistentSnapshotText.Length == 0 || readOnlyText.Length == 0) ? "" : ",";
-			cmd.CommandText = $"start transaction{consistentSnapshotText}{separatorText}{readOnlyText};";
-			await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			readOnlyText.CopyTo(payload.AsSpan(startIndex));
+			startIndex += readOnlyText.Length;
+			length += readOnlyText.Length;
+
+			payload[startIndex] = (byte) ';';
+			startIndex++;
+			length++;
+
+			// store length at the beginning of the second payload
+			payload[payload[0] + 4] = (byte) length;
+
+			s_startTransactionPayloads[index] = new ReadOnlyMemory<byte>(payload, 0, payload[0] + payload[payload[0] + 4] + 8);
 		}
 
-		var transaction = new MySqlTransaction(this, isolationLevel);
-		CurrentTransaction = transaction;
-		return transaction;
+		return s_startTransactionPayloads[index];
 	}
 
 	public override void EnlistTransaction(System.Transactions.Transaction? transaction)
@@ -1123,6 +1240,7 @@ private ConnectionSettings GetConnectionSettings() =>
 	private static readonly StateChangeEventArgs s_stateChangeOpenClosed = new(ConnectionState.Open, ConnectionState.Closed);
 	private static readonly object s_lock = new();
 	private static readonly Dictionary<System.Transactions.Transaction, List<EnlistedTransactionBase>> s_transactionConnections = [];
+	private static readonly ReadOnlyMemory<byte>[] s_startTransactionPayloads = new ReadOnlyMemory<byte>[5 * 3 * 2];
 
 	private readonly MySqlDataSource? m_dataSource;
 	private readonly ILogger m_logger;
diff --git a/tests/MySqlConnector.Tests/MySqlConnectionTests.cs b/tests/MySqlConnector.Tests/MySqlConnectionTests.cs
new file mode 100644
index 000000000..249fbe56b
--- /dev/null
+++ b/tests/MySqlConnector.Tests/MySqlConnectionTests.cs
@@ -0,0 +1,43 @@
+using System.Text;
+
+namespace MySqlConnector.Tests;
+
+public class MySqlConnectionTests
+{
+	[Theory]
+	[InlineData(IsolationLevel.ReadUncommitted, null, false, "\x3A\0\0\0\x03set session transaction isolation level read uncommitted;\x13\0\0\0\x03start transaction;")]
+	[InlineData(IsolationLevel.ReadUncommitted, null, true, "\x3C\0\0\0\x03\0\x01set session transaction isolation level read uncommitted;\x15\0\0\0\x03\0\x01start transaction;")]
+	[InlineData(IsolationLevel.ReadUncommitted, false, false, "\x3A\0\0\0\x03set session transaction isolation level read uncommitted;\x1E\0\0\0\x03start transaction read write;")]
+	[InlineData(IsolationLevel.ReadUncommitted, false, true, "\x3C\0\0\0\x03\0\x01set session transaction isolation level read uncommitted;\x20\0\0\0\x03\0\x01start transaction read write;")]
+	[InlineData(IsolationLevel.ReadUncommitted, true, false, "\x3A\0\0\0\x03set session transaction isolation level read uncommitted;\x1D\0\0\0\x03start transaction read only;")]
+	[InlineData(IsolationLevel.ReadUncommitted, true, true, "\x3C\0\0\0\x03\0\x01set session transaction isolation level read uncommitted;\x1F\0\0\0\x03\0\x01start transaction read only;")]
+	[InlineData(IsolationLevel.ReadCommitted, null, false, "\x38\0\0\0\x03set session transaction isolation level read committed;\x13\0\0\0\x03start transaction;")]
+	[InlineData(IsolationLevel.ReadCommitted, null, true, "\x3A\0\0\0\x03\0\x01set session transaction isolation level read committed;\x15\0\0\0\x03\0\x01start transaction;")]
+	[InlineData(IsolationLevel.ReadCommitted, false, false, "\x38\0\0\0\x03set session transaction isolation level read committed;\x1E\0\0\0\x03start transaction read write;")]
+	[InlineData(IsolationLevel.ReadCommitted, false, true, "\x3A\0\0\0\x03\0\x01set session transaction isolation level read committed;\x20\0\0\0\x03\0\x01start transaction read write;")]
+	[InlineData(IsolationLevel.ReadCommitted, true, false, "\x38\0\0\0\x03set session transaction isolation level read committed;\x1D\0\0\0\x03start transaction read only;")]
+	[InlineData(IsolationLevel.ReadCommitted, true, true, "\x3A\0\0\0\x03\0\x01set session transaction isolation level read committed;\x1F\0\0\0\x03\0\x01start transaction read only;")]
+	[InlineData(IsolationLevel.Serializable, null, false, "\x36\0\0\0\x03set session transaction isolation level serializable;\x13\0\0\0\x03start transaction;")]
+	[InlineData(IsolationLevel.Serializable, null, true, "\x38\0\0\0\x03\0\x01set session transaction isolation level serializable;\x15\0\0\0\x03\0\x01start transaction;")]
+	[InlineData(IsolationLevel.Serializable, false, false, "\x36\0\0\0\x03set session transaction isolation level serializable;\x1E\0\0\0\x03start transaction read write;")]
+	[InlineData(IsolationLevel.Serializable, false, true, "\x38\0\0\0\x03\0\x01set session transaction isolation level serializable;\x20\0\0\0\x03\0\x01start transaction read write;")]
+	[InlineData(IsolationLevel.Serializable, true, false, "\x36\0\0\0\x03set session transaction isolation level serializable;\x1D\0\0\0\x03start transaction read only;")]
+	[InlineData(IsolationLevel.Serializable, true, true, "\x38\0\0\0\x03\0\x01set session transaction isolation level serializable;\x1F\0\0\0\x03\0\x01start transaction read only;")]
+	[InlineData(IsolationLevel.RepeatableRead, null, false, "\x39\0\0\0\x03set session transaction isolation level repeatable read;\x13\0\0\0\x03start transaction;")]
+	[InlineData(IsolationLevel.RepeatableRead, null, true, "\x3B\0\0\0\x03\0\x01set session transaction isolation level repeatable read;\x15\0\0\0\x03\0\x01start transaction;")]
+	[InlineData(IsolationLevel.RepeatableRead, false, false, "\x39\0\0\0\x03set session transaction isolation level repeatable read;\x1E\0\0\0\x03start transaction read write;")]
+	[InlineData(IsolationLevel.RepeatableRead, false, true, "\x3B\0\0\0\x03\0\x01set session transaction isolation level repeatable read;\x20\0\0\0\x03\0\x01start transaction read write;")]
+	[InlineData(IsolationLevel.RepeatableRead, true, false, "\x39\0\0\0\x03set session transaction isolation level repeatable read;\x1D\0\0\0\x03start transaction read only;")]
+	[InlineData(IsolationLevel.RepeatableRead, true, true, "\x3B\0\0\0\x03\0\x01set session transaction isolation level repeatable read;\x1F\0\0\0\x03\0\x01start transaction read only;")]
+	[InlineData(IsolationLevel.Snapshot, null, false, "\x39\0\0\0\x03set session transaction isolation level repeatable read;\x2C\0\0\0\x03start transaction with consistent snapshot;")]
+	[InlineData(IsolationLevel.Snapshot, null, true, "\x3B\0\0\0\x03\0\x01set session transaction isolation level repeatable read;\x2E\0\0\0\x03\0\x01start transaction with consistent snapshot;")]
+	[InlineData(IsolationLevel.Snapshot, false, false, "\x39\0\0\0\x03set session transaction isolation level repeatable read;\x38\0\0\0\x03start transaction with consistent snapshot, read write;")]
+	[InlineData(IsolationLevel.Snapshot, false, true, "\x3B\0\0\0\x03\0\x01set session transaction isolation level repeatable read;\x3A\0\0\0\x03\0\x01start transaction with consistent snapshot, read write;")]
+	[InlineData(IsolationLevel.Snapshot, true, false, "\x39\0\0\0\x03set session transaction isolation level repeatable read;\x37\0\0\0\x03start transaction with consistent snapshot, read only;")]
+	[InlineData(IsolationLevel.Snapshot, true, true, "\x3B\0\0\0\x03\0\x01set session transaction isolation level repeatable read;\x39\0\0\0\x03\0\x01start transaction with consistent snapshot, read only;")]
+	public void GetStartTransactionPayload(IsolationLevel isolationLevel, bool? isReadOnly, bool supportsQueryAttributes, string expected)
+	{
+		var payload = MySqlConnection.GetStartTransactionPayload(isolationLevel, isReadOnly, supportsQueryAttributes);
+		Assert.Equal(expected, Encoding.ASCII.GetString(payload.Span.ToArray()));
+	}
+}
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 4992175b1..b7c7f164e 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -47,7 +47,7 @@
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
     <PackageReference Include="MySql.Data" />
-    <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ColumnCountPayloadTests.cs;ColumnReaderTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterAppendBinaryTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
+    <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ColumnCountPayloadTests.cs;ColumnReaderTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlConnectionTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterAppendBinaryTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
     <Compile Remove="Metrics\*.cs" />
     <Using Include="MySql.Data.MySqlClient" />
     <Using Include="MySql.Data.Types" />

From 957dd42d0aa744ff9474f9658a5644ac2a69d3cd Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 22 Jan 2024 20:07:18 -0800
Subject: [PATCH 264/393] Update MySql.Data to 8.3.0.

Run CI integration tests against MySQL Server 8.3.
---
 .ci/docker-run.sh                                      | 2 +-
 Directory.Packages.props                               | 2 +-
 azure-pipelines.yml                                    | 4 ++--
 docs/content/connection-options.md                     | 7 ++++++-
 docs/content/tutorials/migrating-from-connector-net.md | 6 +++---
 tests/IntegrationTests/app.config                      | 2 +-
 6 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index 8bae51e2f..9abcf5f45 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -25,7 +25,7 @@ OMIT_FEATURES=$3
 MYSQL_EXTRA=
 MYSQL=mysql
 
-if [[ "$IMAGE" == mysql:8.0 || "$IMAGE" == mysql:8.2 ]]; then
+if [[ "$IMAGE" == mysql:8.0 || "$IMAGE" == mysql:8.3 ]]; then
   MYSQL_EXTRA='--default-authentication-plugin=mysql_native_password'
 fi
 if [[ "$IMAGE" == mariadb* ]]; then
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 2222551b3..0a89bfb44 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -16,7 +16,7 @@
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
-    <PackageVersion Include="MySql.Data" Version="8.2.0" />
+    <PackageVersion Include="MySql.Data" Version="8.3.0" />
     <PackageVersion Include="NLog" Version="4.5.0" />
     <PackageVersion Include="Serilog" Version="2.6.0" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 36b3a5b83..2c0389e04 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -233,8 +233,8 @@ jobs:
         image: 'mysql:8.0'
         connectionStringExtra: ''
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
-      'MySQL 8.2':
-        image: 'mysql:8.2'
+      'MySQL 8.3':
+        image: 'mysql:8.3'
         connectionStringExtra: ''
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
       'MariaDB 10.6':
diff --git a/docs/content/connection-options.md b/docs/content/connection-options.md
index 2961f5da4..1f4de9d31 100644
--- a/docs/content/connection-options.md
+++ b/docs/content/connection-options.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2024-01-14
+lastmod: 2024-01-22
 date: 2016-10-16
 title: Connection Options
 customtitle: MySQL Connection String Options for .NET/C#
@@ -514,6 +514,11 @@ from your connection string when migrating from Connector/NET to MySqlConnector.
     <td>false</td>
     <td>Use <a href="/overview/logging/">MySqlConnector logging</a> (which is more flexible) instead.</td>
   </tr>
+  <tr id="OldGetStringBehavior">
+    <td></td>
+    <td>false</td>
+    <td>This option is temporary in Connector/NET and unsupported in MySqlConnector.</td>
+  </tr>
   <tr id="OldSyntax">
     <td>OldSyntax, Old Syntax, UseOldSyntax, Use Old Syntax</td>
     <td>false</td>
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 3c8f26f74..bb7387060 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2023-04-24
+lastmod: 2024-01-22
 date: 2016-10-16
 menu:
   main:
@@ -41,7 +41,7 @@ In a .NET Framework application, make the following `app.config` change to regis
     <!-- add name="MySQL Data Provider"
       invariant="MySql.Data.MySqlClient"
       description=".Net Framework Data Provider for MySQL"
-      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.2.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.3.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
     <add name="MySqlConnector"
@@ -122,7 +122,7 @@ and follow the [best practices in async programming](https://msdn.microsoft.com/
 
 ### Implicit Conversions
 
-Connector/NET allows `MySqlDataReader.GetString()` to be called on many non-textual columns, and will implicitly
+Prior to v8.3.0, Connector/NET allows `MySqlDataReader.GetString()` to be called on many non-textual columns, and will implicitly
 convert the value to a `string` (using the current locale). This is a frequent source of locale-dependent bugs, so
 MySqlConnector follows typical ADO.NET practice (e.g., SqlClient, npgsql) and disallows this (by throwing an `InvalidCastException`).
 
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index ecc810d31..5e4b1c55c 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -3,7 +3,7 @@
   <system.data>
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.2.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.3.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>

From e40095084ffd8c5129c202644591c93f8546521c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 23 Jan 2024 18:12:27 -0800
Subject: [PATCH 265/393] Use pipelining to begin a transaction. Fixes #1286

---
 src/MySqlConnector/Core/ServerSession.cs | 35 ++++++++++++++++++++----
 src/MySqlConnector/MySqlConnection.cs    | 29 +++++++++++++++-----
 2 files changed, 52 insertions(+), 12 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 290c8b4c9..f83c03e7a 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -586,15 +586,13 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 					Log.SendingPipelinedResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
 
 					// send both packets at once
-					await m_payloadHandler!.ByteHandler.WriteBytesAsync(m_pipelinedResetConnectionBytes!, ioBehavior).ConfigureAwait(false);
+					await SendRawAsync(m_pipelinedResetConnectionBytes, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 					// read two OK replies
-					m_payloadHandler.SetNextSequenceNumber(1);
-					payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+					payload = await ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
 					OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 
-					m_payloadHandler.SetNextSequenceNumber(1);
-					payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+					payload = await ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
 					OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
 
 					return true;
@@ -896,6 +894,12 @@ public async ValueTask<PayloadData> ReceiveReplyAsync(IOBehavior ioBehavior, Can
 		return payload;
 	}
 
+	public ValueTask<PayloadData> ReceiveReplyAsync(int expectedSequenceNumber, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	{
+		m_payloadHandler!.SetNextSequenceNumber(expectedSequenceNumber);
+		return ReceiveReplyAsync(ioBehavior, cancellationToken);
+	}
+
 	// Continues a conversation with the server by sending a reply to a packet received with 'Receive' or 'ReceiveReply'.
 	public async ValueTask SendReplyAsync(PayloadData payload, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
@@ -916,6 +920,27 @@ public async ValueTask SendReplyAsync(PayloadData payload, IOBehavior ioBehavior
 		}
 	}
 
+	// Sends raw bytes over the wire without formatting them into a payload. The caller is expected to set the packet header(s) correctly.
+	public async ValueTask SendRawAsync(ReadOnlyMemory<byte> data, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	{
+		if (CreateExceptionForInvalidState() is { } exception)
+		{
+			Log.FailedInSendReplyAsync(m_logger, exception, Id);
+			throw exception;
+		}
+
+		try
+		{
+			// send both packets at once
+			await m_payloadHandler!.ByteHandler.WriteBytesAsync(data, ioBehavior).ConfigureAwait(false);
+		}
+		catch (Exception ex)
+		{
+			SetFailed(ex);
+			throw;
+		}
+	}
+
 	public static void ThrowIfStatementContainsDelimiter(MySqlException exception, IMySqlCommand command)
 	{
 		// check if the command used "DELIMITER"
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index d9b658c23..55279be1a 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -149,16 +149,31 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
 		// get the bytes for both payloads concatenated together (suitable for pipelining)
 		var startTransactionPayload = GetStartTransactionPayload(isolationLevel, isReadOnly, m_session!.SupportsQueryAttributes);
 
-		// send the two packets separately
-		await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(4, startTransactionPayload.Span[0])), ioBehavior, cancellationToken).ConfigureAwait(false);
+		if (GetInitializedConnectionSettings() is { UseCompression: false, Pipelining: not false })
+		{
+			// send the two packets together
+			await m_session.SendRawAsync(startTransactionPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 
-		var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-		OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+			// read the two OK replies
+			var payload = await m_session.ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
+			OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+
+			payload = await m_session.ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
+			OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+		}
+		else
+		{
+			// send the two packets separately
+			await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(4, startTransactionPayload.Span[0])), ioBehavior, cancellationToken).ConfigureAwait(false);
 
-		await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(8 + startTransactionPayload.Span[0], startTransactionPayload.Span[startTransactionPayload.Span[0] + 4])), ioBehavior, cancellationToken).ConfigureAwait(false);
+			var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
 
-		payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-		OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+			await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(8 + startTransactionPayload.Span[0], startTransactionPayload.Span[startTransactionPayload.Span[0] + 4])), ioBehavior, cancellationToken).ConfigureAwait(false);
+
+			payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+		}
 
 		var transaction = new MySqlTransaction(this, isolationLevel);
 		CurrentTransaction = transaction;

From 7d38fe5d18e0213f2dff3ddad309b18f45d3eb4d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 28 Jan 2024 08:25:22 -0800
Subject: [PATCH 266/393] Add transaction logging. Fixes #1411

---
 src/MySqlConnector/Logging/EventIds.cs         |  8 ++++++++
 src/MySqlConnector/Logging/Log.cs              | 18 ++++++++++++++++++
 .../MySqlConnectorLoggingConfiguration.cs      |  1 +
 src/MySqlConnector/MySqlConnection.cs          |  8 ++++++--
 src/MySqlConnector/MySqlTransaction.cs         | 12 +++++++++++-
 5 files changed, 44 insertions(+), 3 deletions(-)

diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index ddb99123d..568acdcf3 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -131,6 +131,14 @@ internal static class EventIds
 	public const int ColumnMappingAlreadyHasExpression = 2508;
 	public const int SettingExpressionToMapColumn = 2509;
 
+	// Transaction events, 2700-2799
+	public const int StartingTransaction = 2700;
+	public const int StartedTransaction = 2701;
+	public const int CommittingTransaction = 2702;
+	public const int CommittedTransaction = 2703;
+	public const int RollingBackTransaction = 2704;
+	public const int RolledBackTransaction = 2705;
+
 	// Connection pool events, 3000-3099
 	public const int WaitingForAvailableSession = 3000;
 	public const int CreatingNewConnectionPool = 3001;
diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index e9c0bccc1..7cb3bd432 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -324,6 +324,24 @@ internal static partial class Log
 	[LoggerMessage(EventIds.SettingExpressionToMapColumn, LogLevel.Trace, "Setting expression to map column {SourceOrdinal} to {DestinationColumn}: {Expression}")]
 	public static partial void SettingExpressionToMapColumn(ILogger logger, int sourceOrdinal, string destinationColumn, string expression);
 
+	[LoggerMessage(EventIds.StartingTransaction, LogLevel.Debug, "Session {SessionId} starting transaction")]
+	public static partial void StartingTransaction(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.StartedTransaction, LogLevel.Trace, "Session {SessionId} started transaction")]
+	public static partial void StartedTransaction(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.CommittingTransaction, LogLevel.Trace, "Session {SessionId} committing transaction")]
+	public static partial void CommittingTransaction(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.CommittedTransaction, LogLevel.Debug, "Session {SessionId} committed transaction")]
+	public static partial void CommittedTransaction(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.RollingBackTransaction, LogLevel.Trace, "Session {SessionId} rolling back transaction")]
+	public static partial void RollingBackTransaction(ILogger logger, string sessionId);
+
+	[LoggerMessage(EventIds.RolledBackTransaction, LogLevel.Debug, "Session {SessionId} rolled back transaction")]
+	public static partial void RolledBackTransaction(ILogger logger, string sessionId);
+
 	[LoggerMessage(EventIds.WaitingForAvailableSession, LogLevel.Trace, "Pool {PoolId} waiting for an available session")]
 	public static partial void WaitingForAvailableSession(ILogger logger, int poolId);
 
diff --git a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
index 0ea00778f..211980ec6 100644
--- a/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
+++ b/src/MySqlConnector/Logging/MySqlConnectorLoggingConfiguration.cs
@@ -10,6 +10,7 @@ internal sealed class MySqlConnectorLoggingConfiguration(ILoggerFactory loggerFa
 	public ILogger CommandLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.MySqlCommand");
 	public ILogger PoolLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.ConnectionPool");
 	public ILogger BulkCopyLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.MySqlBulkCopy");
+	public ILogger TransactionLogger { get; } = loggerFactory.CreateLogger("MySqlConnector.Transaction");
 
 	public static MySqlConnectorLoggingConfiguration NullConfiguration { get; } = new MySqlConnectorLoggingConfiguration(NullLoggerFactory.Instance);
 	public static MySqlConnectorLoggingConfiguration GlobalConfiguration { get; set; } = NullConfiguration;
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 55279be1a..5ab8ff9f8 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -46,6 +46,7 @@ private MySqlConnection(string connectionString, MySqlConnectorLoggingConfigurat
 		m_connectionString = connectionString;
 		LoggingConfiguration = loggingConfiguration;
 		m_logger = loggingConfiguration.ConnectionLogger;
+		m_transactionLogger = loggingConfiguration.TransactionLogger;
 	}
 
 #pragma warning disable CA2012 // Safe because method completes synchronously
@@ -146,8 +147,10 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
 		if (m_enlistedTransaction is not null)
 			throw new InvalidOperationException("Cannot begin a transaction when already enlisted in a transaction.");
 
+		Log.StartingTransaction(m_transactionLogger, m_session!.Id);
+
 		// get the bytes for both payloads concatenated together (suitable for pipelining)
-		var startTransactionPayload = GetStartTransactionPayload(isolationLevel, isReadOnly, m_session!.SupportsQueryAttributes);
+		var startTransactionPayload = GetStartTransactionPayload(isolationLevel, isReadOnly, m_session.SupportsQueryAttributes);
 
 		if (GetInitializedConnectionSettings() is { UseCompression: false, Pipelining: not false })
 		{
@@ -175,7 +178,7 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
 			OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
 		}
 
-		var transaction = new MySqlTransaction(this, isolationLevel);
+		var transaction = new MySqlTransaction(this, isolationLevel, m_transactionLogger);
 		CurrentTransaction = transaction;
 		return transaction;
 	}
@@ -1259,6 +1262,7 @@ private ConnectionSettings GetConnectionSettings() =>
 
 	private readonly MySqlDataSource? m_dataSource;
 	private readonly ILogger m_logger;
+	private readonly ILogger m_transactionLogger;
 	private string m_connectionString;
 	private ConnectionSettings? m_connectionSettings;
 	private ServerSession? m_session;
diff --git a/src/MySqlConnector/MySqlTransaction.cs b/src/MySqlConnector/MySqlTransaction.cs
index 7039da08a..4896d357c 100644
--- a/src/MySqlConnector/MySqlTransaction.cs
+++ b/src/MySqlConnector/MySqlTransaction.cs
@@ -1,4 +1,6 @@
 using System.Net.Sockets;
+using Microsoft.Extensions.Logging;
+using MySqlConnector.Logging;
 using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
@@ -30,11 +32,13 @@ private async Task CommitAsync(IOBehavior ioBehavior, CancellationToken cancella
 		VerifyValid();
 
 		using var activity = Connection!.Session.StartActivity("Commit");
+		Log.CommittingTransaction(m_logger, Connection.Session.Id);
 		try
 		{
 			using (var cmd = new MySqlCommand("commit", Connection, this) { NoActivity = true })
 				await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 			Connection!.CurrentTransaction = null;
+			Log.CommittedTransaction(m_logger, Connection.Session.Id);
 			Connection = null;
 		}
 		catch (Exception ex) when (activity is { IsAllDataRequested: true })
@@ -254,19 +258,24 @@ private async Task DoDisposeAsync(IOBehavior ioBehavior, CancellationToken cance
 		Connection = null;
 	}
 
-	internal MySqlTransaction(MySqlConnection connection, IsolationLevel isolationLevel)
+	internal MySqlTransaction(MySqlConnection connection, IsolationLevel isolationLevel, ILogger logger)
 	{
 		Connection = connection;
 		IsolationLevel = isolationLevel;
+		m_logger = logger;
+
+		Log.StartedTransaction(m_logger, Connection.Session.Id);
 	}
 
 	private async Task DoRollback(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		using var activity = Connection!.Session.StartActivity("Rollback");
+		Log.RollingBackTransaction(m_logger, Connection.Session.Id);
 		try
 		{
 			using var cmd = new MySqlCommand("rollback", Connection, this) { NoActivity = true };
 			await cmd.ExecuteNonQueryAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			Log.RolledBackTransaction(m_logger, Connection.Session.Id);
 		}
 		catch (Exception ex) when (activity is { IsAllDataRequested: true })
 		{
@@ -293,5 +302,6 @@ private void VerifyValid()
 
 	private static string QuoteIdentifier(string identifier) => "`" + identifier.Replace("`", "``") + "`";
 
+	private readonly ILogger m_logger;
 	private bool m_isDisposed;
 }

From b279b92684090388067bd3414411e0fafdcf0875 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 28 Jan 2024 12:32:53 -0800
Subject: [PATCH 267/393] Use Inter font for code examples in docs.

---
 docs/assets/css/site.css          | 92 ++++++++++++++++++++++++++++++-
 docs/config.toml                  |  4 ++
 docs/layouts/partials/header.html |  3 +
 3 files changed, 97 insertions(+), 2 deletions(-)

diff --git a/docs/assets/css/site.css b/docs/assets/css/site.css
index d9f00dc88..b9b8d87dc 100644
--- a/docs/assets/css/site.css
+++ b/docs/assets/css/site.css
@@ -343,8 +343,8 @@ a:not([class]):hover {
 }
 
 code {
-  font-family: "SFMono-Regular", menlo, consolas, monospace;
-  font-size: 0.75em;
+  font-family: Inter,InterVariable,Verdana,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";
+  font-size: 0.9em;
   line-height: 1.4;
 }
 
@@ -3095,3 +3095,91 @@ blockquote.warning-title > p:nth-child(2) {
 blockquote.warning-title > p:last-child {
   margin-bottom: 0;
 }
+
+/* Background */ .bg { background-color: #f5f6fa; }
+/* PreWrapper */ .chroma { background-color: #f5f6fa; }
+pre.chroma { border: 1px solid #eeebee; }
+/* Other */ .chroma .x {  }
+/* Error */ .chroma .err {  }
+/* CodeLine */ .chroma .cl {  }
+/* LineLink */ .chroma .lnlinks { outline: none; text-decoration: none; color: inherit }
+/* LineTableTD */ .chroma .lntd { vertical-align: top; padding: 0; margin: 0; border: 0; }
+/* LineTable */ .chroma .lntable { border-spacing: 0; padding: 0; margin: 0; border: 0; }
+/* LineHighlight */ .chroma .hl { background-color: #ffffcc }
+/* LineNumbersTable */ .chroma .lnt { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* LineNumbers */ .chroma .ln { white-space: pre; user-select: none; margin-right: 0.4em; padding: 0 0.4em 0 0.4em;color: #7f7f7f }
+/* Line */ .chroma .line { display: flex; }
+/* Keyword */ .chroma .k { color: #0000ff }
+/* KeywordConstant */ .chroma .kc { color: #0000ff }
+/* KeywordDeclaration */ .chroma .kd { color: #0000ff }
+/* KeywordNamespace */ .chroma .kn { color: #0000ff }
+/* KeywordPseudo */ .chroma .kp { color: #0000ff }
+/* KeywordReserved */ .chroma .kr { color: #0000ff }
+/* KeywordType */ .chroma .kt { color: #2b91af }
+/* Name */ .chroma .n {  }
+/* NameAttribute */ .chroma .na {  }
+/* NameBuiltin */ .chroma .nb {  }
+/* NameBuiltinPseudo */ .chroma .bp {  }
+/* NameClass */ .chroma .nc { color: #2b91af }
+/* NameConstant */ .chroma .no {  }
+/* NameDecorator */ .chroma .nd {  }
+/* NameEntity */ .chroma .ni {  }
+/* NameException */ .chroma .ne {  }
+/* NameFunction */ .chroma .nf {  }
+/* NameFunctionMagic */ .chroma .fm {  }
+/* NameLabel */ .chroma .nl {  }
+/* NameNamespace */ .chroma .nn {  }
+/* NameOther */ .chroma .nx {  }
+/* NameProperty */ .chroma .py {  }
+/* NameTag */ .chroma .nt {  }
+/* NameVariable */ .chroma .nv {  }
+/* NameVariableClass */ .chroma .vc {  }
+/* NameVariableGlobal */ .chroma .vg {  }
+/* NameVariableInstance */ .chroma .vi {  }
+/* NameVariableMagic */ .chroma .vm {  }
+/* Literal */ .chroma .l {  }
+/* LiteralDate */ .chroma .ld {  }
+/* LiteralString */ .chroma .s { color: #a31515 }
+/* LiteralStringAffix */ .chroma .sa { color: #a31515 }
+/* LiteralStringBacktick */ .chroma .sb { color: #a31515 }
+/* LiteralStringChar */ .chroma .sc { color: #a31515 }
+/* LiteralStringDelimiter */ .chroma .dl { color: #a31515 }
+/* LiteralStringDoc */ .chroma .sd { color: #a31515 }
+/* LiteralStringDouble */ .chroma .s2 { color: #a31515 }
+/* LiteralStringEscape */ .chroma .se { color: #a31515 }
+/* LiteralStringHeredoc */ .chroma .sh { color: #a31515 }
+/* LiteralStringInterpol */ .chroma .si { color: #a31515 }
+/* LiteralStringOther */ .chroma .sx { color: #a31515 }
+/* LiteralStringRegex */ .chroma .sr { color: #a31515 }
+/* LiteralStringSingle */ .chroma .s1 { color: #a31515 }
+/* LiteralStringSymbol */ .chroma .ss { color: #a31515 }
+/* LiteralNumber */ .chroma .m {  }
+/* LiteralNumberBin */ .chroma .mb {  }
+/* LiteralNumberFloat */ .chroma .mf {  }
+/* LiteralNumberHex */ .chroma .mh {  }
+/* LiteralNumberInteger */ .chroma .mi {  }
+/* LiteralNumberIntegerLong */ .chroma .il {  }
+/* LiteralNumberOct */ .chroma .mo {  }
+/* Operator */ .chroma .o {  }
+/* OperatorWord */ .chroma .ow { color: #0000ff }
+/* Punctuation */ .chroma .p {  }
+/* Comment */ .chroma .c { color: #008000 }
+/* CommentHashbang */ .chroma .ch { color: #008000 }
+/* CommentMultiline */ .chroma .cm { color: #008000 }
+/* CommentSingle */ .chroma .c1 { color: #008000 }
+/* CommentSpecial */ .chroma .cs { color: #008000 }
+/* CommentPreproc */ .chroma .cp { color: #0000ff }
+/* CommentPreprocFile */ .chroma .cpf { color: #0000ff }
+/* Generic */ .chroma .g {  }
+/* GenericDeleted */ .chroma .gd {  }
+/* GenericEmph */ .chroma .ge { font-style: italic }
+/* GenericError */ .chroma .gr {  }
+/* GenericHeading */ .chroma .gh { font-weight: bold }
+/* GenericInserted */ .chroma .gi {  }
+/* GenericOutput */ .chroma .go {  }
+/* GenericPrompt */ .chroma .gp { font-weight: bold }
+/* GenericStrong */ .chroma .gs { font-weight: bold }
+/* GenericSubheading */ .chroma .gu { font-weight: bold }
+/* GenericTraceback */ .chroma .gt {  }
+/* GenericUnderline */ .chroma .gl {  }
+/* TextWhitespace */ .chroma .w {  }
diff --git a/docs/config.toml b/docs/config.toml
index 2c9814d63..bde8f3da3 100644
--- a/docs/config.toml
+++ b/docs/config.toml
@@ -14,6 +14,10 @@ disablePathToLower = true
     [markup.goldmark.renderer]
       unsafe = true
 
+  [markup.highlight]
+    noClasses=false
+    style = 'vs'
+
 [params]
   description = "MySqlConnector is a high-performance, asynchronous C# ADO.NET driver for MySQL Server, MariaDB, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL for MySQL, Percona Server and more."
   author = "MySqlConnector Authors"
diff --git a/docs/layouts/partials/header.html b/docs/layouts/partials/header.html
index f2c063118..59b8aa401 100644
--- a/docs/layouts/partials/header.html
+++ b/docs/layouts/partials/header.html
@@ -14,6 +14,8 @@
 
       gtag('config', 'UA-142489411-1');
     </script>
+    <link rel="preconnect" href="https://fonts.googleapis.com">
+    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
     {{ .Scratch.Add "description" "" }}{{ if .Description }}{{ .Scratch.Set "description" .Description }}{{ else }}{{ .Scratch.Set "description" .Site.Params.description }}{{ end }}
     <meta name="description" content="{{ .Scratch.Get "description" }}">
     {{ with .Site.Params.author }}<meta name="author" content="{{ . }}">{{ end }}
@@ -53,6 +55,7 @@
 
     {{ $style := resources.Get "css/site.css" | resources.Minify | resources.Fingerprint }}
     <link rel="stylesheet" href="{{ $style.Permalink }}">
+    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
     <link rel="canonical" href="{{ .Page.Permalink }}" />
   </head>
 

From ce21aa3e4005f48534b10e6ab8a85a7002889cda Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 29 Jan 2024 13:08:10 -0800
Subject: [PATCH 268/393] Always set CLIENT_MULTI_RESULTS flag in handshake.
 Fixes #1445

This fixes a regression in b35721f77dbb8a53e1c5f916f33623d1db7d8518 that cleared this flag.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Payloads/HandshakeResponse41Payload.cs    | 29 +++++++++----------
 1 file changed, 14 insertions(+), 15 deletions(-)

diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
index ba0e22f37..4628d1507 100644
--- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
@@ -9,27 +9,26 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 	{
 		var writer = new ByteBufferWriter();
 
-		var clientCapabilities = (ProtocolCapabilities.Protocol41 |
-			(cs.InteractiveSession ? ProtocolCapabilities.Interactive : 0) |
-			ProtocolCapabilities.LongPassword |
-			ProtocolCapabilities.Transactions |
+		var clientCapabilities =
+			ProtocolCapabilities.Protocol41 |
+			(cs.InteractiveSession ? (serverCapabilities & ProtocolCapabilities.Interactive) : 0) |
+			(serverCapabilities & ProtocolCapabilities.LongPassword) |
+			(serverCapabilities & ProtocolCapabilities.Transactions) |
 			ProtocolCapabilities.SecureConnection |
-			ProtocolCapabilities.PluginAuth |
-			ProtocolCapabilities.PluginAuthLengthEncodedClientData |
+			(serverCapabilities & ProtocolCapabilities.PluginAuth) |
+			(serverCapabilities & ProtocolCapabilities.PluginAuthLengthEncodedClientData) |
 			ProtocolCapabilities.MultiStatements |
 			ProtocolCapabilities.MultiResults |
 			(cs.AllowLoadLocalInfile ? ProtocolCapabilities.LocalFiles : 0) |
-			(string.IsNullOrWhiteSpace(cs.Database)
-				? 0
-				: ProtocolCapabilities.ConnectWithDatabase) |
+			(string.IsNullOrWhiteSpace(cs.Database) ? 0 : ProtocolCapabilities.ConnectWithDatabase) |
 			(cs.UseAffectedRows ? 0 : ProtocolCapabilities.FoundRows) |
 			(useCompression ? ProtocolCapabilities.Compress : ProtocolCapabilities.None) |
-			ProtocolCapabilities.ConnectionAttributes |
-			ProtocolCapabilities.SessionTrack |
-			ProtocolCapabilities.DeprecateEof |
-			ProtocolCapabilities.QueryAttributes |
-			ProtocolCapabilities.MariaDbCacheMetadata |
-			additionalCapabilities) & serverCapabilities;
+			(serverCapabilities & ProtocolCapabilities.ConnectionAttributes) |
+			(serverCapabilities & ProtocolCapabilities.SessionTrack) |
+			(serverCapabilities & ProtocolCapabilities.DeprecateEof) |
+			(serverCapabilities & ProtocolCapabilities.QueryAttributes) |
+			(serverCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) |
+			additionalCapabilities;
 
 		writer.Write((int) clientCapabilities);
 		writer.Write(0x4000_0000);

From fcb2d1b7e32c1a2f5aac709058fdcca34e5120ef Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 1 Feb 2024 08:52:52 -0800
Subject: [PATCH 269/393] Update integration tests to MariaDB 11.2.

---
 .ci/docker-run.sh    | 2 +-
 azure-pipelines.yml  | 4 ++--
 docs/content/home.md | 4 ++--
 tests/README.md      | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index 9abcf5f45..754c5fe9b 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -31,7 +31,7 @@ fi
 if [[ "$IMAGE" == mariadb* ]]; then
   MYSQL_EXTRA='--in-predicate-conversion-threshold=100000'
 fi
-if [ "$IMAGE" == "mariadb:11.1" ]; then
+if [ "$IMAGE" == "mariadb:11.2" ]; then
   MYSQL='mariadb'
 fi
 
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 2c0389e04..837deae98 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -245,8 +245,8 @@ jobs:
         image: 'mariadb:10.11'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
-      'MariaDB 11.1':
-        image: 'mariadb:11.1'
+      'MariaDB 11.2':
+        image: 'mariadb:11.2'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
   steps:
diff --git a/docs/content/home.md b/docs/content/home.md
index 0ba958a29..e93eb9c52 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -64,8 +64,8 @@ Server  | Versions | Notes
 Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://mysqlconnector.net/troubleshooting/aurora-freeze/)
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
-MariaDB | 10.x, 11.x (**10.6**, **10.11**, **11.1**) |
-MySQL | 5.5, 5.6, **5.7**, **8.0**, 8.1, **8.2** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
+MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.2**) |
+MySQL | 5.5, 5.6, **5.7**, 8.x (**8.0**, **8.3**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
 ProxySQL | 2.x | Some [compatibility issues](https://github.com/search?q=repo%3Amysql-net%2FMySqlConnector+proxysql&type=issues)
diff --git a/tests/README.md b/tests/README.md
index e132c52c1..ebd10e2dc 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,7 @@ It also verifies that MySqlConnector and MySQL Connector/NET (MySql.Data) have s
 
 The tests require a MySQL server. The simplest way to run one is with [Docker](https://www.docker.com/community-edition):
 
-    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:8.2 --max-allowed-packet=96M --character-set-server=utf8mb4 --log-bin-trust-function-creators=1 --local-infile=1 --max-connections=250
+    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:8.3 --max-allowed-packet=96M --character-set-server=utf8mb4 --log-bin-trust-function-creators=1 --local-infile=1 --max-connections=250
     docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes';"
 
 Copy the file `IntegrationTests/config.json.example` to `IntegrationTests/config.json`, then edit

From b8dccf91d3f6d617b195929bdef7acae02c1d7c2 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 3 Feb 2024 20:51:04 -0800
Subject: [PATCH 270/393] Stop using deprecated mysql_native_password plugin.
 Fixes #1345

---
 .ci/docker-run.sh               | 3 ---
 .ci/integration-tests-steps.yml | 6 +++---
 azure-pipelines.yml             | 4 ++--
 3 files changed, 5 insertions(+), 8 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index 754c5fe9b..b4d207e82 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -25,9 +25,6 @@ OMIT_FEATURES=$3
 MYSQL_EXTRA=
 MYSQL=mysql
 
-if [[ "$IMAGE" == mysql:8.0 || "$IMAGE" == mysql:8.3 ]]; then
-  MYSQL_EXTRA='--default-authentication-plugin=mysql_native_password'
-fi
 if [[ "$IMAGE" == mariadb* ]]; then
   MYSQL_EXTRA='--in-predicate-conversion-threshold=100000'
 fi
diff --git a/.ci/integration-tests-steps.yml b/.ci/integration-tests-steps.yml
index d56e29c11..14f8a0cc6 100644
--- a/.ci/integration-tests-steps.yml
+++ b/.ci/integration-tests-steps.yml
@@ -20,7 +20,7 @@ steps:
 - template: 'conformance-test-steps.yml'
   parameters:
     image: ${{ parameters.image }}
-    connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=conformance;ssl mode=none;DefaultCommandTimeout=3600'
+    connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=conformance;ssl mode=none;DefaultCommandTimeout=3600;${{ parameters.connectionStringExtra }}'
 - template: 'integration-test-steps.yml'
   parameters:
     image: ${{ parameters.image }}
@@ -32,14 +32,14 @@ steps:
   parameters:
     image: ${{ parameters.image }}
     unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
-    connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;UseCompression=True;DefaultCommandTimeout=3600'
+    connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;UseCompression=True;DefaultCommandTimeout=3600;${{ parameters.connectionStringExtra }}'
     platform: 'net6.0'
     description: 'Compression'
 - template: 'integration-test-steps.yml'
   parameters:
     image: ${{ parameters.image }}
     unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
-    connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600'
+    connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;${{ parameters.connectionStringExtra }}'
     platform: 'net6.0'
     description: 'No SSL'
 - template: 'integration-test-steps.yml'
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 837deae98..c0c83dd5b 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -231,11 +231,11 @@ jobs:
         unsupportedFeatures: 'CachingSha2Password,Ed25519,QueryAttributes,Tls11,Tls13,UuidToBin'
       'MySQL 8.0':
         image: 'mysql:8.0'
-        connectionStringExtra: ''
+        connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
       'MySQL 8.3':
         image: 'mysql:8.3'
-        connectionStringExtra: ''
+        connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
       'MariaDB 10.6':
         image: 'mariadb:10.6'

From 88a5b6e59976ec57cd635ed624fe85fed583c72c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 4 Feb 2024 07:00:24 -0800
Subject: [PATCH 271/393] Avoid deprecated log_bin_trust_function_creators
 flag. Fixes #1346

---
 .ci/docker-run.sh | 2 +-
 tests/README.md   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index b4d207e82..b879e7a5f 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -43,7 +43,7 @@ docker run -d \
 	-e MYSQL_ROOT_PASSWORD='test' \
 	--tmpfs /var/lib/mysql \
 	$IMAGE \
-  --log-bin-trust-function-creators=1 \
+  --disable-log-bin \
   --local-infile=1 \
   --secure-file-priv=/var/tmp \
   --max-connections=250 \
diff --git a/tests/README.md b/tests/README.md
index ebd10e2dc..a504f1467 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,7 @@ It also verifies that MySqlConnector and MySQL Connector/NET (MySql.Data) have s
 
 The tests require a MySQL server. The simplest way to run one is with [Docker](https://www.docker.com/community-edition):
 
-    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:8.3 --max-allowed-packet=96M --character-set-server=utf8mb4 --log-bin-trust-function-creators=1 --local-infile=1 --max-connections=250
+    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:8.3 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
     docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes';"
 
 Copy the file `IntegrationTests/config.json.example` to `IntegrationTests/config.json`, then edit

From b78e12ab9f0df23f57f4d0f4a437b777848efba9 Mon Sep 17 00:00:00 2001
From: Richard Banks <rbanks54@msn.com>
Date: Tue, 6 Feb 2024 09:40:33 +1100
Subject: [PATCH 272/393] [Docs] Update migration guide

Signed-off-by: Richard Banks <rbanks54@msn.com>
---
 .../tutorials/migrating-from-connector-net.md | 98 ++++++++++---------
 1 file changed, 51 insertions(+), 47 deletions(-)

diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index bb7387060..77863ad26 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -91,7 +91,7 @@ MySqlConnector has some different default connection string options:
     <td><code>LoadBalance</code></td>
     <td>Default is <code>RoundRobin</code></td>
     <td>(not configurable, effective default is <code>FailOver</code>)</td>
-    <td>Connector/NET currently has <a href="https://bugs.mysql.com/bug.php?id=81650" title="MySQL bug #81650">a bug</a> that prevents multiple host names being used.</td>
+    <td>Connector/NET versions prior to v8.0.19 have <a href="https://bugs.mysql.com/bug.php?id=81650" title="MySQL bug #81650">a bug</a> that prevents multiple host names being used.</td>
   </tr>
   <tr>
     <td><code>ServerRSAPublicKeyFile</code></td>
@@ -112,8 +112,8 @@ supported in MySqlConnector, see the [Connection Options](/connection-options).
 
 Connector/NET implements the standard ADO.NET async methods, and adds some new ones (e.g., `MySqlConnection.BeginTransactionAsync`,
 `MySqlDataAdapter.FillAsync`) that don't exist in ADO.NET. None of these methods have an asynchronous implementation,
-but all execute synchronously then return a completed `Task`. This is a [longstanding known bug](https://bugs.mysql.com/bug.php?id=70111)
-in Connector/NET.
+but all execute synchronously then return a completed `Task`. This [longstanding known bug](https://bugs.mysql.com/bug.php?id=70111) was fixed in
+in Connector/NET v8.0.33.
 
 Because the Connector/NET methods aren't actually asynchronous, porting client code to MySqlConnector (which is asynchronous)
 can expose bugs that only occur when an async method completes asynchronously and resumes the `await`-ing code
@@ -202,85 +202,102 @@ for various precondition checks that indicate misuse of the API (and not a probl
 
 ## Fixed Bugs
 
-The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~~Strikethrough~~ indicates bugs that have since been fixed in a newer version of Connector/NET, but were fixed first in MySqlConnector.)
+The following bugs in Connector/NET are fixed by switching to MySqlConnector.
 
-* [#14115](https://bugs.mysql.com/bug.php?id=14115): Compound statements are not supported by `MySqlCommand.Prepare`
+Open Bugs:
+
+* [#14115](https://bugs.mysql.com/bug.php?id=14115): Compound statements are not supported by `MySqlCommand.Prepare` (Will never be supported)
 * [#37283](https://bugs.mysql.com/bug.php?id=37283), [#70587](https://bugs.mysql.com/bug.php?id=70587): Distributed transactions are not supported
 * [#50773](https://bugs.mysql.com/bug.php?id=50773): Can’t use multiple connections within one TransactionScope
 * [#61477](https://bugs.mysql.com/bug.php?id=61477): `ColumnOrdinal` in schema table is 1-based
-* ~~[#66476](https://bugs.mysql.com/bug.php?id=66476), [#106368](https://bugs.mysql.com/bug.php?id=106368): Connection pool uses queue instead of stack~~
-* ~[#70111](https://bugs.mysql.com/bug.php?id=70111): `Async` methods execute synchronously~
-* ~~[#70686](https://bugs.mysql.com/bug.php?id=70686): `TIME(3)` and `TIME(6)` fields serialize milliseconds incorrectly~~
 * [#72494](https://bugs.mysql.com/bug.php?id=72494), [#83330](https://bugs.mysql.com/bug.php?id=83330): EndOfStreamException inserting large blob with UseCompression=True
-* [#73610](https://bugs.mysql.com/bug.php?id=73610): Invalid password exception has wrong number
 * [#73788](https://bugs.mysql.com/bug.php?id=73788): Can’t use `DateTimeOffset`
-* ~~[#74392](https://bugs.mysql.com/bug.php?id=74392): Can't use `Stream` to bulk load data~~
-* ~~[#75604](https://bugs.mysql.com/bug.php?id=75604): Crash after 29.4 days of uptime~~
 * [#75917](https://bugs.mysql.com/bug.php?id=75917), [#76597](https://bugs.mysql.com/bug.php?id=76597), [#77691](https://bugs.mysql.com/bug.php?id=77691), [#78650](https://bugs.mysql.com/bug.php?id=78650), [#78919](https://bugs.mysql.com/bug.php?id=78919), [#80921](https://bugs.mysql.com/bug.php?id=80921), [#82136](https://bugs.mysql.com/bug.php?id=82136): “Reading from the stream has failed” when connecting to a server
 * [#77421](https://bugs.mysql.com/bug.php?id=77421): Connection is not reset when pulled from the connection pool
+* [#83229](https://bugs.mysql.com/bug.php?id=83329): “Unknown command” exception inserting large blob with UseCompression=True
+* [#85185](https://bugs.mysql.com/bug.php?id=85185): `ConnectionReset=True` does not preserve connection charset
+* [#88124](https://bugs.mysql.com/bug.php?id=88124): CommandTimeout isn’t reset when calling Read/NextResult
+* [#88611](https://bugs.mysql.com/bug.php?id=88611): `MySqlCommand` can be executed even if it has “wrong” transaction
+* [#89085](https://bugs.mysql.com/bug.php?id=89085): `MySqlConnection.Database` not updated after `USE database;`
+* [#89335](https://bugs.mysql.com/bug.php?id=89335): `MySqlCommandBuilder.DeriveParameters` fails for `JSON` type
+* [#91123](https://bugs.mysql.com/bug.php?id=91123): Database names are case-sensitive when calling a stored procedure
+* [#91199](https://bugs.mysql.com/bug.php?id=91199): Can't insert `MySqlDateTime` values
+* [#91753](https://bugs.mysql.com/bug.php?id=91753): Unnamed parameter not supported by `MySqlCommand.Prepare`
+* [#91754](https://bugs.mysql.com/bug.php?id=91754): Inserting 16MiB `BLOB` shifts it by four bytes when prepared
+* [#92367](https://bugs.mysql.com/bug.php?id=92367): `MySqlDataReader.GetDateTime` and `GetValue` return inconsistent values
+* [#93047](https://bugs.mysql.com/bug.php?id=93047): `MySqlDataAdapter` throws timeout exception when an error occurs
+* [#93220](https://bugs.mysql.com/bug.php?id=93220): Can’t call FUNCTION when parameter name contains parentheses
+* [#93825](https://bugs.mysql.com/bug.php?id=93825): `MySqlException` loses data when serialized
+* [#94760](https://bugs.mysql.com/bug.php?id=94760): `MySqlConnection.OpenAsync(CancellationToken)` doesn’t respect cancellation token
+* [#95348](https://bugs.mysql.com/bug.php?id=95348): Inefficient query when executing stored procedures
+* [#95436](https://bugs.mysql.com/bug.php?id=95436): Client doesn't authenticate with PEM certificate
+* [#96500](https://bugs.mysql.com/bug.php?id=96500): `MySqlDataReader.GetFieldValue<MySqlGeometry>` throws `InvalidCastException`
+* [#96636](https://bugs.mysql.com/bug.php?id=96636): `MySqlConnection.Open()` slow under load when using SSL
+* [#96717](https://bugs.mysql.com/bug.php?id=96717): Not compatible with MySQL Server 5.0
+* [#97067](https://bugs.mysql.com/bug.php?id=97067): Aggregate functions on BIT(n) columns return wrong result
+* [#97738](https://bugs.mysql.com/bug.php?id=97738): Cannot use PEM files when account uses `require subject`
+* [#97872](https://bugs.mysql.com/bug.php?id=97872): `KeepAlive` in connection string throws exception on .NET Core
+* [#103819](https://bugs.mysql.com/bug.php?id=103819): Can't use `StringBuilder` containing non-BMP characters as `MySqlParameter.Value`
+* [#105965](https://bugs.mysql.com/bug.php?id=105965): `MySqlParameterCollection.Add(object)` has quadratic performance
+* [#107259](https://bugs.mysql.com/bug.php?id=107259): Character Set 'utf8mb3' is not supported by .NET Framework
+* [#108970](https://bugs.mysql.com/bug.php?id=108970): `MySqlConnectionStringBuilder.ContainsKey` method gives wrong result
+* [#109141](https://bugs.mysql.com/bug.php?id=109141): Insert of data into a table results in `System.ArgumentException`
+* [#109331](https://bugs.mysql.com/bug.php?id=109331): MySQL Connector/NET is incompatible with MariaDB 10.10 and later
+* [#109390](https://bugs.mysql.com/bug.php?id=109390): Transaction lock held after connection timeout exception
+* [#109476](https://bugs.mysql.com/bug.php?id=109476): `TransactionScope.Dispose` throws "Connection must be valid and open to rollback"
+* [#110789](https://bugs.mysql.com/bug.php?id=110789): `OpenAsync` throws unhandled exception from thread pool
+* [#110790](https://bugs.mysql.com/bug.php?id=110790): `ExecuteReaderAsync` hangs instead of cancelling query after `CommandTimeout`
+* [#110791](https://bugs.mysql.com/bug.php?id=110791): `OpenAsync(CancellationToken)` doesn't throw for cancelled token
+* [#111797](https://bugs.mysql.com/bug.php?id=111797): Certain sequence of special characters can break connection string validation.
+* [#112088](https://bugs.mysql.com/bug.php?id=112088): `BIT(n)` parameter size incorrectly returns 0
+
+Fixed first by MySqlConnector:
+* ~~[#66476](https://bugs.mysql.com/bug.php?id=66476), [#106368](https://bugs.mysql.com/bug.php?id=106368): Connection pool uses queue instead of stack (fixed in v8.0.30)~~
+* ~~[#70111](https://bugs.mysql.com/bug.php?id=70111): `Async` methods execute synchronously (fixed in v8.0.33)~~
+* ~~[#70686](https://bugs.mysql.com/bug.php?id=70686): `TIME(3)` and `TIME(6)` fields serialize milliseconds incorrectly~~
+* ~~[#73610](https://bugs.mysql.com/bug.php?id=73610): Invalid password exception has wrong number (fixed in v8.0.32)~~
+* ~~[#74392](https://bugs.mysql.com/bug.php?id=74392): Can't use `Stream` to bulk load data~~
+* ~~[#75604](https://bugs.mysql.com/bug.php?id=75604): Crash after 29.4 days of uptime~~
 * ~~[#78426](https://bugs.mysql.com/bug.php?id=78426): Unknown database exception has wrong number~~
 * ~~[#78760](https://bugs.mysql.com/bug.php?id=78760): Error when using tabs and newlines in SQL statements~~
 * ~~[#78917](https://bugs.mysql.com/bug.php?id=78917), [#79196](https://bugs.mysql.com/bug.php?id=79196), [#82292](https://bugs.mysql.com/bug.php?id=82292), [#89040](https://bugs.mysql.com/bug.php?id=89040): `TINYINT(1)` values start being returned as `sbyte` after `NULL`~~
 * ~~[#80030](https://bugs.mysql.com/bug.php?id=80030): Slow to connect with pooling disabled~~
 * ~~[#81650](https://bugs.mysql.com/bug.php?id=81650), [#88962](https://bugs.mysql.com/bug.php?id=88962): `Server` connection string option may now contain multiple, comma separated hosts that will be tried in order until a connection succeeds~~
-* [#83229](https://bugs.mysql.com/bug.php?id=83329): “Unknown command” exception inserting large blob with UseCompression=True
 * ~~[#83649](https://bugs.mysql.com/bug.php?id=83649): Connection cannot be made using IPv6~~
 * ~~[#84220](https://bugs.mysql.com/bug.php?id=84220): Cannot call a stored procedure with `.` in its name~~
 * ~~[#84701](https://bugs.mysql.com/bug.php?id=84701): Can’t create a parameter using a 64-bit enum with a value greater than int.MaxValue~~
-* [#85185](https://bugs.mysql.com/bug.php?id=85185): `ConnectionReset=True` does not preserve connection charset
 * ~~[#86263](https://bugs.mysql.com/bug.php?id=86263): Transaction isolation level affects all transactions in session~~
 * ~~[#87307](https://bugs.mysql.com/bug.php?id=87307): NextResult hangs instead of timing out~~
 * ~~[#87316](https://bugs.mysql.com/bug.php?id=87316): MySqlCommand.CommandTimeout can be set to a negative value~~
 * ~~[#87868](https://bugs.mysql.com/bug.php?id=87868): `ColumnSize` in schema table is incorrect for `CHAR(36)` and `BLOB` columns~~
 * ~~[#87876](https://bugs.mysql.com/bug.php?id=87876): `IsLong` is schema table is incorrect for `LONGTEXT` and `LONGBLOB` columns~~
 * ~~[#88058](https://bugs.mysql.com/bug.php?id=88058): `decimal(n, 0)` has wrong `NumericPrecision`~~
-* [#88124](https://bugs.mysql.com/bug.php?id=88124): CommandTimeout isn’t reset when calling Read/NextResult
 * ~~[#88472](https://bugs.mysql.com/bug.php?id=88472): `TINYINT(1)` is not returned as `bool` if `MySqlCommand.Prepare` is called~~
-* [#88611](https://bugs.mysql.com/bug.php?id=88611): `MySqlCommand` can be executed even if it has “wrong” transaction
 * ~~[#88660](https://bugs.mysql.com/bug.php?id=88660): `MySqlClientFactory.Instance.CreateDataAdapter()` and `CreateCommandBuilder` return `null`~~
-* [#89085](https://bugs.mysql.com/bug.php?id=89085): `MySqlConnection.Database` not updated after `USE database;`
 * ~~[#89159](https://bugs.mysql.com/bug.php?id=89159), [#97242](https://bugs.mysql.com/bug.php?id=97242): `MySqlDataReader` cannot outlive `MySqlCommand`~~
-* [#89335](https://bugs.mysql.com/bug.php?id=89335): `MySqlCommandBuilder.DeriveParameters` fails for `JSON` type
 * ~~[#89639](https://bugs.mysql.com/bug.php?id=89639): `ReservedWords` schema contains incorrect data~~
 * ~~[#90086](https://bugs.mysql.com/bug.php?id=90086): `MySqlDataReader` is closed by an unrelated command disposal~~
-* [#91123](https://bugs.mysql.com/bug.php?id=91123): Database names are case-sensitive when calling a stored procedure
-* [#91199](https://bugs.mysql.com/bug.php?id=91199): Can't insert `MySqlDateTime` values
 * ~~[#91751](https://bugs.mysql.com/bug.php?id=91751): `YEAR` column retrieved incorrectly with prepared command~~
 * ~~[#91752](https://bugs.mysql.com/bug.php?id=91752): `00:00:00` is converted to `NULL` with prepared command~~
-* [#91753](https://bugs.mysql.com/bug.php?id=91753): Unnamed parameter not supported by `MySqlCommand.Prepare`
-* [#91754](https://bugs.mysql.com/bug.php?id=91754): Inserting 16MiB `BLOB` shifts it by four bytes when prepared
 * ~~[#91770](https://bugs.mysql.com/bug.php?id=91770): `TIME(n)` column loses microseconds with prepared command~~
-* [#92367](https://bugs.mysql.com/bug.php?id=92367): `MySqlDataReader.GetDateTime` and `GetValue` return inconsistent values
 * ~~[#92465](https://bugs.mysql.com/bug.php?id=92465): “There is already an open DataReader” `MySqlException` thrown from `TransactionScope.Dispose`~~
 * ~~[#92734](https://bugs.mysql.com/bug.php?id=92734): `MySqlParameter.Clone` doesn't copy all property values~~
 * ~~[#92789](https://bugs.mysql.com/bug.php?id=92789): Illegal connection attributes written for non-ASCII values~~
 * ~~[#92912](https://bugs.mysql.com/bug.php?id=92912): `MySqlDbType.LongText` values encoded incorrectly with prepared statements~~
 * ~~[#92982](https://bugs.mysql.com/bug.php?id=92982), [#93399](https://bugs.mysql.com/bug.php?id=93399): `FormatException` thrown when connecting to MySQL Server 8.0.13~~
-* [#93047](https://bugs.mysql.com/bug.php?id=93047): `MySqlDataAdapter` throws timeout exception when an error occurs
 * ~~[#93202](https://bugs.mysql.com/bug.php?id=93202): Connector runs `SHOW VARIABLES` when connection is made~~
-* [#93220](https://bugs.mysql.com/bug.php?id=93220): Can’t call FUNCTION when parameter name contains parentheses
 * ~~[#93370](https://bugs.mysql.com/bug.php?id=93370): `MySqlParameterCollection.Add` precondition check isn't consistent~~
 * ~~[#93374](https://bugs.mysql.com/bug.php?id=93374): `MySqlDataReader.GetStream` throws `IndexOutOfRangeException`~~
-* [#93825](https://bugs.mysql.com/bug.php?id=93825): `MySqlException` loses data when serialized
 * ~~[#94075](https://bugs.mysql.com/bug.php?id=94075): `MySqlCommand.Cancel` throws exception~~
-* [#94760](https://bugs.mysql.com/bug.php?id=94760): `MySqlConnection.OpenAsync(CancellationToken)` doesn’t respect cancellation token
-* [#95348](https://bugs.mysql.com/bug.php?id=95348): Inefficient query when executing stored procedures
-* [#95436](https://bugs.mysql.com/bug.php?id=95436): Client doesn't authenticate with PEM certificate
 * ~~[#95984](https://bugs.mysql.com/bug.php?id=95984): “Incorrect arguments to mysqld_stmt_execute” using prepared statement with `MySqlDbType.JSON`~~
 * ~~[#95986](https://bugs.mysql.com/bug.php?id=95986): “Incorrect integer value” using prepared statement with `MySqlDbType.Int24`~~
 * ~~[#96355](https://bugs.mysql.com/bug.php?id=96355), [#96614](https://bugs.mysql.com/bug.php?id=96614): `Could not load file or assembly 'Renci.SshNet'` when opening connection~~
 * ~~[#96498](https://bugs.mysql.com/bug.php?id=96498): `WHERE` clause using `MySqlGeometry` as parameter finds no rows~~
 * ~~[#96499](https://bugs.mysql.com/bug.php?id=96499): `MySqlException` when inserting a `MySqlGeometry` value~~
-* [#96500](https://bugs.mysql.com/bug.php?id=96500): `MySqlDataReader.GetFieldValue<MySqlGeometry>` throws `InvalidCastException`
-* [#96636](https://bugs.mysql.com/bug.php?id=96636): `MySqlConnection.Open()` slow under load when using SSL
-* [#96717](https://bugs.mysql.com/bug.php?id=96717): Not compatible with MySQL Server 5.0
 * ~~[#97061](https://bugs.mysql.com/bug.php?id=97061): `MySqlCommand.LastInsertedId` returns 0 after executing multiple statements~~
-* [#97067](https://bugs.mysql.com/bug.php?id=97067): Aggregate functions on BIT(n) columns return wrong result
 * ~~[#97300](https://bugs.mysql.com/bug.php?id=97300): `GetSchemaTable()` returns table for stored procedure with output parameters~~
 * ~~[#97448](https://bugs.mysql.com/bug.php?id=97448): Connecting fails if more than one IP is found in DNS for a named host~~
 * ~~[#97473](https://bugs.mysql.com/bug.php?id=97473): `MySqlConnection.Clone` discloses connection password~~
-* [#97738](https://bugs.mysql.com/bug.php?id=97738): Cannot use PEM files when account uses `require subject`
-* [#97872](https://bugs.mysql.com/bug.php?id=97872): `KeepAlive` in connection string throws exception on .NET Core
 * ~~[#98322](https://bugs.mysql.com/bug.php?id=98322): `new MySqlConnection(null)` throws `NullReferenceException`~~
 * ~~[#99091](https://bugs.mysql.com/bug.php?id=99091): Unexpected return value getting integer for `TINYINT(1)` column~~
 * ~~[#99793](https://bugs.mysql.com/bug.php?id=99793): Prepared stored procedure command doesn't verify parameter types~~
@@ -300,7 +317,6 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * ~~[#103390](https://bugs.mysql.com/bug.php?id=103390): Can't query `CHAR(36)` column if `MySqlCommand` is prepared~~
 * ~~[#103430](https://bugs.mysql.com/bug.php?id=103430): Can't connect using named pipe on Windows~~
 * ~~[#103801](https://bugs.mysql.com/bug.php?id=103801): `TimeSpan` parameters lose microseconds with prepared statement~~
-* [#103819](https://bugs.mysql.com/bug.php?id=103819): Can't use `StringBuilder` containing non-BMP characters as `MySqlParameter.Value`
 * ~~[#104910](https://bugs.mysql.com/bug.php?id=104910): `MySqlConnectionStringBuilder.TryGetValue` always returns `false`~~
 * ~~[#104913](https://bugs.mysql.com/bug.php?id=104913): Cannot execute stored procedure with backtick in name~~
 * ~~[#105209](https://bugs.mysql.com/bug.php?id=105209): Timespan value of zero can't be read with prepared command~~
@@ -308,25 +324,13 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
 * ~~[#105728](https://bugs.mysql.com/bug.php?id=105728): Named command parameters override query attribute values~~
 * ~~[#105730](https://bugs.mysql.com/bug.php?id=105730): `MySqlCommand.Clone` doesn't clone attributes~~
 * ~~[#105768](https://bugs.mysql.com/bug.php?id=105768): `MySqlCommandBuilder` doesn't support tables with `BIGINT UNSIGNED` primary key~~
-* [#105965](https://bugs.mysql.com/bug.php?id=105965): `MySqlParameterCollection.Add(object)` has quadratic performance
 * ~~[#106242](https://bugs.mysql.com/bug.php?id=106242): `MySqlConnection.Open` throws `AggregateException` instead of `MySqlException`~~
 * ~~[#106243](https://bugs.mysql.com/bug.php?id=106243): `CancellationToken` doesn't cancel `MySqlConnection.OpenAsync`~~
 * ~~[#106244](https://bugs.mysql.com/bug.php?id=106244): `MySqlDataReader.GetFieldValue<Stream>` throws `InvalidCastException`~~
 * ~~[#106247](https://bugs.mysql.com/bug.php?id=106247): Can't use ~~`MySqlDbType.Enum` or~~ `MySqlDbType.Set` with prepared command~~
-* [#107259](https://bugs.mysql.com/bug.php?id=107259): Character Set 'utf8mb3' is not supported by .NET Framework
 * ~~[#108756](https://bugs.mysql.com/bug.php?id=108756): Can't insert negative number using prepared statement with `MySqlDbType.Int24`~~
-* [#108970](https://bugs.mysql.com/bug.php?id=108970): `MySqlConnectionStringBuilder.ContainsKey` method gives wrong result
-* [#109141](https://bugs.mysql.com/bug.php?id=109141): Insert of data into a table results in `System.ArgumentException`
-* [#109331](https://bugs.mysql.com/bug.php?id=109331): MySQL Connector/NET is incompatible with MariaDB 10.10 and later
-* [#109390](https://bugs.mysql.com/bug.php?id=109390): Transaction lock held after connection timeout exception
-* [#109476](https://bugs.mysql.com/bug.php?id=109476): `TransactionScope.Dispose` throws "Connection must be valid and open to rollback"
 * ~~[#109670](https://bugs.mysql.com/bug.php?id=109670), [#109752](https://bugs.mysql.com/bug.php?id=109752): Many MSB3246 errors when referencing MySql.Data 8.0.32~~
 * ~~[#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.~~
 * ~~[#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.~~
 * ~~[#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'~~
 * ~~[#110717](https://bugs.mysql.com/bug.php?id=110717), [#111759](https://bugs.mysql.com/bug.php?id=111759): MySql.Data is not thread-safe~~
-* [#110789](https://bugs.mysql.com/bug.php?id=110789): `OpenAsync` throws unhandled exception from thread pool
-* [#110790](https://bugs.mysql.com/bug.php?id=110790): `ExecuteReaderAsync` hangs instead of cancelling query after `CommandTimeout`
-* [#110791](https://bugs.mysql.com/bug.php?id=110791): `OpenAsync(CancellationToken)` doesn't throw for cancelled token
-* [#111797](https://bugs.mysql.com/bug.php?id=111797): Certain sequence of special characters can break connection string validation.
-* [#112088](https://bugs.mysql.com/bug.php?id=112088): `BIT(n)` parameter size incorrectly returns 0

From dd518bf99fa385104111a1de7be9f28b2db39e9f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 5 Feb 2024 17:00:37 -0800
Subject: [PATCH 273/393] Remove strikethrough from fixed bugs.

Now that these are in their own section, the text decoration (which was hard to read) is no longer necessary.
---
 .../tutorials/migrating-from-connector-net.md | 173 +++++++++---------
 1 file changed, 88 insertions(+), 85 deletions(-)

diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 77863ad26..84032f7d3 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2024-01-22
+lastmod: 2024-02-05
 date: 2016-10-16
 menu:
   main:
@@ -204,7 +204,7 @@ for various precondition checks that indicate misuse of the API (and not a probl
 
 The following bugs in Connector/NET are fixed by switching to MySqlConnector.
 
-Open Bugs:
+### Open Bugs
 
 * [#14115](https://bugs.mysql.com/bug.php?id=14115): Compound statements are not supported by `MySqlCommand.Prepare` (Will never be supported)
 * [#37283](https://bugs.mysql.com/bug.php?id=37283), [#70587](https://bugs.mysql.com/bug.php?id=70587): Distributed transactions are not supported
@@ -251,86 +251,89 @@ Open Bugs:
 * [#111797](https://bugs.mysql.com/bug.php?id=111797): Certain sequence of special characters can break connection string validation.
 * [#112088](https://bugs.mysql.com/bug.php?id=112088): `BIT(n)` parameter size incorrectly returns 0
 
-Fixed first by MySqlConnector:
-* ~~[#66476](https://bugs.mysql.com/bug.php?id=66476), [#106368](https://bugs.mysql.com/bug.php?id=106368): Connection pool uses queue instead of stack (fixed in v8.0.30)~~
-* ~~[#70111](https://bugs.mysql.com/bug.php?id=70111): `Async` methods execute synchronously (fixed in v8.0.33)~~
-* ~~[#70686](https://bugs.mysql.com/bug.php?id=70686): `TIME(3)` and `TIME(6)` fields serialize milliseconds incorrectly~~
-* ~~[#73610](https://bugs.mysql.com/bug.php?id=73610): Invalid password exception has wrong number (fixed in v8.0.32)~~
-* ~~[#74392](https://bugs.mysql.com/bug.php?id=74392): Can't use `Stream` to bulk load data~~
-* ~~[#75604](https://bugs.mysql.com/bug.php?id=75604): Crash after 29.4 days of uptime~~
-* ~~[#78426](https://bugs.mysql.com/bug.php?id=78426): Unknown database exception has wrong number~~
-* ~~[#78760](https://bugs.mysql.com/bug.php?id=78760): Error when using tabs and newlines in SQL statements~~
-* ~~[#78917](https://bugs.mysql.com/bug.php?id=78917), [#79196](https://bugs.mysql.com/bug.php?id=79196), [#82292](https://bugs.mysql.com/bug.php?id=82292), [#89040](https://bugs.mysql.com/bug.php?id=89040): `TINYINT(1)` values start being returned as `sbyte` after `NULL`~~
-* ~~[#80030](https://bugs.mysql.com/bug.php?id=80030): Slow to connect with pooling disabled~~
-* ~~[#81650](https://bugs.mysql.com/bug.php?id=81650), [#88962](https://bugs.mysql.com/bug.php?id=88962): `Server` connection string option may now contain multiple, comma separated hosts that will be tried in order until a connection succeeds~~
-* ~~[#83649](https://bugs.mysql.com/bug.php?id=83649): Connection cannot be made using IPv6~~
-* ~~[#84220](https://bugs.mysql.com/bug.php?id=84220): Cannot call a stored procedure with `.` in its name~~
-* ~~[#84701](https://bugs.mysql.com/bug.php?id=84701): Can’t create a parameter using a 64-bit enum with a value greater than int.MaxValue~~
-* ~~[#86263](https://bugs.mysql.com/bug.php?id=86263): Transaction isolation level affects all transactions in session~~
-* ~~[#87307](https://bugs.mysql.com/bug.php?id=87307): NextResult hangs instead of timing out~~
-* ~~[#87316](https://bugs.mysql.com/bug.php?id=87316): MySqlCommand.CommandTimeout can be set to a negative value~~
-* ~~[#87868](https://bugs.mysql.com/bug.php?id=87868): `ColumnSize` in schema table is incorrect for `CHAR(36)` and `BLOB` columns~~
-* ~~[#87876](https://bugs.mysql.com/bug.php?id=87876): `IsLong` is schema table is incorrect for `LONGTEXT` and `LONGBLOB` columns~~
-* ~~[#88058](https://bugs.mysql.com/bug.php?id=88058): `decimal(n, 0)` has wrong `NumericPrecision`~~
-* ~~[#88472](https://bugs.mysql.com/bug.php?id=88472): `TINYINT(1)` is not returned as `bool` if `MySqlCommand.Prepare` is called~~
-* ~~[#88660](https://bugs.mysql.com/bug.php?id=88660): `MySqlClientFactory.Instance.CreateDataAdapter()` and `CreateCommandBuilder` return `null`~~
-* ~~[#89159](https://bugs.mysql.com/bug.php?id=89159), [#97242](https://bugs.mysql.com/bug.php?id=97242): `MySqlDataReader` cannot outlive `MySqlCommand`~~
-* ~~[#89639](https://bugs.mysql.com/bug.php?id=89639): `ReservedWords` schema contains incorrect data~~
-* ~~[#90086](https://bugs.mysql.com/bug.php?id=90086): `MySqlDataReader` is closed by an unrelated command disposal~~
-* ~~[#91751](https://bugs.mysql.com/bug.php?id=91751): `YEAR` column retrieved incorrectly with prepared command~~
-* ~~[#91752](https://bugs.mysql.com/bug.php?id=91752): `00:00:00` is converted to `NULL` with prepared command~~
-* ~~[#91770](https://bugs.mysql.com/bug.php?id=91770): `TIME(n)` column loses microseconds with prepared command~~
-* ~~[#92465](https://bugs.mysql.com/bug.php?id=92465): “There is already an open DataReader” `MySqlException` thrown from `TransactionScope.Dispose`~~
-* ~~[#92734](https://bugs.mysql.com/bug.php?id=92734): `MySqlParameter.Clone` doesn't copy all property values~~
-* ~~[#92789](https://bugs.mysql.com/bug.php?id=92789): Illegal connection attributes written for non-ASCII values~~
-* ~~[#92912](https://bugs.mysql.com/bug.php?id=92912): `MySqlDbType.LongText` values encoded incorrectly with prepared statements~~
-* ~~[#92982](https://bugs.mysql.com/bug.php?id=92982), [#93399](https://bugs.mysql.com/bug.php?id=93399): `FormatException` thrown when connecting to MySQL Server 8.0.13~~
-* ~~[#93202](https://bugs.mysql.com/bug.php?id=93202): Connector runs `SHOW VARIABLES` when connection is made~~
-* ~~[#93370](https://bugs.mysql.com/bug.php?id=93370): `MySqlParameterCollection.Add` precondition check isn't consistent~~
-* ~~[#93374](https://bugs.mysql.com/bug.php?id=93374): `MySqlDataReader.GetStream` throws `IndexOutOfRangeException`~~
-* ~~[#94075](https://bugs.mysql.com/bug.php?id=94075): `MySqlCommand.Cancel` throws exception~~
-* ~~[#95984](https://bugs.mysql.com/bug.php?id=95984): “Incorrect arguments to mysqld_stmt_execute” using prepared statement with `MySqlDbType.JSON`~~
-* ~~[#95986](https://bugs.mysql.com/bug.php?id=95986): “Incorrect integer value” using prepared statement with `MySqlDbType.Int24`~~
-* ~~[#96355](https://bugs.mysql.com/bug.php?id=96355), [#96614](https://bugs.mysql.com/bug.php?id=96614): `Could not load file or assembly 'Renci.SshNet'` when opening connection~~
-* ~~[#96498](https://bugs.mysql.com/bug.php?id=96498): `WHERE` clause using `MySqlGeometry` as parameter finds no rows~~
-* ~~[#96499](https://bugs.mysql.com/bug.php?id=96499): `MySqlException` when inserting a `MySqlGeometry` value~~
-* ~~[#97061](https://bugs.mysql.com/bug.php?id=97061): `MySqlCommand.LastInsertedId` returns 0 after executing multiple statements~~
-* ~~[#97300](https://bugs.mysql.com/bug.php?id=97300): `GetSchemaTable()` returns table for stored procedure with output parameters~~
-* ~~[#97448](https://bugs.mysql.com/bug.php?id=97448): Connecting fails if more than one IP is found in DNS for a named host~~
-* ~~[#97473](https://bugs.mysql.com/bug.php?id=97473): `MySqlConnection.Clone` discloses connection password~~
-* ~~[#98322](https://bugs.mysql.com/bug.php?id=98322): `new MySqlConnection(null)` throws `NullReferenceException`~~
-* ~~[#99091](https://bugs.mysql.com/bug.php?id=99091): Unexpected return value getting integer for `TINYINT(1)` column~~
-* ~~[#99793](https://bugs.mysql.com/bug.php?id=99793): Prepared stored procedure command doesn't verify parameter types~~
-* ~~[#100159](https://bugs.mysql.com/bug.php?id=100159): SQL with DateTime parameter returns String value~~
-* ~~[#100208](https://bugs.mysql.com/bug.php?id=100208): `GetSchema("Procedures")` returns `ROUTINE_DEFINITION` of `"System.Byte[]"`~~
-* ~~[#100218](https://bugs.mysql.com/bug.php?id=100218): `TIME(n)` microsecond values deserialized incorrectly with prepared command~~
-* ~~[#100306](https://bugs.mysql.com/bug.php?id=100306): `Command.Prepare` sends wrong statement to server~~
-* ~~[#100522](https://bugs.mysql.com/bug.php?id=100522): `	MySqlCommand.Parameters.Insert(-1)` succeeds but should fail~~
-* ~~[#101252](https://bugs.mysql.com/bug.php?id=101252): Can't query `CHAR(36)` column containing `NULL`~~
-* ~~[#101253](https://bugs.mysql.com/bug.php?id=101253): Default value for `MySqlParameter.Value` changed from null to `0`~~
-* ~~[#101302](https://bugs.mysql.com/bug.php?id=101302): Stored Procedure `BOOL` parameter can only be mapped to `MySqlDbType.Byte`~~
-* ~~[#101485](https://bugs.mysql.com/bug.php?id=101485): Stored Procedure `JSON` parameter throws “Unhandled type encountered” `MySqlException`~~
-* ~~[#101507](https://bugs.mysql.com/bug.php?id=101507): `MySqlCommand.Cancel` throws `NullReferenceException` for a closed connection~~
-* ~~[#101714](https://bugs.mysql.com/bug.php?id=101714): Extremely slow performance reading result sets~~
-* ~~[#102303](https://bugs.mysql.com/bug.php?id=102303): Preparing command with incorrect parameter type puts connection in bad state~~
-* ~~[#102593](https://bugs.mysql.com/bug.php?id=102593): Can't use `MemoryStream` as `MySqlParameter.Value`~~
-* ~~[#103390](https://bugs.mysql.com/bug.php?id=103390): Can't query `CHAR(36)` column if `MySqlCommand` is prepared~~
-* ~~[#103430](https://bugs.mysql.com/bug.php?id=103430): Can't connect using named pipe on Windows~~
-* ~~[#103801](https://bugs.mysql.com/bug.php?id=103801): `TimeSpan` parameters lose microseconds with prepared statement~~
-* ~~[#104910](https://bugs.mysql.com/bug.php?id=104910): `MySqlConnectionStringBuilder.TryGetValue` always returns `false`~~
-* ~~[#104913](https://bugs.mysql.com/bug.php?id=104913): Cannot execute stored procedure with backtick in name~~
-* ~~[#105209](https://bugs.mysql.com/bug.php?id=105209): Timespan value of zero can't be read with prepared command~~
-* ~~[#105516](https://bugs.mysql.com/bug.php?id=105516): Connector/Net doesn't recognize utf8mb3~~
-* ~~[#105728](https://bugs.mysql.com/bug.php?id=105728): Named command parameters override query attribute values~~
-* ~~[#105730](https://bugs.mysql.com/bug.php?id=105730): `MySqlCommand.Clone` doesn't clone attributes~~
-* ~~[#105768](https://bugs.mysql.com/bug.php?id=105768): `MySqlCommandBuilder` doesn't support tables with `BIGINT UNSIGNED` primary key~~
-* ~~[#106242](https://bugs.mysql.com/bug.php?id=106242): `MySqlConnection.Open` throws `AggregateException` instead of `MySqlException`~~
-* ~~[#106243](https://bugs.mysql.com/bug.php?id=106243): `CancellationToken` doesn't cancel `MySqlConnection.OpenAsync`~~
-* ~~[#106244](https://bugs.mysql.com/bug.php?id=106244): `MySqlDataReader.GetFieldValue<Stream>` throws `InvalidCastException`~~
-* ~~[#106247](https://bugs.mysql.com/bug.php?id=106247): Can't use ~~`MySqlDbType.Enum` or~~ `MySqlDbType.Set` with prepared command~~
-* ~~[#108756](https://bugs.mysql.com/bug.php?id=108756): Can't insert negative number using prepared statement with `MySqlDbType.Int24`~~
-* ~~[#109670](https://bugs.mysql.com/bug.php?id=109670), [#109752](https://bugs.mysql.com/bug.php?id=109752): Many MSB3246 errors when referencing MySql.Data 8.0.32~~
-* ~~[#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.~~
-* ~~[#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.~~
-* ~~[#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'~~
-* ~~[#110717](https://bugs.mysql.com/bug.php?id=110717), [#111759](https://bugs.mysql.com/bug.php?id=111759): MySql.Data is not thread-safe~~
+### Fixed First by MySqlConnector
+
+These bugs are fixed in the latest version of MySQL Connector/NET, but were fixed first in MySqlConnector.
+
+* [#66476](https://bugs.mysql.com/bug.php?id=66476), [#106368](https://bugs.mysql.com/bug.php?id=106368): Connection pool uses queue instead of stack (fixed in v8.0.30)
+* [#70111](https://bugs.mysql.com/bug.php?id=70111): `Async` methods execute synchronously (fixed in v8.0.33)
+* [#70686](https://bugs.mysql.com/bug.php?id=70686): `TIME(3)` and `TIME(6)` fields serialize milliseconds incorrectly
+* [#73610](https://bugs.mysql.com/bug.php?id=73610): Invalid password exception has wrong number (fixed in v8.0.32)
+* [#74392](https://bugs.mysql.com/bug.php?id=74392): Can't use `Stream` to bulk load data
+* [#75604](https://bugs.mysql.com/bug.php?id=75604): Crash after 29.4 days of uptime
+* [#78426](https://bugs.mysql.com/bug.php?id=78426): Unknown database exception has wrong number
+* [#78760](https://bugs.mysql.com/bug.php?id=78760): Error when using tabs and newlines in SQL statements
+* [#78917](https://bugs.mysql.com/bug.php?id=78917), [#79196](https://bugs.mysql.com/bug.php?id=79196), [#82292](https://bugs.mysql.com/bug.php?id=82292), [#89040](https://bugs.mysql.com/bug.php?id=89040): `TINYINT(1)` values start being returned as `sbyte` after `NULL`
+* [#80030](https://bugs.mysql.com/bug.php?id=80030): Slow to connect with pooling disabled
+* [#81650](https://bugs.mysql.com/bug.php?id=81650), [#88962](https://bugs.mysql.com/bug.php?id=88962): `Server` connection string option may now contain multiple, comma separated hosts that will be tried in order until a connection succeeds
+* [#83649](https://bugs.mysql.com/bug.php?id=83649): Connection cannot be made using IPv6
+* [#84220](https://bugs.mysql.com/bug.php?id=84220): Cannot call a stored procedure with `.` in its name
+* [#84701](https://bugs.mysql.com/bug.php?id=84701): Can’t create a parameter using a 64-bit enum with a value greater than int.MaxValue
+* [#86263](https://bugs.mysql.com/bug.php?id=86263): Transaction isolation level affects all transactions in session
+* [#87307](https://bugs.mysql.com/bug.php?id=87307): NextResult hangs instead of timing out
+* [#87316](https://bugs.mysql.com/bug.php?id=87316): MySqlCommand.CommandTimeout can be set to a negative value
+* [#87868](https://bugs.mysql.com/bug.php?id=87868): `ColumnSize` in schema table is incorrect for `CHAR(36)` and `BLOB` columns
+* [#87876](https://bugs.mysql.com/bug.php?id=87876): `IsLong` is schema table is incorrect for `LONGTEXT` and `LONGBLOB` columns
+* [#88058](https://bugs.mysql.com/bug.php?id=88058): `decimal(n, 0)` has wrong `NumericPrecision`
+* [#88472](https://bugs.mysql.com/bug.php?id=88472): `TINYINT(1)` is not returned as `bool` if `MySqlCommand.Prepare` is called
+* [#88660](https://bugs.mysql.com/bug.php?id=88660): `MySqlClientFactory.Instance.CreateDataAdapter()` and `CreateCommandBuilder` return `null`
+* [#89159](https://bugs.mysql.com/bug.php?id=89159), [#97242](https://bugs.mysql.com/bug.php?id=97242): `MySqlDataReader` cannot outlive `MySqlCommand`
+* [#89639](https://bugs.mysql.com/bug.php?id=89639): `ReservedWords` schema contains incorrect data
+* [#90086](https://bugs.mysql.com/bug.php?id=90086): `MySqlDataReader` is closed by an unrelated command disposal
+* [#91751](https://bugs.mysql.com/bug.php?id=91751): `YEAR` column retrieved incorrectly with prepared command
+* [#91752](https://bugs.mysql.com/bug.php?id=91752): `00:00:00` is converted to `NULL` with prepared command
+* [#91770](https://bugs.mysql.com/bug.php?id=91770): `TIME(n)` column loses microseconds with prepared command
+* [#92465](https://bugs.mysql.com/bug.php?id=92465): “There is already an open DataReader” `MySqlException` thrown from `TransactionScope.Dispose`
+* [#92734](https://bugs.mysql.com/bug.php?id=92734): `MySqlParameter.Clone` doesn't copy all property values
+* [#92789](https://bugs.mysql.com/bug.php?id=92789): Illegal connection attributes written for non-ASCII values
+* [#92912](https://bugs.mysql.com/bug.php?id=92912): `MySqlDbType.LongText` values encoded incorrectly with prepared statements
+* [#92982](https://bugs.mysql.com/bug.php?id=92982), [#93399](https://bugs.mysql.com/bug.php?id=93399): `FormatException` thrown when connecting to MySQL Server 8.0.13
+* [#93202](https://bugs.mysql.com/bug.php?id=93202): Connector runs `SHOW VARIABLES` when connection is made
+* [#93370](https://bugs.mysql.com/bug.php?id=93370): `MySqlParameterCollection.Add` precondition check isn't consistent
+* [#93374](https://bugs.mysql.com/bug.php?id=93374): `MySqlDataReader.GetStream` throws `IndexOutOfRangeException`
+* [#94075](https://bugs.mysql.com/bug.php?id=94075): `MySqlCommand.Cancel` throws exception
+* [#95984](https://bugs.mysql.com/bug.php?id=95984): “Incorrect arguments to mysqld_stmt_execute” using prepared statement with `MySqlDbType.JSON`
+* [#95986](https://bugs.mysql.com/bug.php?id=95986): “Incorrect integer value” using prepared statement with `MySqlDbType.Int24`
+* [#96355](https://bugs.mysql.com/bug.php?id=96355), [#96614](https://bugs.mysql.com/bug.php?id=96614): `Could not load file or assembly 'Renci.SshNet'` when opening connection
+* [#96498](https://bugs.mysql.com/bug.php?id=96498): `WHERE` clause using `MySqlGeometry` as parameter finds no rows
+* [#96499](https://bugs.mysql.com/bug.php?id=96499): `MySqlException` when inserting a `MySqlGeometry` value
+* [#97061](https://bugs.mysql.com/bug.php?id=97061): `MySqlCommand.LastInsertedId` returns 0 after executing multiple statements
+* [#97300](https://bugs.mysql.com/bug.php?id=97300): `GetSchemaTable()` returns table for stored procedure with output parameters
+* [#97448](https://bugs.mysql.com/bug.php?id=97448): Connecting fails if more than one IP is found in DNS for a named host
+* [#97473](https://bugs.mysql.com/bug.php?id=97473): `MySqlConnection.Clone` discloses connection password
+* [#98322](https://bugs.mysql.com/bug.php?id=98322): `new MySqlConnection(null)` throws `NullReferenceException`
+* [#99091](https://bugs.mysql.com/bug.php?id=99091): Unexpected return value getting integer for `TINYINT(1)` column
+* [#99793](https://bugs.mysql.com/bug.php?id=99793): Prepared stored procedure command doesn't verify parameter types
+* [#100159](https://bugs.mysql.com/bug.php?id=100159): SQL with DateTime parameter returns String value
+* [#100208](https://bugs.mysql.com/bug.php?id=100208): `GetSchema("Procedures")` returns `ROUTINE_DEFINITION` of `"System.Byte[]"`
+* [#100218](https://bugs.mysql.com/bug.php?id=100218): `TIME(n)` microsecond values deserialized incorrectly with prepared command
+* [#100306](https://bugs.mysql.com/bug.php?id=100306): `Command.Prepare` sends wrong statement to server
+* [#100522](https://bugs.mysql.com/bug.php?id=100522): `	MySqlCommand.Parameters.Insert(-1)` succeeds but should fail
+* [#101252](https://bugs.mysql.com/bug.php?id=101252): Can't query `CHAR(36)` column containing `NULL`
+* [#101253](https://bugs.mysql.com/bug.php?id=101253): Default value for `MySqlParameter.Value` changed from null to `0`
+* [#101302](https://bugs.mysql.com/bug.php?id=101302): Stored Procedure `BOOL` parameter can only be mapped to `MySqlDbType.Byte`
+* [#101485](https://bugs.mysql.com/bug.php?id=101485): Stored Procedure `JSON` parameter throws “Unhandled type encountered” `MySqlException`
+* [#101507](https://bugs.mysql.com/bug.php?id=101507): `MySqlCommand.Cancel` throws `NullReferenceException` for a closed connection
+* [#101714](https://bugs.mysql.com/bug.php?id=101714): Extremely slow performance reading result sets
+* [#102303](https://bugs.mysql.com/bug.php?id=102303): Preparing command with incorrect parameter type puts connection in bad state
+* [#102593](https://bugs.mysql.com/bug.php?id=102593): Can't use `MemoryStream` as `MySqlParameter.Value`
+* [#103390](https://bugs.mysql.com/bug.php?id=103390): Can't query `CHAR(36)` column if `MySqlCommand` is prepared
+* [#103430](https://bugs.mysql.com/bug.php?id=103430): Can't connect using named pipe on Windows
+* [#103801](https://bugs.mysql.com/bug.php?id=103801): `TimeSpan` parameters lose microseconds with prepared statement
+* [#104910](https://bugs.mysql.com/bug.php?id=104910): `MySqlConnectionStringBuilder.TryGetValue` always returns `false`
+* [#104913](https://bugs.mysql.com/bug.php?id=104913): Cannot execute stored procedure with backtick in name
+* [#105209](https://bugs.mysql.com/bug.php?id=105209): Timespan value of zero can't be read with prepared command
+* [#105516](https://bugs.mysql.com/bug.php?id=105516): Connector/Net doesn't recognize utf8mb3
+* [#105728](https://bugs.mysql.com/bug.php?id=105728): Named command parameters override query attribute values
+* [#105730](https://bugs.mysql.com/bug.php?id=105730): `MySqlCommand.Clone` doesn't clone attributes
+* [#105768](https://bugs.mysql.com/bug.php?id=105768): `MySqlCommandBuilder` doesn't support tables with `BIGINT UNSIGNED` primary key
+* [#106242](https://bugs.mysql.com/bug.php?id=106242): `MySqlConnection.Open` throws `AggregateException` instead of `MySqlException`
+* [#106243](https://bugs.mysql.com/bug.php?id=106243): `CancellationToken` doesn't cancel `MySqlConnection.OpenAsync`
+* [#106244](https://bugs.mysql.com/bug.php?id=106244): `MySqlDataReader.GetFieldValue<Stream>` throws `InvalidCastException`
+* [#106247](https://bugs.mysql.com/bug.php?id=106247): Can't use `MySqlDbType.Enum` or `MySqlDbType.Set` with prepared command
+* [#108756](https://bugs.mysql.com/bug.php?id=108756): Can't insert negative number using prepared statement with `MySqlDbType.Int24`
+* [#109670](https://bugs.mysql.com/bug.php?id=109670), [#109752](https://bugs.mysql.com/bug.php?id=109752): Many MSB3246 errors when referencing MySql.Data 8.0.32
+* [#109682](https://bugs.mysql.com/bug.php?id=109682): `MySqlParameter.Clone` loses specific `MySqlDbType`.
+* [#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.
+* [#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'
+* [#110717](https://bugs.mysql.com/bug.php?id=110717), [#111759](https://bugs.mysql.com/bug.php?id=111759): MySql.Data is not thread-safe

From 848eda146bda01dedb116613410e548bccdea8dc Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 14 Feb 2024 16:02:42 -0800
Subject: [PATCH 274/393] Fix missing option name in documentation.

---
 docs/content/connection-options.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/content/connection-options.md b/docs/content/connection-options.md
index 1f4de9d31..9ef2cadf1 100644
--- a/docs/content/connection-options.md
+++ b/docs/content/connection-options.md
@@ -515,7 +515,7 @@ from your connection string when migrating from Connector/NET to MySqlConnector.
     <td>Use <a href="/overview/logging/">MySqlConnector logging</a> (which is more flexible) instead.</td>
   </tr>
   <tr id="OldGetStringBehavior">
-    <td></td>
+    <td>OldGetStringBehavior</td>
     <td>false</td>
     <td>This option is temporary in Connector/NET and unsupported in MySqlConnector.</td>
   </tr>

From 7343cfdf99f1850044d08579b177173004e36f33 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 18 Feb 2024 08:12:33 -0800
Subject: [PATCH 275/393] Update test packages.

---
 Directory.Packages.props | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0a89bfb44..9528addf4 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -15,15 +15,15 @@
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
-    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
+    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
     <PackageVersion Include="MySql.Data" Version="8.3.0" />
     <PackageVersion Include="NLog" Version="4.5.0" />
     <PackageVersion Include="Serilog" Version="2.6.0" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageVersion Include="xunit" Version="2.6.6" />
-    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6" />
+    <PackageVersion Include="xunit" Version="2.7.0" />
+    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7" />
     <PackageVersion Include="YamlDotNet" Version="13.7.1" />
   </ItemGroup>
 

From 31fe2cc2a151121809f90ba3a7d3e4ed9d4516a1 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 18 Feb 2024 08:05:02 -0800
Subject: [PATCH 276/393] Support .NET 9.0.

---
 .ci/build-steps.yml                           | 34 +++++++-------
 .ci/conformance-test-steps.yml                |  6 +--
 .ci/integration-tests-steps.yml               | 10 ++---
 .ci/mysqlconnector-tests-steps.yml            |  6 +--
 .ci/test.ps1                                  |  4 +-
 appveyor.yml                                  |  2 +-
 azure-pipelines.yml                           | 45 +++++++------------
 global.json                                   |  2 +-
 src/MySqlConnector/MySqlConnector.csproj      |  2 +-
 .../Conformance.Tests.csproj                  |  2 +-
 tests/IntegrationTests/ClientFactoryTests.cs  |  2 +-
 .../IntegrationTests/IntegrationTests.csproj  |  6 +--
 tests/IntegrationTests/Transaction.cs         |  4 +-
 ...Connector.DependencyInjection.Tests.csproj |  2 +-
 .../MySqlConnector.Tests.csproj               |  2 +-
 .../SchemaCollectionGenerator.csproj          |  2 +-
 16 files changed, 58 insertions(+), 73 deletions(-)

diff --git a/.ci/build-steps.yml b/.ci/build-steps.yml
index e81044196..384c6aed7 100644
--- a/.ci/build-steps.yml
+++ b/.ci/build-steps.yml
@@ -27,58 +27,58 @@ steps:
   displayName: 'Publish MySqlConnector.Tests'
   inputs:
     command: 'publish'
-    arguments: '-c Release -f net8.0 --no-build tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj'
+    arguments: '-c Release -f net9.0 --no-build tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
 - task: PublishPipelineArtifact@0
   inputs:
-    artifactName: 'MySqlConnector.Tests-8.0-$(Agent.OS)'
-    targetPath: 'artifacts/publish/MySqlConnector.Tests/release_net8.0'
+    artifactName: 'MySqlConnector.Tests-9.0-$(Agent.OS)'
+    targetPath: 'artifacts/publish/MySqlConnector.Tests/release_net9.0'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish Conformance.Tests'
   inputs:
     command: 'publish'
-    arguments: '-c Release -f net8.0 --no-build tests/Conformance.Tests/Conformance.Tests.csproj'
+    arguments: '-c Release -f net9.0 --no-build tests/Conformance.Tests/Conformance.Tests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
 - task: PublishPipelineArtifact@0
   inputs:
-    artifactName: 'Conformance.Tests-8.0-$(Agent.OS)'
-    targetPath: 'artifacts/publish/Conformance.Tests/release_net8.0'
+    artifactName: 'Conformance.Tests-9.0-$(Agent.OS)'
+    targetPath: 'artifacts/publish/Conformance.Tests/release_net9.0'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish MySqlConnector.DependencyInjection.Tests'
   inputs:
     command: 'publish'
-    arguments: '-c Release -f net8.0 --no-build tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj'
+    arguments: '-c Release -f net9.0 --no-build tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
 - task: PublishPipelineArtifact@0
   inputs:
-    artifactName: 'MySqlConnector.DependencyInjection.Tests-8.0-$(Agent.OS)'
-    targetPath: 'artifacts/publish/MySqlConnector.DependencyInjection.Tests/release_net8.0'
+    artifactName: 'MySqlConnector.DependencyInjection.Tests-9.0-$(Agent.OS)'
+    targetPath: 'artifacts/publish/MySqlConnector.DependencyInjection.Tests/release_net9.0'
 
 - task: DotNetCoreCLI@2
-  displayName: 'Publish IntegrationTests (7.0)'
+  displayName: 'Publish IntegrationTests (9.0)'
   inputs:
     command: 'publish'
-    arguments: '-c Release -f net8.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
+    arguments: '-c Release -f net9.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
 - task: PublishPipelineArtifact@0
   inputs:
-    artifactName: 'IntegrationTests-net8.0-$(Agent.OS)'
-    targetPath: 'artifacts/publish/IntegrationTests/release_net8.0'
+    artifactName: 'IntegrationTests-net9.0-$(Agent.OS)'
+    targetPath: 'artifacts/publish/IntegrationTests/release_net9.0'
 
 - task: DotNetCoreCLI@2
-  displayName: 'Publish IntegrationTests (6.0)'
+  displayName: 'Publish IntegrationTests (8.0)'
   inputs:
     command: 'publish'
-    arguments: '-c Release -f net6.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
+    arguments: '-c Release -f net8.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
 - task: PublishPipelineArtifact@0
   inputs:
-    artifactName: 'IntegrationTests-net6.0-$(Agent.OS)'
-    targetPath: 'artifacts/publish/IntegrationTests/release_net6.0'
+    artifactName: 'IntegrationTests-net8.0-$(Agent.OS)'
+    targetPath: 'artifacts/publish/IntegrationTests/release_net8.0'
diff --git a/.ci/conformance-test-steps.yml b/.ci/conformance-test-steps.yml
index 02d700623..349663215 100644
--- a/.ci/conformance-test-steps.yml
+++ b/.ci/conformance-test-steps.yml
@@ -10,14 +10,14 @@ steps:
 - task: DownloadPipelineArtifact@0
   condition: always()
   inputs:
-    artifactName: 'Conformance.Tests-8.0-$(Agent.OS)'
-    targetPath: '$(Build.BinariesDirectory)/8.0'
+    artifactName: 'Conformance.Tests-9.0-$(Agent.OS)'
+    targetPath: '$(Build.BinariesDirectory)/9.0'
 - task: DotNetCoreCLI@2
   displayName: 'Conformance Tests'
   inputs:
     command: 'custom'
     custom: 'vstest'
-    arguments: '$(Build.BinariesDirectory)/8.0/Conformance.Tests.dll /logger:trx'
+    arguments: '$(Build.BinariesDirectory)/9.0/Conformance.Tests.dll /logger:trx'
   env:
     CONNECTION_STRING: ${{ parameters.connectionString }}
 - task: PublishTestResults@2
diff --git a/.ci/integration-tests-steps.yml b/.ci/integration-tests-steps.yml
index 14f8a0cc6..7d7bd5d08 100644
--- a/.ci/integration-tests-steps.yml
+++ b/.ci/integration-tests-steps.yml
@@ -8,9 +8,9 @@ steps:
 - bash: ${{ format('.ci/docker-run.sh {0} 3300 {1}', parameters.image, parameters.unsupportedFeatures) }}
   displayName: 'Start Docker container'
 - task: UseDotNet@2
-  displayName: 'Install .NET 6.0'
+  displayName: 'Install .NET 8.0'
   inputs:
-    version: 6.0.x
+    version: 8.0.x
     packageType: runtime
 - task: UseDotNet@2
   displayName: 'Install .NET'
@@ -33,19 +33,19 @@ steps:
     image: ${{ parameters.image }}
     unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
     connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;UseCompression=True;DefaultCommandTimeout=3600;${{ parameters.connectionStringExtra }}'
-    platform: 'net6.0'
+    platform: 'net8.0'
     description: 'Compression'
 - template: 'integration-test-steps.yml'
   parameters:
     image: ${{ parameters.image }}
     unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
     connectionString: 'server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;${{ parameters.connectionStringExtra }}'
-    platform: 'net6.0'
+    platform: 'net9.0'
     description: 'No SSL'
 - template: 'integration-test-steps.yml'
   parameters:
     image: ${{ parameters.image }}
     unsupportedFeatures: ${{ parameters.unsupportedFeatures }}
     connectionString: server=localhost;port=3300;user id=mysqltest;password=test;database=mysqltest;ssl mode=required;DefaultCommandTimeout=3600;certificate file=$(Build.Repository.LocalPath)/.ci/server/certs/ssl-client.pfx;${{ parameters.connectionStringExtra }}
-    platform: 'net6.0'
+    platform: 'net9.0'
     description: 'SSL'
diff --git a/.ci/mysqlconnector-tests-steps.yml b/.ci/mysqlconnector-tests-steps.yml
index 012b11a0b..ef0e6a095 100644
--- a/.ci/mysqlconnector-tests-steps.yml
+++ b/.ci/mysqlconnector-tests-steps.yml
@@ -6,7 +6,7 @@ steps:
     includePreviewVersions: true
 - task: DownloadPipelineArtifact@0
   inputs:
-    artifactName: 'MySqlConnector.Tests-8.0-$(Agent.OS)'
+    artifactName: 'MySqlConnector.Tests-9.0-$(Agent.OS)'
     targetPath: $(System.DefaultWorkingDirectory)
 - task: DotNetCoreCLI@2
   displayName: 'Run MySqlConnector.Tests'
@@ -16,7 +16,7 @@ steps:
     arguments: 'MySqlConnector.Tests.dll /logger:trx'
 - task: DownloadPipelineArtifact@0
   inputs:
-    artifactName: 'MySqlConnector.DependencyInjection.Tests-8.0-$(Agent.OS)'
+    artifactName: 'MySqlConnector.DependencyInjection.Tests-9.0-$(Agent.OS)'
     targetPath: $(System.DefaultWorkingDirectory)
 - task: DotNetCoreCLI@2
   displayName: 'Run MySqlConnector.DependencyInjection.Tests'
@@ -28,4 +28,4 @@ steps:
   inputs:
     testResultsFormat: VSTest
     testResultsFiles: '**/*.trx'
-    testRunTitle: 'MySqlConnector.Tests-8.0-$(Agent.OS)'
+    testRunTitle: 'MySqlConnector.Tests-9.0-$(Agent.OS)'
diff --git a/.ci/test.ps1 b/.ci/test.ps1
index a619c6402..7d595e35e 100644
--- a/.ci/test.ps1
+++ b/.ci/test.ps1
@@ -38,14 +38,14 @@ dotnet test -c Release -f net462
 if ($LASTEXITCODE -ne 0){
     exit $LASTEXITCODE;
 }
-dotnet test -c Release -f net7.0
+dotnet test -c Release -f net9.0
 if ($LASTEXITCODE -ne 0){
     exit $LASTEXITCODE;
 }
 
 echo "Executing integration tests with Compression, No SSL"
 Copy-Item -Force ..\..\.ci\config\config.compression.json config.json
-dotnet test -c Release -f net6.0
+dotnet test -c Release -f net8.0
 if ($LASTEXITCODE -ne 0){
     exit $LASTEXITCODE;
 }
diff --git a/appveyor.yml b/appveyor.yml
index c4f4f64c4..ef905dcaa 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -8,8 +8,8 @@ cache:
 install:
   - ps: Invoke-WebRequest -Uri "https://dot.net/v1/dotnet-install.ps1" -OutFile "install-dotnet.ps1"
   - ps: .\install-dotnet.ps1 -Channel 6.0 -InstallDir "dotnetcli"
-  - ps: .\install-dotnet.ps1 -Channel 7.0 -InstallDir "dotnetcli"
   - ps: .\install-dotnet.ps1 -Channel 8.0 -InstallDir "dotnetcli"
+  - ps: .\install-dotnet.ps1 -Channel 9.0 -InstallDir "dotnetcli"
 build_script:
 - dotnet --info
 before_test:
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index c0c83dd5b..7bc876b2c 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -1,5 +1,5 @@
 variables:
-  DotNetCoreSdkVersion: '8.x'
+  DotNetCoreSdkVersion: '9.x'
   NUGET_PACKAGES: '$(Pipeline.Workspace)/.nuget/packages'
 
 jobs:
@@ -16,17 +16,6 @@ jobs:
     vmimage: 'windows-latest'
   steps:
   - template: '.ci/build-steps.yml'
-  - task: DotNetCoreCLI@2
-    displayName: 'Publish IntegrationTests (net472)'
-    inputs:
-      command: 'publish'
-      arguments: '-c Release -f net472 tests/IntegrationTests/IntegrationTests.csproj'
-      publishWebProjects: false
-      zipAfterPublish: false
-  - task: PublishPipelineArtifact@0
-    inputs:
-      artifactName: 'IntegrationTests-net472-$(Agent.OS)'
-      targetPath: 'artifacts/publish/IntegrationTests/release_net472'
 
 - job: windows_mysql_data
   displayName: 'MySql.Data Tests'
@@ -41,9 +30,9 @@ jobs:
       contents: 'config.json'
       targetFolder: 'tests/IntegrationTests'
   - task: UseDotNet@2
-    displayName: 'Install .NET 7.0'
+    displayName: 'Install .NET 8.0'
     inputs:
-      version: '7.0.x'
+      version: '8.0.x'
   - task: UseDotNet@2
     displayName: 'Install .NET'
     inputs:
@@ -91,11 +80,6 @@ jobs:
     vmimage: 'windows-2019'
   steps:
   - template: '.ci/install-mysql-windows.yml'
-  - task: UseDotNet@2
-    displayName: 'Install .NET Core 3.1'
-    inputs:
-      version: 3.1.x
-      packageType: runtime
   - template: '.ci/mysqlconnector-tests-steps.yml'
   - template: '.ci/conformance-test-steps.yml'
     parameters:
@@ -117,6 +101,7 @@ jobs:
     displayName: 'Install .NET'
     inputs:
       version: $(DotNetCoreSdkVersion)
+      includePreviewVersions: true
   - task: PowerShell@2
     displayName: 'Copy Azure config'
     inputs:
@@ -127,12 +112,12 @@ jobs:
     inputs:
       command: 'restore'
   - task: DotNetCoreCLI@2
-    displayName: 'Integration tests (net8.0)'
+    displayName: 'Integration tests (net9.0)'
     inputs:
       command: 'test'
       projects: 'tests/IntegrationTests/IntegrationTests.csproj'
-      arguments: '-c Release -f net8.0 --no-restore'
-      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'Azure', 'net8.0', 'SSL') }}
+      arguments: '-c Release -f net9.0 --no-restore'
+      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'Azure', 'net9.0', 'SSL') }}
     env:
       DATA__UNSUPPORTEDFEATURES: 'CachingSha2Password,Ed25519,GlobalLog,KnownCertificateAuthority,QueryAttributes,RsaEncryption,Sha256Password,StreamingResults,Timeout,Tls11,Tls13,UnixDomainSocket,ZeroDateTime'
       DATA__CONNECTIONSTRING: "$(AzureConnectionString);database=mysqltest_$(Build.BuildId);ssl mode=Required;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True"
@@ -164,18 +149,18 @@ jobs:
     displayName: 'Remove target frameworks'
     inputs:
       targetType: 'inline'
-      script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''<TargetFrameworks>.*</TargetFrameworks>'', ''<TargetFrameworks>net472;net8.0</TargetFrameworks>'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
+      script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''<TargetFrameworks>.*</TargetFrameworks>'', ''<TargetFrameworks>net481;net9.0</TargetFrameworks>'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
   - task: DotNetCoreCLI@2
     displayName: 'Restore packages'
     inputs:
       command: 'restore'
   - task: DotNetCoreCLI@2
-    displayName: 'Integration tests (net472/net8.0)'
+    displayName: 'Integration tests (net481/net9.0)'
     inputs:
       command: 'test'
       projects: 'tests/IntegrationTests/IntegrationTests.csproj'
       arguments: '-c Release --no-restore'
-      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net472/net8.0', 'No SSL') }}
+      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net481/net9.0', 'No SSL') }}
     env:
       DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True'
@@ -187,9 +172,9 @@ jobs:
   steps:
   - template: '.ci/install-mysql-windows.yml'
   - task: UseDotNet@2
-    displayName: 'Install .NET 6.0'
+    displayName: 'Install .NET 8.0'
     inputs:
-      version: 6.0.x
+      version: 8.0.x
       packageType: runtime
   - task: UseDotNet@2
     displayName: 'Install .NET'
@@ -202,18 +187,18 @@ jobs:
     displayName: 'Remove target frameworks'
     inputs:
       targetType: 'inline'
-      script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''<TargetFrameworks>.*</TargetFrameworks>'', ''<TargetFrameworks>net6.0</TargetFrameworks>'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
+      script: '((Get-Content .\tests\IntegrationTests\IntegrationTests.csproj -Raw) -replace(''<TargetFrameworks>.*</TargetFrameworks>'', ''<TargetFrameworks>net8.0</TargetFrameworks>'')) | Set-Content .\tests\IntegrationTests\IntegrationTests.csproj'
   - task: DotNetCoreCLI@2
     displayName: 'Restore packages'
     inputs:
       command: 'restore'
   - task: DotNetCoreCLI@2
-    displayName: 'Integration tests (net6.0)'
+    displayName: 'Integration tests (net8.0)'
     inputs:
       command: 'test'
       projects: 'tests/IntegrationTests/IntegrationTests.csproj'
       arguments: '-c Release --no-restore'
-      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net6.0', 'No SSL') }}
+      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net8.0', 'No SSL') }}
     env:
       DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
diff --git a/global.json b/global.json
index 3a69170ca..746f46e86 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
 {
   "sdk": {
-    "version": "8.0"
+    "version": "9.0"
   }
 }
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 4cb3425f5..7e679aeb9 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;net471;net48;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net462;net471;net48;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
     <Description>A truly async MySQL ADO.NET provider, supporting MySQL Server, MariaDB, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL, and more.</Description>
     <Copyright>Copyright 2016–2024 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
diff --git a/tests/Conformance.Tests/Conformance.Tests.csproj b/tests/Conformance.Tests/Conformance.Tests.csproj
index 246efa5b1..e2a4ca928 100644
--- a/tests/Conformance.Tests/Conformance.Tests.csproj
+++ b/tests/Conformance.Tests/Conformance.Tests.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
  <PropertyGroup>
-    <TargetFrameworks>net8.0</TargetFrameworks>
+    <TargetFrameworks>net9.0</TargetFrameworks>
     <VersionPrefix>0.1.0</VersionPrefix>
     <SignAssembly>true</SignAssembly>
     <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
diff --git a/tests/IntegrationTests/ClientFactoryTests.cs b/tests/IntegrationTests/ClientFactoryTests.cs
index bebd2f5e0..f74017a73 100644
--- a/tests/IntegrationTests/ClientFactoryTests.cs
+++ b/tests/IntegrationTests/ClientFactoryTests.cs
@@ -51,7 +51,7 @@ public void DbProviderFactoriesGetFactory()
 #else
 		var providerInvariantName = "MySqlConnector";
 #endif
-#if !NET462 && !NET472
+#if !NETFRAMEWORK
 		DbProviderFactories.RegisterFactory(providerInvariantName, MySqlConnectorFactory.Instance);
 #endif
 		var factory = DbProviderFactories.GetFactory(providerInvariantName);
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index 8c975155b..7b6a5c1a3 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -1,12 +1,12 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup Condition=" '$(Configuration)' != 'MySqlData' ">
-    <TargetFrameworks>net462;net472;net6.0;net8.0</TargetFrameworks>
+    <TargetFrameworks>net462;net481;net6.0;net8.0;net9.0</TargetFrameworks>
     <CheckEolTargetFramework>false</CheckEolTargetFramework>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(Configuration)' == 'MySqlData' ">
-    <TargetFrameworks>net8.0</TargetFrameworks>
+    <TargetFrameworks>net9.0</TargetFrameworks>
     <DefineConstants>MYSQL_DATA</DefineConstants>
     <NoWarn>$(NoWarn);MSB3246</NoWarn>
   </PropertyGroup>
@@ -60,7 +60,7 @@
     <Using Include="MySql.Data.MySqlClient" />
   </ItemGroup>
 
-  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net472' ">
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' OR '$(TargetFramework)' == 'net481' ">
     <Reference Include="System" />
     <Reference Include="System.Net.Http" />
     <Reference Include="System.Transactions" />
diff --git a/tests/IntegrationTests/Transaction.cs b/tests/IntegrationTests/Transaction.cs
index 7a22b96f5..ac33ab5e0 100644
--- a/tests/IntegrationTests/Transaction.cs
+++ b/tests/IntegrationTests/Transaction.cs
@@ -169,7 +169,7 @@ public async Task ReadWriteTransactionAsync()
 		Assert.Equal(new[] { 1, 2 }, results);
 	}
 
-#if !NET462 && !NET472
+#if !NETFRAMEWORK
 	[Fact]
 	public async Task DbConnectionCommitAsync()
 	{
@@ -241,7 +241,7 @@ public async Task RollbackDisposeAsync()
 		Assert.Equal(new int[0], results);
 	}
 
-#if !NET462 && !NET472
+#if !NETFRAMEWORK
 	[Fact]
 	public async Task DbConnectionRollbackAsync()
 	{
diff --git a/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj b/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj
index fac726a3a..ebeeef845 100644
--- a/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj
+++ b/tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net8.0</TargetFrameworks>
+    <TargetFrameworks>net9.0</TargetFrameworks>
     <SignAssembly>true</SignAssembly>
     <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
     <AssemblyOriginatorKeyFile>..\..\MySqlConnector.snk</AssemblyOriginatorKeyFile>
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index b7c7f164e..5d14a2cce 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup Condition=" '$(Configuration)' != 'MySqlData' ">
-    <TargetFrameworks>net481;net8.0</TargetFrameworks>
+    <TargetFrameworks>net481;net9.0</TargetFrameworks>
   </PropertyGroup>
 
  <PropertyGroup Condition=" '$(Configuration)' == 'MySqlData' ">
diff --git a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
index 73e62aa5e..85c1ed799 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
+++ b/tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj
@@ -2,7 +2,7 @@
 
   <PropertyGroup>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net8.0</TargetFramework>
+    <TargetFramework>net9.0</TargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
     <GenerateDocumentationFile>true</GenerateDocumentationFile>

From bbdbd782e7434b765154805b1cb61d8daac68112 Mon Sep 17 00:00:00 2001
From: CRC32EX <25629617+CRC32EX@users.noreply.github.com>
Date: Thu, 22 Feb 2024 05:51:15 +0900
Subject: [PATCH 277/393] Update connection-options.md

---
 docs/content/connection-options.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/content/connection-options.md b/docs/content/connection-options.md
index 9ef2cadf1..3f822ed57 100644
--- a/docs/content/connection-options.md
+++ b/docs/content/connection-options.md
@@ -229,7 +229,7 @@ Connection pooling is enabled by default. These options are used to configure it
     <td>The minimum number of connections to leave in the pool if ConnectionIdleTimeout is reached.</td>
   </tr>
   <tr id="MaximumPoolSize">
-    <td>Maximum Pool Size, Max Pool Size, MaximumPoolsize, maxpoolsize</td>
+    <td>Maximum Pool Size, Max Pool Size, MaximumPoolSize, maxpoolsize</td>
     <td>100</td>
     <td>The maximum number of connections allowed in the pool.</td>
   </tr>

From 6ab339c12c83741c6b2bb35e3404a0e71186d99c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 12 Mar 2024 20:41:14 -0700
Subject: [PATCH 278/393] Use Lock type in .NET 9.0.

---
 .../Authentication/AuthenticationPlugins.cs   |  4 +++
 src/MySqlConnector/Core/ILoadBalancer.cs      | 26 ++++++++++++++++---
 src/MySqlConnector/MySqlBulkLoader.cs         | 16 +++++++++---
 src/MySqlConnector/MySqlConnection.cs         | 22 +++++++++++-----
 src/MySqlConnector/Utilities/TimerQueue.cs    |  4 +++
 5 files changed, 58 insertions(+), 14 deletions(-)

diff --git a/src/MySqlConnector/Authentication/AuthenticationPlugins.cs b/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
index 025362e0f..92fdf19c7 100644
--- a/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
+++ b/src/MySqlConnector/Authentication/AuthenticationPlugins.cs
@@ -35,6 +35,10 @@ internal static bool TryGetPlugin(string name, [NotNullWhen(true)] out IAuthenti
 			return s_plugins.TryGetValue(name, out plugin);
 	}
 
+#if NET9_0_OR_GREATER
+	private static readonly Lock s_lock = new();
+#else
 	private static readonly object s_lock = new();
+#endif
 	private static readonly Dictionary<string, IAuthenticationPlugin> s_plugins = [];
 }
diff --git a/src/MySqlConnector/Core/ILoadBalancer.cs b/src/MySqlConnector/Core/ILoadBalancer.cs
index d6dd82ebb..568cf5b26 100644
--- a/src/MySqlConnector/Core/ILoadBalancer.cs
+++ b/src/MySqlConnector/Core/ILoadBalancer.cs
@@ -27,12 +27,18 @@ internal sealed class RandomLoadBalancer : ILoadBalancer
 	public IReadOnlyList<string> LoadBalance(IReadOnlyList<string> hosts)
 	{
 #pragma warning disable CA5394 // Do not use insecure randomness
-		// from https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
+#if NET8_0_OR_GREATER
+		var shuffled = hosts.ToArray();
+		lock (m_lock)
+			m_random.Shuffle(shuffled);
+		return shuffled;
+#else
 		var shuffled = new List<string>(hosts);
+		// from https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm
 		for (var i = hosts.Count - 1; i >= 1; i--)
 		{
 			int j;
-			lock (m_random)
+			lock (m_lock)
 				j = m_random.Next(i + 1);
 			if (i != j)
 			{
@@ -42,11 +48,21 @@ public IReadOnlyList<string> LoadBalance(IReadOnlyList<string> hosts)
 			}
 		}
 		return shuffled;
+#endif
 	}
 
-	private RandomLoadBalancer() => m_random = new();
+	private RandomLoadBalancer()
+	{
+		m_random = new();
+		m_lock = new();
+	}
 
 	private readonly Random m_random;
+#if NET9_0_OR_GREATER
+	private readonly Lock m_lock;
+#else
+	private readonly object m_lock;
+#endif
 }
 
 internal sealed class RoundRobinLoadBalancer : ILoadBalancer
@@ -67,6 +83,10 @@ public IReadOnlyList<string> LoadBalance(IReadOnlyList<string> hosts)
 		return shuffled;
 	}
 
+#if NET9_0_OR_GREATER
+	private readonly Lock m_lock;
+#else
 	private readonly object m_lock;
+#endif
 	private uint m_counter;
 }
diff --git a/src/MySqlConnector/MySqlBulkLoader.cs b/src/MySqlConnector/MySqlBulkLoader.cs
index 1ab11c8fc..8dcf933f4 100644
--- a/src/MySqlConnector/MySqlBulkLoader.cs
+++ b/src/MySqlConnector/MySqlBulkLoader.cs
@@ -176,8 +176,7 @@ internal async ValueTask<int> LoadAsync(IOBehavior ioBehavior, CancellationToken
 			{
 				// replace the file name with a sentinel so that we know (when processing the result set) that it's not spoofed by the server
 				var newFileName = GenerateSourceFileName();
-				lock (s_lock)
-					s_sources.Add(newFileName, CreateFileStream(FileName!));
+				AddSource(newFileName, CreateFileStream(FileName!));
 				FileName = newFileName;
 			}
 		}
@@ -187,8 +186,7 @@ internal async ValueTask<int> LoadAsync(IOBehavior ioBehavior, CancellationToken
 				throw new InvalidOperationException("Local must be true to use SourceStream, SourceDataTable, or SourceDataReader.");
 
 			FileName = GenerateSourceFileName();
-			lock (s_lock)
-				s_sources.Add(FileName, Source!);
+			AddSource(FileName, Source!);
 		}
 
 		var closeConnection = false;
@@ -221,6 +219,12 @@ internal async ValueTask<int> LoadAsync(IOBehavior ioBehavior, CancellationToken
 			if (closeConnection)
 				Connection.Close();
 		}
+
+		static void AddSource(string name, object source)
+		{
+			lock (s_lock)
+				s_sources.Add(name, source);
+		}
 	}
 
 	internal const string SourcePrefix = ":SOURCE:";
@@ -319,6 +323,10 @@ internal static bool TryGetAndRemoveSource(string sourceKey, [NotNullWhen(true)]
 
 	private static string GenerateSourceFileName() => SourcePrefix + Guid.NewGuid().ToString("N");
 
+#if NET9_0_OR_GREATER
+	private static readonly Lock s_lock = new();
+#else
 	private static readonly object s_lock = new();
+#endif
 	private static readonly Dictionary<string, object> s_sources = [];
 }
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 5ab8ff9f8..fc327a4a3 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -1189,16 +1189,20 @@ private async Task DoCloseAsync(bool changeState, IOBehavior ioBehavior)
 			};
 			connection.TakeSessionFrom(this);
 
-			// put the new, idle, connection into the list of sessions for this transaction (replacing this MySqlConnection)
-			lock (s_lock)
+			ReplaceConnection(this, connection);
+			static void ReplaceConnection(MySqlConnection thisConnection, MySqlConnection connection)
 			{
-				foreach (var enlistedTransaction in s_transactionConnections[connection.m_enlistedTransaction!.Transaction])
+				// put the new, idle, connection into the list of sessions for this transaction (replacing this MySqlConnection)
+				lock (s_lock)
 				{
-					if (enlistedTransaction.Connection == this)
+					foreach (var enlistedTransaction in s_transactionConnections[connection.m_enlistedTransaction!.Transaction])
 					{
-						enlistedTransaction.Connection = connection;
-						enlistedTransaction.IsIdle = true;
-						break;
+						if (enlistedTransaction.Connection == thisConnection)
+						{
+							enlistedTransaction.Connection = connection;
+							enlistedTransaction.IsIdle = true;
+							break;
+						}
 					}
 				}
 			}
@@ -1256,7 +1260,11 @@ private ConnectionSettings GetConnectionSettings() =>
 	private static readonly StateChangeEventArgs s_stateChangeClosedConnecting = new(ConnectionState.Closed, ConnectionState.Connecting);
 	private static readonly StateChangeEventArgs s_stateChangeConnectingOpen = new(ConnectionState.Connecting, ConnectionState.Open);
 	private static readonly StateChangeEventArgs s_stateChangeOpenClosed = new(ConnectionState.Open, ConnectionState.Closed);
+#if NET9_0_OR_GREATER
+	private static readonly Lock s_lock = new();
+#else
 	private static readonly object s_lock = new();
+#endif
 	private static readonly Dictionary<System.Transactions.Transaction, List<EnlistedTransactionBase>> s_transactionConnections = [];
 	private static readonly ReadOnlyMemory<byte>[] s_startTransactionPayloads = new ReadOnlyMemory<byte>[5 * 3 * 2];
 
diff --git a/src/MySqlConnector/Utilities/TimerQueue.cs b/src/MySqlConnector/Utilities/TimerQueue.cs
index 5aa05f736..3b120264e 100644
--- a/src/MySqlConnector/Utilities/TimerQueue.cs
+++ b/src/MySqlConnector/Utilities/TimerQueue.cs
@@ -131,7 +131,11 @@ public Data(uint id, int time, Action action)
 		public Action Action { get; }
 	}
 
+#if NET9_0_OR_GREATER
+	private readonly Lock m_lock;
+#else
 	private readonly object m_lock;
+#endif
 	private readonly Timer m_timer;
 	private readonly List<Data> m_timeoutActions;
 	private uint m_counter;

From 31b0ea9f9b466866f2d7c84266f96c7a0b0866e3 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 12 Mar 2024 22:00:55 -0700
Subject: [PATCH 279/393] Run multiple TFM tests sequentially.

These are now run in parallel by default (see https://github.com/dotnet/core/blob/main/release-notes/9.0/preview/preview2/sdk.md#parallel-testing-and-terminal-logger-test-display) which will cause interference (due to using the same database).
---
 azure-pipelines.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 7bc876b2c..5d3752974 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -159,7 +159,7 @@ jobs:
     inputs:
       command: 'test'
       projects: 'tests/IntegrationTests/IntegrationTests.csproj'
-      arguments: '-c Release --no-restore'
+      arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net481/net9.0', 'No SSL') }}
     env:
       DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket'
@@ -197,7 +197,7 @@ jobs:
     inputs:
       command: 'test'
       projects: 'tests/IntegrationTests/IntegrationTests.csproj'
-      arguments: '-c Release --no-restore'
+      arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net8.0', 'No SSL') }}
     env:
       DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket'

From d9e59c7995592c9ea11300382a6aa8914b5a60ab Mon Sep 17 00:00:00 2001
From: Daniel Lenski <dlenski@amazon.com>
Date: Thu, 14 Mar 2024 12:31:44 -0700
Subject: [PATCH 280/393] Mention MariaDB prominently in the README

While several of the detailed documentation pages *do* mention compatibility
with MariaDB, the README does not mention it explicitly.

This might cause MariaDB users to overlook this package as a good solution
for connecting to their databases from .NET applications.
---
 README.md | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index f9b0150b6..ec8675935 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,8 @@
 [![NuGet](https://img.shields.io/nuget/vpre/MySqlConnector.svg)](https://www.nuget.org/packages/MySqlConnector/)
 
 This is an [ADO.NET](https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/) data
-provider for [MySQL](https://www.mysql.com/). It provides implementations of
+provider for [MySQL](https://www.mysql.com/) and other compatible servers including [MariaDB](https://www.mariadb.org).
+It provides implementations of
 `DbConnection`, `DbCommand`, `DbDataReader`, `DbTransaction`—the classes
 needed to query and update databases from managed code.
 
@@ -21,7 +22,7 @@ This library outperforms MySQL Connector/NET (`MySql.Data`) on benchmarks:
 
 ### Server Compatibility
 
-This library is compatible with [many MySQL-compatible servers](https://mysqlconnector.net/#server-compatibility).
+This library is compatible with [many MySQL-compatible servers](https://mysqlconnector.net/#server-compatibility), including MySQL 5.5 and newer and MariaDB 10.x and newer.
 MySql.Data [only supports MySQL Server](https://bugs.mysql.com/bug.php?id=109331).
 
 ### Bug Fixes

From e85521655dc8bf6eee62ee3b721e7cf311a5afe1 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 17 Mar 2024 15:10:01 -0700
Subject: [PATCH 281/393] Fix SSL CA validation. Fixes #1462

Build a chain from the last certificate in the certificate chain presented by the remote server.

If we have an untrusted root, verify that the root certificate in the chain is one of the certificates specified by MySqlConnectionStringBuilder.SslCa.
---
 src/MySqlConnector/Core/ServerSession.cs | 22 +++++++++++++++++-----
 1 file changed, 17 insertions(+), 5 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index f83c03e7a..3c2e71393 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1389,18 +1389,30 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 
 		bool ValidateRemoteCertificate(object rcbSender, X509Certificate? rcbCertificate, X509Chain? rcbChain, SslPolicyErrors rcbPolicyErrors)
 		{
+			// if no CA verification is required, then we trust any remote certificate
 			if (cs.SslMode is MySqlSslMode.Preferred or MySqlSslMode.Required)
 				return true;
 
+			// if there are errors, then try to build a path to a root certificate from the certificates presented by the remote host
 			if ((rcbPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0 &&
-				rcbCertificate is not null &&
+				rcbChain is not null &&
 				caCertificateChain is not null &&
-				caCertificateChain.Build((X509Certificate2) rcbCertificate) &&
+				caCertificateChain.Build(rcbChain.ChainElements[^1].Certificate) &&
 				caCertificateChain.ChainStatus.Length > 0)
 			{
-				var chainStatus = caCertificateChain.ChainStatus[0].Status & ~X509ChainStatusFlags.UntrustedRoot;
-				if (chainStatus == X509ChainStatusFlags.NoError)
-					rcbPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors;
+				// if the only error is an Untrusted Root Certificate, then check all provided SSL CA certificates to see if one is the root
+				if (caCertificateChain.ChainStatus[0].Status == X509ChainStatusFlags.UntrustedRoot)
+				{
+					var rootCertificate = caCertificateChain.ChainElements[^1].Certificate;
+					foreach (var sslCaCertificate in caCertificateChain.ChainPolicy.ExtraStore)
+					{
+						if (rootCertificate.RawData.AsSpan().SequenceEqual(sslCaCertificate.RawData))
+						{
+							rcbPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors;
+							break;
+						}
+					}
+				}
 			}
 
 			if (cs.SslMode == MySqlSslMode.VerifyCA)

From 1d508bcfd74f221e71a892bbb3086a545f1a5451 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 17 Mar 2024 16:03:11 -0700
Subject: [PATCH 282/393] Add overload with an IServiceProvider parameter.
 Fixes #1316

---
 ...SqlConnectorServiceCollectionExtensions.cs | 66 ++++++++++++++++---
 .../DependencyInjectionTests.cs               | 45 +++++++++++++
 2 files changed, 101 insertions(+), 10 deletions(-)

diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs b/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
index 9509902f2..b7fb0b921 100644
--- a/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
+++ b/src/MySqlConnector.DependencyInjection/MySqlConnectorServiceCollectionExtensions.cs
@@ -23,7 +23,7 @@ public static IServiceCollection AddMySqlDataSource(
 		string connectionString,
 		ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
 		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
-		DoAddMySqlDataSource(serviceCollection, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
+		DoAddMySqlDataSource(serviceCollection, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime, builderActionState: null);
 
 	/// <summary>
 	/// Registers a <see cref="MySqlDataSource" /> and a <see cref="MySqlConnection" /> in the <see cref="IServiceCollection" />.
@@ -40,7 +40,24 @@ public static IServiceCollection AddMySqlDataSource(
 		Action<MySqlDataSourceBuilder> dataSourceBuilderAction,
 		ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
 		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
-		DoAddMySqlDataSource(serviceCollection, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
+		DoAddMySqlDataSource(serviceCollection, connectionString, DataSourceBuilderThunk, connectionLifetime, dataSourceLifetime, builderActionState: dataSourceBuilderAction);
+
+	/// <summary>
+	/// Registers a <see cref="MySqlDataSource" /> and a <see cref="MySqlConnection" /> in the <see cref="IServiceCollection" />.
+	/// </summary>
+	/// <param name="serviceCollection">The <see cref="IServiceCollection" /> to add services to.</param>
+	/// <param name="connectionString">A MySQL connection string.</param>
+	/// <param name="dataSourceBuilderAction">An action to configure the <see cref="MySqlDataSourceBuilder" /> for further customizations of the <see cref="MySqlDataSource" />.</param>
+	/// <param name="connectionLifetime">The lifetime with which to register the <see cref="MySqlConnection" /> in the container. Defaults to <see cref="ServiceLifetime.Transient" />.</param>
+	/// <param name="dataSourceLifetime">The lifetime with which to register the <see cref="MySqlDataSource" /> service in the container. Defaults to <see cref="ServiceLifetime.Singleton" />.</param>
+	/// <returns>The same service collection so that multiple calls can be chained.</returns>
+	public static IServiceCollection AddMySqlDataSource(
+		this IServiceCollection serviceCollection,
+		string connectionString,
+		Action<IServiceProvider, MySqlDataSourceBuilder> dataSourceBuilderAction,
+		ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
+		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
+		DoAddMySqlDataSource(serviceCollection, connectionString, ServiceProviderDataSourceBuilderThunk, connectionLifetime, dataSourceLifetime, builderActionState: dataSourceBuilderAction);
 
 	/// <summary>
 	/// Registers a <see cref="MySqlDataSource" /> and a <see cref="MySqlConnection" /> in the <see cref="IServiceCollection" />.
@@ -58,7 +75,7 @@ public static IServiceCollection AddKeyedMySqlDataSource(
 		string connectionString,
 		ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
 		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
-		DoAddMySqlDataSource(serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime);
+		DoAddMySqlDataSource(serviceCollection, serviceKey, connectionString, dataSourceBuilderAction: null, connectionLifetime, dataSourceLifetime, builderActionState: null);
 
 	/// <summary>
 	/// Registers a <see cref="MySqlDataSource" /> and a <see cref="MySqlConnection" /> in the <see cref="IServiceCollection" />.
@@ -79,14 +96,36 @@ public static IServiceCollection AddKeyedMySqlDataSource(
 		Action<MySqlDataSourceBuilder> dataSourceBuilderAction,
 		ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
 		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
-		DoAddMySqlDataSource(serviceCollection, serviceKey, connectionString, dataSourceBuilderAction, connectionLifetime, dataSourceLifetime);
+		DoAddMySqlDataSource(serviceCollection, serviceKey, connectionString, DataSourceBuilderThunk, connectionLifetime, dataSourceLifetime, builderActionState: dataSourceBuilderAction);
+
+	/// <summary>
+	/// Registers a <see cref="MySqlDataSource" /> and a <see cref="MySqlConnection" /> in the <see cref="IServiceCollection" />.
+	/// </summary>
+	/// <param name="serviceCollection">The <see cref="IServiceCollection" /> to add services to.</param>
+	/// <param name="serviceKey">The <see cref="ServiceDescriptor.ServiceKey"/> of the service.</param>
+	/// <param name="connectionString">A MySQL connection string.</param>
+	/// <param name="dataSourceBuilderAction">An action to configure the <see cref="MySqlDataSourceBuilder" /> for further customizations of the <see cref="MySqlDataSource" />.</param>
+	/// <param name="connectionLifetime">The lifetime with which to register the <see cref="MySqlConnection" /> in the container. Defaults to <see cref="ServiceLifetime.Transient" />.</param>
+	/// <param name="dataSourceLifetime">The lifetime with which to register the <see cref="MySqlDataSource" /> service in the container. Defaults to <see cref="ServiceLifetime.Singleton" />.</param>
+	/// <returns>The same service collection so that multiple calls can be chained.</returns>
+	/// <remarks>If the <paramref name="serviceKey"/> is a <see langword="string"/>, it will automatically be used to initialize the data source name; this can
+	/// be overridden by the <paramref name="dataSourceBuilderAction"/> configuration action.</remarks>
+	public static IServiceCollection AddKeyedMySqlDataSource(
+		this IServiceCollection serviceCollection,
+		object? serviceKey,
+		string connectionString,
+		Action<IServiceProvider, MySqlDataSourceBuilder> dataSourceBuilderAction,
+		ServiceLifetime connectionLifetime = ServiceLifetime.Transient,
+		ServiceLifetime dataSourceLifetime = ServiceLifetime.Singleton) =>
+		DoAddMySqlDataSource(serviceCollection, serviceKey, connectionString, ServiceProviderDataSourceBuilderThunk, connectionLifetime, dataSourceLifetime, builderActionState: dataSourceBuilderAction);
 
 	private static IServiceCollection DoAddMySqlDataSource(
 		this IServiceCollection serviceCollection,
 		string connectionString,
-		Action<MySqlDataSourceBuilder>? dataSourceBuilderAction,
+		Action<IServiceProvider, MySqlDataSourceBuilder, object?>? dataSourceBuilderAction,
 		ServiceLifetime connectionLifetime,
-		ServiceLifetime dataSourceLifetime)
+		ServiceLifetime dataSourceLifetime,
+		object? builderActionState)
 	{
 		serviceCollection.TryAdd(
 			new ServiceDescriptor(
@@ -95,7 +134,7 @@ private static IServiceCollection DoAddMySqlDataSource(
 				{
 					var dataSourceBuilder = new MySqlDataSourceBuilder(connectionString)
 						.UseLoggerFactory(serviceProvider.GetService<ILoggerFactory>());
-					dataSourceBuilderAction?.Invoke(dataSourceBuilder);
+					dataSourceBuilderAction?.Invoke(serviceProvider, dataSourceBuilder, builderActionState);
 					return dataSourceBuilder.Build();
 				},
 				dataSourceLifetime));
@@ -115,9 +154,10 @@ private static IServiceCollection DoAddMySqlDataSource(
 		this IServiceCollection serviceCollection,
 		object? serviceKey,
 		string connectionString,
-		Action<MySqlDataSourceBuilder>? dataSourceBuilderAction,
+		Action<IServiceProvider, MySqlDataSourceBuilder, object?>? dataSourceBuilderAction,
 		ServiceLifetime connectionLifetime,
-		ServiceLifetime dataSourceLifetime)
+		ServiceLifetime dataSourceLifetime,
+		object? builderActionState)
 	{
 		serviceCollection.TryAdd(
 			new ServiceDescriptor(
@@ -128,7 +168,7 @@ private static IServiceCollection DoAddMySqlDataSource(
 					var dataSourceBuilder = new MySqlDataSourceBuilder(connectionString)
 						.UseLoggerFactory(serviceProvider.GetService<ILoggerFactory>())
 						.UseName(serviceKey as string);
-					dataSourceBuilderAction?.Invoke(dataSourceBuilder);
+					dataSourceBuilderAction?.Invoke(serviceProvider, dataSourceBuilder, builderActionState);
 					return dataSourceBuilder.Build();
 				},
 				dataSourceLifetime));
@@ -143,4 +183,10 @@ private static IServiceCollection DoAddMySqlDataSource(
 
 		return serviceCollection;
 	}
+
+	private static void DataSourceBuilderThunk(IServiceProvider serviceProvider, MySqlDataSourceBuilder dataSourceBuilder, object? state) =>
+		((Action<MySqlDataSourceBuilder>) state!)(dataSourceBuilder);
+
+	private static void ServiceProviderDataSourceBuilderThunk(IServiceProvider serviceProvider, MySqlDataSourceBuilder dataSourceBuilder, object? state) =>
+		((Action<IServiceProvider, MySqlDataSourceBuilder>) state!)(serviceProvider, dataSourceBuilder);
 }
diff --git a/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs b/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs
index 6d9bf4775..7779bba61 100644
--- a/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs
+++ b/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs
@@ -70,6 +70,22 @@ public async Task MySqlDataSourceCanSetName()
 		Assert.Equal("MyName", dataSource.Name);
 	}
 
+	[Fact]
+	public async Task MySqlDataSourceCanSetNameFromServiceProvider()
+	{
+		var serviceCollection = new ServiceCollection();
+
+		serviceCollection.AddSingleton("MyName");
+		serviceCollection.AddMySqlDataSource(c_connectionString, (sp, builder) =>
+		{
+			builder.UseName(sp.GetRequiredService<string>());
+		});
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+		var dataSource = serviceProvider.GetRequiredService<MySqlDataSource>();
+		Assert.Equal("MyName", dataSource.Name);
+	}
+
 	[Fact]
 	public async Task KeyedMySqlDataSourceIsRegistered()
 	{
@@ -98,6 +114,35 @@ public async Task StringKeyedMySqlDataSourceHasNameSet()
 		Assert.Equal(c_connectionString, connection.ConnectionString);
 	}
 
+	[Fact]
+	public async Task KeyedMySqlDataSourceCanSetName()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddKeyedMySqlDataSource("key", c_connectionString, builder => builder.UseName("MyName"));
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		var dataSource = serviceProvider.GetRequiredKeyedService<MySqlDataSource>("key");
+		Assert.Equal("MyName", dataSource.Name);
+		await using var connection = dataSource.CreateConnection();
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
+	[Fact]
+	public async Task KeyedMySqlDataSourceCanSetNameFromServiceProvider()
+	{
+		var serviceCollection = new ServiceCollection();
+		serviceCollection.AddSingleton("MyName");
+		serviceCollection.AddKeyedMySqlDataSource("key", c_connectionString, (sp, builder) => builder.UseName(sp.GetRequiredService<string>()));
+
+		await using var serviceProvider = serviceCollection.BuildServiceProvider();
+
+		var dataSource = serviceProvider.GetRequiredKeyedService<MySqlDataSource>("key");
+		Assert.Equal("MyName", dataSource.Name);
+		await using var connection = dataSource.CreateConnection();
+		Assert.Equal(c_connectionString, connection.ConnectionString);
+	}
+
 	[Fact]
 	public async Task KeyedMySqlDataSourceRetrievedWithStringKeyHasName()
 	{

From 033abeacbc1fc997bca49a37b5d7522c5b095d99 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 20 Mar 2024 10:58:15 -0700
Subject: [PATCH 283/393] Release 2.3.6.

---
 docs/content/overview/version-history.md | 17 ++++++++++++++++-
 1 file changed, 16 insertions(+), 1 deletion(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 735323b3e..116960940 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2024-01-20
+lastmod: 2024-03-20
 date: 2017-03-27
 menu:
   main:
@@ -11,6 +11,21 @@ weight: 30
 
 # Version History
 
+### 2.3.6
+
+* Fix `VerifyCA` incompatibility with AWS Aurora: [#1462](https://github.com/mysql-net/MySqlConnector/issues/1462).
+* Verify that the server's root certificate is present (in the list of provided CA certificates) when using the `SslCa` connection string option and `SslMode` is `VerifyCA` or `VerifyFull`.
+* Optimization: Use pipelining to begin a transaction: [#1286](https://github.com/mysql-net/MySqlConnector/issues/1286).
+  * Use `Pipelining = False;` in your connection string to disable this optimization if there are compatibility issues.
+* Optimization: cache `START TRANSACTION` payloads.
+* Add transaction logging: [#1411](https://github.com/mysql-net/MySqlConnector/issues/1411).
+* Fix incompatibility with MySQL Server 5.1: [#1445](https://github.com/mysql-net/MySqlConnector/issues/1445).
+  * This fixes a regression introduced in 2.3.0.
+
+#### MySqlConnector.DependencyInjection
+
+* Add overload of `AddMySqlDataSource` that takes an `Action<IServiceProvider, MySqlDataSourceBuilder>` action to configure the `MySqlDataSourceBuilder` instance: [#1316](https://github.com/mysql-net/MySqlConnector/issues/1316).
+
 ### 2.3.5
 
 * DLL files within the NuGet packages are now digitally signed.

From 4329de324695288a34aa2187ea6b6eebe5432d32 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 11 Apr 2024 16:51:02 -0700
Subject: [PATCH 284/393] Use new TimeSpan.From overloads.

---
 src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs | 4 +++-
 src/MySqlConnector/Utilities/Utility.cs                    | 4 +++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
index f8ff90a41..ff2b17fff 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryTimeColumnReader.cs
@@ -28,7 +28,9 @@ public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayloa
 			microseconds = -microseconds;
 		}
 
-#if NET7_0_OR_GREATER
+#if NET9_0_OR_GREATER
+		return TimeSpan.FromDays(days, hours, minutes, seconds, microseconds: microseconds);
+#elif NET7_0_OR_GREATER
 		return new TimeSpan(days, hours, minutes, seconds, microseconds / 1000, microseconds % 1000);
 #else
 		return new TimeSpan(days, hours, minutes, seconds) + TimeSpan.FromTicks(microseconds * 10);
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 1909c49fc..e2b4b567b 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -451,7 +451,9 @@ public static TimeSpan ParseTimeSpan(ReadOnlySpan<byte> value)
 			seconds = -seconds;
 			microseconds = -microseconds;
 		}
-#if NET7_0_OR_GREATER
+#if NET9_0_OR_GREATER
+		return TimeSpan.FromHours(hours, minutes, seconds, microseconds: microseconds);
+#elif NET7_0_OR_GREATER
 		return new TimeSpan(0, hours, minutes, seconds, microseconds / 1000, microseconds % 1000);
 #else
 		return new TimeSpan(0, hours, minutes, seconds, microseconds / 1000) + TimeSpan.FromTicks(microseconds % 1000 * 10);

From aee6fdf04e84d40fedafb98eb5d4fb39d548bc44 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 14 Apr 2024 20:17:34 -0700
Subject: [PATCH 285/393] Fix/suppress new code style warnings.

These were detected by either .NET 9 Preview 3 SDK or Visual Studio 2022 v17.10 Preview 3.
---
 .editorconfig                                 |  7 ++
 .../BinaryDateTimeColumnReader.cs             |  2 +-
 .../ColumnReaders/GuidBinary16ColumnReader.cs |  2 +-
 .../GuidTimeSwapBinary16ColumnReader.cs       |  2 +-
 src/MySqlConnector/Core/CachedProcedure.cs    | 12 +--
 src/MySqlConnector/Core/ConnectionPool.cs     | 22 +++--
 src/MySqlConnector/Core/ConnectionSettings.cs |  6 +-
 .../Core/EnlistedTransactionBase.cs           | 12 +--
 src/MySqlConnector/Core/ILoadBalancer.cs      |  6 +-
 src/MySqlConnector/Core/MetricsReporter.cs    |  6 +-
 src/MySqlConnector/Core/NormalizedSchema.cs   | 16 ++--
 src/MySqlConnector/Core/ResultSet.cs          | 11 +--
 src/MySqlConnector/Core/Row.cs                | 11 +--
 src/MySqlConnector/Core/SchemaProvider.cs     |  5 +-
 src/MySqlConnector/Core/ServerSession.cs      | 76 +++++++---------
 src/MySqlConnector/Core/SqlParser.cs          |  2 +-
 src/MySqlConnector/Core/StatementPreparer.cs  | 10 +--
 src/MySqlConnector/Core/TypeMapper.cs         |  4 +-
 .../Logging/ConsoleLoggerProvider.cs          | 10 +--
 src/MySqlConnector/MySqlBatch.cs              |  3 +-
 .../MySqlBatchCommandCollection.cs            | 10 +--
 src/MySqlConnector/MySqlBulkCopy.cs           |  2 +-
 src/MySqlConnector/MySqlBulkLoader.cs         |  7 +-
 src/MySqlConnector/MySqlCommand.cs            |  6 +-
 src/MySqlConnector/MySqlCommandBuilder.cs     |  2 +-
 src/MySqlConnector/MySqlConnection.cs         | 14 +--
 src/MySqlConnector/MySqlConnectorFactory.cs   |  4 +-
 src/MySqlConnector/MySqlDataAdapter.cs        |  8 +-
 src/MySqlConnector/MySqlDataReader.cs         | 28 +++---
 src/MySqlConnector/MySqlDataSource.cs         |  4 +-
 src/MySqlConnector/MySqlDateTime.cs           |  2 +-
 src/MySqlConnector/MySqlGeometry.cs           |  2 +-
 src/MySqlConnector/MySqlParameter.cs          |  8 +-
 src/MySqlConnector/MySqlProtocolException.cs  |  2 +-
 .../Protocol/Payloads/ErrorPayload.cs         |  2 +-
 .../Protocol/Serialization/AuthGSSAPI.cs      |  8 +-
 .../Serialization/AuthenticationUtility.cs    |  6 +-
 .../Serialization/ByteBufferWriter.cs         |  4 +-
 .../Serialization/StreamByteHandler.cs        |  6 +-
 .../Utilities/ResizableArraySegment.cs        |  2 +-
 src/MySqlConnector/Utilities/Utility.cs       | 82 ++++++++---------
 tests/IntegrationTests/AppConfig.cs           |  2 +-
 tests/IntegrationTests/BulkLoaderAsync.cs     | 68 ++++++++------
 tests/IntegrationTests/BulkLoaderSync.cs      | 72 +++++++++------
 tests/IntegrationTests/ConnectionPool.cs      |  2 +-
 tests/IntegrationTests/ConnectionTests.cs     | 12 ++-
 tests/IntegrationTests/DataTypes.cs           | 14 +--
 tests/IntegrationTests/DatabaseFixture.cs     |  2 +-
 tests/IntegrationTests/ParameterCollection.cs |  4 +-
 tests/IntegrationTests/QueryTests.cs          |  8 +-
 .../IntegrationTests/TransactionScopeTests.cs |  2 +-
 .../CachedProcedureTests.cs                   | 90 ++++++++-----------
 .../MySqlConnector.Tests/CancellationTests.cs |  4 +-
 tests/MySqlConnector.Tests/ConnectionTests.cs | 12 ++-
 tests/MySqlConnector.Tests/FakeMySqlServer.cs |  4 +-
 .../FakeMySqlServerConnection.cs              | 32 +++----
 .../Metrics/MetricsTestsBase.cs               | 12 +--
 .../MySqlAttributeCollectionTests.cs          |  2 +-
 .../MySqlConnectionStringBuilderTests.cs      |  2 +-
 ...ySqlParameterCollectionNameToIndexTests.cs |  8 +-
 .../MySqlParameterCollectionTests.cs          |  2 +-
 .../StatementPreparerTests.cs                 | 72 +++++++--------
 tests/MySqlConnector.Tests/UtilityTests.cs    | 16 ++--
 63 files changed, 446 insertions(+), 430 deletions(-)

diff --git a/.editorconfig b/.editorconfig
index 117e019fe..4a511abec 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -16,13 +16,16 @@ indent_style = tab
 indent_size = 4
 
 # changes from VS2017 defaults
+csharp_style_expression_bodied_local_functions = true:suggestion
 csharp_style_expression_bodied_methods = true:suggestion
 csharp_style_expression_bodied_constructors = true:suggestion
 csharp_style_expression_bodied_operators = true:suggestion
+csharp_style_namespace_declarations = file_scoped
 csharp_prefer_braces = false:none
 csharp_indent_switch_labels = true
 csharp_space_after_cast = true
 csharp_preserve_single_line_statements = false
+dotnet_style_prefer_conditional_expression_over_return = false:suggestion
 
 # use VS2017 defaults, but make them warnings (instead of none)
 dotnet_style_qualification_for_field = false:warning
@@ -84,7 +87,11 @@ dotnet_diagnostic.CA2100.severity = none # Review SQL queries for security vulne
 dotnet_diagnostic.CA2213.severity = silent # Disposable fields should be disposed.
 dotnet_diagnostic.CA5398.severity = none # Avoid hardcoded SslProtocols values.
 dotnet_diagnostic.IDE0044.severity = error # Make fields readonly.
+dotnet_diagnostic.IDE0045.severity = suggestion # Use conditional expression for assignment.
+dotnet_diagnostic.IDE0058.severity = none # Remove unnecessary expression value.
+dotnet_diagnostic.IDE0059.severity = none # Remove unnecessary value assignment.
 dotnet_diagnostic.IDE0065.severity = error # Using directives must be placed outside of a namespace declaration.
+dotnet_diagnostic.IDE0072.severity = silent # Add missing cases to switch expression.
 dotnet_diagnostic.SA1003.severity = none # Operator must not be followed by whitespace.
 dotnet_diagnostic.SA1008.severity = none # Opening parenthesis must not be preceded by a space.
 dotnet_diagnostic.SA1009.severity = none # Closing parenthesis must not be followed by a space.
diff --git a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
index 48b548a39..defa68c19 100644
--- a/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/BinaryDateTimeColumnReader.cs
@@ -47,7 +47,7 @@ public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayloa
 
 		try
 		{
-			return m_allowZeroDateTime ? (object) new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
+			return m_allowZeroDateTime ? new MySqlDateTime(year, month, day, hour, minute, second, microseconds) :
 #if NET7_0_OR_GREATER
 				new DateTime(year, month, day, hour, minute, second, microseconds / 1000, microseconds % 1000, m_dateTimeKind);
 #else
diff --git a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
index 0f4df3ff6..1c7f31c43 100644
--- a/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidBinary16ColumnReader.cs
@@ -20,6 +20,6 @@ public static Guid ReadGuid(ReadOnlySpan<byte> data) =>
 #elif NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 		new(stackalloc byte[16] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #else
-		new(new[] { data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
+		new([data[3], data[2], data[1], data[0], data[5], data[4], data[7], data[6], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]]);
 #endif
 }
diff --git a/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs b/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
index 8a642708e..b6822167d 100644
--- a/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/GuidTimeSwapBinary16ColumnReader.cs
@@ -18,6 +18,6 @@ public static Guid ReadGuid(ReadOnlySpan<byte> data) =>
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 		new(stackalloc byte[16] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
 #else
-		new(new[] { data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15] });
+		new([data[7], data[6], data[5], data[4], data[3], data[2], data[1], data[0], data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15]]);
 #endif
 }
diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index 995edb8ec..021d89c71 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -79,9 +79,9 @@ FROM information_schema.parameters
 			cmd.Parameters.AddWithValue("@component", component);
 
 			using var reader = await cmd.ExecuteReaderNoResetTimeoutAsync(CommandBehavior.Default, ioBehavior, cancellationToken).ConfigureAwait(false);
-			await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			_ = await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 			routineCount = reader.GetInt32(0);
-			await reader.NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			_ = await reader.NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			while (await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false))
 			{
@@ -168,17 +168,17 @@ internal static List<CachedParameter> ParseParameters(string parametersSql)
 			if (parameter.StartsWith("INOUT ", StringComparison.OrdinalIgnoreCase))
 			{
 				direction = "INOUT";
-				parameter = parameter.Substring(6);
+				parameter = parameter[6..];
 			}
 			else if (parameter.StartsWith("OUT ", StringComparison.OrdinalIgnoreCase))
 			{
 				direction = "OUT";
-				parameter = parameter.Substring(4);
+				parameter = parameter[4..];
 			}
 			else if (parameter.StartsWith("IN ", StringComparison.OrdinalIgnoreCase))
 			{
 				direction = "IN";
-				parameter = parameter.Substring(3);
+				parameter = parameter[3..];
 			}
 
 			var parts = s_parameterName.Match(parameter);
@@ -206,7 +206,7 @@ internal static string ParseDataType(string sql, out bool unsigned, out int leng
 		}
 
 		var list = sql.Trim().Split(' ');
-		var type = string.Empty;
+		string? type;
 
 		if (list.Length < 2 || !s_typeMapping.TryGetValue(list[0] + ' ' + list[1], out type))
 		{
diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 763056ed8..758fffd36 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -1,8 +1,6 @@
 using System.Collections.Concurrent;
 using System.Diagnostics;
-using System.Diagnostics.Metrics;
 using System.Net;
-using System.Security.Authentication;
 using Microsoft.Extensions.Logging;
 using MySqlConnector.Logging;
 using MySqlConnector.Protocol.Serialization;
@@ -140,7 +138,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 				}
 			}
 
-			m_sessionSemaphore.Release();
+			_ = m_sessionSemaphore.Release();
 			throw;
 		}
 	}
@@ -172,14 +170,14 @@ public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 		try
 		{
 			lock (m_leasedSessions)
-				m_leasedSessions.Remove(session.Id);
+				_ = m_leasedSessions.Remove(session.Id);
 			MetricsReporter.RemoveUsed(this);
 			session.OwningConnection = null;
 			var sessionHealth = GetSessionHealth(session);
 			if (sessionHealth == 0)
 			{
 				lock (m_sessions)
-					m_sessions.AddFirst(session);
+					_ = m_sessions.AddFirst(session);
 				MetricsReporter.AddIdle(this);
 			}
 			else
@@ -194,7 +192,7 @@ public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 		}
 		finally
 		{
-			m_sessionSemaphore.Release();
+			_ = m_sessionSemaphore.Release();
 		}
 	}
 
@@ -202,7 +200,7 @@ public async Task ClearAsync(IOBehavior ioBehavior, CancellationToken cancellati
 	{
 		// increment the generation of the connection pool
 		Log.ClearingConnectionPool(m_logger, Id);
-		Interlocked.Increment(ref m_generation);
+		_ = Interlocked.Increment(ref m_generation);
 		m_procedureCache = null;
 		await RecoverLeakedSessionsAsync(ioBehavior).ConfigureAwait(false);
 		await CleanPoolAsync(ioBehavior, session => session.PoolGeneration != m_generation, false, cancellationToken).ConfigureAwait(false);
@@ -406,13 +404,13 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh
 				var session = await ConnectSessionAsync(connection, s_createdToReachMinimumPoolSize, Stopwatch.GetTimestamp(), null, ioBehavior, cancellationToken).ConfigureAwait(false);
 				AdjustHostConnectionCount(session, 1);
 				lock (m_sessions)
-					m_sessions.AddFirst(session);
+					_ = m_sessions.AddFirst(session);
 				MetricsReporter.AddIdle(this);
 			}
 			finally
 			{
 				// connection is in pool; semaphore shouldn't be held any more
-				m_sessionSemaphore.Release();
+				_ = m_sessionSemaphore.Release();
 			}
 		}
 	}
@@ -452,7 +450,7 @@ private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection conne
 					var redirectedSession = new ServerSession(m_connectionLogger, this, m_generation, Interlocked.Increment(ref m_lastSessionId));
 					try
 					{
-						await redirectedSession.ConnectAsync(redirectedSettings, connection, startingTimestamp, m_loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+						_ = await redirectedSession.ConnectAsync(redirectedSettings, connection, startingTimestamp, m_loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
 					}
 					catch (Exception ex)
 					{
@@ -614,7 +612,7 @@ private ConnectionPool(MySqlConnectorLoggingConfiguration loggingConfiguration,
 			cs.HostNames!.Count == 1 || cs.LoadBalance == MySqlLoadBalance.FailOver ? FailOverLoadBalancer.Instance :
 			cs.LoadBalance == MySqlLoadBalance.Random ? RandomLoadBalancer.Instance :
 			cs.LoadBalance == MySqlLoadBalance.LeastConnections ? new LeastConnectionsLoadBalancer(m_hostSessions!) :
-			(ILoadBalancer) new RoundRobinLoadBalancer();
+			 new RoundRobinLoadBalancer();
 
 		// create tag lists for reporting pool metrics
 		var connectionString = cs.ConnectionStringBuilder.GetConnectionString(includePassword: false);
@@ -812,7 +810,7 @@ private static void OnAppDomainShutDown(object? sender, EventArgs e) =>
 		ClearPoolsAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 
 	private static readonly ConcurrentDictionary<string, ConnectionPool?> s_pools = new();
-	private static readonly List<ConnectionPool> s_allPools = new();
+	private static readonly List<ConnectionPool> s_allPools = [];
 	private static readonly Action<ILogger, int, string, Exception?> s_createdNewSession = LoggerMessage.Define<int, string>(
 		LogLevel.Debug, new EventId(EventIds.PoolCreatedNewSession, nameof(EventIds.PoolCreatedNewSession)),
 		"Pool {PoolId} has no pooled session available; created new session {SessionId}");
diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index f0c5e6cee..c93e16004 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -1,4 +1,6 @@
+#if NETCOREAPP3_0_OR_GREATER
 using System.Net.Security;
+#endif
 using System.Security.Authentication;
 using MySqlConnector.Utilities;
 
@@ -148,7 +150,7 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
 		static int ToSigned(uint value) => value >= int.MaxValue ? int.MaxValue : (int) value;
 	}
 
-	public ConnectionSettings CloneWith(string host, int port, string userId) => new ConnectionSettings(this, host, port, userId);
+	public ConnectionSettings CloneWith(string host, int port, string userId) => new(this, host, port, userId);
 
 	private static MySqlGuidFormat GetEffectiveGuidFormat(MySqlGuidFormat guidFormat, bool oldGuids)
 	{
@@ -326,5 +328,5 @@ private ConnectionSettings(ConnectionSettings other, string host, int port, stri
 		UseXaTransactions = other.UseXaTransactions;
 	}
 
-	private static readonly string[] s_localhostPipeServer = { "." };
+	private static readonly string[] s_localhostPipeServer = ["."];
 }
diff --git a/src/MySqlConnector/Core/EnlistedTransactionBase.cs b/src/MySqlConnector/Core/EnlistedTransactionBase.cs
index 44260e309..c688d596b 100644
--- a/src/MySqlConnector/Core/EnlistedTransactionBase.cs
+++ b/src/MySqlConnector/Core/EnlistedTransactionBase.cs
@@ -2,10 +2,10 @@
 
 namespace MySqlConnector.Core;
 
-internal abstract class EnlistedTransactionBase : IEnlistmentNotification
+internal abstract class EnlistedTransactionBase(Transaction transaction, MySqlConnection connection) : IEnlistmentNotification
 {
 	// A MySqlConnection that holds the ServerSession that was enrolled in the transaction
-	public MySqlConnection Connection { get; set; }
+	public MySqlConnection Connection { get; set; } = connection;
 
 	// Whether the connection is idle, i.e., a client has closed it and is no longer using it
 	public bool IsIdle { get; set; }
@@ -13,7 +13,7 @@ internal abstract class EnlistedTransactionBase : IEnlistmentNotification
 	// Whether the distributed transaction was prepared successfully
 	public bool IsPrepared { get; private set; }
 
-	public Transaction Transaction { get; private set; }
+	public Transaction Transaction { get; private set; } = transaction;
 
 	public void Start()
 	{
@@ -51,12 +51,6 @@ void IEnlistmentNotification.Rollback(Enlistment enlistment)
 
 	public void InDoubt(Enlistment enlistment) => throw new NotImplementedException();
 
-	protected EnlistedTransactionBase(Transaction transaction, MySqlConnection connection)
-	{
-		Transaction = transaction;
-		Connection = connection;
-	}
-
 	protected abstract void OnStart();
 	protected abstract void OnPrepare(PreparingEnlistment enlistment);
 	protected abstract void OnCommit(Enlistment enlistment);
diff --git a/src/MySqlConnector/Core/ILoadBalancer.cs b/src/MySqlConnector/Core/ILoadBalancer.cs
index d6dd82ebb..8e3b106d8 100644
--- a/src/MySqlConnector/Core/ILoadBalancer.cs
+++ b/src/MySqlConnector/Core/ILoadBalancer.cs
@@ -35,11 +35,7 @@ public IReadOnlyList<string> LoadBalance(IReadOnlyList<string> hosts)
 			lock (m_random)
 				j = m_random.Next(i + 1);
 			if (i != j)
-			{
-				var temp = shuffled[i];
-				shuffled[i] = shuffled[j];
-				shuffled[j] = temp;
-			}
+				(shuffled[j], shuffled[i]) = (shuffled[i], shuffled[j]);
 		}
 		return shuffled;
 	}
diff --git a/src/MySqlConnector/Core/MetricsReporter.cs b/src/MySqlConnector/Core/MetricsReporter.cs
index e4720f8d5..22440918a 100644
--- a/src/MySqlConnector/Core/MetricsReporter.cs
+++ b/src/MySqlConnector/Core/MetricsReporter.cs
@@ -28,13 +28,13 @@ public static void RemovePendingRequest(ConnectionPool? pool)
 
 	static MetricsReporter()
 	{
-		ActivitySourceHelper.Meter.CreateObservableUpDownCounter<int>("db.client.connections.idle.max",
+		_ = ActivitySourceHelper.Meter.CreateObservableUpDownCounter<int>("db.client.connections.idle.max",
 			observeValues: GetMaximumConnections, unit: "{connection}",
 			description: "The maximum number of idle open connections allowed; this corresponds to MaximumPoolSize in the connection string.");
-		ActivitySourceHelper.Meter.CreateObservableUpDownCounter<int>("db.client.connections.idle.min",
+		_ = ActivitySourceHelper.Meter.CreateObservableUpDownCounter<int>("db.client.connections.idle.min",
 			observeValues: GetMinimumConnections, unit: "{connection}",
 			description: "The minimum number of idle open connections allowed; this corresponds to MinimumPoolSize in the connection string.");
-		ActivitySourceHelper.Meter.CreateObservableUpDownCounter<int>("db.client.connections.max",
+		_ = ActivitySourceHelper.Meter.CreateObservableUpDownCounter<int>("db.client.connections.max",
 			observeValues: GetMaximumConnections, unit: "{connection}",
 			description: "The maximum number of open connections allowed; this corresponds to MaximumPoolSize in the connection string.");
 
diff --git a/src/MySqlConnector/Core/NormalizedSchema.cs b/src/MySqlConnector/Core/NormalizedSchema.cs
index d0d5bd997..d8667c30e 100644
--- a/src/MySqlConnector/Core/NormalizedSchema.cs
+++ b/src/MySqlConnector/Core/NormalizedSchema.cs
@@ -17,15 +17,13 @@ internal sealed partial class NormalizedSchema
 	private static readonly Regex s_nameRegex = new(ReName, RegexOptions.Compiled);
 #endif
 
-	public static NormalizedSchema MustNormalize(string name, string? defaultSchema = null)
-	{
-		var normalized = new NormalizedSchema(name, defaultSchema);
-		if (normalized.Component is null)
-			throw new ArgumentException("Could not determine function/procedure name", nameof(name));
-		if (normalized.Schema is null)
-			throw new ArgumentException("Could not determine schema", nameof(defaultSchema));
-		return normalized;
-	}
+	public static NormalizedSchema MustNormalize(string name, string? defaultSchema = null) =>
+		new NormalizedSchema(name, defaultSchema) switch
+		{
+			{ Component: null } => throw new ArgumentException("Could not determine function/procedure name", nameof(name)),
+			{ Schema: null } => throw new ArgumentException("Could not determine schema", nameof(defaultSchema)),
+			{ } normalized => normalized,
+		};
 
 	public NormalizedSchema(string name, string? defaultSchema = null)
 	{
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index c3704b826..184bf071d 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -159,7 +159,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 					if (!Session.SupportsDeprecateEof)
 					{
 						payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
-						EofPayload.Create(payload.Span);
+						_ = EofPayload.Create(payload.Span);
 					}
 
 					if (ColumnDefinitions.Length == (Command?.OutParameters?.Count + 1) && ColumnDefinitions[0].Name == SingleCommandPayloadCreator.OutParameterSentinelColumnName)
@@ -182,16 +182,13 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 		}
 	}
 
-	private static bool IsHostVerified(MySqlConnection connection)
-	{
-		return connection.SslMode == MySqlSslMode.VerifyCA
-			|| connection.SslMode == MySqlSslMode.VerifyFull;
-	}
+	private static bool IsHostVerified(MySqlConnection connection) =>
+		connection.SslMode is MySqlSslMode.VerifyCA or MySqlSslMode.VerifyFull;
 
 	public async Task ReadEntireAsync(IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		while (State is ResultSetState.ReadingRows or ResultSetState.ReadResultSetHeader)
-			await ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			_ = await ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 	}
 
 	public bool Read()
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index 7025a8b30..bdbc81658 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -1,10 +1,11 @@
-using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Text;
 using MySqlConnector.ColumnReaders;
 using MySqlConnector.Protocol;
 using MySqlConnector.Protocol.Serialization;
+#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
 using MySqlConnector.Utilities;
+#endif
 
 namespace MySqlConnector.Core;
 
@@ -30,7 +31,7 @@ public void SetData(ReadOnlyMemory<byte> data)
 			Array.Clear(m_dataOffsetLengths, 0, m_dataOffsetLengths.Length);
 			for (var column = 0; column < m_dataOffsetLengths.Length; column++)
 			{
-				if ((data.Span[(column + 2) / 8 + 1] & (1 << ((column + 2) % 8))) != 0)
+				if ((data.Span[((column + 2) / 8) + 1] & (1 << ((column + 2) % 8))) != 0)
 				{
 					// column is NULL
 					m_dataOffsetLengths[column] = (-1, 0);
@@ -40,7 +41,7 @@ public void SetData(ReadOnlyMemory<byte> data)
 			var reader = new ByteArrayReader(data.Span);
 
 			// skip packet header (1 byte) and NULL bitmap (formula for length at https://dev.mysql.com/doc/internals/en/null-bitmap.html)
-			reader.Offset += 1 + (m_dataOffsetLengths.Length + 7 + 2) / 8;
+			reader.Offset += 1 + ((m_dataOffsetLengths.Length + 7 + 2) / 8);
 			for (var column = 0; column < m_dataOffsetLengths.Length; column++)
 			{
 				if (m_dataOffsetLengths[column].Offset != -1)
@@ -341,7 +342,7 @@ public DateTime GetDateTime(int ordinal)
 		return (DateTime) value;
 	}
 
-	public DateTimeOffset GetDateTimeOffset(int ordinal) => new DateTimeOffset(DateTime.SpecifyKind(GetDateTime(ordinal), DateTimeKind.Utc));
+	public DateTimeOffset GetDateTimeOffset(int ordinal) => new(DateTime.SpecifyKind(GetDateTime(ordinal), DateTimeKind.Utc));
 
 	public Stream GetStream(int ordinal)
 	{
@@ -387,7 +388,7 @@ public float GetFloat(int ordinal)
 		{
 			float floatValue => floatValue,
 			double doubleValue when doubleValue is >= float.MinValue and <= float.MaxValue => (float) doubleValue,
-			double _ => throw new InvalidCastException("The value cannot be safely cast to Single."),
+			double => throw new InvalidCastException("The value cannot be safely cast to Single."),
 			decimal decimalValue => (float) decimalValue,
 			_ => (float) value,
 		};
diff --git a/src/MySqlConnector/Core/SchemaProvider.cs b/src/MySqlConnector/Core/SchemaProvider.cs
index a9c2fc7e6..b91e308d8 100644
--- a/src/MySqlConnector/Core/SchemaProvider.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.cs
@@ -1,5 +1,6 @@
+#if NET6_0_OR_GREATER
 using System.Globalization;
-using System.Text;
+#endif
 using MySqlConnector.Protocol.Serialization;
 
 namespace MySqlConnector.Core;
@@ -431,7 +432,7 @@ private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable
 			while (await reader.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false))
 			{
 				var rowValues = new object[dataTable.Columns.Count];
-				reader.GetValues(rowValues);
+				_ = reader.GetValues(rowValues);
 				dataTable.Rows.Add(rowValues);
 			}
 		}
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 3c2e71393..58b088d56 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1,7 +1,6 @@
 using System.Buffers.Text;
 using System.ComponentModel;
 using System.Diagnostics;
-using System.Diagnostics.Metrics;
 using System.Globalization;
 using System.IO.Pipes;
 using System.Net;
@@ -64,10 +63,10 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public IPEndPoint? IPEndPoint => m_tcpClient?.Client.RemoteEndPoint as IPEndPoint;
 	public string? UserID { get; private set; }
 	public WeakReference<MySqlConnection>? OwningConnection { get; set; }
-	public bool SupportsDeprecateEof => m_supportsDeprecateEof;
+	public bool SupportsDeprecateEof { get; private set; }
 	public bool SupportsCachedPreparedMetadata { get; private set; }
 	public bool SupportsQueryAttributes { get; private set; }
-	public bool SupportsSessionTrack => m_supportsSessionTrack;
+	public bool SupportsSessionTrack { get; private set; }
 	public bool ProcAccessDenied { get; set; }
 	public ICollection<KeyValuePair<string, object?>> ActivityTags => m_activityTags;
 	public MySqlDataReader DataReader { get; }
@@ -128,7 +127,7 @@ public void DoCancel(ICancellableCommand commandToCancel, MySqlCommand killComma
 			// a unique identifier of the command itself). As a mitigation, we set the CommandTimeout to a low value to avoid
 			// blocking the other thread for an extended duration.
 			Log.CancelingCommand(m_logger, killCommand.Connection!.Session.Id, commandToCancel.CommandId, killCommand.CommandText);
-			killCommand.ExecuteNonQuery();
+			_ = killCommand.ExecuteNonQuery();
 		}
 	}
 
@@ -161,7 +160,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
 
 			var parameterCount = cachedProcedure.Parameters.Count;
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
-			commandToPrepare = string.Create(commandText.Length + 7 + parameterCount * 2 + (parameterCount == 0 ? 1 : 0), (commandText, parameterCount), static (buffer, state) =>
+			commandToPrepare = string.Create(commandText.Length + 7 + (parameterCount * 2) + (parameterCount == 0 ? 1 : 0), (commandText, parameterCount), static (buffer, state) =>
 			{
 				buffer[0] = 'C';
 				buffer[1] = 'A';
@@ -188,7 +187,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
 				buffer[1] = ';';
 			});
 #else
-			var callStatement = new StringBuilder("CALL ", commandText.Length + 8 + parameterCount * 2);
+			var callStatement = new StringBuilder("CALL ", commandText.Length + 8 + (parameterCount * 2));
 			callStatement.Append(commandText);
 			callStatement.Append('(');
 			for (int i = 0; i < parameterCount; i++)
@@ -196,7 +195,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
 			if (parameterCount == 0)
 				callStatement.Append(')');
 			else
-				callStatement[callStatement.Length - 1] = ')';
+				callStatement[^1] = ')';
 			callStatement.Append(';');
 			commandToPrepare = callStatement.ToString();
 #endif
@@ -394,8 +393,6 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 
 	public async Task<string?> ConnectAsync(ConnectionSettings cs, MySqlConnection connection, long startingTimestamp, ILoadBalancer? loadBalancer, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		string? statusInfo = null;
-
 		try
 		{
 			lock (m_lock)
@@ -414,9 +411,9 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 					m_activityTags.Add(ActivitySourceHelper.DatabaseNameTagName, cs.Database);
 				if (activity is { IsAllDataRequested: true })
 				{
-					activity.SetTag(ActivitySourceHelper.DatabaseSystemTagName, ActivitySourceHelper.DatabaseSystemValue);
-					activity.SetTag(ActivitySourceHelper.DatabaseConnectionStringTagName, connectionString);
-					activity.SetTag(ActivitySourceHelper.DatabaseUserTagName, cs.UserID);
+					activity.SetTag(ActivitySourceHelper.DatabaseSystemTagName, ActivitySourceHelper.DatabaseSystemValue)
+						.SetTag(ActivitySourceHelper.DatabaseConnectionStringTagName, connectionString)
+						.SetTag(ActivitySourceHelper.DatabaseUserTagName, cs.UserID);
 					if (cs.Database.Length != 0)
 						activity.SetTag(ActivitySourceHelper.DatabaseNameTagName, cs.Database);
 				}
@@ -446,13 +443,11 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			var initialHandshake = InitialHandshakePayload.Create(payload.Span);
 
 			// if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
-			string authPluginName;
-			if ((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
-				authPluginName = initialHandshake.AuthPluginName!;
-			else
-				authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" : "mysql_native_password";
+			var authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0 ? initialHandshake.AuthPluginName! :
+				(initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" :
+				"mysql_native_password";
 			Log.ServerSentAuthPluginName(m_logger, Id, authPluginName);
-			if (authPluginName != "mysql_native_password" && authPluginName != "sha256_password" && authPluginName != "caching_sha2_password")
+			if (authPluginName is not "mysql_native_password" and not "sha256_password" and not "caching_sha2_password")
 			{
 				Log.UnsupportedAuthenticationMethod(m_logger, Id, authPluginName);
 				throw new NotSupportedException($"Authentication method '{initialHandshake.AuthPluginName}' is not supported.");
@@ -474,10 +469,10 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			}
 
 			m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
-			m_supportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
+			SupportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
 			SupportsCachedPreparedMetadata = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
 			SupportsQueryAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
-			m_supportsSessionTrack = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
+			SupportsSessionTrack = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
 			var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0;
 			m_characterSet = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4GeneralCaseInsensitive : CharacterSet.Utf8Mb3GeneralCaseInsensitive;
 			m_setNamesPayload = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ?
@@ -510,7 +505,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				}
 			}
 
-			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, m_supportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, m_supportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
+			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, SupportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, SupportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
 
 			if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
 			{
@@ -538,7 +533,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			}
 
 			var ok = OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
-			statusInfo = ok.StatusInfo;
+			var statusInfo = ok.StatusInfo;
 
 			if (m_useCompression)
 				m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
@@ -552,6 +547,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				await GetRealServerDetailsAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 
 			m_payloadHandler.ByteHandler.RemainingTimeout = Constants.InfiniteTimeout;
+			return statusInfo;
 		}
 		catch (ArgumentException ex)
 		{
@@ -563,8 +559,6 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			Log.CouldNotConnectToServer(m_logger, ex, Id);
 			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Couldn't connect to server", ex);
 		}
-
-		return statusInfo;
 	}
 
 	public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConnection connection, IOBehavior ioBehavior, CancellationToken cancellationToken)
@@ -888,10 +882,7 @@ public async ValueTask<PayloadData> ReceiveReplyAsync(IOBehavior ioBehavior, Can
 		}
 
 		var payload = new PayloadData(bytes);
-		if (payload.HeaderByte == ErrorPayload.Signature)
-			throw CreateExceptionForErrorPayload(payload.Span);
-
-		return payload;
+		return payload.HeaderByte == ErrorPayload.Signature ? throw CreateExceptionForErrorPayload(payload.Span) : payload;
 	}
 
 	public ValueTask<PayloadData> ReceiveReplyAsync(int expectedSequenceNumber, IOBehavior ioBehavior, CancellationToken cancellationToken)
@@ -953,15 +944,16 @@ public static void ThrowIfStatementContainsDelimiter(MySqlException exception, I
 		}
 	}
 
-	private Exception? CreateExceptionForInvalidState()
+	private InvalidOperationException? CreateExceptionForInvalidState()
 	{
 		lock (m_lock)
 		{
-			if (m_state == State.Closed)
-				return new ObjectDisposedException(nameof(ServerSession));
-			if (m_state != State.Connected && m_state != State.Querying && m_state != State.CancelingQuery && m_state != State.ClearingPendingCancellation && m_state != State.Closing)
-				return new InvalidOperationException("ServerSession is not connected.");
-			return null;
+			return m_state switch
+			{
+				State.Closed => new ObjectDisposedException(nameof(ServerSession)),
+				State.Connected or State.Querying or State.CancelingQuery or State.ClearingPendingCancellation or State.Closing => null,
+				_ => new InvalidOperationException("ServerSession is not connected."),
+			};
 		}
 	}
 
@@ -1136,8 +1128,8 @@ private async Task<bool> OpenUnixSocketAsync(ConnectionSettings cs, Activity? ac
 			m_activityTags.Add(ActivitySourceHelper.NetPeerNameTagName, cs.UnixSocket);
 			if (activity is { IsAllDataRequested: true })
 			{
-				activity.SetTag(ActivitySourceHelper.NetTransportTagName, ActivitySourceHelper.NetTransportUnixValue);
-				activity.SetTag(ActivitySourceHelper.NetPeerNameTagName, cs.UnixSocket);
+				activity.SetTag(ActivitySourceHelper.NetTransportTagName, ActivitySourceHelper.NetTransportUnixValue)
+					.SetTag(ActivitySourceHelper.NetPeerNameTagName, cs.UnixSocket);
 			}
 		}
 
@@ -1145,7 +1137,7 @@ private async Task<bool> OpenUnixSocketAsync(ConnectionSettings cs, Activity? ac
 		var unixEp = new UnixDomainSocketEndPoint(cs.UnixSocket!);
 		try
 		{
-			using (cancellationToken.Register(() => socket.Dispose()))
+			using (cancellationToken.Register(socket.Dispose))
 			{
 				try
 				{
@@ -1644,16 +1636,16 @@ private async Task GetRealServerDetailsAsync(IOBehavior ioBehavior, Cancellation
 			await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			// column count: 2
-			await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+			_ = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			// CONNECTION_ID() and VERSION() columns
-			await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
-			await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
+			_ = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
+			_ = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 
 			if (!SupportsDeprecateEof)
 			{
 				payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
-				EofPayload.Create(payload.Span);
+				_ = EofPayload.Create(payload.Span);
 			}
 
 			// first (and only) row
@@ -1935,8 +1927,6 @@ protected override void OnStatementBegin(int index)
 	private bool m_useCompression;
 	private bool m_isSecureConnection;
 	private bool m_supportsConnectionAttributes;
-	private bool m_supportsDeprecateEof;
-	private bool m_supportsSessionTrack;
 	private bool m_supportsPipelining;
 	private CharacterSet m_characterSet;
 	private PayloadData m_setNamesPayload;
diff --git a/src/MySqlConnector/Core/SqlParser.cs b/src/MySqlConnector/Core/SqlParser.cs
index b2240de89..de08d26ff 100644
--- a/src/MySqlConnector/Core/SqlParser.cs
+++ b/src/MySqlConnector/Core/SqlParser.cs
@@ -212,7 +212,7 @@ public void Parse(string sql)
 			}
 			else
 			{
-				if (state != State.Beginning && state != State.Statement)
+				if (state is not State.Beginning and not State.Statement)
 					throw new InvalidOperationException($"Unexpected state: {state}");
 
 				if (ch == '-' && index < sql.Length - 2 && sql[index + 1] == '-' && sql[index + 2] == ' ')
diff --git a/src/MySqlConnector/Core/StatementPreparer.cs b/src/MySqlConnector/Core/StatementPreparer.cs
index 8e53a6203..1ae527984 100644
--- a/src/MySqlConnector/Core/StatementPreparer.cs
+++ b/src/MySqlConnector/Core/StatementPreparer.cs
@@ -17,9 +17,9 @@ public ParsedStatements SplitStatements()
 		parser.Parse(CommandText);
 		for (var i = 0; i < statements.Count; i++)
 #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP3_1_OR_GREATER
-			statements[i].StatementBytes = writer.ArraySegment[statementStartEndIndexes[i * 2]..statementStartEndIndexes[i * 2 + 1]];
+			statements[i].StatementBytes = writer.ArraySegment[statementStartEndIndexes[i * 2]..statementStartEndIndexes[(i * 2) + 1]];
 #else
-			statements[i].StatementBytes = writer.ArraySegment.Slice(statementStartEndIndexes[i * 2], statementStartEndIndexes[i * 2 + 1] - statementStartEndIndexes[i * 2]);
+			statements[i].StatementBytes = writer.ArraySegment.Slice(statementStartEndIndexes[i * 2], statementStartEndIndexes[(i * 2) + 1] - statementStartEndIndexes[i * 2]);
 #endif
 		return new ParsedStatements(statements, writer.ToPayloadData());
 	}
@@ -128,9 +128,9 @@ private void DoAppendParameter(string? parameterName, int parameterIndex, int te
 			Writer.Write((byte) '?');
 
 			// store the parameter index
-			Statements[Statements.Count - 1].ParameterNames.Add(parameterName);
-			Statements[Statements.Count - 1].NormalizedParameterNames.Add(parameterName == null ? null : MySqlParameter.NormalizeParameterName(parameterName));
-			Statements[Statements.Count - 1].ParameterIndexes.Add(parameterIndex);
+			Statements[^1].ParameterNames.Add(parameterName);
+			Statements[^1].NormalizedParameterNames.Add(parameterName == null ? null : MySqlParameter.NormalizeParameterName(parameterName));
+			Statements[^1].ParameterIndexes.Add(parameterIndex);
 		}
 
 		protected override void OnStatementEnd(int index)
diff --git a/src/MySqlConnector/Core/TypeMapper.cs b/src/MySqlConnector/Core/TypeMapper.cs
index 5ba28e210..6397f9182 100644
--- a/src/MySqlConnector/Core/TypeMapper.cs
+++ b/src/MySqlConnector/Core/TypeMapper.cs
@@ -169,13 +169,13 @@ private void AddColumnTypeMetadata(ColumnTypeMetadata columnTypeMetadata)
 	{
 		if (clrType.IsEnum)
 			clrType = Enum.GetUnderlyingType(clrType);
-		m_dbTypeMappingsByClrType.TryGetValue(clrType, out var dbTypeMapping);
+		_ = m_dbTypeMappingsByClrType.TryGetValue(clrType, out var dbTypeMapping);
 		return dbTypeMapping;
 	}
 
 	internal DbTypeMapping? GetDbTypeMapping(DbType dbType)
 	{
-		m_dbTypeMappingsByDbType.TryGetValue(dbType, out var dbTypeMapping);
+		_ = m_dbTypeMappingsByDbType.TryGetValue(dbType, out var dbTypeMapping);
 		return dbTypeMapping;
 	}
 
diff --git a/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs b/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs
index e2e9a827f..1c210e730 100644
--- a/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs
+++ b/src/MySqlConnector/Logging/ConsoleLoggerProvider.cs
@@ -7,7 +7,7 @@ public class ConsoleLoggerProvider : IMySqlConnectorLoggerProvider
 {
 	public ConsoleLoggerProvider(MySqlConnectorLogLevel minimumLevel = MySqlConnectorLogLevel.Info, bool isColored = true)
 	{
-		if (minimumLevel < MySqlConnectorLogLevel.Trace || minimumLevel > MySqlConnectorLogLevel.Fatal)
+		if (minimumLevel is < MySqlConnectorLogLevel.Trace or > MySqlConnectorLogLevel.Fatal)
 			throw new ArgumentOutOfRangeException(nameof(minimumLevel), "minimumLevel must be between Trace and Fatal");
 
 		m_minimumLevel = minimumLevel;
@@ -57,7 +57,7 @@ public void Log(MySqlConnectorLogLevel level, string message, object?[]? args =
 		}
 
 		private static readonly string[] s_levels =
-		{
+		[
 			"",
 			"[TRACE]",
 			"[DEBUG]",
@@ -65,10 +65,10 @@ public void Log(MySqlConnectorLogLevel level, string message, object?[]? args =
 			"[WARN]",
 			"[ERROR]",
 			"[FATAL]",
-		};
+		];
 
 		private static readonly ConsoleColor[] s_colors =
-		{
+		[
 			ConsoleColor.Black,
 			ConsoleColor.DarkGray,
 			ConsoleColor.Gray,
@@ -76,7 +76,7 @@ public void Log(MySqlConnectorLogLevel level, string message, object?[]? args =
 			ConsoleColor.Yellow,
 			ConsoleColor.Red,
 			ConsoleColor.Red,
-		};
+		];
 
 		private ConsoleLoggerProvider Provider { get; } = provider;
 		private string Name { get; } = name;
diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index b3ed8d0cb..df3f881e0 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -327,12 +327,11 @@ private async Task<int> ExecuteNonQueryAsync(IOBehavior ioBehavior, Cancellation
 
 	private bool IsValid([NotNullWhen(false)] out Exception? exception)
 	{
-		exception = null;
 		if (m_isDisposed)
 			exception = new ObjectDisposedException(GetType().Name);
 		else if (Connection is null)
 			exception = new InvalidOperationException("Connection property must be non-null.");
-		else if (Connection.State != ConnectionState.Open && Connection.State != ConnectionState.Connecting)
+		else if (Connection.State is not ConnectionState.Open and not ConnectionState.Connecting)
 			exception = new InvalidOperationException($"Connection must be Open; current state is {Connection.State}");
 		else if (!Connection.IgnoreCommandTransaction && Transaction != Connection.CurrentTransaction)
 			exception = new InvalidOperationException("The transaction associated with this batch is not the connection's active transaction; see https://fl.vu/mysql-trans");
diff --git a/src/MySqlConnector/MySqlBatchCommandCollection.cs b/src/MySqlConnector/MySqlBatchCommandCollection.cs
index e9eb3a04a..45382f90b 100644
--- a/src/MySqlConnector/MySqlBatchCommandCollection.cs
+++ b/src/MySqlConnector/MySqlBatchCommandCollection.cs
@@ -34,18 +34,18 @@ public override IEnumerator<DbBatchCommand> GetEnumerator()
 	public MySqlBatchCommand this[int index] { get => m_commands[index]; set => m_commands[index] = value; }
 	public int Count => m_commands.Count;
 	public bool IsReadOnly => false;
-	public void Add(MySqlBatchCommand item) => m_commands.Add((MySqlBatchCommand) item);
+	public void Add(MySqlBatchCommand item) => m_commands.Add(item);
 	public void Clear() => m_commands.Clear();
-	public bool Contains(MySqlBatchCommand item) => m_commands.Contains((MySqlBatchCommand) item);
+	public bool Contains(MySqlBatchCommand item) => m_commands.Contains(item);
 	public void CopyTo(MySqlBatchCommand[] array, int arrayIndex) => throw new NotImplementedException();
 	public IEnumerator<MySqlBatchCommand> GetEnumerator()
 	{
 		foreach (var command in m_commands)
 			yield return command;
 	}
-	public int IndexOf(MySqlBatchCommand item) => m_commands.IndexOf((MySqlBatchCommand) item);
-	public void Insert(int index, MySqlBatchCommand item) => m_commands.Insert(index, (MySqlBatchCommand) item);
-	public bool Remove(MySqlBatchCommand item) => m_commands.Remove((MySqlBatchCommand) item);
+	public int IndexOf(MySqlBatchCommand item) => m_commands.IndexOf(item);
+	public void Insert(int index, MySqlBatchCommand item) => m_commands.Insert(index, item);
+	public bool Remove(MySqlBatchCommand item) => m_commands.Remove(item);
 	public void RemoveAt(int index) => m_commands.RemoveAt(index);
 	IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
 #endif
diff --git a/src/MySqlConnector/MySqlBulkCopy.cs b/src/MySqlConnector/MySqlBulkCopy.cs
index 1d2510a2e..b029cf2aa 100644
--- a/src/MySqlConnector/MySqlBulkCopy.cs
+++ b/src/MySqlConnector/MySqlBulkCopy.cs
@@ -701,7 +701,7 @@ static bool WriteBytes(ReadOnlySpan<byte> value, ref int inputIndex, Span<byte>
 	}
 
 	private static readonly char[] s_specialCharacters = ['\t', '\\', '\n'];
-	private static readonly UTF8Encoding s_utf8Encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
+	private static readonly UTF8Encoding s_utf8Encoding = new(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true);
 
 	private readonly MySqlConnection m_connection;
 	private readonly MySqlTransaction? m_transaction;
diff --git a/src/MySqlConnector/MySqlBulkLoader.cs b/src/MySqlConnector/MySqlBulkLoader.cs
index 1ab11c8fc..89b0dd836 100644
--- a/src/MySqlConnector/MySqlBulkLoader.cs
+++ b/src/MySqlConnector/MySqlBulkLoader.cs
@@ -298,7 +298,7 @@ internal static object GetAndRemoveSource(string sourceKey)
 		lock (s_lock)
 		{
 			var source = s_sources[sourceKey];
-			s_sources.Remove(sourceKey);
+			_ = s_sources.Remove(sourceKey);
 			return source;
 		}
 	}
@@ -308,10 +308,7 @@ internal static bool TryGetAndRemoveSource(string sourceKey, [NotNullWhen(true)]
 		lock (s_lock)
 		{
 			if (s_sources.TryGetValue(sourceKey, out source))
-			{
-				s_sources.Remove(sourceKey);
-				return true;
-			}
+				return s_sources.Remove(sourceKey);
 		}
 
 		return false;
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index d8a124ad8..a7101d3e1 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -181,7 +181,7 @@ private bool NeedsPrepare(out Exception? exception)
 		if (exception is not null || Connection!.IgnorePrepare)
 			return false;
 
-		if (CommandType != CommandType.StoredProcedure && CommandType != CommandType.Text)
+		if (CommandType is not CommandType.StoredProcedure and not CommandType.Text)
 		{
 			exception = new NotSupportedException("Only CommandType.Text and CommandType.StoredProcedure are currently supported by MySqlCommand.Prepare.");
 			return false;
@@ -247,7 +247,7 @@ public override CommandType CommandType
 		get => m_commandType;
 		set
 		{
-			if (value != CommandType.Text && value != CommandType.StoredProcedure)
+			if (value is not CommandType.Text and not CommandType.StoredProcedure)
 				throw new ArgumentException("CommandType must be Text or StoredProcedure.", nameof(value));
 			m_commandType = value;
 		}
@@ -445,7 +445,7 @@ private bool IsValid([NotNullWhen(false)] out Exception? exception)
 			exception = new ObjectDisposedException(GetType().Name);
 		else if (Connection is null)
 			exception = new InvalidOperationException("Connection property must be non-null.");
-		else if (Connection.State != ConnectionState.Open && Connection.State != ConnectionState.Connecting)
+		else if (Connection.State is not ConnectionState.Open and not ConnectionState.Connecting)
 			exception = new InvalidOperationException($"Connection must be Open; current state is {Connection.State}");
 		else if (!Connection.IgnoreCommandTransaction && Transaction != Connection.CurrentTransaction)
 			exception = new InvalidOperationException("The transaction associated with this command is not the connection's active transaction; see https://fl.vu/mysql-trans");
diff --git a/src/MySqlConnector/MySqlCommandBuilder.cs b/src/MySqlConnector/MySqlCommandBuilder.cs
index 828f973fb..7b63dd5ef 100644
--- a/src/MySqlConnector/MySqlCommandBuilder.cs
+++ b/src/MySqlConnector/MySqlCommandBuilder.cs
@@ -81,7 +81,7 @@ protected override void ApplyParameterInfo(DbParameter parameter, DataRow row, S
 
 	protected override void SetRowUpdatingHandler(DbDataAdapter adapter)
 	{
-		if (!(adapter is MySqlDataAdapter mySqlDataAdapter))
+		if (adapter is not MySqlDataAdapter mySqlDataAdapter)
 			throw new ArgumentException("adapter needs to be a MySqlDataAdapter", nameof(adapter));
 
 		if (adapter == DataAdapter)
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 5ab8ff9f8..e9b21042e 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -1,6 +1,8 @@
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+#if NET6_0_OR_GREATER
 using System.Globalization;
+#endif
 using System.Net.Security;
 using System.Net.Sockets;
 using System.Security.Authentication;
@@ -211,7 +213,7 @@ internal static ReadOnlyMemory<byte> GetStartTransactionPayload(IsolationLevel i
 			true => 2,
 		};
 
-		var index = ((supportsQueryAttributes ? 1 : 0) * 5 + isolationLevelIndex + consistentSnapshotIndex) * 3 + readOnlyIndex;
+		var index = ((((supportsQueryAttributes ? 1 : 0) * 5) + isolationLevelIndex + consistentSnapshotIndex) * 3) + readOnlyIndex;
 
 		if (s_startTransactionPayloads[index].IsEmpty)
 		{
@@ -341,7 +343,7 @@ public override void EnlistTransaction(System.Transactions.Transaction? transact
 			else
 			{
 				m_enlistedTransaction = GetInitializedConnectionSettings().UseXaTransactions ?
-					(EnlistedTransactionBase) new XaEnlistedTransaction(transaction, this) :
+					 new XaEnlistedTransaction(transaction, this) :
 					new StandardEnlistedTransaction(transaction, this);
 				m_enlistedTransaction.Start();
 
@@ -1059,12 +1061,14 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 				var loadBalancer = connectionSettings.LoadBalance == MySqlLoadBalance.Random && connectionSettings.HostNames!.Count > 1 ?
 					RandomLoadBalancer.Instance : FailOverLoadBalancer.Instance;
 
-				var session = new ServerSession(m_logger);
-				session.OwningConnection = new WeakReference<MySqlConnection>(this);
+				var session = new ServerSession(m_logger)
+				{
+					OwningConnection = new WeakReference<MySqlConnection>(this),
+				};
 				Log.CreatedNonPooledSession(m_logger, session.Id);
 				try
 				{
-					await session.ConnectAsync(connectionSettings, this, startingTimestamp, loadBalancer, activity, actualIOBehavior, connectToken).ConfigureAwait(false);
+					_ = await session.ConnectAsync(connectionSettings, this, startingTimestamp, loadBalancer, activity, actualIOBehavior, connectToken).ConfigureAwait(false);
 					return session;
 				}
 				catch (Exception)
diff --git a/src/MySqlConnector/MySqlConnectorFactory.cs b/src/MySqlConnector/MySqlConnectorFactory.cs
index 02a358d10..8e5c2889c 100644
--- a/src/MySqlConnector/MySqlConnectorFactory.cs
+++ b/src/MySqlConnector/MySqlConnectorFactory.cs
@@ -65,7 +65,7 @@ public sealed class MySqlConnectorFactory : DbProviderFactory
 #if NET6_0_OR_GREATER
 	public override DbBatch CreateBatch() => new MySqlBatch();
 #else
-	public MySqlBatch CreateBatch() => new MySqlBatch();
+	public MySqlBatch CreateBatch() => new();
 #endif
 
 	/// <summary>
@@ -74,7 +74,7 @@ public sealed class MySqlConnectorFactory : DbProviderFactory
 #if NET6_0_OR_GREATER
 	public override DbBatchCommand CreateBatchCommand() => new MySqlBatchCommand();
 #else
-	public MySqlBatchCommand CreateBatchCommand() => new MySqlBatchCommand();
+	public MySqlBatchCommand CreateBatchCommand() => new();
 #endif
 
 	/// <summary>
diff --git a/src/MySqlConnector/MySqlDataAdapter.cs b/src/MySqlConnector/MySqlDataAdapter.cs
index f9a66d0c7..91fa33ec2 100644
--- a/src/MySqlConnector/MySqlDataAdapter.cs
+++ b/src/MySqlConnector/MySqlDataAdapter.cs
@@ -158,7 +158,7 @@ protected override int ExecuteBatch()
 		}
 
 		// ensure that the VALUES(...) clause contained only parameters, and that all were consumed
-		var remainingValues = parser.CommandText.Substring(match.Index + 6).Trim();
+		var remainingValues = parser.CommandText[(match.Index + 6)..].Trim();
 		remainingValues = remainingValues.TrimEnd(';').Trim().TrimStart('(').TrimEnd(')');
 		remainingValues = remainingValues.Replace(",", "");
 		if (!string.IsNullOrWhiteSpace(remainingValues))
@@ -166,7 +166,7 @@ protected override int ExecuteBatch()
 
 		// build one INSERT statement with concatenated VALUES
 		var combinedCommand = new MySqlCommand();
-		var sqlBuilder = new StringBuilder(sql.Substring(0, match.Index + 6));
+		var sqlBuilder = new StringBuilder(sql[..(match.Index + 6)]);
 		var combinedParameterIndex = 0;
 		for (var i = 0; i < batch.BatchCommands.Count; i++)
 		{
@@ -219,7 +219,7 @@ protected override void OnNamedParameter(int index, int length)
 #if NETCOREAPP3_0_OR_GREATER
 			CommandText = string.Concat(CommandText.AsSpan(0, index), new string(' ', length), CommandText.AsSpan(index + length));
 #else
-			CommandText = CommandText.Substring(0, index) + new string(' ', length) + CommandText.Substring(index + length);
+			CommandText = CommandText[..index] + new string(' ', length) + CommandText[(index + length)..];
 #endif
 		}
 
@@ -231,7 +231,7 @@ protected override void OnPositionalParameter(int index)
 #if NETCOREAPP3_0_OR_GREATER
 			CommandText = string.Concat(CommandText.AsSpan(0, index), " ", CommandText.AsSpan(index + 1));
 #else
-			CommandText = CommandText.Substring(0, index) + " " + CommandText.Substring(index + 1);
+			CommandText = CommandText[..index] + " " + CommandText[(index + 1)..];
 #endif
 		}
 
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 4bdc9c8ac..6490fc48b 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -177,9 +177,9 @@ public override int FieldCount
 		get
 		{
 			VerifyNotDisposed();
-			if (m_resultSet is null)
-				throw new InvalidOperationException("There is no current result set.");
-			return m_resultSet.ContainsCommandParameters ? 0 : m_resultSet.FieldCount;
+			return m_resultSet is null ? throw new InvalidOperationException("There is no current result set.") :
+				m_resultSet.ContainsCommandParameters ? 0 :
+				m_resultSet.FieldCount;
 		}
 	}
 
@@ -192,9 +192,8 @@ public override bool HasRows
 		get
 		{
 			VerifyNotDisposed();
-			if (m_resultSet is null)
-				throw new InvalidOperationException("There is no current result set.");
-			return !m_resultSet.ContainsCommandParameters && m_resultSet.HasRows;
+			return m_resultSet is null ? throw new InvalidOperationException("There is no current result set.") :
+				!m_resultSet.ContainsCommandParameters && m_resultSet.HasRows;
 		}
 	}
 
@@ -489,7 +488,7 @@ internal async Task InitAsync(CommandListPosition commandListPosition, ICommandP
 			// if the command list has multiple commands, keep reading until a result set is found
 			while (m_resultSet.State == ResultSetState.NoMoreData && commandListPosition.CommandIndex < commandListPosition.CommandCount)
 			{
-				await NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+				_ = await NextResultAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 			}
 		}
 		catch (Exception ex)
@@ -509,8 +508,11 @@ internal async Task InitAsync(CommandListPosition commandListPosition, ICommandP
 		if (!m_resultSet.HasResultSet || m_resultSet.ContainsCommandParameters)
 			return null;
 
-		var schemaTable = new DataTable("SchemaTable") { Locale = CultureInfo.InvariantCulture };
-		schemaTable.MinimumCapacity = m_resultSet.ColumnDefinitions.Length;
+		var schemaTable = new DataTable("SchemaTable")
+		{
+			Locale = CultureInfo.InvariantCulture,
+			MinimumCapacity = m_resultSet.ColumnDefinitions.Length,
+		};
 
 		var columnName = new DataColumn(SchemaTableColumn.ColumnName, typeof(string));
 		var ordinal = new DataColumn(SchemaTableColumn.ColumnOrdinal, typeof(int));
@@ -651,7 +653,7 @@ internal async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancel
 	// execution. These values will be stored in the parameters of the associated command.
 	private static async Task ReadOutParametersAsync(IMySqlCommand command, ResultSet resultSet, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
-		await resultSet.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+		_ = await resultSet.ReadAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 		var row = resultSet.GetCurrentRow();
 		if (row.GetString(0) != SingleCommandPayloadCreator.OutParameterSentinelColumnName)
@@ -688,9 +690,9 @@ private void VerifyNotDisposed()
 	private ResultSet GetResultSet()
 	{
 		VerifyNotDisposed();
-		if (m_resultSet is null || m_resultSet.ContainsCommandParameters)
-			throw new InvalidOperationException("There is no current result set.");
-		return m_resultSet;
+		return m_resultSet is null || m_resultSet.ContainsCommandParameters ?
+			throw new InvalidOperationException("There is no current result set.") :
+			m_resultSet;
 	}
 
 	private readonly ResultSet m_resultSet;
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index 986ad011e..a975b60c2 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -181,13 +181,13 @@ private async Task RefreshPassword()
 			// set the password from the callback, then queue another refresh after the 'success' interval
 			m_password = await m_periodicPasswordProvider!(m_providePasswordContext!, m_passwordProviderTimerCancellationTokenSource!.Token).ConfigureAwait(false);
 			m_providePasswordCallback = ProvidePasswordFromField;
-			m_passwordProviderTimer!.Change(m_periodicPasswordProviderSuccessRefreshInterval, Timeout.InfiniteTimeSpan);
+			_ = m_passwordProviderTimer!.Change(m_periodicPasswordProviderSuccessRefreshInterval, Timeout.InfiniteTimeSpan);
 		}
 		catch (Exception e)
 		{
 			// queue a refresh after the 'failure' interval
 			Log.PeriodicPasswordProviderFailed(m_logger, e, m_id, e.Message);
-			m_passwordProviderTimer!.Change(m_periodicPasswordProviderFailureRefreshInterval, Timeout.InfiniteTimeSpan);
+			_ = m_passwordProviderTimer!.Change(m_periodicPasswordProviderFailureRefreshInterval, Timeout.InfiniteTimeSpan);
 			throw new MySqlException("The periodic password provider failed", e);
 		}
 	}
diff --git a/src/MySqlConnector/MySqlDateTime.cs b/src/MySqlConnector/MySqlDateTime.cs
index 909880506..3b1533f1d 100644
--- a/src/MySqlConnector/MySqlDateTime.cs
+++ b/src/MySqlConnector/MySqlDateTime.cs
@@ -182,7 +182,7 @@ readonly int IComparable<MySqlDateTime>.CompareTo(MySqlDateTime other)
 	string IConvertible.ToString(IFormatProvider? provider) => IsValidDateTime ? GetDateTime().ToString(provider) : "0000-00-00";
 
 	object IConvertible.ToType(Type conversionType, IFormatProvider? provider) =>
-		conversionType == typeof(DateTime) ? (object) GetDateTime() :
+		conversionType == typeof(DateTime) ? GetDateTime() :
 		conversionType == typeof(string) ? ((IConvertible) this).ToString(provider) :
 		throw new InvalidCastException();
 
diff --git a/src/MySqlConnector/MySqlGeometry.cs b/src/MySqlConnector/MySqlGeometry.cs
index 0c8e0b821..b5af9cf83 100644
--- a/src/MySqlConnector/MySqlGeometry.cs
+++ b/src/MySqlConnector/MySqlGeometry.cs
@@ -27,7 +27,7 @@ public static MySqlGeometry FromWkb(int srid, ReadOnlySpan<byte> wkb)
 	/// <param name="value">The raw bytes of MySQL's internal GEOMETRY format.</param>
 	/// <returns>A new <see cref="MySqlGeometry"/> containing the specified geometry.</returns>
 	/// <remarks>See <a href="https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html#gis-internal-format">Internal Geometry Storage Format</a>.</remarks>
-	public static MySqlGeometry FromMySql(ReadOnlySpan<byte> value) => new MySqlGeometry(value.ToArray());
+	public static MySqlGeometry FromMySql(ReadOnlySpan<byte> value) => new(value.ToArray());
 
 	/// <summary>
 	/// The Spatial Reference System ID of this geometry.
diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 3b4761c45..00e2caca1 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -88,8 +88,8 @@ public override ParameterDirection Direction
 		get => m_direction.GetValueOrDefault(ParameterDirection.Input);
 		set
 		{
-			if (value != ParameterDirection.Input && value != ParameterDirection.Output &&
-				value != ParameterDirection.InputOutput && value != ParameterDirection.ReturnValue)
+			if (value is not (ParameterDirection.Input or ParameterDirection.Output or
+				ParameterDirection.InputOutput or ParameterDirection.ReturnValue))
 			{
 				throw new ArgumentOutOfRangeException(nameof(value), $"{value} is not a supported value for ParameterDirection");
 			}
@@ -152,11 +152,11 @@ public override void ResetDbType()
 		HasSetDbType = false;
 	}
 
-	public MySqlParameter Clone() => new MySqlParameter(this);
+	public MySqlParameter Clone() => new(this);
 
 	object ICloneable.Clone() => Clone();
 
-	internal MySqlParameter WithParameterName(string parameterName) => new MySqlParameter(this, parameterName);
+	internal MySqlParameter WithParameterName(string parameterName) => new(this, parameterName);
 
 	private MySqlParameter(MySqlParameter other)
 	{
diff --git a/src/MySqlConnector/MySqlProtocolException.cs b/src/MySqlConnector/MySqlProtocolException.cs
index f4f4ffbfd..894534ca6 100644
--- a/src/MySqlConnector/MySqlProtocolException.cs
+++ b/src/MySqlConnector/MySqlProtocolException.cs
@@ -15,7 +15,7 @@ public sealed class MySqlProtocolException : InvalidOperationException
 	/// <param name="packetSequenceNumber">The actual packet sequence number.</param>
 	/// <returns>A new <see cref="MySqlProtocolException"/>.</returns>
 	internal static MySqlProtocolException CreateForPacketOutOfOrder(int expectedSequenceNumber, int packetSequenceNumber) =>
-		new MySqlProtocolException($"Packet received out-of-order. Expected {expectedSequenceNumber:d}; got {packetSequenceNumber:d}.");
+		new($"Packet received out-of-order. Expected {expectedSequenceNumber:d}; got {packetSequenceNumber:d}.");
 
 #if NET8_0_OR_GREATER
 	[Obsolete(DiagnosticId = "SYSLIB0051")]
diff --git a/src/MySqlConnector/Protocol/Payloads/ErrorPayload.cs b/src/MySqlConnector/Protocol/Payloads/ErrorPayload.cs
index d87523d55..23c2b2274 100644
--- a/src/MySqlConnector/Protocol/Payloads/ErrorPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/ErrorPayload.cs
@@ -11,7 +11,7 @@ internal readonly struct ErrorPayload
 	public string State { get; }
 	public string Message { get; }
 
-	public MySqlException ToException() => new MySqlException((MySqlErrorCode) ErrorCode, State, Message);
+	public MySqlException ToException() => new((MySqlErrorCode) ErrorCode, State, Message);
 
 	public static ErrorPayload Create(ReadOnlySpan<byte> span)
 	{
diff --git a/src/MySqlConnector/Protocol/Serialization/AuthGSSAPI.cs b/src/MySqlConnector/Protocol/Serialization/AuthGSSAPI.cs
index 39baf77b9..412c6360e 100644
--- a/src/MySqlConnector/Protocol/Serialization/AuthGSSAPI.cs
+++ b/src/MySqlConnector/Protocol/Serialization/AuthGSSAPI.cs
@@ -136,14 +136,14 @@ public override async Task WriteAsync(byte[] buffer, int offset, int count, Canc
 			{
 				throw new FormatException($"Unknown version of NegotiateStream protocol {majorProtocolVersion:d}.{minorProtocolVersion:d}, expected {NegotiateStreamConstants.MajorVersion:d}.{NegotiateStreamConstants.MinorVersion:d}");
 			}
-			if (messageId != NegotiateStreamConstants.HandshakeDone &&
-				messageId != NegotiateStreamConstants.HandshakeError &&
-				messageId != NegotiateStreamConstants.HandshakeInProgress)
+			if (messageId is not NegotiateStreamConstants.HandshakeDone and
+				not NegotiateStreamConstants.HandshakeError and
+				not NegotiateStreamConstants.HandshakeInProgress)
 			{
 				throw new FormatException($"Invalid NegotiateStream MessageId 0x{messageId:X2}");
 			}
 
-			m_writePayloadLength = (int) payloadSizeLow + ((int) payloadSizeHigh << 8);
+			m_writePayloadLength = payloadSizeLow + (payloadSizeHigh << 8);
 			if (messageId == NegotiateStreamConstants.HandshakeDone)
 				m_clientHandshakeDone = true;
 
diff --git a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
index 4cbdfc6ea..fc0301a90 100644
--- a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
@@ -1,7 +1,11 @@
+#if NET5_0_OR_GREATER
 using System.Runtime.CompilerServices;
+#endif
 using System.Security.Cryptography;
 using System.Text;
+#if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
 using MySqlConnector.Utilities;
+#endif
 
 namespace MySqlConnector.Protocol.Serialization;
 
@@ -50,7 +54,7 @@ public static byte[] HashPassword(ReadOnlySpan<byte> challenge, string password)
 		SHA1.TryHashData(hashedPassword, combined[20..], out _);
 #else
 		sha1.TryComputeHash(passwordBytes, hashedPassword, out _);
-		sha1.TryComputeHash(hashedPassword, combined.Slice(20), out _);
+		sha1.TryComputeHash(hashedPassword, combined[20..], out _);
 #endif
 
 		Span<byte> xorBytes = stackalloc byte[20];
diff --git a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
index 6deb3e6a2..9017404bb 100644
--- a/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ByteBufferWriter.cs
@@ -17,9 +17,9 @@ public ByteBufferWriter(int capacity = 0)
 
 	public int Position => m_buffer.Length - m_output.Length;
 
-	public ArraySegment<byte> ArraySegment => new ArraySegment<byte>(m_buffer, 0, Position);
+	public ArraySegment<byte> ArraySegment => new(m_buffer, 0, Position);
 
-	public PayloadData ToPayloadData() => new PayloadData(ArraySegment, isPooled: true);
+	public PayloadData ToPayloadData() => new(ArraySegment, isPooled: true);
 
 	public Memory<byte> GetMemory(int sizeHint = 0)
 	{
diff --git a/src/MySqlConnector/Protocol/Serialization/StreamByteHandler.cs b/src/MySqlConnector/Protocol/Serialization/StreamByteHandler.cs
index 7bf4e0998..6e61538a2 100644
--- a/src/MySqlConnector/Protocol/Serialization/StreamByteHandler.cs
+++ b/src/MySqlConnector/Protocol/Serialization/StreamByteHandler.cs
@@ -35,9 +35,9 @@ ValueTask<int> DoReadBytesSync(Memory<byte> buffer)
 			}
 			catch (Exception ex)
 			{
-				if (RemainingTimeout != Constants.InfiniteTimeout && ex is IOException { InnerException: SocketException { SocketErrorCode: SocketError.TimedOut } })
-					return ValueTaskExtensions.FromException<int>(MySqlException.CreateForTimeout(ex));
-				return ValueTaskExtensions.FromException<int>(ex);
+				return RemainingTimeout != Constants.InfiniteTimeout && ex is IOException { InnerException: SocketException { SocketErrorCode: SocketError.TimedOut } } ?
+					ValueTaskExtensions.FromException<int>(MySqlException.CreateForTimeout(ex)) :
+					ValueTaskExtensions.FromException<int>(ex);
 			}
 			if (RemainingTimeout != Constants.InfiniteTimeout)
 				RemainingTimeout -= unchecked(Environment.TickCount - startTime);
diff --git a/src/MySqlConnector/Utilities/ResizableArraySegment.cs b/src/MySqlConnector/Utilities/ResizableArraySegment.cs
index 1137ddde8..d3ae564cb 100644
--- a/src/MySqlConnector/Utilities/ResizableArraySegment.cs
+++ b/src/MySqlConnector/Utilities/ResizableArraySegment.cs
@@ -10,5 +10,5 @@ internal readonly struct ResizableArraySegment<T>(ResizableArray<T> array, int o
 	public int Offset { get; } = offset;
 	public int Count { get; } = count;
 
-	public static implicit operator ReadOnlySpan<T>(ResizableArraySegment<T> segment) => new ReadOnlySpan<T>(segment.Array.Array, segment.Offset, segment.Count);
+	public static implicit operator ReadOnlySpan<T>(ResizableArraySegment<T> segment) => new(segment.Array.Array, segment.Offset, segment.Count);
 }
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 1909c49fc..d1d3ce02b 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -1,9 +1,13 @@
+#if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 using System.Buffers;
+#endif
 using System.Buffers.Text;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
+#if NET462
 using System.Net;
 using System.Reflection;
+#endif
 using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using System.Security.Authentication;
@@ -119,7 +123,7 @@ public static RSAParameters GetRsaParameters(string key)
 #if NETCOREAPP3_0_OR_GREATER
 			throw new FormatException(string.Concat("Unrecognized PEM header: ", key.AsSpan(0, Math.Min(key.Length, 80))));
 #else
-			throw new FormatException("Unrecognized PEM header: " + key.Substring(0, Math.Min(key.Length, 80)));
+			throw new FormatException("Unrecognized PEM header: " + key[..Math.Min(key.Length, 80)]);
 #endif
 		}
 
@@ -129,18 +133,16 @@ public static RSAParameters GetRsaParameters(string key)
 #if NETCOREAPP3_0_OR_GREATER
 			throw new FormatException(string.Concat("Missing expected '", pemFooter, "' PEM footer: ", key.AsSpan(Math.Max(key.Length - 80, 0))));
 #else
-			throw new FormatException($"Missing expected '{pemFooter}' PEM footer: " + key.Substring(Math.Max(key.Length - 80, 0)));
+			throw new FormatException($"Missing expected '{pemFooter}' PEM footer: " + key[Math.Max(key.Length - 80, 0)..]);
 #endif
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 		var keyChars = key.AsSpan()[keyStartIndex..keyEndIndex];
 		var bufferLength = keyChars.Length / 4 * 3;
 		byte[]? buffer = null;
-		scoped Span<byte> bufferBytes;
-		if (bufferLength > 1024)
-			bufferBytes = buffer = ArrayPool<byte>.Shared.Rent(bufferLength);
-		else
-			bufferBytes = stackalloc byte[bufferLength];
+		Span<byte> bufferBytes = bufferLength > 1024 ?
+			(Span<byte>) (buffer = ArrayPool<byte>.Shared.Rent(bufferLength)) :
+			stackalloc byte[bufferLength];
 		try
 		{
 			if (!System.Convert.TryFromBase64Chars(keyChars, bufferBytes, out var bytesWritten))
@@ -160,7 +162,7 @@ public static RSAParameters GetRsaParameters(string key)
 				ArrayPool<byte>.Shared.Return(buffer);
 		}
 #else
-		key = key.Substring(keyStartIndex, keyEndIndex - keyStartIndex);
+		key = key[keyStartIndex..keyEndIndex];
 		return GetRsaParameters(System.Convert.FromBase64String(key), isPrivate);
 #endif
 	}
@@ -172,57 +174,57 @@ private static RSAParameters GetRsaParameters(ReadOnlySpan<byte> data, bool isPr
 		// read header (30 81 xx, or 30 82 xx xx)
 		if (data[0] != 0x30)
 			throw new FormatException($"Expected 0x30 but read 0x{data[0]:X2}");
-		data = data.Slice(1);
+		data = data[1..];
 
 		if (!TryReadAsnLength(data, out var length, out var bytesConsumed))
 			throw new FormatException("Couldn't read key length");
-		data = data.Slice(bytesConsumed);
+		data = data[bytesConsumed..];
 
 		if (!isPrivate)
 		{
 			// encoded OID sequence for  PKCS #1 rsaEncryption szOID_RSA_RSA = "1.2.840.113549.1.1.1"
 			ReadOnlySpan<byte> rsaOid = [0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x01, 0x05, 0x00];
-			if (!data.Slice(0, rsaOid.Length).SequenceEqual(rsaOid))
-				throw new FormatException($"Expected RSA OID but read {BitConverter.ToString(data.Slice(0, 15).ToArray())}");
-			data = data.Slice(rsaOid.Length);
+			if (!data[..rsaOid.Length].SequenceEqual(rsaOid))
+				throw new FormatException($"Expected RSA OID but read {BitConverter.ToString(data[..15].ToArray())}");
+			data = data[rsaOid.Length..];
 
 			// BIT STRING (0x03) followed by length
 			if (data[0] != 0x03)
 				throw new FormatException($"Expected 0x03 but read 0x{data[0]:X2}");
-			data = data.Slice(1);
+			data = data[1..];
 
 			if (!TryReadAsnLength(data, out length, out bytesConsumed))
 				throw new FormatException("Couldn't read length");
-			data = data.Slice(bytesConsumed);
+			data = data[bytesConsumed..];
 
 			// skip NULL byte
 			if (data[0] != 0x00)
 				throw new FormatException($"Expected 0x00 but read 0x{data[0]:X2}");
-			data = data.Slice(1);
+			data = data[1..];
 
 			// skip next header (30 81 xx, or 30 82 xx xx)
 			if (data[0] != 0x30)
 				throw new FormatException($"Expected 0x30 but read 0x{data[0]:X2}");
-			data = data.Slice(1);
+			data = data[1..];
 
 			if (!TryReadAsnLength(data, out length, out bytesConsumed))
 				throw new FormatException("Couldn't read length");
-			data = data.Slice(bytesConsumed);
+			data = data[bytesConsumed..];
 		}
 		else
 		{
 			if (!TryReadAsnInteger(data, out var zero, out bytesConsumed) || zero.Length != 1 || zero[0] != 0)
 				throw new FormatException("Couldn't read zero.");
-			data = data.Slice(bytesConsumed);
+			data = data[bytesConsumed..];
 		}
 
 		if (!TryReadAsnInteger(data, out var modulus, out bytesConsumed))
 			throw new FormatException("Couldn't read modulus");
-		data = data.Slice(bytesConsumed);
+		data = data[bytesConsumed..];
 
 		if (!TryReadAsnInteger(data, out var exponent, out bytesConsumed))
 			throw new FormatException("Couldn't read exponent");
-		data = data.Slice(bytesConsumed);
+		data = data[bytesConsumed..];
 
 		if (!isPrivate)
 		{
@@ -235,27 +237,27 @@ private static RSAParameters GetRsaParameters(ReadOnlySpan<byte> data, bool isPr
 
 		if (!TryReadAsnInteger(data, out var d, out bytesConsumed))
 			throw new FormatException("Couldn't read D");
-		data = data.Slice(bytesConsumed);
+		data = data[bytesConsumed..];
 
 		if (!TryReadAsnInteger(data, out var p, out bytesConsumed))
 			throw new FormatException("Couldn't read P");
-		data = data.Slice(bytesConsumed);
+		data = data[bytesConsumed..];
 
 		if (!TryReadAsnInteger(data, out var q, out bytesConsumed))
 			throw new FormatException("Couldn't read Q");
-		data = data.Slice(bytesConsumed);
+		data = data[bytesConsumed..];
 
 		if (!TryReadAsnInteger(data, out var dp, out bytesConsumed))
 			throw new FormatException("Couldn't read DP");
-		data = data.Slice(bytesConsumed);
+		data = data[bytesConsumed..];
 
 		if (!TryReadAsnInteger(data, out var dq, out bytesConsumed))
 			throw new FormatException("Couldn't read DQ");
-		data = data.Slice(bytesConsumed);
+		data = data[bytesConsumed..];
 
 		if (!TryReadAsnInteger(data, out var iq, out bytesConsumed))
 			throw new FormatException("Couldn't read IQ");
-		data = data.Slice(bytesConsumed);
+		data = data[bytesConsumed..];
 
 		return new RSAParameters
 		{
@@ -279,7 +281,7 @@ private static RSAParameters GetRsaParameters(ReadOnlySpan<byte> data, bool isPr
 	/// <param name="index">The non-negative, zero-based starting index of the new slice (relative to <see cref="ArraySegment{T}.Offset"/> of <paramref name="arraySegment"/>.</param>
 	/// <returns>A new <see cref="ArraySegment{T}"/> starting at the <paramref name="index"/>th element of <paramref name="arraySegment"/> and continuing to the end of <paramref name="arraySegment"/>.</returns>
 	public static ArraySegment<T> Slice<T>(this ArraySegment<T> arraySegment, int index) =>
-		new ArraySegment<T>(arraySegment.Array!, arraySegment.Offset + index, arraySegment.Count - index);
+		new(arraySegment.Array!, arraySegment.Offset + index, arraySegment.Count - index);
 
 	/// <summary>
 	/// Returns a new <see cref="ArraySegment{T}"/> that starts at index <paramref name="index"/> into <paramref name="arraySegment"/> and has a length of <paramref name="length"/>.
@@ -289,7 +291,7 @@ public static ArraySegment<T> Slice<T>(this ArraySegment<T> arraySegment, int in
 	/// <param name="length">The non-negative length of the new slice.</param>
 	/// <returns>A new <see cref="ArraySegment{T}"/> of length <paramref name="length"/>, starting at the <paramref name="index"/>th element of <paramref name="arraySegment"/>.</returns>
 	public static ArraySegment<T> Slice<T>(this ArraySegment<T> arraySegment, int index, int length) =>
-		new ArraySegment<T>(arraySegment.Array!, arraySegment.Offset + index, length);
+		new(arraySegment.Array!, arraySegment.Offset + index, length);
 #endif
 
 #if !NET5_0_OR_GREATER
@@ -319,7 +321,7 @@ public static byte[] ArraySlice(byte[] input, int offset, int length)
 	/// <returns>The offset of <paramref name="pattern"/> within <paramref name="data"/>, or <c>-1</c> if <paramref name="pattern"/> was not found.</returns>
 	public static int FindNextIndex(ReadOnlySpan<byte> data, int offset, ReadOnlySpan<byte> pattern)
 	{
-		var index = MemoryExtensions.IndexOf(data.Slice(offset), pattern);
+		var index = MemoryExtensions.IndexOf(data[offset..], pattern);
 		return index == -1 ? -1 : offset + index;
 	}
 
@@ -356,7 +358,7 @@ public static bool TryParseRedirectionHeader(string header, out string host, out
 			if (closeSquareBracketIndex == -1)
 				return false;
 
-			host = header.Substring(hostIndex, closeSquareBracketIndex - hostIndex);
+			host = header[hostIndex..closeSquareBracketIndex];
 			if (header.Length <= closeSquareBracketIndex + 2)
 				return false;
 			if (header[closeSquareBracketIndex + 1] != ':')
@@ -374,7 +376,7 @@ public static bool TryParseRedirectionHeader(string header, out string host, out
 			if (colonIndex == -1)
 				return false;
 
-			host = header.Substring(hostIndex, colonIndex - hostIndex);
+			host = header[hostIndex..colonIndex];
 			portIndex = colonIndex + 1;
 		}
 
@@ -385,7 +387,7 @@ public static bool TryParseRedirectionHeader(string header, out string host, out
 #if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
 		if (!int.TryParse(header.AsSpan(portIndex, userIndex - portIndex), out port) || port <= 0)
 #else
-		if (!int.TryParse(header.Substring(portIndex, userIndex - portIndex), out port) || port <= 0)
+		if (!int.TryParse(header[portIndex..userIndex], out port) || port <= 0)
 #endif
 			return false;
 
@@ -394,7 +396,7 @@ public static bool TryParseRedirectionHeader(string header, out string host, out
 		var newlineIndex = header.IndexOf('\n', userIndex);
 		var terminatorIndex = ampersandIndex == -1 ? (newlineIndex == -1 ? header.Length : newlineIndex) :
 			(newlineIndex == -1 ? ampersandIndex : Math.Min(ampersandIndex, newlineIndex));
-		user = header.Substring(userIndex, terminatorIndex - userIndex);
+		user = header[userIndex..terminatorIndex];
 		return user.Length != 0;
 	}
 
@@ -415,7 +417,7 @@ public static TimeSpan ParseTimeSpan(ReadOnlySpan<byte> value)
 			goto InvalidTimeSpan;
 		if (value.Length == bytesConsumed || value[bytesConsumed] != 58)
 			goto InvalidTimeSpan;
-		value = value.Slice(bytesConsumed + 1);
+		value = value[(bytesConsumed + 1)..];
 
 		// parse minutes (0-59)
 		if (!Utf8Parser.TryParse(value, out int minutes, out bytesConsumed) || bytesConsumed != 2 || minutes < 0 || minutes > 59)
@@ -485,25 +487,25 @@ public static ReadOnlySpan<byte> TrimZeroByte(ReadOnlySpan<byte> value) =>
 #if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
 	public static int Read(this Stream stream, Memory<byte> buffer)
 	{
-		MemoryMarshal.TryGetArray<byte>(buffer, out var arraySegment);
+		_ = MemoryMarshal.TryGetArray<byte>(buffer, out var arraySegment);
 		return stream.Read(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
 	}
 
 	public static Task<int> ReadAsync(this Stream stream, Memory<byte> buffer)
 	{
-		MemoryMarshal.TryGetArray<byte>(buffer, out var arraySegment);
+		_ = MemoryMarshal.TryGetArray<byte>(buffer, out var arraySegment);
 		return stream.ReadAsync(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
 	}
 
 	public static void Write(this Stream stream, ReadOnlyMemory<byte> data)
 	{
-		MemoryMarshal.TryGetArray(data, out var arraySegment);
+		_ = MemoryMarshal.TryGetArray(data, out var arraySegment);
 		stream.Write(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
 	}
 
 	public static Task WriteAsync(this Stream stream, ReadOnlyMemory<byte> data)
 	{
-		MemoryMarshal.TryGetArray(data, out var arraySegment);
+		_ = MemoryMarshal.TryGetArray(data, out var arraySegment);
 		return stream.WriteAsync(arraySegment.Array, arraySegment.Offset, arraySegment.Count);
 	}
 #else
@@ -642,7 +644,7 @@ private static bool TryReadAsnLength(ReadOnlySpan<byte> data, out int length, ou
 
 		if (leadByte == 0x82)
 		{
-			length = data[1] * 256 + data[2];
+			length = (data[1] * 256) + data[2];
 			bytesConsumed = 3;
 			return true;
 		}
diff --git a/tests/IntegrationTests/AppConfig.cs b/tests/IntegrationTests/AppConfig.cs
index faabbcf0c..5ffa32192 100644
--- a/tests/IntegrationTests/AppConfig.cs
+++ b/tests/IntegrationTests/AppConfig.cs
@@ -52,7 +52,7 @@ private static IConfiguration BuildConfiguration()
 	public static string MySqlBulkLoaderTsvFile => Config.GetValue<string>("Data:MySqlBulkLoaderTsvFile");
 	public static string MySqlBulkLoaderLocalTsvFile => Config.GetValue<string>("Data:MySqlBulkLoaderLocalTsvFile");
 
-	public static MySqlConnectionStringBuilder CreateConnectionStringBuilder() => new MySqlConnectionStringBuilder(ConnectionString);
+	public static MySqlConnectionStringBuilder CreateConnectionStringBuilder() => new(ConnectionString);
 
 	public static MySqlConnectionStringBuilder CreateSha256ConnectionStringBuilder()
 	{
diff --git a/tests/IntegrationTests/BulkLoaderAsync.cs b/tests/IntegrationTests/BulkLoaderAsync.cs
index cc147866e..b697a0b4b 100644
--- a/tests/IntegrationTests/BulkLoaderAsync.cs
+++ b/tests/IntegrationTests/BulkLoaderAsync.cs
@@ -34,9 +34,11 @@ one int primary key
 	public async Task BulkLoadTsvFile()
 	{
 		using var connection = new MySqlConnection(GetConnectionString());
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = AppConfig.MySqlBulkLoaderTsvFile;
-		bl.TableName = m_testTable;
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = AppConfig.MySqlBulkLoaderTsvFile,
+			TableName = m_testTable,
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.Expressions.Add("five = UNHEX(five)");
@@ -49,9 +51,11 @@ public async Task BulkLoadTsvFile()
 	public async Task BulkLoadLocalTsvFile()
 	{
 		using var connection = new MySqlConnection(GetLocalConnectionString());
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = AppConfig.MySqlBulkLoaderLocalTsvFile;
-		bl.TableName = m_testTable;
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = AppConfig.MySqlBulkLoaderLocalTsvFile,
+			TableName = m_testTable,
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.Expressions.Add("five = UNHEX(five)");
@@ -64,9 +68,11 @@ public async Task BulkLoadLocalTsvFile()
 	public async Task BulkLoadCsvFile()
 	{
 		using var connection = new MySqlConnection(GetConnectionString());
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = AppConfig.MySqlBulkLoaderCsvFile;
-		bl.TableName = m_testTable;
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = AppConfig.MySqlBulkLoaderCsvFile,
+			TableName = m_testTable,
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
@@ -83,10 +89,12 @@ public async Task BulkLoadLocalCsvFile()
 	{
 		using var connection = new MySqlConnection(GetLocalConnectionString());
 		await connection.OpenAsync();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = AppConfig.MySqlBulkLoaderLocalCsvFile;
-		bl.TableName = m_testTable;
-		bl.CharacterSet = "UTF8";
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = AppConfig.MySqlBulkLoaderLocalCsvFile,
+			TableName = m_testTable,
+			CharacterSet = "UTF8",
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
@@ -108,10 +116,12 @@ public async Task BulkLoadCsvFileNotFound()
 		if (string.IsNullOrEmpty(secureFilePath) || secureFilePath == "NULL")
 			return;
 
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = Path.Combine(secureFilePath, AppConfig.MySqlBulkLoaderCsvFile + "-junk");
-		bl.TableName = m_testTable;
-		bl.CharacterSet = "UTF8";
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = Path.Combine(secureFilePath, AppConfig.MySqlBulkLoaderCsvFile + "-junk"),
+			TableName = m_testTable,
+			CharacterSet = "UTF8",
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
@@ -148,11 +158,13 @@ public async Task BulkLoadLocalCsvFileNotFound()
 	{
 		using var connection = new MySqlConnection(GetLocalConnectionString());
 		await connection.OpenAsync();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.Timeout = 3; //Set a short timeout for this test because the file not found exception takes a long time otherwise, the timeout does not change the result
-		bl.FileName = AppConfig.MySqlBulkLoaderLocalCsvFile + "-junk";
-		bl.TableName = m_testTable;
-		bl.CharacterSet = "UTF8";
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			Timeout = 3, //Set a short timeout for this test because the file not found exception takes a long time otherwise, the timeout does not change the result
+			FileName = AppConfig.MySqlBulkLoaderLocalCsvFile + "-junk",
+			TableName = m_testTable,
+			CharacterSet = "UTF8",
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
@@ -255,8 +267,10 @@ public async Task BulkLoadMissingFileName()
 	{
 		using var connection = new MySqlConnection(GetConnectionString());
 		await connection.OpenAsync();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.TableName = m_testTable;
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			TableName = m_testTable,
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
@@ -282,8 +296,10 @@ public async Task BulkLoadMissingTableName()
 	{
 		using var connection = new MySqlConnection(GetConnectionString());
 		await connection.OpenAsync();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = AppConfig.MySqlBulkLoaderLocalCsvFile;
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = AppConfig.MySqlBulkLoaderLocalCsvFile,
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
diff --git a/tests/IntegrationTests/BulkLoaderSync.cs b/tests/IntegrationTests/BulkLoaderSync.cs
index 73b81bce7..3226f2223 100644
--- a/tests/IntegrationTests/BulkLoaderSync.cs
+++ b/tests/IntegrationTests/BulkLoaderSync.cs
@@ -51,9 +51,11 @@ public void BulkLoadTsvFile()
 	{
 		using var connection = new MySqlConnection(GetConnectionString());
 		connection.Open();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = AppConfig.MySqlBulkLoaderTsvFile;
-		bl.TableName = m_testTable;
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = AppConfig.MySqlBulkLoaderTsvFile,
+			TableName = m_testTable,
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.Expressions.Add("five = UNHEX(five)");
@@ -67,9 +69,11 @@ public void BulkLoadLocalTsvFile()
 	{
 		using var connection = new MySqlConnection(GetLocalConnectionString());
 		connection.Open();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = AppConfig.MySqlBulkLoaderLocalTsvFile;
-		bl.TableName = m_testTable;
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = AppConfig.MySqlBulkLoaderLocalTsvFile,
+			TableName = m_testTable,
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.Expressions.Add("five = UNHEX(five)");
@@ -83,10 +87,12 @@ public void BulkLoadCsvFile()
 	{
 		using var connection = new MySqlConnection(GetConnectionString());
 		connection.Open();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = AppConfig.MySqlBulkLoaderCsvFile;
-		bl.TableName = m_testTable;
-		bl.CharacterSet = "UTF8";
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = AppConfig.MySqlBulkLoaderCsvFile,
+			TableName = m_testTable,
+			CharacterSet = "UTF8",
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
@@ -103,10 +109,12 @@ public void BulkLoadLocalCsvFile()
 	{
 		using var connection = new MySqlConnection(GetLocalConnectionString());
 		connection.Open();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = AppConfig.MySqlBulkLoaderLocalCsvFile;
-		bl.TableName = m_testTable;
-		bl.CharacterSet = "UTF8";
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = AppConfig.MySqlBulkLoaderLocalCsvFile,
+			TableName = m_testTable,
+			CharacterSet = "UTF8",
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
@@ -127,10 +135,12 @@ public void BulkLoadCsvFileNotFound()
 		if (string.IsNullOrEmpty(secureFilePath) || secureFilePath == "NULL")
 			return;
 
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = Path.Combine(secureFilePath, AppConfig.MySqlBulkLoaderCsvFile + "-junk");
-		bl.TableName = m_testTable;
-		bl.CharacterSet = "UTF8";
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = Path.Combine(secureFilePath, AppConfig.MySqlBulkLoaderCsvFile + "-junk"),
+			TableName = m_testTable,
+			CharacterSet = "UTF8",
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
@@ -167,11 +177,13 @@ public void BulkLoadLocalCsvFileNotFound()
 	{
 		using var connection = new MySqlConnection(GetLocalConnectionString());
 		connection.Open();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.Timeout = 3; //Set a short timeout for this test because the file not found exception takes a long time otherwise, the timeout does not change the result
-		bl.FileName = AppConfig.MySqlBulkLoaderLocalCsvFile + "-junk";
-		bl.TableName = m_testTable;
-		bl.CharacterSet = "UTF8";
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			Timeout = 3, //Set a short timeout for this test because the file not found exception takes a long time otherwise, the timeout does not change the result
+			FileName = AppConfig.MySqlBulkLoaderLocalCsvFile + "-junk",
+			TableName = m_testTable,
+			CharacterSet = "UTF8",
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
@@ -334,9 +346,11 @@ public void BulkLoadMissingFileName()
 	{
 		using var connection = new MySqlConnection(GetConnectionString());
 		connection.Open();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.TableName = m_testTable;
-		bl.CharacterSet = "UTF8";
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			TableName = m_testTable,
+			CharacterSet = "UTF8",
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
@@ -356,8 +370,10 @@ public void BulkLoadMissingTableName()
 	{
 		using var connection = new MySqlConnection(GetConnectionString());
 		connection.Open();
-		MySqlBulkLoader bl = new MySqlBulkLoader(connection);
-		bl.FileName = AppConfig.MySqlBulkLoaderLocalCsvFile;
+		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
+		{
+			FileName = AppConfig.MySqlBulkLoaderLocalCsvFile,
+		};
 		bl.Columns.AddRange(new string[] { "one", "two", "three", "four", "five" });
 		bl.NumberOfLinesToSkip = 1;
 		bl.FieldTerminator = ",";
diff --git a/tests/IntegrationTests/ConnectionPool.cs b/tests/IntegrationTests/ConnectionPool.cs
index 4f671623f..330582f58 100644
--- a/tests/IntegrationTests/ConnectionPool.cs
+++ b/tests/IntegrationTests/ConnectionPool.cs
@@ -313,5 +313,5 @@ private Task ClearPoolAsync(MySqlConnection connection)
 	}
 
 	private static HashSet<long> GetConnectionIds(IEnumerable<MySqlConnection> connections)
-		=> new HashSet<long>(connections.Select(x => (long) x.ServerThread));
+		=> new(connections.Select(x => (long) x.ServerThread));
 }
diff --git a/tests/IntegrationTests/ConnectionTests.cs b/tests/IntegrationTests/ConnectionTests.cs
index e086a9580..0f8941f36 100644
--- a/tests/IntegrationTests/ConnectionTests.cs
+++ b/tests/IntegrationTests/ConnectionTests.cs
@@ -218,8 +218,10 @@ public void CloneWithUsesExistingPassword()
 		var newConnectionString = "user=root;server=example.com;database=test";
 		using var connection2 = connection.CloneWith(newConnectionString);
 
-		var builder = new MySqlConnectionStringBuilder(newConnectionString);
-		builder.Password = AppConfig.CreateConnectionStringBuilder().Password;
+		var builder = new MySqlConnectionStringBuilder(newConnectionString)
+		{
+			Password = AppConfig.CreateConnectionStringBuilder().Password,
+		};
 		Assert.Equal(builder.ConnectionString, connection2.ConnectionString);
 	}
 
@@ -249,8 +251,10 @@ public void CloneWithDoesDiscloseExistingPasswordIfPersistSecurityInfo(bool pers
 		var newConnectionString = "user=root;server=example.com;database=test;Persist Security Info=" + persistSecurityInfo;
 		using var connection2 = connection.CloneWith(newConnectionString);
 
-		var builder = new MySqlConnectionStringBuilder(newConnectionString);
-		builder.Password = AppConfig.CreateConnectionStringBuilder().Password;
+		var builder = new MySqlConnectionStringBuilder(newConnectionString)
+		{
+			Password = AppConfig.CreateConnectionStringBuilder().Password,
+		};
 		Assert.Equal(builder.ConnectionString, connection2.ConnectionString);
 	}
 
diff --git a/tests/IntegrationTests/DataTypes.cs b/tests/IntegrationTests/DataTypes.cs
index a25cdb9a6..033751097 100644
--- a/tests/IntegrationTests/DataTypes.cs
+++ b/tests/IntegrationTests/DataTypes.cs
@@ -562,11 +562,11 @@ insert into guid_format(c36, c32, b16, tsb16, leb16, t, b) values(
 				new() { Value = guidAsLittleEndianBinary16 },
 				new() { Value = guidAsChar36 },
 				new() { Value = isBinary16 ? guidAsBinary16 : isTimeSwapBinary16 ? guidAsTimeSwapBinary16 : guidAsLittleEndianBinary16 },
-				new() { Value = isChar36 ? (object) guid : guidAsChar36 },
-				new() { Value = isChar32 ? (object) guid : guidAsChar32 },
-				new() { Value = isBinary16 ? (object) guid : guidAsBinary16 },
-				new() { Value = isTimeSwapBinary16 ? (object) guid : guidAsTimeSwapBinary16 },
-				new() { Value = isLittleEndianBinary16 ? (object) guid : guidAsLittleEndianBinary16 },
+				new() { Value = isChar36 ?  guid : guidAsChar36 },
+				new() { Value = isChar32 ?  guid : guidAsChar32 },
+				new() { Value = isBinary16 ?  guid : guidAsBinary16 },
+				new() { Value = isTimeSwapBinary16 ?  guid : guidAsTimeSwapBinary16 },
+				new() { Value = isLittleEndianBinary16 ?  guid : guidAsLittleEndianBinary16 },
 				new() { Value = guidAsChar32 },
 				new() { Value = isBinary16 ? guidAsBinary16 : isTimeSwapBinary16 ? guidAsTimeSwapBinary16 : guidAsLittleEndianBinary16 },
 			}
@@ -669,7 +669,7 @@ public void QueryZeroDateTime(bool convertZeroDateTime)
 		using var cmd = connection.CreateCommand();
 		cmd.CommandText = @"select cast(0 as date), cast(0 as datetime);";
 
-		using var reader = (MySqlDataReader) cmd.ExecuteReader();
+		using var reader = cmd.ExecuteReader();
 		Assert.True(reader.Read());
 		if (convertZeroDateTime)
 		{
@@ -1846,6 +1846,6 @@ private static int[] SplitAndParse(object obj)
 
 	private MySqlConnection Connection { get; }
 
-	private MySqlConnectionStringBuilder CreateConnectionStringBuilder() => new MySqlConnectionStringBuilder(AppConfig.ConnectionString);
+	private MySqlConnectionStringBuilder CreateConnectionStringBuilder() => new(AppConfig.ConnectionString);
 }
 
diff --git a/tests/IntegrationTests/DatabaseFixture.cs b/tests/IntegrationTests/DatabaseFixture.cs
index eb53da67c..29424330b 100644
--- a/tests/IntegrationTests/DatabaseFixture.cs
+++ b/tests/IntegrationTests/DatabaseFixture.cs
@@ -53,6 +53,6 @@ protected virtual void Dispose(bool disposing)
 		}
 	}
 
-	static object s_lock = new object();
+	static readonly object s_lock = new();
 	static bool s_isInitialized;
 }
diff --git a/tests/IntegrationTests/ParameterCollection.cs b/tests/IntegrationTests/ParameterCollection.cs
index 5e1b54c98..0e6398e12 100644
--- a/tests/IntegrationTests/ParameterCollection.cs
+++ b/tests/IntegrationTests/ParameterCollection.cs
@@ -299,6 +299,6 @@ public void SetTwoParametersToSameNAme()
 #endif
 	}
 
-	MySqlCommand m_command;
-	MySqlParameterCollection m_parameterCollection;
+	readonly MySqlCommand m_command;
+	readonly MySqlParameterCollection m_parameterCollection;
 }
diff --git a/tests/IntegrationTests/QueryTests.cs b/tests/IntegrationTests/QueryTests.cs
index cd0181739..051e88c30 100644
--- a/tests/IntegrationTests/QueryTests.cs
+++ b/tests/IntegrationTests/QueryTests.cs
@@ -19,8 +19,10 @@ public void Dispose()
 	[InlineData(false, false, true)]
 	public void Bug1096(bool sprocBeforeInsert, bool sprocAfterInsert, bool sprocTwiceBeforeInsert)
 	{
-		var csb = new MySqlConnectionStringBuilder(AppConfig.ConnectionString);
-		csb.UseAffectedRows = true;
+		var csb = new MySqlConnectionStringBuilder(AppConfig.ConnectionString)
+		{
+			UseAffectedRows = true,
+		};
 		using var connection = new MySqlConnection(csb.ConnectionString);
 		connection.Open();
 
@@ -1404,7 +1406,7 @@ public void GetIntForTinyInt1(bool treatTinyAsBoolean, bool prepare)
 				Assert.Equal((byte) expected[i], reader.GetByte(0));
 			Assert.Equal((short) expected[i], reader.GetInt16(0));
 			Assert.Equal(expected[i], reader.GetInt32(0));
-			Assert.Equal((long) expected[i], reader.GetInt64(0));
+			Assert.Equal(expected[i], reader.GetInt64(0));
 			Assert.Equal(expected[i], reader.GetFieldValue<int>(0));
 		}
 
diff --git a/tests/IntegrationTests/TransactionScopeTests.cs b/tests/IntegrationTests/TransactionScopeTests.cs
index ad88fed2e..ffe14b594 100644
--- a/tests/IntegrationTests/TransactionScopeTests.cs
+++ b/tests/IntegrationTests/TransactionScopeTests.cs
@@ -888,6 +888,6 @@ public void Bug1348()
 	}
 #endif
 
-	DatabaseFixture m_database;
+	readonly DatabaseFixture m_database;
 }
 
diff --git a/tests/MySqlConnector.Tests/CachedProcedureTests.cs b/tests/MySqlConnector.Tests/CachedProcedureTests.cs
index c715b46c8..c6966b821 100644
--- a/tests/MySqlConnector.Tests/CachedProcedureTests.cs
+++ b/tests/MySqlConnector.Tests/CachedProcedureTests.cs
@@ -27,118 +27,101 @@ public static IEnumerable<object[]> CreateParseableParameters()
 			{
 				"", new object[0],
 			},
-			new object[]
-			{
+			[
 				"/* no, parameters */", new object[0],
-			},
-			new object[]
-			{
+			],
+			[
 				"IN test INT", new object[]
 				{
 					new CachedParameter(1, "IN", "test", "INT", false, 0),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"IN test INT UNSIGNED", new object[]
 				{
 					new CachedParameter(1, "IN", "test", "INT", true, 0),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"-- IN ignored INT UNSIGNED,\r\nIN notignored INT", new object[]
 				{
 					new CachedParameter(1, "IN", "notignored", "INT", false, 0),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"IN param1 INT,\r\nIN param2 INT", new object[]
 				{
 					new CachedParameter(1, "IN", "param1", "INT", false, 0),
 					new CachedParameter(2, "IN", "param2", "INT", false, 0),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"IN /* ignored BIGINT,\r\nIN*/ param1 INT", new object[]
 				{
 					new CachedParameter(1, "IN", "param1", "INT", false, 0),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"IN param1 INT(11)", new object[]
 				{
 					new CachedParameter(1, "IN", "param1", "INT", false, 11),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"param1 BIGINT(21) UNSIGNED ZEROFILL", new object[]
 				{
 					new CachedParameter(1, "IN", "param1", "BIGINT", true, 21),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"param1 VARCHAR(63)", new object[]
 				{
 					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"param1 VARCHAR(63) CHARSET latin1", new object[]
 				{
 					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"param1 VARCHAR(63) COLLATE utf8bin", new object[]
 				{
 					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"param1 VARCHAR(63) CHARACTER SET latin1 COLLATE latin1_bin", new object[]
 				{
 					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"`par``am` INT", new object[]
 				{
 					new CachedParameter(1, "IN", "par`am", "INT", false, 0),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"IN input enum ('One', 'Two', 'Three')", new object[]
 				{
 					new CachedParameter(1, "IN", "input", "ENUM", false, 0),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"OUT param DECIMAL(10,5)", new object[]
 				{
 					new CachedParameter(1, "OUT", "param", "DECIMAL", false, 0),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				"INOUT param LONGTEXT", new object[]
 				{
 					new CachedParameter(1, "INOUT", "param", "LONGTEXT", false, 0),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				@"IN param1 DATETIME(6),
 -- ignored1
 OUT param2 /* ignore */ INT,
@@ -155,9 +138,8 @@ param5 bigint(20) unsigned zerofill,
 					new CachedParameter(5, "IN", "param5", "BIGINT", true, 20),
 					new CachedParameter(6, "OUT", "param6", "TINYINT", false, 1),
 				}
-			},
-			new object[]
-			{
+			],
+			[
 				@"
 param1 boolean,
 param2 nvarchar,
@@ -172,7 +154,7 @@ param4 INTEGER(3)
 					new CachedParameter(3, "IN", "param3", "DOUBLE", false, 20),
 					new CachedParameter(4, "IN", "param4", "INT", false, 3),
 				}
-			},
+			],
 		};
 	}
 
diff --git a/tests/MySqlConnector.Tests/CancellationTests.cs b/tests/MySqlConnector.Tests/CancellationTests.cs
index 3e2c16ed7..e195fc619 100644
--- a/tests/MySqlConnector.Tests/CancellationTests.cs
+++ b/tests/MySqlConnector.Tests/CancellationTests.cs
@@ -420,8 +420,8 @@ public static IEnumerable<object[]> GetAsyncMethodSteps()
 		}
 	}
 
-	private static readonly Func<MySqlCommand, int>[] s_executeMethods = new Func<MySqlCommand, int>[] { ExecuteScalar, ExecuteNonQuery, ExecuteReader };
-	private static readonly Func<MySqlCommand, CancellationToken, Task<int>>[] s_executeAsyncMethods = new Func<MySqlCommand, CancellationToken, Task<int>>[] { ExecuteScalarAsync, ExecuteNonQueryAsync, ExecuteReaderAsync };
+	private static readonly Func<MySqlCommand, int>[] s_executeMethods = [ExecuteScalar, ExecuteNonQuery, ExecuteReader];
+	private static readonly Func<MySqlCommand, CancellationToken, Task<int>>[] s_executeAsyncMethods = [ExecuteScalarAsync, ExecuteNonQueryAsync, ExecuteReaderAsync];
 
 	private static int ExecuteScalar(MySqlCommand command) => (int) command.ExecuteScalar();
 	private static async Task<int> ExecuteScalarAsync(MySqlCommand command, CancellationToken token) => (int) await command.ExecuteScalarAsync(token);
diff --git a/tests/MySqlConnector.Tests/ConnectionTests.cs b/tests/MySqlConnector.Tests/ConnectionTests.cs
index 73f7b0447..0c0833a1f 100644
--- a/tests/MySqlConnector.Tests/ConnectionTests.cs
+++ b/tests/MySqlConnector.Tests/ConnectionTests.cs
@@ -180,8 +180,10 @@ public void PingWhenClosed()
 	public void ConnectionTimeout()
 	{
 		m_server.ConnectDelay = TimeSpan.FromSeconds(10);
-		var csb = new MySqlConnectionStringBuilder(m_csb.ConnectionString);
-		csb.ConnectionTimeout = 4;
+		var csb = new MySqlConnectionStringBuilder(m_csb.ConnectionString)
+		{
+			ConnectionTimeout = 4,
+		};
 		using var connection = new MySqlConnection(csb.ConnectionString);
 		var stopwatch = Stopwatch.StartNew();
 		try
@@ -199,8 +201,10 @@ public void ConnectionTimeout()
 	[Fact]
 	public void ResetConnectionTimeout()
 	{
-		var csb = new MySqlConnectionStringBuilder(m_csb.ConnectionString);
-		csb.ConnectionTimeout = 4;
+		var csb = new MySqlConnectionStringBuilder(m_csb.ConnectionString)
+		{
+			ConnectionTimeout = 4,
+		};
 		using var connection = new MySqlConnection(csb.ConnectionString);
 		connection.Open();
 		connection.Close();
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServer.cs b/tests/MySqlConnector.Tests/FakeMySqlServer.cs
index d070a085f..035e48b51 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServer.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServer.cs
@@ -9,8 +9,8 @@ public FakeMySqlServer()
 	{
 		m_tcpListener = new(IPAddress.Any, 0);
 		m_lock = new();
-		m_connections = new();
-		m_tasks = new();
+		m_connections = [];
+		m_tasks = [];
 	}
 
 	public void Start()
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
index cb57cfb3d..2d1447779 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
@@ -33,7 +33,7 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 
 				if (m_server.SendIncompletePostHandshakeResponse)
 				{
-					await stream.WriteAsync(new byte[] { 1, 0, 0, 2 }, 0, 4);
+					await stream.WriteAsync([1, 0, 0, 2], 0, 4);
 					return;
 				}
 
@@ -108,18 +108,18 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 								var packets = new[]
 								{
 								new byte[] { 0xFF, 0x25, 0x05, 0x23, 0x37, 0x30, 0x31, 0x30, 0x30 }.Concat(Encoding.ASCII.GetBytes("Query execution was interrupted")).ToArray(), // error
-								new byte[] { 1 }, // one column
-								new byte[] { 3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0 }, // column definition
-								new byte[] { 0xFE, 0, 0, 2, 0 }, // EOF
+								[1], // one column
+								[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0], // column definition
+								[0xFE, 0, 0, 2, 0], // EOF
 								data,
 								negativeOne,
 								negativeOne,
-								new byte[] { 0xFE, 0, 0, 10, 0 }, // EOF, more results exist
-								new byte[] { 1 }, // one column
-								new byte[] { 3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0 }, // column definition
-								new byte[] { 0xFE, 0, 0, 2, 0 }, // EOF
+								[0xFE, 0, 0, 10, 0], // EOF, more results exist
+								[1], // one column
+								[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0], // column definition
+								[0xFE, 0, 0, 2, 0], // EOF
 								negativeOne,
-								new byte[] { 0xFE, 0, 0, 2, 0 }, // EOF
+								[0xFE, 0, 0, 2, 0], // EOF
 							};
 
 								if (bufferOutput)
@@ -168,13 +168,13 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 								var packets = new[]
 								{
 								new byte[] { 2 }, // two columns
-								new byte[] { 3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x46, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 4, 0x01, 0, 0x1F, 0, 0 }, // column definition (float)
-								new byte[] { 3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x44, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 5, 0x01, 0, 0x1F, 0, 0 }, // column definition (double)
-								new byte[] { 0xFE, 0, 0, 2, 0 }, // EOF
-								new byte[] { 3, 0x6e, 0x61, 0x6e, 3, 0x6e, 0x61, 0x6e }, // nan
-								new byte[] { 3, 0x69, 0x6e, 0x66, 3, 0x69, 0x6e, 0x66 }, // inf
-								new byte[] { 4, 0x2d, 0x69, 0x6e, 0x66, 4, 0x2d, 0x69, 0x6e, 0x66 }, // -inf
-								new byte[] { 0xFE, 0, 0, 2, 0 }, // EOF
+								[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x46, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 4, 0x01, 0, 0x1F, 0, 0], // column definition (float)
+								[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x44, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 5, 0x01, 0, 0x1F, 0, 0], // column definition (double)
+								[0xFE, 0, 0, 2, 0], // EOF
+								[3, 0x6e, 0x61, 0x6e, 3, 0x6e, 0x61, 0x6e], // nan
+								[3, 0x69, 0x6e, 0x66, 3, 0x69, 0x6e, 0x66], // inf
+								[4, 0x2d, 0x69, 0x6e, 0x66, 4, 0x2d, 0x69, 0x6e, 0x66], // -inf
+								[0xFE, 0, 0, 2, 0], // EOF
 							};
 								for (var packetIndex = 0; packetIndex < packets.Length; packetIndex++)
 									await SendAsync(stream, packetIndex + 1, x => x.Write(packets[packetIndex]));
diff --git a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
index a40c4d425..2a3f977f2 100644
--- a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
+++ b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
@@ -8,8 +8,8 @@ public abstract class MetricsTestsBase : IDisposable
 {
 	public MetricsTestsBase()
 	{
-		m_measurements = new();
-		m_timeMeasurements = new();
+		m_measurements = [];
+		m_timeMeasurements = [];
 
 		Server = new FakeMySqlServer();
 		Server.Start();
@@ -46,7 +46,7 @@ protected virtual void Dispose(bool disposing)
 	protected FakeMySqlServer Server { get; }
 
 	protected MySqlConnectionStringBuilder CreateConnectionStringBuilder() =>
-		new MySqlConnectionStringBuilder
+		new()
 		{
 			Server = "localhost",
 			Port = (uint) Server.Port,
@@ -71,8 +71,8 @@ protected void AssertMeasurement(string name, int expected)
 	protected List<double> GetAndClearMeasurements(string name)
 	{
 		if (!m_timeMeasurements.TryGetValue(name, out var list))
-			list = new();
-		m_timeMeasurements[name] = new List<double>();
+			list = [];
+		m_timeMeasurements[name] = [];
 		return list;
 	}
 
@@ -105,7 +105,7 @@ private void OnMeasurementRecorded(Instrument instrument, double measurement, Re
 		lock (m_timeMeasurements)
 		{
 			if (!m_timeMeasurements.TryGetValue(instrument.Name, out var list))
-				list = m_timeMeasurements[instrument.Name] = new List<double>();
+				list = m_timeMeasurements[instrument.Name] = [];
 			list.Add(measurement);
 		}
 	}
diff --git a/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs b/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs
index b245dcbf0..eae0484f1 100644
--- a/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlAttributeCollectionTests.cs
@@ -81,7 +81,7 @@ private MySqlAttribute AssertSingle()
 		Assert.Equal(1, m_collection.Count);
 		return m_collection[0];
 #else
-		return (MySqlAttribute) Assert.Single(m_collection);
+		return Assert.Single(m_collection);
 #endif
 	}
 
diff --git a/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs b/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
index 9fbeab2c8..b602b431f 100644
--- a/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
@@ -446,7 +446,7 @@ public void ParseTlsVersion(string input, string expected)
 	{
 		var csb = new MySqlConnectionStringBuilder { TlsVersion = input };
 #if !MYSQL_DATA
-		string[] normalizedVersions = new[] { "TLS 1.0", "TLS 1.1", "TLS 1.2", "TLS 1.3" };
+		string[] normalizedVersions = ["TLS 1.0", "TLS 1.1", "TLS 1.2", "TLS 1.3"];
 #else
 		string[] normalizedVersions = new[] { "Tls", "Tls11", "Tls12", "Tls13" };
 #endif
diff --git a/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs b/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs
index 4a1127f61..603cda983 100644
--- a/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs
@@ -4,12 +4,12 @@ public class MySqlParameterCollectionNameToIndexTests
 {
 	public MySqlParameterCollectionNameToIndexTests()
 	{
-		m_collection = new MySqlParameterCollection
-		{
+		m_collection =
+		[
 			new MySqlParameter { ParameterName = "A", Value = 1 },
 			new MySqlParameter { ParameterName = "B", Value = 2 },
 			new MySqlParameter { ParameterName = "C", Value = 3 },
-		};
+		];
 	}
 
 	[Fact]
@@ -124,5 +124,5 @@ public void ChangeParameter()
 		Assert.Equal(1, m_collection.NormalizedIndexOf("D"));
 	}
 
-	MySqlParameterCollection m_collection;
+	readonly MySqlParameterCollection m_collection;
 }
diff --git a/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs b/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs
index 1f1ccf0f6..708de4aa3 100644
--- a/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs
@@ -19,5 +19,5 @@ public MySqlParameterCollectionTests()
 	[Fact]
 	public void RemoveAtEnd() => Assert.Throws<ArgumentOutOfRangeException>(() => m_collection.RemoveAt(0));
 
-	MySqlParameterCollection m_collection;
+	readonly MySqlParameterCollection m_collection;
 }
diff --git a/tests/MySqlConnector.Tests/StatementPreparerTests.cs b/tests/MySqlConnector.Tests/StatementPreparerTests.cs
index 4d5565ac7..bac8351ca 100644
--- a/tests/MySqlConnector.Tests/StatementPreparerTests.cs
+++ b/tests/MySqlConnector.Tests/StatementPreparerTests.cs
@@ -146,44 +146,44 @@ public void FormatParameter(object parameterValue, string replacedValue, bool no
 		new[]
 		{
 			new object[] { (byte) 200, "200" },
-			new object[] { (sbyte) -100, "-100" },
-			new object[] { (short) -12345, "-12345" },
-			new object[] { (ushort) 45678, "45678" },
-			new object[] { -1_234_567_890, "-1234567890" },
-			new object[] { 3_456_789_012u, "3456789012" },
-			new object[] { -12_345_678_901L, "-12345678901" },
-			new object[] { 12_345_678_901UL, "12345678901" },
+			[(sbyte) -100, "-100"],
+			[(short) -12345, "-12345"],
+			[(ushort) 45678, "45678"],
+			[-1_234_567_890, "-1234567890"],
+			[3_456_789_012u, "3456789012"],
+			[-12_345_678_901L, "-12345678901"],
+			[12_345_678_901UL, "12345678901"],
 #if NET481
-			new object[] { 1.0123456f, "1.01234555" },
+			[1.0123456f, "1.01234555"],
 #else
-			new object[] { 1.0123456f, "1.0123456" },
+			[1.0123456f, "1.0123456"],
 #endif
-			new object[] { 1.0123456789012346, "1.0123456789012346" },
-			new object[] { 123456789.123456789m, "123456789.123456789" },
-			new object[] { "1234", "'1234'" },
-			new object[] { "it's", "'it''s'" },
-			new object[] { "it's", "'it''s'", true },
-			new object[] { 'a', "'a'" },
-			new object[] { '\'', "''''" },
-			new object[] { '\'', "''''", true },
-			new object[] { '\\', "'\\\\'" },
-			new object[] { '\\', "'\\'", true },
-			new object[] { "\\'", "'\\\\'''" },
-			new object[] { "\\'", "'\\'''", true },
-			new object[] { 'ﬃ', "'ﬃ'" },
-			new object[] { new DateTime(1234, 12, 23, 12, 34, 56, 789), "timestamp('1234-12-23 12:34:56.789000')" },
-			new object[] { new DateTimeOffset(1234, 12, 23, 12, 34, 56, 789, TimeSpan.FromHours(2)), "timestamp('1234-12-23 10:34:56.789000')" },
-			new object[] { new TimeSpan(2, 3, 4, 5, 6), "time '51:04:05.006000'" },
-			new object[] { new TimeSpan(-2, -3, -4, -5, -6), "time '-51:04:05.006000'" },
-			new object[] { new Guid("00112233-4455-6677-8899-AABBCCDDEEFF"), "'00112233-4455-6677-8899-aabbccddeeff'" },
-			new object[] { new byte[] { 0x41, 0x42, 0x27, 0x61, 0x5c, 0x62 }, @"_binary'AB''a\\b'" },
-			new object[] { new byte[] { 0x41, 0x42, 0x27, 0x61, 0x5c, 0x62 }, @"_binary'AB''a\b'", true },
-			new object[] { new MemoryStream(new byte[] { 0x41, 0x42, 0x27, 0x61, 0x5c, 0x62 }), @"_binary'AB''a\\b'" },
-			new object[] { new MemoryStream(new byte[] { 0x41, 0x42, 0x27, 0x61, 0x5c, 0x62 }), @"_binary'AB''a\b'", true },
-			new object[] { new MemoryStream(new byte[] { 0, 0x41, 0x42, 0x27, 0x61, 0x5c, 0x62, 0x63 }, 1, 6, false, true), @"_binary'AB''a\\b'" },
-			new object[] { new MemoryStream(new byte[] { 0, 0x41, 0x42, 0x27, 0x61, 0x5c, 0x62, 0x63 }, 1, 6, false, true), @"_binary'AB''a\b'", true },
-			new object[] { "\"AB\\ab'".AsMemory(), @"'""AB\\ab'''" },
-			new object[] { new StringBuilder("\"AB\\ab'"), @"'""AB\\ab'''" },
+			[1.0123456789012346, "1.0123456789012346"],
+			[123456789.123456789m, "123456789.123456789"],
+			["1234", "'1234'"],
+			["it's", "'it''s'"],
+			["it's", "'it''s'", true],
+			['a', "'a'"],
+			['\'', "''''"],
+			['\'', "''''", true],
+			['\\', "'\\\\'"],
+			['\\', "'\\'", true],
+			["\\'", "'\\\\'''"],
+			["\\'", "'\\'''", true],
+			['ﬃ', "'ﬃ'"],
+			[new DateTime(1234, 12, 23, 12, 34, 56, 789), "timestamp('1234-12-23 12:34:56.789000')"],
+			[new DateTimeOffset(1234, 12, 23, 12, 34, 56, 789, TimeSpan.FromHours(2)), "timestamp('1234-12-23 10:34:56.789000')"],
+			[new TimeSpan(2, 3, 4, 5, 6), "time '51:04:05.006000'"],
+			[new TimeSpan(-2, -3, -4, -5, -6), "time '-51:04:05.006000'"],
+			[new Guid("00112233-4455-6677-8899-AABBCCDDEEFF"), "'00112233-4455-6677-8899-aabbccddeeff'"],
+			[new byte[] { 0x41, 0x42, 0x27, 0x61, 0x5c, 0x62 }, @"_binary'AB''a\\b'"],
+			[new byte[] { 0x41, 0x42, 0x27, 0x61, 0x5c, 0x62 }, @"_binary'AB''a\b'", true],
+			[new MemoryStream([0x41, 0x42, 0x27, 0x61, 0x5c, 0x62]), @"_binary'AB''a\\b'"],
+			[new MemoryStream([0x41, 0x42, 0x27, 0x61, 0x5c, 0x62]), @"_binary'AB''a\b'", true],
+			[new MemoryStream([0, 0x41, 0x42, 0x27, 0x61, 0x5c, 0x62, 0x63], 1, 6, false, true), @"_binary'AB''a\\b'"],
+			[new MemoryStream([0, 0x41, 0x42, 0x27, 0x61, 0x5c, 0x62, 0x63], 1, 6, false, true), @"_binary'AB''a\b'", true],
+			["\"AB\\ab'".AsMemory(), @"'""AB\\ab'''"],
+			[new StringBuilder("\"AB\\ab'"), @"'""AB\\ab'''"],
 		};
 
 	[Theory]
@@ -296,7 +296,7 @@ public void SplitStatement(string sql, string[] expectedStatements, string expec
 
 	private static string GetParsedSql(string input, MySqlParameterCollection parameters = null, StatementPreparerOptions options = StatementPreparerOptions.None)
 	{
-		var preparer = new StatementPreparer(input, parameters ?? new MySqlParameterCollection(), options);
+		var preparer = new StatementPreparer(input, parameters ?? [], options);
 		var writer = new ByteBufferWriter();
 		preparer.ParseAndBindParameters(writer);
 		using var payload = writer.ToPayloadData();
diff --git a/tests/MySqlConnector.Tests/UtilityTests.cs b/tests/MySqlConnector.Tests/UtilityTests.cs
index 6df2da131..57cdcc1b1 100644
--- a/tests/MySqlConnector.Tests/UtilityTests.cs
+++ b/tests/MySqlConnector.Tests/UtilityTests.cs
@@ -181,12 +181,12 @@ public void DecodePrivateKey(string pre, string post)
 joMn7CFmwV9EYjPTxwkByKs5a8AbNYJDSfNCa3KoHTFjOvVMPec90Q==
 -----END RSA PRIVATE KEY-----";
 
-	static readonly byte[] s_modulus = new byte[] { 0xC2, 0x69, 0x43, 0x5F, 0xAD, 0xA1, 0x40, 0x04, 0x0D, 0xBD, 0x22, 0x59, 0xFC, 0x70, 0x0E, 0x52, 0x30, 0x9B, 0x02, 0x21, 0x0F, 0x42, 0xAB, 0x84, 0xC8, 0xFA, 0x71, 0x8E, 0xA0, 0xF1, 0xCB, 0x1D, 0x5F, 0x57, 0x12, 0xFD, 0xDD, 0x4D, 0x5A, 0xBB, 0x9D, 0x31, 0x1A, 0x4E, 0x2C, 0x69, 0xDE, 0x0E, 0x1F, 0xE1, 0x92, 0x64, 0x20, 0xA1, 0xAA, 0x3D, 0xE4, 0xBF, 0x02, 0x61, 0x5D, 0xCE, 0x76, 0x88, 0x77, 0x41, 0xCE, 0x34, 0x1B, 0xB9, 0xC1, 0x98, 0x94, 0x2D, 0x70, 0x2C, 0xD3, 0x1C, 0xD4, 0x5F, 0x87, 0xF3, 0xDB, 0xCC, 0x8C, 0x4A, 0xB5, 0x65, 0xEC, 0x6C, 0x44, 0x35, 0x90, 0x4A, 0xB8, 0x0B, 0xD4, 0x8E, 0xA2, 0xF4, 0xC9, 0xF9, 0x34, 0x58, 0x12, 0xE7, 0xAD, 0xF2, 0x3C, 0x1A, 0x4E, 0xC9, 0xF6, 0x32, 0x19, 0x60, 0xAB, 0x2F, 0x9C, 0x3B, 0xC8, 0x8F, 0xB0, 0x7B, 0x73, 0x02, 0xC3, 0x2D, 0xAA, 0xF0, 0x92, 0x0E, 0xC9, 0x97, 0xA0, 0xCE, 0x1E, 0xFA, 0x5B, 0xE7, 0xF7, 0x17, 0xDA, 0x45, 0x76, 0x77, 0xE3, 0x40, 0xEF, 0xAF, 0x8C, 0xFC, 0x8D, 0xBA, 0x99, 0xD3, 0x10, 0x3B, 0x58, 0x6C, 0x68, 0xC5, 0x61, 0xAA, 0x98, 0x21, 0x33, 0x67, 0x71, 0x9F, 0x1C, 0x2F, 0x52, 0xD8, 0x57, 0x0B, 0x94, 0x5D, 0x6D, 0x0F, 0x5B, 0xC6, 0xDF, 0x94, 0x47, 0x45, 0x77, 0xF0, 0x6B, 0xF1, 0xFC, 0xC2, 0x80, 0xAE, 0x08, 0x55, 0xD4, 0xA6, 0xE8, 0xE5, 0x4F, 0xCD, 0x26, 0x5A, 0xB5, 0xAC, 0x38, 0xDA, 0x37, 0x17, 0xDA, 0xD3, 0x53, 0xF1, 0xFD, 0xCF, 0xDC, 0x29, 0x40, 0xBC, 0xBD, 0x55, 0xDE, 0x3E, 0x04, 0x74, 0x21, 0xF2, 0x60, 0xDA, 0xCA, 0x26, 0xAC, 0x19, 0xEE, 0x74, 0xED, 0x8E, 0xA0, 0x41, 0x3E, 0xA3, 0x66, 0x36, 0x27, 0x11, 0xDD, 0xE3, 0x3C, 0xB2, 0xE1, 0xA3, 0x31, 0x6C, 0xA6, 0x2F };
-	static readonly byte[] s_exponent = new byte[] { 0x01, 0x00, 0x01 };
-	static readonly byte[] s_d = new byte[] { 0x11, 0xB3, 0x61, 0xD6, 0x01, 0x4A, 0x10, 0x39, 0x0E, 0x43, 0x2D, 0x30, 0x82, 0x42, 0x75, 0x9B, 0x58, 0x1F, 0x94, 0xE7, 0x0B, 0xAB, 0xA7, 0x50, 0x34, 0xB8, 0x50, 0xC4, 0x8A, 0xB4, 0xD9, 0x28, 0x78, 0x78, 0xC6, 0x1A, 0xE5, 0x1E, 0x58, 0xE7, 0x82, 0x1D, 0x69, 0x66, 0xBA, 0xB6, 0x7C, 0xE8, 0x4E, 0x50, 0xCC, 0x72, 0x5F, 0x62, 0x38, 0xCC, 0xDF, 0xD9, 0xE0, 0x4E, 0x9B, 0x2A, 0xE4, 0x31, 0xF2, 0xA1, 0xC4, 0x33, 0x8C, 0x5E, 0xB2, 0x58, 0xC1, 0x0D, 0x0E, 0x0C, 0x96, 0xC0, 0x26, 0x39, 0xF8, 0x77, 0xE4, 0x88, 0x50, 0x31, 0xB1, 0x28, 0x71, 0x89, 0x3E, 0x00, 0x9E, 0x7D, 0x9F, 0x1A, 0x3C, 0xA5, 0x2C, 0x03, 0x1B, 0xAC, 0x9B, 0xEE, 0x74, 0xF2, 0x31, 0x2D, 0x3F, 0xF9, 0xA9, 0x38, 0xCC, 0x38, 0xAD, 0x84, 0xD6, 0xB1, 0x44, 0x09, 0x51, 0x6E, 0x7B, 0xC4, 0x11, 0xA9, 0xAB, 0xB9, 0x80, 0xD4, 0xA8, 0x87, 0xEF, 0x2C, 0x76, 0x51, 0x64, 0x3E, 0xE5, 0x37, 0x7F, 0xF1, 0x26, 0xD7, 0xC0, 0x80, 0xEB, 0x96, 0xA9, 0x1A, 0x4D, 0xB8, 0xAF, 0xC9, 0xE5, 0xA2, 0x43, 0x7A, 0x6C, 0xEB, 0x9F, 0xF3, 0x8A, 0x72, 0xDD, 0xE7, 0xD2, 0xEB, 0x60, 0xD6, 0x01, 0xDD, 0x25, 0x40, 0x8B, 0x71, 0xBA, 0x0D, 0x49, 0x55, 0x3A, 0x1D, 0x9B, 0x60, 0x71, 0x2C, 0x37, 0xAF, 0xFB, 0xD9, 0x3C, 0xEF, 0x77, 0xF1, 0x43, 0xE9, 0x7F, 0xE9, 0x2C, 0xDC, 0x76, 0xDC, 0x34, 0x48, 0xFF, 0x4F, 0xB4, 0xBE, 0x93, 0x82, 0xC3, 0x14, 0x42, 0xC6, 0xE4, 0x01, 0xF5, 0xB0, 0xDE, 0xED, 0x64, 0x82, 0xB9, 0x1C, 0x17, 0x0A, 0x28, 0x89, 0x66, 0xC2, 0x45, 0xA3, 0x4E, 0xBC, 0x61, 0xB4, 0xD7, 0xCC, 0x47, 0xDC, 0x86, 0x8E, 0x1F, 0xEE, 0x8E, 0x0D, 0x0F, 0x51, 0xB1, 0xAC, 0x62, 0xEF, 0xDC, 0x6D, 0x90, 0x81 };
-	static readonly byte[] s_p = new byte[] { 0xFA, 0x63, 0x13, 0x19, 0xF3, 0x13, 0x7A, 0xC0, 0x70, 0x67, 0xAD, 0xE8, 0x05, 0x26, 0x70, 0x68, 0x9F, 0x30, 0xFB, 0x06, 0x9E, 0x45, 0xFF, 0x38, 0xF2, 0x56, 0xA3, 0x35, 0x4F, 0xAE, 0x21, 0x20, 0x94, 0x44, 0x80, 0x42, 0x61, 0x39, 0xC3, 0xEB, 0xA9, 0x8F, 0xD2, 0x61, 0x88, 0xE9, 0x6A, 0x62, 0x42, 0xF0, 0x18, 0x55, 0xDE, 0x50, 0x73, 0x1E, 0xFA, 0x70, 0xE5, 0x25, 0x73, 0x28, 0x6F, 0x29, 0x40, 0xE0, 0x09, 0x1A, 0xB6, 0x11, 0xF4, 0xB1, 0xEC, 0x89, 0x25, 0x66, 0x7D, 0x41, 0xC7, 0x53, 0xFA, 0x3A, 0x58, 0xF0, 0xB2, 0x27, 0x73, 0xE8, 0x63, 0x49, 0x06, 0x15, 0xF1, 0x4C, 0x35, 0x9D, 0x33, 0xE5, 0x28, 0x3E, 0xBF, 0x2F, 0x9F, 0xE4, 0x33, 0xE9, 0xCE, 0x6F, 0x91, 0x18, 0xAD, 0x95, 0xD4, 0xF5, 0x12, 0xD6, 0x92, 0xBD, 0xDA, 0xDF, 0x64, 0xA8, 0xB4, 0xEC, 0x7E, 0xD8, 0x1A, 0x41 };
-	static readonly byte[] s_q = new byte[] { 0xC6, 0xC4, 0xF4, 0x19, 0x1A, 0x74, 0xFB, 0xA1, 0x07, 0x3D, 0x8A, 0x80, 0x9A, 0x6F, 0xC5, 0x60, 0xCB, 0x68, 0x32, 0x14, 0xCA, 0x9D, 0x21, 0x59, 0x7C, 0xA5, 0xAC, 0x0B, 0x68, 0x0E, 0x8C, 0xE2, 0x82, 0x77, 0x6C, 0xBC, 0xC2, 0xA6, 0xB3, 0xF2, 0x8B, 0xE4, 0xC8, 0xDF, 0xF9, 0x7B, 0x92, 0x82, 0xE6, 0x20, 0x84, 0xCB, 0xE0, 0x46, 0x44, 0x0A, 0xB4, 0xD7, 0x48, 0x43, 0x29, 0x78, 0xE7, 0xCA, 0xE4, 0x8F, 0xEC, 0x52, 0x2A, 0x6F, 0xDA, 0x58, 0x8D, 0xAA, 0xEB, 0xC0, 0x5E, 0x4F, 0xC1, 0x70, 0x9A, 0x97, 0xCE, 0xE4, 0xFF, 0x70, 0xEC, 0xA1, 0x2A, 0xED, 0xD8, 0x7A, 0xA1, 0xA5, 0x1C, 0x79, 0x2A, 0x8E, 0xBC, 0x74, 0xA3, 0x45, 0x20, 0x27, 0x14, 0xE1, 0xB9, 0x16, 0x80, 0x26, 0x81, 0xC2, 0x17, 0x26, 0xBB, 0x83, 0x04, 0x88, 0xFB, 0x0B, 0xB2, 0x06, 0x49, 0x88, 0x9A, 0xC0, 0x44, 0x6F };
-	static readonly byte[] s_dp = new byte[] { 0xA9, 0x1C, 0xD1, 0x81, 0xED, 0x53, 0x72, 0xCD, 0x17, 0x1E, 0x6F, 0xAF, 0x0E, 0x0B, 0x70, 0x50, 0xB9, 0x74, 0x73, 0x97, 0x7C, 0xBE, 0xFA, 0x2A, 0x94, 0x43, 0x3F, 0xE8, 0x79, 0xF0, 0xBA, 0x1F, 0x2F, 0x05, 0x35, 0x3B, 0xA0, 0x3F, 0x4B, 0xBC, 0x97, 0xC1, 0xB3, 0xEE, 0x7C, 0x72, 0x6A, 0x90, 0x03, 0x75, 0xF6, 0x79, 0xC8, 0xB5, 0xBD, 0x8B, 0x66, 0xCB, 0x69, 0x79, 0x69, 0xD6, 0x1D, 0x7E, 0x57, 0x7A, 0xF9, 0xAB, 0x38, 0xDD, 0xD7, 0xCF, 0x74, 0x5B, 0x0D, 0x0F, 0xB3, 0xFA, 0x01, 0x8F, 0x0E, 0xAA, 0xE9, 0xF7, 0x67, 0xDC, 0x8D, 0xC4, 0x9F, 0x8A, 0x6A, 0xB9, 0xD7, 0x48, 0x09, 0xCE, 0x2B, 0x86, 0xD3, 0x2F, 0xCE, 0x21, 0x74, 0x74, 0xC5, 0x3B, 0x5D, 0x5A, 0xAD, 0x3D, 0x65, 0xC0, 0x95, 0x1D, 0x11, 0xA4, 0x2B, 0x68, 0x5F, 0xB4, 0x12, 0x60, 0x80, 0x12, 0x5B, 0x79, 0xCA, 0x01 };
-	static readonly byte[] s_dq = new byte[] { 0x99, 0x7F, 0xF2, 0x0E, 0x0C, 0xDB, 0x78, 0x93, 0x63, 0x97, 0x08, 0x05, 0xC6, 0xBE, 0x38, 0x4C, 0x46, 0xE9, 0x21, 0x7B, 0xE2, 0xF8, 0xB3, 0x8F, 0x7A, 0xEE, 0x2A, 0x4D, 0xE8, 0xBF, 0x0B, 0xD7, 0xC4, 0xEF, 0x5B, 0x3E, 0xEE, 0x87, 0x74, 0x82, 0x03, 0xBC, 0xDB, 0xCF, 0xF3, 0xC7, 0x95, 0x5E, 0x97, 0x3F, 0x57, 0xAE, 0x66, 0x75, 0x7D, 0x08, 0x53, 0x9D, 0xC9, 0x85, 0x6C, 0x5A, 0x9D, 0x8E, 0x97, 0x31, 0xFA, 0x4B, 0x7D, 0xD0, 0x41, 0x6B, 0x8F, 0x84, 0x06, 0x69, 0xD9, 0xAB, 0x77, 0xF4, 0x70, 0xBE, 0x79, 0x9D, 0x69, 0x13, 0x18, 0xFA, 0x61, 0xBF, 0xAA, 0x25, 0x11, 0xFE, 0x03, 0x5E, 0x23, 0x1A, 0x75, 0xB9, 0x11, 0x79, 0x49, 0x9A, 0x44, 0xCB, 0x08, 0x77, 0xB2, 0xE1, 0x89, 0xE5, 0x8C, 0xA1, 0x71, 0xBF, 0x7D, 0x5A, 0x17, 0x9D, 0x71, 0x8E, 0xDF, 0x6C, 0xAA, 0x2D, 0x7E, 0xEB };
-	static readonly byte[] s_iq = new byte[] { 0x2F, 0x91, 0x84, 0x58, 0xAB, 0x47, 0xAE, 0xE6, 0x92, 0xBC, 0xE4, 0x13, 0xBA, 0x9C, 0xDD, 0x66, 0xFF, 0x91, 0xA8, 0x94, 0x41, 0xBF, 0xD6, 0x54, 0x1B, 0x6D, 0x92, 0xC0, 0x67, 0xC6, 0x08, 0x23, 0xA4, 0x6D, 0x24, 0xA1, 0xC1, 0xBD, 0xCB, 0xC5, 0xAF, 0x4E, 0x96, 0x39, 0x46, 0x4A, 0x44, 0xD7, 0x10, 0x8F, 0x35, 0x76, 0xF5, 0xCB, 0x86, 0xE2, 0x9A, 0xAD, 0x32, 0xCD, 0x2C, 0xC0, 0xB8, 0x4A, 0x31, 0xB0, 0xAC, 0xF8, 0xFA, 0xAB, 0xEB, 0x0E, 0x8A, 0x7D, 0x92, 0x0D, 0xB5, 0x5C, 0x8B, 0x36, 0xD6, 0xC3, 0x56, 0x8F, 0x6B, 0xC0, 0x54, 0x5C, 0x8E, 0x83, 0x27, 0xEC, 0x21, 0x66, 0xC1, 0x5F, 0x44, 0x62, 0x33, 0xD3, 0xC7, 0x09, 0x01, 0xC8, 0xAB, 0x39, 0x6B, 0xC0, 0x1B, 0x35, 0x82, 0x43, 0x49, 0xF3, 0x42, 0x6B, 0x72, 0xA8, 0x1D, 0x31, 0x63, 0x3A, 0xF5, 0x4C, 0x3D, 0xE7, 0x3D, 0xD1 };
+	static readonly byte[] s_modulus = [0xC2, 0x69, 0x43, 0x5F, 0xAD, 0xA1, 0x40, 0x04, 0x0D, 0xBD, 0x22, 0x59, 0xFC, 0x70, 0x0E, 0x52, 0x30, 0x9B, 0x02, 0x21, 0x0F, 0x42, 0xAB, 0x84, 0xC8, 0xFA, 0x71, 0x8E, 0xA0, 0xF1, 0xCB, 0x1D, 0x5F, 0x57, 0x12, 0xFD, 0xDD, 0x4D, 0x5A, 0xBB, 0x9D, 0x31, 0x1A, 0x4E, 0x2C, 0x69, 0xDE, 0x0E, 0x1F, 0xE1, 0x92, 0x64, 0x20, 0xA1, 0xAA, 0x3D, 0xE4, 0xBF, 0x02, 0x61, 0x5D, 0xCE, 0x76, 0x88, 0x77, 0x41, 0xCE, 0x34, 0x1B, 0xB9, 0xC1, 0x98, 0x94, 0x2D, 0x70, 0x2C, 0xD3, 0x1C, 0xD4, 0x5F, 0x87, 0xF3, 0xDB, 0xCC, 0x8C, 0x4A, 0xB5, 0x65, 0xEC, 0x6C, 0x44, 0x35, 0x90, 0x4A, 0xB8, 0x0B, 0xD4, 0x8E, 0xA2, 0xF4, 0xC9, 0xF9, 0x34, 0x58, 0x12, 0xE7, 0xAD, 0xF2, 0x3C, 0x1A, 0x4E, 0xC9, 0xF6, 0x32, 0x19, 0x60, 0xAB, 0x2F, 0x9C, 0x3B, 0xC8, 0x8F, 0xB0, 0x7B, 0x73, 0x02, 0xC3, 0x2D, 0xAA, 0xF0, 0x92, 0x0E, 0xC9, 0x97, 0xA0, 0xCE, 0x1E, 0xFA, 0x5B, 0xE7, 0xF7, 0x17, 0xDA, 0x45, 0x76, 0x77, 0xE3, 0x40, 0xEF, 0xAF, 0x8C, 0xFC, 0x8D, 0xBA, 0x99, 0xD3, 0x10, 0x3B, 0x58, 0x6C, 0x68, 0xC5, 0x61, 0xAA, 0x98, 0x21, 0x33, 0x67, 0x71, 0x9F, 0x1C, 0x2F, 0x52, 0xD8, 0x57, 0x0B, 0x94, 0x5D, 0x6D, 0x0F, 0x5B, 0xC6, 0xDF, 0x94, 0x47, 0x45, 0x77, 0xF0, 0x6B, 0xF1, 0xFC, 0xC2, 0x80, 0xAE, 0x08, 0x55, 0xD4, 0xA6, 0xE8, 0xE5, 0x4F, 0xCD, 0x26, 0x5A, 0xB5, 0xAC, 0x38, 0xDA, 0x37, 0x17, 0xDA, 0xD3, 0x53, 0xF1, 0xFD, 0xCF, 0xDC, 0x29, 0x40, 0xBC, 0xBD, 0x55, 0xDE, 0x3E, 0x04, 0x74, 0x21, 0xF2, 0x60, 0xDA, 0xCA, 0x26, 0xAC, 0x19, 0xEE, 0x74, 0xED, 0x8E, 0xA0, 0x41, 0x3E, 0xA3, 0x66, 0x36, 0x27, 0x11, 0xDD, 0xE3, 0x3C, 0xB2, 0xE1, 0xA3, 0x31, 0x6C, 0xA6, 0x2F];
+	static readonly byte[] s_exponent = [0x01, 0x00, 0x01];
+	static readonly byte[] s_d = [0x11, 0xB3, 0x61, 0xD6, 0x01, 0x4A, 0x10, 0x39, 0x0E, 0x43, 0x2D, 0x30, 0x82, 0x42, 0x75, 0x9B, 0x58, 0x1F, 0x94, 0xE7, 0x0B, 0xAB, 0xA7, 0x50, 0x34, 0xB8, 0x50, 0xC4, 0x8A, 0xB4, 0xD9, 0x28, 0x78, 0x78, 0xC6, 0x1A, 0xE5, 0x1E, 0x58, 0xE7, 0x82, 0x1D, 0x69, 0x66, 0xBA, 0xB6, 0x7C, 0xE8, 0x4E, 0x50, 0xCC, 0x72, 0x5F, 0x62, 0x38, 0xCC, 0xDF, 0xD9, 0xE0, 0x4E, 0x9B, 0x2A, 0xE4, 0x31, 0xF2, 0xA1, 0xC4, 0x33, 0x8C, 0x5E, 0xB2, 0x58, 0xC1, 0x0D, 0x0E, 0x0C, 0x96, 0xC0, 0x26, 0x39, 0xF8, 0x77, 0xE4, 0x88, 0x50, 0x31, 0xB1, 0x28, 0x71, 0x89, 0x3E, 0x00, 0x9E, 0x7D, 0x9F, 0x1A, 0x3C, 0xA5, 0x2C, 0x03, 0x1B, 0xAC, 0x9B, 0xEE, 0x74, 0xF2, 0x31, 0x2D, 0x3F, 0xF9, 0xA9, 0x38, 0xCC, 0x38, 0xAD, 0x84, 0xD6, 0xB1, 0x44, 0x09, 0x51, 0x6E, 0x7B, 0xC4, 0x11, 0xA9, 0xAB, 0xB9, 0x80, 0xD4, 0xA8, 0x87, 0xEF, 0x2C, 0x76, 0x51, 0x64, 0x3E, 0xE5, 0x37, 0x7F, 0xF1, 0x26, 0xD7, 0xC0, 0x80, 0xEB, 0x96, 0xA9, 0x1A, 0x4D, 0xB8, 0xAF, 0xC9, 0xE5, 0xA2, 0x43, 0x7A, 0x6C, 0xEB, 0x9F, 0xF3, 0x8A, 0x72, 0xDD, 0xE7, 0xD2, 0xEB, 0x60, 0xD6, 0x01, 0xDD, 0x25, 0x40, 0x8B, 0x71, 0xBA, 0x0D, 0x49, 0x55, 0x3A, 0x1D, 0x9B, 0x60, 0x71, 0x2C, 0x37, 0xAF, 0xFB, 0xD9, 0x3C, 0xEF, 0x77, 0xF1, 0x43, 0xE9, 0x7F, 0xE9, 0x2C, 0xDC, 0x76, 0xDC, 0x34, 0x48, 0xFF, 0x4F, 0xB4, 0xBE, 0x93, 0x82, 0xC3, 0x14, 0x42, 0xC6, 0xE4, 0x01, 0xF5, 0xB0, 0xDE, 0xED, 0x64, 0x82, 0xB9, 0x1C, 0x17, 0x0A, 0x28, 0x89, 0x66, 0xC2, 0x45, 0xA3, 0x4E, 0xBC, 0x61, 0xB4, 0xD7, 0xCC, 0x47, 0xDC, 0x86, 0x8E, 0x1F, 0xEE, 0x8E, 0x0D, 0x0F, 0x51, 0xB1, 0xAC, 0x62, 0xEF, 0xDC, 0x6D, 0x90, 0x81];
+	static readonly byte[] s_p = [0xFA, 0x63, 0x13, 0x19, 0xF3, 0x13, 0x7A, 0xC0, 0x70, 0x67, 0xAD, 0xE8, 0x05, 0x26, 0x70, 0x68, 0x9F, 0x30, 0xFB, 0x06, 0x9E, 0x45, 0xFF, 0x38, 0xF2, 0x56, 0xA3, 0x35, 0x4F, 0xAE, 0x21, 0x20, 0x94, 0x44, 0x80, 0x42, 0x61, 0x39, 0xC3, 0xEB, 0xA9, 0x8F, 0xD2, 0x61, 0x88, 0xE9, 0x6A, 0x62, 0x42, 0xF0, 0x18, 0x55, 0xDE, 0x50, 0x73, 0x1E, 0xFA, 0x70, 0xE5, 0x25, 0x73, 0x28, 0x6F, 0x29, 0x40, 0xE0, 0x09, 0x1A, 0xB6, 0x11, 0xF4, 0xB1, 0xEC, 0x89, 0x25, 0x66, 0x7D, 0x41, 0xC7, 0x53, 0xFA, 0x3A, 0x58, 0xF0, 0xB2, 0x27, 0x73, 0xE8, 0x63, 0x49, 0x06, 0x15, 0xF1, 0x4C, 0x35, 0x9D, 0x33, 0xE5, 0x28, 0x3E, 0xBF, 0x2F, 0x9F, 0xE4, 0x33, 0xE9, 0xCE, 0x6F, 0x91, 0x18, 0xAD, 0x95, 0xD4, 0xF5, 0x12, 0xD6, 0x92, 0xBD, 0xDA, 0xDF, 0x64, 0xA8, 0xB4, 0xEC, 0x7E, 0xD8, 0x1A, 0x41];
+	static readonly byte[] s_q = [0xC6, 0xC4, 0xF4, 0x19, 0x1A, 0x74, 0xFB, 0xA1, 0x07, 0x3D, 0x8A, 0x80, 0x9A, 0x6F, 0xC5, 0x60, 0xCB, 0x68, 0x32, 0x14, 0xCA, 0x9D, 0x21, 0x59, 0x7C, 0xA5, 0xAC, 0x0B, 0x68, 0x0E, 0x8C, 0xE2, 0x82, 0x77, 0x6C, 0xBC, 0xC2, 0xA6, 0xB3, 0xF2, 0x8B, 0xE4, 0xC8, 0xDF, 0xF9, 0x7B, 0x92, 0x82, 0xE6, 0x20, 0x84, 0xCB, 0xE0, 0x46, 0x44, 0x0A, 0xB4, 0xD7, 0x48, 0x43, 0x29, 0x78, 0xE7, 0xCA, 0xE4, 0x8F, 0xEC, 0x52, 0x2A, 0x6F, 0xDA, 0x58, 0x8D, 0xAA, 0xEB, 0xC0, 0x5E, 0x4F, 0xC1, 0x70, 0x9A, 0x97, 0xCE, 0xE4, 0xFF, 0x70, 0xEC, 0xA1, 0x2A, 0xED, 0xD8, 0x7A, 0xA1, 0xA5, 0x1C, 0x79, 0x2A, 0x8E, 0xBC, 0x74, 0xA3, 0x45, 0x20, 0x27, 0x14, 0xE1, 0xB9, 0x16, 0x80, 0x26, 0x81, 0xC2, 0x17, 0x26, 0xBB, 0x83, 0x04, 0x88, 0xFB, 0x0B, 0xB2, 0x06, 0x49, 0x88, 0x9A, 0xC0, 0x44, 0x6F];
+	static readonly byte[] s_dp = [0xA9, 0x1C, 0xD1, 0x81, 0xED, 0x53, 0x72, 0xCD, 0x17, 0x1E, 0x6F, 0xAF, 0x0E, 0x0B, 0x70, 0x50, 0xB9, 0x74, 0x73, 0x97, 0x7C, 0xBE, 0xFA, 0x2A, 0x94, 0x43, 0x3F, 0xE8, 0x79, 0xF0, 0xBA, 0x1F, 0x2F, 0x05, 0x35, 0x3B, 0xA0, 0x3F, 0x4B, 0xBC, 0x97, 0xC1, 0xB3, 0xEE, 0x7C, 0x72, 0x6A, 0x90, 0x03, 0x75, 0xF6, 0x79, 0xC8, 0xB5, 0xBD, 0x8B, 0x66, 0xCB, 0x69, 0x79, 0x69, 0xD6, 0x1D, 0x7E, 0x57, 0x7A, 0xF9, 0xAB, 0x38, 0xDD, 0xD7, 0xCF, 0x74, 0x5B, 0x0D, 0x0F, 0xB3, 0xFA, 0x01, 0x8F, 0x0E, 0xAA, 0xE9, 0xF7, 0x67, 0xDC, 0x8D, 0xC4, 0x9F, 0x8A, 0x6A, 0xB9, 0xD7, 0x48, 0x09, 0xCE, 0x2B, 0x86, 0xD3, 0x2F, 0xCE, 0x21, 0x74, 0x74, 0xC5, 0x3B, 0x5D, 0x5A, 0xAD, 0x3D, 0x65, 0xC0, 0x95, 0x1D, 0x11, 0xA4, 0x2B, 0x68, 0x5F, 0xB4, 0x12, 0x60, 0x80, 0x12, 0x5B, 0x79, 0xCA, 0x01];
+	static readonly byte[] s_dq = [0x99, 0x7F, 0xF2, 0x0E, 0x0C, 0xDB, 0x78, 0x93, 0x63, 0x97, 0x08, 0x05, 0xC6, 0xBE, 0x38, 0x4C, 0x46, 0xE9, 0x21, 0x7B, 0xE2, 0xF8, 0xB3, 0x8F, 0x7A, 0xEE, 0x2A, 0x4D, 0xE8, 0xBF, 0x0B, 0xD7, 0xC4, 0xEF, 0x5B, 0x3E, 0xEE, 0x87, 0x74, 0x82, 0x03, 0xBC, 0xDB, 0xCF, 0xF3, 0xC7, 0x95, 0x5E, 0x97, 0x3F, 0x57, 0xAE, 0x66, 0x75, 0x7D, 0x08, 0x53, 0x9D, 0xC9, 0x85, 0x6C, 0x5A, 0x9D, 0x8E, 0x97, 0x31, 0xFA, 0x4B, 0x7D, 0xD0, 0x41, 0x6B, 0x8F, 0x84, 0x06, 0x69, 0xD9, 0xAB, 0x77, 0xF4, 0x70, 0xBE, 0x79, 0x9D, 0x69, 0x13, 0x18, 0xFA, 0x61, 0xBF, 0xAA, 0x25, 0x11, 0xFE, 0x03, 0x5E, 0x23, 0x1A, 0x75, 0xB9, 0x11, 0x79, 0x49, 0x9A, 0x44, 0xCB, 0x08, 0x77, 0xB2, 0xE1, 0x89, 0xE5, 0x8C, 0xA1, 0x71, 0xBF, 0x7D, 0x5A, 0x17, 0x9D, 0x71, 0x8E, 0xDF, 0x6C, 0xAA, 0x2D, 0x7E, 0xEB];
+	static readonly byte[] s_iq = [0x2F, 0x91, 0x84, 0x58, 0xAB, 0x47, 0xAE, 0xE6, 0x92, 0xBC, 0xE4, 0x13, 0xBA, 0x9C, 0xDD, 0x66, 0xFF, 0x91, 0xA8, 0x94, 0x41, 0xBF, 0xD6, 0x54, 0x1B, 0x6D, 0x92, 0xC0, 0x67, 0xC6, 0x08, 0x23, 0xA4, 0x6D, 0x24, 0xA1, 0xC1, 0xBD, 0xCB, 0xC5, 0xAF, 0x4E, 0x96, 0x39, 0x46, 0x4A, 0x44, 0xD7, 0x10, 0x8F, 0x35, 0x76, 0xF5, 0xCB, 0x86, 0xE2, 0x9A, 0xAD, 0x32, 0xCD, 0x2C, 0xC0, 0xB8, 0x4A, 0x31, 0xB0, 0xAC, 0xF8, 0xFA, 0xAB, 0xEB, 0x0E, 0x8A, 0x7D, 0x92, 0x0D, 0xB5, 0x5C, 0x8B, 0x36, 0xD6, 0xC3, 0x56, 0x8F, 0x6B, 0xC0, 0x54, 0x5C, 0x8E, 0x83, 0x27, 0xEC, 0x21, 0x66, 0xC1, 0x5F, 0x44, 0x62, 0x33, 0xD3, 0xC7, 0x09, 0x01, 0xC8, 0xAB, 0x39, 0x6B, 0xC0, 0x1B, 0x35, 0x82, 0x43, 0x49, 0xF3, 0x42, 0x6B, 0x72, 0xA8, 0x1D, 0x31, 0x63, 0x3A, 0xF5, 0x4C, 0x3D, 0xE7, 0x3D, 0xD1];
 }

From a7987abb4af4df4b58465aa8fafb293e89553a88 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 18 Apr 2024 20:07:13 -0700
Subject: [PATCH 286/393] Fix bug preventing asynchronous creation of "Foreign
 Keys" schema.

This fixes an error introduced in https://github.com/mysql-net/MySqlConnector/pull/1419.
---
 src/MySqlConnector/Core/SchemaProvider.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/SchemaProvider.cs b/src/MySqlConnector/Core/SchemaProvider.cs
index b91e308d8..a5a8b74be 100644
--- a/src/MySqlConnector/Core/SchemaProvider.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.cs
@@ -441,7 +441,7 @@ private async Task FillDataTableAsync(IOBehavior ioBehavior, DataTable dataTable
 	}
 
 	private Task DoFillForeignKeysAsync(IOBehavior ioBehavior, DataTable dataTable, string?[]? restrictionValues, CancellationToken cancellationToken) =>
-		FillDataTableAsync(IOBehavior.Synchronous, dataTable, command =>
+		FillDataTableAsync(ioBehavior, dataTable, command =>
 		{
 			command.CommandText = """
 				SELECT rc.constraint_catalog, rc.constraint_schema, rc.constraint_name,

From 320a007082456d72c67b627b77976c77331c8b3c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 21 Apr 2024 15:59:22 -0700
Subject: [PATCH 287/393] Stop recycling MySqlDataReader instances. Fixes #1459

---
 src/MySqlConnector/Core/ConnectionPool.cs | 1 +
 src/MySqlConnector/Core/ServerSession.cs  | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 758fffd36..17227b631 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -173,6 +173,7 @@ public async ValueTask ReturnAsync(IOBehavior ioBehavior, ServerSession session)
 				_ = m_leasedSessions.Remove(session.Id);
 			MetricsReporter.RemoveUsed(this);
 			session.OwningConnection = null;
+			session.DataReader = new();
 			var sessionHealth = GetSessionHealth(session);
 			if (sessionHealth == 0)
 			{
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 58b088d56..db1505ad5 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -69,7 +69,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public bool SupportsSessionTrack { get; private set; }
 	public bool ProcAccessDenied { get; set; }
 	public ICollection<KeyValuePair<string, object?>> ActivityTags => m_activityTags;
-	public MySqlDataReader DataReader { get; }
+	public MySqlDataReader DataReader { get; set; }
 
 	public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? owningConnection)
 	{

From 0a8feb16190a70e30e874cf96a7eeec9bd358d70 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 21 Apr 2024 18:46:47 -0700
Subject: [PATCH 288/393] Fix potential NullReferenceException during
 cancellation. Fixes #1472

---
 src/MySqlConnector/Core/ServerSession.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index db1505ad5..5c173aa05 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1025,7 +1025,7 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 				{
 					tcpClient = new(ipAddress.AddressFamily);
 
-					using (cancellationToken.Register(() => tcpClient.Client?.Dispose()))
+					using (cancellationToken.Register(() => tcpClient?.Client?.Dispose()))
 					{
 						try
 						{

From 72ee684ed0e04e5f39d6e0757a1963e4aa36807f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 22 Apr 2024 07:02:14 -0700
Subject: [PATCH 289/393] Release 2.3.7.

---
 docs/content/overview/version-history.md | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 116960940..b6fb6fc6c 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -1,5 +1,5 @@
 ---
-lastmod: 2024-03-20
+lastmod: 2024-04-22
 date: 2017-03-27
 menu:
   main:
@@ -11,6 +11,14 @@ weight: 30
 
 # Version History
 
+### 2.3.7
+
+* Fix `NullReferenceException` in `MySqlDataReader.ActivateResultSet` and `MySqlDataReader.DisposeAsync`: [#1459](https://github.com/mysql-net/MySqlConnector/issues/1459).
+  * This bug likely also caused "Packet received out-of-order" errors.
+  * This was introduced in 2.3.0.
+* Fix rare `NullReferenceException` in `ServerSession` during cancellation: [#1472](https://github.com/mysql-net/MySqlConnector/issues/1472).
+* Fix `Foreign Keys` schema not being generated asynchronously.
+
 ### 2.3.6
 
 * Fix `VerifyCA` incompatibility with AWS Aurora: [#1462](https://github.com/mysql-net/MySqlConnector/issues/1462).

From bc6785e88874627b04231799fdf12c1fc40169f5 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 24 Apr 2024 14:19:21 -0700
Subject: [PATCH 290/393] Use troubleshooting URLs at the mysqlconnector.net
 domain.

---
 docs/content/overview/version-history.md      |  2 +-
 .../troubleshooting/transaction-usage.md      |  4 +--
 netlify.toml                                  | 31 +++++++++++++++++++
 src/MySqlConnector/Core/ResultSet.cs          |  4 +--
 src/MySqlConnector/Core/ServerSession.cs      |  4 +--
 src/MySqlConnector/MySqlBatch.cs              |  2 +-
 src/MySqlConnector/MySqlBulkLoader.cs         |  2 +-
 src/MySqlConnector/MySqlCommand.cs            |  2 +-
 src/MySqlConnector/MySqlParameter.cs          |  4 +--
 9 files changed, 43 insertions(+), 12 deletions(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index b6fb6fc6c..de0e514dd 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -723,7 +723,7 @@ weight: 30
 
 * **Breaking** `MySqlBulkLoader` (for local files) and `LOAD DATA LOCAL INFILE` are disabled by default.
   * Set `AllowLoadLocalInfile=true` in the connection string to enable loading local data.
-  * This is a security measure; see https://fl.vu/mysql-load-data for details.
+  * This is a security measure; see https://mysqlconnector.net/load-data for details.
 * Add `AllowLoadLocalInfile` connection string option: [#643](https://github.com/mysql-net/MySqlConnector/issues/643).
 * Add `SslCert` and `SslKey` connection string options to specify a client certificate using PEM files: [#641](https://github.com/mysql-net/MySqlConnector/issues/641).
 * Add `SslCa` alias for the `CACertificateFile` connection string option: [#640](https://github.com/mysql-net/MySqlConnector/issues/640).
diff --git a/docs/content/troubleshooting/transaction-usage.md b/docs/content/troubleshooting/transaction-usage.md
index 4ed620d32..7d6c803bc 100644
--- a/docs/content/troubleshooting/transaction-usage.md
+++ b/docs/content/troubleshooting/transaction-usage.md
@@ -16,7 +16,7 @@ menu:
 
 When using `MySqlTransaction` from a C# program, you may receive the following error:
 
-* **System.InvalidOperationException: The transaction associated with this command is not the connection's active transaction; see https://fl.vu/mysql-trans**
+* **System.InvalidOperationException: The transaction associated with this command is not the connection's active transaction**
 
 By default, MySqlConnector requires `MySqlCommand.Transaction` to be set to the connection's active transaction in order for the command to be executed successfully. This strictness is intended to catch programming bugs related to using the wrong transaction, a disposed transaction, or forgetting to set the transaction (and using the default value `null`).
 
@@ -24,7 +24,7 @@ However, this strictness can make migrating from Connector/NET more difficult, a
 
 ## Workaround: Use IgnoreCommandTransaction=true
 
-To easily migrate code from Connector/NET, use the `IgnoreCommandTransaction=true` connection string setting to emulate Connector/NET's behaviour and not validate the value of `MySqlCommand.Transaction`. By doing this, you will not need the code fixes prescribed below.
+To easily migrate code from Connector/NET, use the `IgnoreCommandTransaction=true` connection string setting to emulate Connector/NET's behavior and not validate the value of `MySqlCommand.Transaction`. By doing this, you will not need the code fixes prescribed below.
 
 ## Code Fix: Set MySqlCommand.Transaction
 
diff --git a/netlify.toml b/netlify.toml
index e6a1ba6c1..835e01d1d 100644
--- a/netlify.toml
+++ b/netlify.toml
@@ -124,3 +124,34 @@ from = "/connection%E2%80%91options/"
 to = "/connection-options/"
 status = 301
 force = true
+
+[[redirects]]
+from = "/conn-reuse"
+to = "/troubleshooting/connection-reuse/"
+status = 301
+force = true
+
+[[redirects]]
+from = "/delimiter"
+to = "/troubleshooting/delimiter/"
+status = 301
+force = true
+
+[[redirects]]
+from = "/load-data"
+to = "/troubleshooting/load-data-local-infile/"
+status = 301
+force = true
+
+[[redirects]]
+from = "/param-type"
+to = "/troubleshooting/parameter-types/"
+status = 301
+force = true
+
+[[redirects]]
+from = "/trans"
+to = "/troubleshooting/transaction-usage/"
+status = 301
+force = true
+
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 184bf071d..a0887aa2c 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -62,11 +62,11 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 					try
 					{
 						if (!Connection.AllowLoadLocalInfile)
-							throw new NotSupportedException("To use LOAD DATA LOCAL INFILE, set AllowLoadLocalInfile=true in the connection string. See https://fl.vu/mysql-load-data");
+							throw new NotSupportedException("To use LOAD DATA LOCAL INFILE, set AllowLoadLocalInfile=true in the connection string. See https://mysqlconnector.net/load-data");
 						var localInfile = LocalInfilePayload.Create(payload.Span);
 						var hasSourcePrefix = localInfile.FileName.StartsWith(MySqlBulkLoader.SourcePrefix, StringComparison.Ordinal);
 						if (!IsHostVerified(Connection) && !hasSourcePrefix)
-							throw new NotSupportedException("Use SourceStream or SslMode >= VerifyCA for LOAD DATA LOCAL INFILE. See https://fl.vu/mysql-load-data");
+							throw new NotSupportedException("Use SourceStream or SslMode >= VerifyCA for LOAD DATA LOCAL INFILE. See https://mysqlconnector.net/load-data");
 
 						var source = hasSourcePrefix ?
 							MySqlBulkLoader.GetAndRemoveSource(localInfile.FileName) :
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 5c173aa05..dec7faea3 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -285,7 +285,7 @@ public void StartQuerying(ICancellableCommand command)
 			if (m_state is State.Querying or State.CancelingQuery)
 			{
 				CannotExecuteNewCommandInState(m_logger, Id, m_state);
-				throw new InvalidOperationException("This MySqlConnection is already in use. See https://fl.vu/mysql-conn-reuse");
+				throw new InvalidOperationException("This MySqlConnection is already in use. See https://mysqlconnector.net/conn-reuse");
 			}
 
 			VerifyState(State.Connected);
@@ -940,7 +940,7 @@ public static void ThrowIfStatementContainsDelimiter(MySqlException exception, I
 			var parser = new DelimiterSqlParser(command);
 			parser.Parse(command.CommandText);
 			if (parser.HasDelimiter)
-				throw new MySqlException(MySqlErrorCode.DelimiterNotSupported, "'DELIMITER' should not be used with MySqlConnector. See https://fl.vu/mysql-delimiter", exception);
+				throw new MySqlException(MySqlErrorCode.DelimiterNotSupported, "'DELIMITER' should not be used with MySqlConnector. See https://mysqlconnector.net/delimiter", exception);
 		}
 	}
 
diff --git a/src/MySqlConnector/MySqlBatch.cs b/src/MySqlConnector/MySqlBatch.cs
index df3f881e0..27145cd03 100644
--- a/src/MySqlConnector/MySqlBatch.cs
+++ b/src/MySqlConnector/MySqlBatch.cs
@@ -334,7 +334,7 @@ private bool IsValid([NotNullWhen(false)] out Exception? exception)
 		else if (Connection.State is not ConnectionState.Open and not ConnectionState.Connecting)
 			exception = new InvalidOperationException($"Connection must be Open; current state is {Connection.State}");
 		else if (!Connection.IgnoreCommandTransaction && Transaction != Connection.CurrentTransaction)
-			exception = new InvalidOperationException("The transaction associated with this batch is not the connection's active transaction; see https://fl.vu/mysql-trans");
+			exception = new InvalidOperationException("The transaction associated with this batch is not the connection's active transaction; see https://mysqlconnector.net/trans");
 		else if (BatchCommands.Count == 0)
 			exception = new InvalidOperationException("BatchCommands must contain a command");
 		else
diff --git a/src/MySqlConnector/MySqlBulkLoader.cs b/src/MySqlConnector/MySqlBulkLoader.cs
index 89b0dd836..07f3bbb41 100644
--- a/src/MySqlConnector/MySqlBulkLoader.cs
+++ b/src/MySqlConnector/MySqlBulkLoader.cs
@@ -202,7 +202,7 @@ internal async ValueTask<int> LoadAsync(IOBehavior ioBehavior, CancellationToken
 		try
 		{
 			if (Local && !Connection.AllowLoadLocalInfile)
-				throw new NotSupportedException("To use MySqlBulkLoader.Local=true, set AllowLoadLocalInfile=true in the connection string. See https://fl.vu/mysql-load-data");
+				throw new NotSupportedException("To use MySqlBulkLoader.Local=true, set AllowLoadLocalInfile=true in the connection string. See https://mysqlconnector.net/load-data");
 
 			using var cmd = new MySqlCommand(CreateSql(), Connection, Connection.CurrentTransaction)
 			{
diff --git a/src/MySqlConnector/MySqlCommand.cs b/src/MySqlConnector/MySqlCommand.cs
index a7101d3e1..0e72806c3 100644
--- a/src/MySqlConnector/MySqlCommand.cs
+++ b/src/MySqlConnector/MySqlCommand.cs
@@ -448,7 +448,7 @@ private bool IsValid([NotNullWhen(false)] out Exception? exception)
 		else if (Connection.State is not ConnectionState.Open and not ConnectionState.Connecting)
 			exception = new InvalidOperationException($"Connection must be Open; current state is {Connection.State}");
 		else if (!Connection.IgnoreCommandTransaction && Transaction != Connection.CurrentTransaction)
-			exception = new InvalidOperationException("The transaction associated with this command is not the connection's active transaction; see https://fl.vu/mysql-trans");
+			exception = new InvalidOperationException("The transaction associated with this command is not the connection's active transaction; see https://mysqlconnector.net/trans");
 		else if (string.IsNullOrWhiteSpace(CommandText))
 			exception = new InvalidOperationException("CommandText must be specified");
 		return exception is null;
diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 00e2caca1..6d493cba8 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -556,7 +556,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		}
 		else
 		{
-			throw new NotSupportedException($"Parameter type {Value.GetType().Name} is not supported; see https://fl.vu/mysql-param-type. Value: {Value}");
+			throw new NotSupportedException($"Parameter type {Value.GetType().Name} is not supported; see https://mysqlconnector.net/param-type. Value: {Value}");
 		}
 
 		static void WriteString(ByteBufferWriter writer, bool noBackslashEscapes, ReadOnlySpan<char> value)
@@ -873,7 +873,7 @@ private void AppendBinary(ByteBufferWriter writer, object value, StatementPrepar
 		}
 		else
 		{
-			throw new NotSupportedException($"Parameter type {value.GetType().Name} is not supported; see https://fl.vu/mysql-param-type. Value: {value}");
+			throw new NotSupportedException($"Parameter type {value.GetType().Name} is not supported; see https://mysqlconnector.net/param-type. Value: {value}");
 		}
 	}
 

From 82c9d618949723dbbf1968134e933ee726ae822a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 1 May 2024 07:22:07 -0700
Subject: [PATCH 291/393] Update to MySQL 8.4.

---
 Directory.Packages.props                               | 2 +-
 azure-pipelines.yml                                    | 4 ++--
 docs/content/tutorials/migrating-from-connector-net.md | 2 +-
 tests/IntegrationTests/SchemaProviderTests.cs          | 1 -
 tests/IntegrationTests/app.config                      | 2 +-
 tests/README.md                                        | 2 +-
 6 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9528addf4..425e6fb20 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -16,7 +16,7 @@
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
-    <PackageVersion Include="MySql.Data" Version="8.3.0" />
+    <PackageVersion Include="MySql.Data" Version="8.4.0" />
     <PackageVersion Include="NLog" Version="4.5.0" />
     <PackageVersion Include="Serilog" Version="2.6.0" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index c0c83dd5b..9347b6d82 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -233,8 +233,8 @@ jobs:
         image: 'mysql:8.0'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
-      'MySQL 8.3':
-        image: 'mysql:8.3'
+      'MySQL 8.4':
+        image: 'mysql:8.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
       'MariaDB 10.6':
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 84032f7d3..b28390464 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -41,7 +41,7 @@ In a .NET Framework application, make the following `app.config` change to regis
     <!-- add name="MySQL Data Provider"
       invariant="MySql.Data.MySqlClient"
       description=".Net Framework Data Provider for MySQL"
-      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.3.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.4.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
     <add name="MySqlConnector"
diff --git a/tests/IntegrationTests/SchemaProviderTests.cs b/tests/IntegrationTests/SchemaProviderTests.cs
index 023dcd363..259d28e1f 100644
--- a/tests/IntegrationTests/SchemaProviderTests.cs
+++ b/tests/IntegrationTests/SchemaProviderTests.cs
@@ -144,7 +144,6 @@ public void MetaDataCollectionsRestriction() =>
 	[InlineData("SchemaPrivileges")]
 	[InlineData("TableConstraints")]
 	[InlineData("TablePrivileges")]
-	[InlineData("TableSpaces")]
 	[InlineData("UserPrivileges")]
 #endif
 	public void GetSchema(string schemaName, string? expectedSchemaName = null)
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index 5e4b1c55c..f3d72d12a 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -3,7 +3,7 @@
   <system.data>
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.3.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.4.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>
diff --git a/tests/README.md b/tests/README.md
index a504f1467..670b1fcbe 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,7 @@ It also verifies that MySqlConnector and MySQL Connector/NET (MySql.Data) have s
 
 The tests require a MySQL server. The simplest way to run one is with [Docker](https://www.docker.com/community-edition):
 
-    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:8.3 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
+    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:8.4 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
     docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes';"
 
 Copy the file `IntegrationTests/config.json.example` to `IntegrationTests/config.json`, then edit

From 9efc679afa3d662fef2684adc89fe4152e272817 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 11 May 2024 09:04:26 -0700
Subject: [PATCH 292/393] Add test for literal @ in SQL command.

This was identified as a potential bug at https://stackoverflow.com/a/78463231/23633; although it doesn't appear to be a current problem, adding a test to catch regressions.
---
 .../StatementPreparerTests.cs                 | 23 +++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/tests/MySqlConnector.Tests/StatementPreparerTests.cs b/tests/MySqlConnector.Tests/StatementPreparerTests.cs
index bac8351ca..368c47645 100644
--- a/tests/MySqlConnector.Tests/StatementPreparerTests.cs
+++ b/tests/MySqlConnector.Tests/StatementPreparerTests.cs
@@ -142,6 +142,29 @@ public void FormatParameter(object parameterValue, string replacedValue, bool no
 		Assert.Equal(sql.Replace("@param", replacedValue), parsedSql);
 	}
 
+	[Theory]
+	[InlineData(true)]
+	[InlineData(false)]
+	public void AlterUserHardcoded(bool noBackslashEscapes)
+	{
+		const string sql = "ALTER USER 'root'@'%' IDENTIFIED BY 'password';";
+		var options = noBackslashEscapes ? StatementPreparerOptions.NoBackslashEscapes : StatementPreparerOptions.None;
+		var parsedSql = GetParsedSql(sql, options: options);
+		Assert.Equal(sql, parsedSql);
+	}
+
+	[Theory]
+	[InlineData(true)]
+	[InlineData(false)]
+	public void AlterUserWithParameters(bool noBackslashEscapes)
+	{
+		const string sql = "ALTER USER @user@'localhost' IDENTIFIED BY @password;";
+		var parameters = new MySqlParameterCollection { new("@user", "root"), new("@password", "P@ssw0rd") };
+		var options = noBackslashEscapes ? StatementPreparerOptions.NoBackslashEscapes : StatementPreparerOptions.None;
+		var parsedSql = GetParsedSql(sql, parameters, options);
+		Assert.Equal("ALTER USER 'root'@'localhost' IDENTIFIED BY 'P@ssw0rd';", parsedSql);
+	}
+
 	public static IEnumerable<object[]> FormatParameterData =>
 		new[]
 		{

From 5a377426aa049f90689e3fffa9894ecd7e2f8e7b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 3 Jun 2024 06:12:29 -0700
Subject: [PATCH 293/393] Delete obsolete contrib directory.

---
 contrib/NewRelic/README.md | 3 ---
 1 file changed, 3 deletions(-)
 delete mode 100644 contrib/NewRelic/README.md

diff --git a/contrib/NewRelic/README.md b/contrib/NewRelic/README.md
deleted file mode 100644
index 2d9dececa..000000000
--- a/contrib/NewRelic/README.md
+++ /dev/null
@@ -1,3 +0,0 @@
-# New Relic Agent
-
-MySqlConnector support is available in the New Relic Agent [version 8.32.0.0](https://docs.newrelic.com/docs/release-notes/agent-release-notes/net-release-notes/net-agent-83200) or later.

From 665792b9e120d46ad75541954c8a8505810940c8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 16 Jun 2024 11:11:36 -0700
Subject: [PATCH 294/393] Add some recent MySql.Data bugs.

---
 docs/content/tutorials/migrating-from-connector-net.md | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index b28390464..31b3bd933 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -248,8 +248,12 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector.
 * [#110789](https://bugs.mysql.com/bug.php?id=110789): `OpenAsync` throws unhandled exception from thread pool
 * [#110790](https://bugs.mysql.com/bug.php?id=110790): `ExecuteReaderAsync` hangs instead of cancelling query after `CommandTimeout`
 * [#110791](https://bugs.mysql.com/bug.php?id=110791): `OpenAsync(CancellationToken)` doesn't throw for cancelled token
-* [#111797](https://bugs.mysql.com/bug.php?id=111797): Certain sequence of special characters can break connection string validation.
+* [#111797](https://bugs.mysql.com/bug.php?id=111797): Certain sequence of special characters can break connection string validation
 * [#112088](https://bugs.mysql.com/bug.php?id=112088): `BIT(n)` parameter size incorrectly returns 0
+* [#114567](https://bugs.mysql.com/bug.php?id=114567): MySql.Data 8.0.33 is twice as slow as previous versions
+* [#115155](https://bugs.mysql.com/bug.php?id=115155): MySql.Data executes `SET NAMES` using an unsupported value
+* [#115168](https://bugs.mysql.com/bug.php?id=115168): `MySqlDataReader.GetX` performs culture-sensitive conversions
+* [#115222](https://bugs.mysql.com/bug.php?id=115222): `MySqlPool.ClearAsync` is not thread-safe
 
 ### Fixed First by MySqlConnector
 
@@ -308,7 +312,7 @@ These bugs are fixed in the latest version of MySQL Connector/NET, but were fixe
 * [#100208](https://bugs.mysql.com/bug.php?id=100208): `GetSchema("Procedures")` returns `ROUTINE_DEFINITION` of `"System.Byte[]"`
 * [#100218](https://bugs.mysql.com/bug.php?id=100218): `TIME(n)` microsecond values deserialized incorrectly with prepared command
 * [#100306](https://bugs.mysql.com/bug.php?id=100306): `Command.Prepare` sends wrong statement to server
-* [#100522](https://bugs.mysql.com/bug.php?id=100522): `	MySqlCommand.Parameters.Insert(-1)` succeeds but should fail
+* [#100522](https://bugs.mysql.com/bug.php?id=100522): `MySqlCommand.Parameters.Insert(-1)` succeeds but should fail
 * [#101252](https://bugs.mysql.com/bug.php?id=101252): Can't query `CHAR(36)` column containing `NULL`
 * [#101253](https://bugs.mysql.com/bug.php?id=101253): Default value for `MySqlParameter.Value` changed from null to `0`
 * [#101302](https://bugs.mysql.com/bug.php?id=101302): Stored Procedure `BOOL` parameter can only be mapped to `MySqlDbType.Byte`
@@ -337,3 +341,4 @@ These bugs are fixed in the latest version of MySQL Connector/NET, but were fixe
 * [#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.
 * [#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'
 * [#110717](https://bugs.mysql.com/bug.php?id=110717), [#111759](https://bugs.mysql.com/bug.php?id=111759): MySql.Data is not thread-safe
+* [#114527](https://bugs.mysql.com/bug.php?id=114527): Connector/NET hangs on second connection attempt to non-running database

From 32cf84c639ee9325f699686a671348a2b57a914d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 3 Jul 2024 07:56:46 -0700
Subject: [PATCH 295/393] Update to MySQL 9.0.

---
 Directory.Packages.props                               | 2 +-
 azure-pipelines.yml                                    | 8 ++++----
 docs/content/home.md                                   | 2 +-
 docs/content/tutorials/migrating-from-connector-net.md | 2 +-
 tests/IntegrationTests/CharacterSetTests.cs            | 2 +-
 tests/IntegrationTests/app.config                      | 3 ++-
 tests/README.md                                        | 4 ++--
 7 files changed, 12 insertions(+), 11 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 425e6fb20..018054e0a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -16,7 +16,7 @@
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
-    <PackageVersion Include="MySql.Data" Version="8.4.0" />
+    <PackageVersion Include="MySql.Data" Version="9.0.0" />
     <PackageVersion Include="NLog" Version="4.5.0" />
     <PackageVersion Include="Serilog" Version="2.6.0" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 9347b6d82..acbb52de5 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -225,10 +225,6 @@ jobs:
     vmimage: 'ubuntu-20.04'
   strategy:
     matrix:
-      'MySQL 5.7':
-        image: 'mysql:5.7'
-        connectionStringExtra: 'Tls Cipher Suites=TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256'
-        unsupportedFeatures: 'CachingSha2Password,Ed25519,QueryAttributes,Tls11,Tls13,UuidToBin'
       'MySQL 8.0':
         image: 'mysql:8.0'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
@@ -237,6 +233,10 @@ jobs:
         image: 'mysql:8.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
+      'MySQL 9.0':
+        image: 'mysql:9.0'
+        connectionStringExtra: 'AllowPublicKeyRetrieval=True'
+        unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
       'MariaDB 10.6':
         image: 'mariadb:10.6'
         connectionStringExtra: ''
diff --git a/docs/content/home.md b/docs/content/home.md
index e93eb9c52..568f4e3cf 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -65,7 +65,7 @@ Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://m
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
 MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.2**) |
-MySQL | 5.5, 5.6, **5.7**, 8.x (**8.0**, **8.3**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
+MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), **9.0** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
 ProxySQL | 2.x | Some [compatibility issues](https://github.com/search?q=repo%3Amysql-net%2FMySqlConnector+proxysql&type=issues)
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 31b3bd933..967fd5c65 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -41,7 +41,7 @@ In a .NET Framework application, make the following `app.config` change to regis
     <!-- add name="MySQL Data Provider"
       invariant="MySql.Data.MySqlClient"
       description=".Net Framework Data Provider for MySQL"
-      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.4.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.0.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
     <add name="MySqlConnector"
diff --git a/tests/IntegrationTests/CharacterSetTests.cs b/tests/IntegrationTests/CharacterSetTests.cs
index 2af81a97a..ad1ee9303 100644
--- a/tests/IntegrationTests/CharacterSetTests.cs
+++ b/tests/IntegrationTests/CharacterSetTests.cs
@@ -78,7 +78,7 @@ public void CollationConnection(bool reopenConnection)
 		}
 
 		var collation = connection.Query<string>(@"select @@collation_connection;").Single();
-		var expected = connection.ServerVersion.StartsWith("8.", StringComparison.Ordinal) ? "utf8mb4_0900_ai_ci" : "utf8mb4_general_ci";
+		var expected = connection.ServerVersion.Substring(0, 2) is "8." or "9." ? "utf8mb4_0900_ai_ci" : "utf8mb4_general_ci";
 		Assert.Equal(expected, collation);
 	}
 
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index f3d72d12a..e92393822 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -3,7 +3,8 @@
   <system.data>
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=8.4.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <remove invariant="MySql.Data.MySqlClient" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.0.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>
diff --git a/tests/README.md b/tests/README.md
index 670b1fcbe..c863a4a2b 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,8 +7,8 @@ It also verifies that MySqlConnector and MySQL Connector/NET (MySql.Data) have s
 
 The tests require a MySQL server. The simplest way to run one is with [Docker](https://www.docker.com/community-edition):
 
-    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:8.4 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
-    docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes';"
+    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.0 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
+    docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes'; CREATE USER 'caching-sha2-user'@'%' IDENTIFIED WITH caching_sha2_password BY 'Cach!ng-Sh@2-Pa55'; GRANT ALL PRIVILEGES ON *.* TO 'caching-sha2-user'@'%';"
 
 Copy the file `IntegrationTests/config.json.example` to `IntegrationTests/config.json`, then edit
 the `config.json` file in order to connect to your server. If you are using the Docker

From e2a0f5884714ee588a48440872450fdedb133b42 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 3 Jul 2024 08:17:34 -0700
Subject: [PATCH 296/393] Run integration tests on MariaDB 11.4.

This is the latest LTS version.
---
 .ci/docker-run.sh                           | 2 +-
 azure-pipelines.yml                         | 4 ++--
 docs/content/home.md                        | 2 +-
 tests/IntegrationTests/CharacterSetTests.cs | 4 +++-
 4 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index b879e7a5f..045f43753 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -28,7 +28,7 @@ MYSQL=mysql
 if [[ "$IMAGE" == mariadb* ]]; then
   MYSQL_EXTRA='--in-predicate-conversion-threshold=100000'
 fi
-if [ "$IMAGE" == "mariadb:11.2" ]; then
+if [ "$IMAGE" == "mariadb:11.4" ]; then
   MYSQL='mariadb'
 fi
 
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index acbb52de5..fdb35d9b8 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -245,8 +245,8 @@ jobs:
         image: 'mariadb:10.11'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
-      'MariaDB 11.2':
-        image: 'mariadb:11.2'
+      'MariaDB 11.4':
+        image: 'mariadb:11.4'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
   steps:
diff --git a/docs/content/home.md b/docs/content/home.md
index 568f4e3cf..0c0f0b4e1 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -64,7 +64,7 @@ Server  | Versions | Notes
 Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://mysqlconnector.net/troubleshooting/aurora-freeze/)
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
-MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.2**) |
+MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**) |
 MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), **9.0** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
diff --git a/tests/IntegrationTests/CharacterSetTests.cs b/tests/IntegrationTests/CharacterSetTests.cs
index ad1ee9303..152ca3449 100644
--- a/tests/IntegrationTests/CharacterSetTests.cs
+++ b/tests/IntegrationTests/CharacterSetTests.cs
@@ -78,7 +78,9 @@ public void CollationConnection(bool reopenConnection)
 		}
 
 		var collation = connection.Query<string>(@"select @@collation_connection;").Single();
-		var expected = connection.ServerVersion.Substring(0, 2) is "8." or "9." ? "utf8mb4_0900_ai_ci" : "utf8mb4_general_ci";
+		var expected = connection.ServerVersion.Substring(0, 2) is "8." or "9." ? "utf8mb4_0900_ai_ci" :
+			connection.ServerVersion.StartsWith("11.4.", StringComparison.Ordinal) ? "utf8mb4_uca1400_ai_ci" :
+			"utf8mb4_general_ci";
 		Assert.Equal(expected, collation);
 	}
 

From 92787d15342d736a8ded40a3375c80427c608f98 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 9 Jul 2024 13:19:39 -0700
Subject: [PATCH 297/393] Remove workaround local methods.

C# 13 now allows lock and ref struct within the body of async methods. https://devblogs.microsoft.com/dotnet/csharp-13-explore-preview-features/#ref-and-unsafe-in-async-methods-and-iterators
---
 src/MySqlConnector/Core/ServerSession.cs | 15 ++++++---------
 src/MySqlConnector/MySqlConnection.cs    | 18 +++++++-----------
 2 files changed, 13 insertions(+), 20 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index f83c03e7a..096747c1f 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1646,15 +1646,12 @@ private async Task GetRealServerDetailsAsync(IOBehavior ioBehavior, Cancellation
 
 			// first (and only) row
 			payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
-			static void ReadRow(ReadOnlySpan<byte> span, out int? connectionId, out ServerVersion? serverVersion)
-			{
-				var reader = new ByteArrayReader(span);
-				var length = reader.ReadLengthEncodedIntegerOrNull();
-				connectionId = (length != -1 && Utf8Parser.TryParse(reader.ReadByteString(length), out int id, out _)) ? id : default(int?);
-				length = reader.ReadLengthEncodedIntegerOrNull();
-				serverVersion = length != -1 ? new ServerVersion(reader.ReadByteString(length)) : default;
-			}
-			ReadRow(payload.Span, out var connectionId, out var serverVersion);
+
+			var reader = new ByteArrayReader(payload.Span);
+			var length = reader.ReadLengthEncodedIntegerOrNull();
+			var connectionId = (length != -1 && Utf8Parser.TryParse(reader.ReadByteString(length), out int id, out _)) ? id : default(int?);
+			length = reader.ReadLengthEncodedIntegerOrNull();
+			var serverVersion = length != -1 ? new ServerVersion(reader.ReadByteString(length)) : default;
 
 			// OK/EOF payload
 			payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index fc327a4a3..07f4288ce 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -1189,20 +1189,16 @@ private async Task DoCloseAsync(bool changeState, IOBehavior ioBehavior)
 			};
 			connection.TakeSessionFrom(this);
 
-			ReplaceConnection(this, connection);
-			static void ReplaceConnection(MySqlConnection thisConnection, MySqlConnection connection)
+			// put the new, idle, connection into the list of sessions for this transaction (replacing this MySqlConnection)
+			lock (s_lock)
 			{
-				// put the new, idle, connection into the list of sessions for this transaction (replacing this MySqlConnection)
-				lock (s_lock)
+				foreach (var enlistedTransaction in s_transactionConnections[connection.m_enlistedTransaction!.Transaction])
 				{
-					foreach (var enlistedTransaction in s_transactionConnections[connection.m_enlistedTransaction!.Transaction])
+					if (enlistedTransaction.Connection == this)
 					{
-						if (enlistedTransaction.Connection == thisConnection)
-						{
-							enlistedTransaction.Connection = connection;
-							enlistedTransaction.IsIdle = true;
-							break;
-						}
+						enlistedTransaction.Connection = connection;
+						enlistedTransaction.IsIdle = true;
+						break;
 					}
 				}
 			}

From 20c39aa51bd8160e167c4d5ec64f7d9b3ab75ce9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 9 Jul 2024 20:22:18 -0700
Subject: [PATCH 298/393] Update NuGet packages.

Fixes https://github.com/mysql-net/MySqlConnector/security/dependabot/6.
---
 Directory.Packages.props                       | 15 ++++++++-------
 tests/IntegrationTests/IntegrationTests.csproj |  1 +
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 018054e0a..961777baa 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -6,30 +6,31 @@
 
   <ItemGroup>
     <PackageVersion Include="AdoNet.Specification.Tests" Version="2.0.0-beta.2" />
-    <PackageVersion Include="Dapper.StrongName" Version="2.1.28" />
+    <PackageVersion Include="Dapper.StrongName" Version="2.1.35" />
     <PackageVersion Include="IndexRange" Version="1.0.2" />
     <PackageVersion Include="log4net" Version="2.0.10" />
-    <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
-    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
+    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
     <PackageVersion Include="MySql.Data" Version="9.0.0" />
     <PackageVersion Include="NLog" Version="4.5.0" />
     <PackageVersion Include="Serilog" Version="2.6.0" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
+    <PackageVersion Include="System.Text.Json" Version="8.0.4" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageVersion Include="xunit" Version="2.7.0" />
-    <PackageVersion Include="xunit.runner.visualstudio" Version="2.5.7" />
-    <PackageVersion Include="YamlDotNet" Version="13.7.1" />
+    <PackageVersion Include="xunit" Version="2.9.0" />
+    <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
+    <PackageVersion Include="YamlDotNet" Version="15.3.0" />
   </ItemGroup>
 
   <ItemGroup>
     <GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
-    <GlobalPackageReference Include="MinVer" Version="4.3.0" />
+    <GlobalPackageReference Include="MinVer" Version="5.0.0" />
   </ItemGroup>
 
 </Project>
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index 8c975155b..55360d60c 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -40,6 +40,7 @@
     <PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
     <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" />
     <PackageReference Include="Microsoft.Extensions.Configuration.Json" />
+    <PackageReference Include="System.Text.Json" />
     <Using Include="System" />
     <Using Include="System.Data" />
     <Using Include="System.Data.Common" />

From 8c01776434151e21889f312d2a78424314c50b93 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 9 Jul 2024 14:01:38 -0700
Subject: [PATCH 299/393] Reflect SSL test failure under .NET 9.0.

This may be related to this release note: https://github.com/dotnet/core/blob/main/release-notes/9.0/preview/preview6/libraries.md#tls-resume-with-client-certificates-on-linux
---
 tests/IntegrationTests/SslTests.cs | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/tests/IntegrationTests/SslTests.cs b/tests/IntegrationTests/SslTests.cs
index e22c034fb..96f0421ed 100644
--- a/tests/IntegrationTests/SslTests.cs
+++ b/tests/IntegrationTests/SslTests.cs
@@ -103,7 +103,9 @@ private async Task DoTestSsl(string connectionString)
 		Assert.True(connection.SslIsEncrypted);
 		Assert.True(connection.SslIsSigned);
 		Assert.True(connection.SslIsAuthenticated);
+#if !NET9_0_OR_GREATER
 		Assert.True(connection.SslIsMutuallyAuthenticated);
+#endif
 #endif
 		cmd.CommandText = "SHOW SESSION STATUS LIKE 'Ssl_version'";
 		var sslVersion = (string) await cmd.ExecuteScalarAsync();
@@ -133,9 +135,11 @@ public async Task ConnectSslClientCertificateFromCertificateStore(string certFil
 			Assert.True(connection.SslIsEncrypted);
 			Assert.True(connection.SslIsSigned);
 			Assert.True(connection.SslIsAuthenticated);
+#if !NET9_0_OR_GREATER
 			Assert.True(connection.SslIsMutuallyAuthenticated);
 #endif
-			cmd.CommandText = "SHOW SESSION STATUS LIKE 'Ssl_version'";
+#endif
+            cmd.CommandText = "SHOW SESSION STATUS LIKE 'Ssl_version'";
 			var sslVersion = (string) await cmd.ExecuteScalarAsync();
 			Assert.False(string.IsNullOrWhiteSpace(sslVersion));
 		}

From 24e53cd306c2f7e85718f1e13d2bdcb20eb8c6e7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 9 Jul 2024 20:49:30 -0700
Subject: [PATCH 300/393] Support SET parameter type. Fixes #1491

---
 src/MySqlConnector/Core/CachedProcedure.cs         | 8 ++++----
 tests/MySqlConnector.Tests/CachedProcedureTests.cs | 7 +++++++
 2 files changed, 11 insertions(+), 4 deletions(-)

diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index 021d89c71..441a7982b 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -153,10 +153,10 @@ internal static List<CachedParameter> ParseParameters(string parametersSql)
 			return [];
 
 		// strip precision specifier containing comma
-		parametersSql = s_numericTypes.Replace(parametersSql, @"$1");
+		parametersSql = s_numericTypes.Replace(parametersSql, "$1");
 
 		// strip enum values containing commas (these would have been stripped by ParseDataType anyway)
-		parametersSql = s_enum.Replace(parametersSql, "ENUM");
+		parametersSql = s_enumOrSet.Replace(parametersSql, "$1");
 
 		var parameters = parametersSql.Split(',');
 		var cachedParameters = new List<CachedParameter>(parameters.Length);
@@ -195,7 +195,7 @@ internal static string ParseDataType(string sql, out bool unsigned, out int leng
 	{
 		sql = s_characterSet.Replace(sql, "");
 		sql = s_collate.Replace(sql, "");
-		sql = s_enum.Replace(sql, "ENUM");
+		sql = s_enumOrSet.Replace(sql, "$1");
 
 		length = 0;
 		var match = s_length.Match(sql);
@@ -260,7 +260,7 @@ private static CachedParameter CreateCachedParameter(int ordinal, string? direct
 	private static readonly Regex s_singleLineComments = new(@"(^|\s)--.*?$", RegexOptions.Multiline);
 	private static readonly Regex s_multipleSpaces = new(@"\s+");
 	private static readonly Regex s_numericTypes = new(@"(DECIMAL|DEC|FIXED|NUMERIC|FLOAT|DOUBLE PRECISION|DOUBLE|REAL)\s*\([0-9]+(,\s*[0-9]+)\)", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
-	private static readonly Regex s_enum = new(@"ENUM\s*\([^)]+\)", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
+	private static readonly Regex s_enumOrSet = new(@"(ENUM|SET)\s*\([^)]+\)", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
 	private static readonly Regex s_parameterName = new(@"^(?:`((?:[\u0001-\u005F\u0061-\uFFFF]+|``)+)`|([A-Za-z0-9$_\u0080-\uFFFF]+)) (.*)$");
 	private static readonly Regex s_characterSet = new(" (CHARSET|CHARACTER SET) [A-Za-z0-9_]+", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
 	private static readonly Regex s_collate = new(" (COLLATE) [A-Za-z0-9_]+", RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
diff --git a/tests/MySqlConnector.Tests/CachedProcedureTests.cs b/tests/MySqlConnector.Tests/CachedProcedureTests.cs
index c6966b821..fcae8eb20 100644
--- a/tests/MySqlConnector.Tests/CachedProcedureTests.cs
+++ b/tests/MySqlConnector.Tests/CachedProcedureTests.cs
@@ -121,6 +121,12 @@ public static IEnumerable<object[]> CreateParseableParameters()
 					new CachedParameter(1, "INOUT", "param", "LONGTEXT", false, 0),
 				}
 			],
+			[
+				"ColSet set('set1','set2','set3')", new object[]
+				{
+					new CachedParameter(1, "IN", "ColSet", "SET", false, 0),
+				}
+			],
 			[
 				@"IN param1 DATETIME(6),
 -- ignored1
@@ -189,6 +195,7 @@ param4 INTEGER(3)
 	[InlineData("NUMERIC(12)", "DECIMAL", false, 12)]
 	[InlineData("FIXED(12)", "DECIMAL", false, 12)]
 	[InlineData("ENUM('a','b','c')", "ENUM", false, 0)]
+	[InlineData("SET('a','b','c')", "SET", false, 0)]
 	public void ParseDataType(string sql, string expectedDataType, bool expectedUnsigned, int expectedLength)
 	{
 		var dataType = CachedProcedure.ParseDataType(sql, out var unsigned, out var length);

From 67946419631a047032d3baccb7c506d7e39bb45d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 9 Jul 2024 21:02:59 -0700
Subject: [PATCH 301/393] Audit transitive NuGet dependencies.

---
 Directory.Build.props | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Directory.Build.props b/Directory.Build.props
index c475ff91a..10bca8b48 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -8,6 +8,7 @@
     <ArtifactsPath>$(MSBuildThisFileDirectory)artifacts</ArtifactsPath>
     <NuGetAudit>true</NuGetAudit>
     <NuGetAuditLevel>low</NuGetAuditLevel>
+    <NuGetAuditMode>all</NuGetAuditMode>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">

From e6bb115e49a3d21386062fb562046562fa5fa0ab Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 11 Jul 2024 10:18:21 -0700
Subject: [PATCH 302/393] Remove Azure Database for MySQL integration tests.

The Azure Credits for Open Source sponsorship has ended.
---
 README.md           |  4 ----
 azure-pipelines.yml | 41 -----------------------------------------
 2 files changed, 45 deletions(-)

diff --git a/README.md b/README.md
index ec8675935..15d8953d8 100644
--- a/README.md
+++ b/README.md
@@ -113,7 +113,3 @@ Development of MySqlConnector is supported by:
 [![Faithlife](https://files.logoscdn.com/v1/files/4319104/content.svg?signature=3szVb3XmOfYMAxIv-LmuNYL_290)](https://faithlife.com/about)
 
 [Faithlife](https://faithlife.com/about) ([View jobs](https://faithlife.com/careers))
-
-[![Microsoft Azure](https://upload.wikimedia.org/wikipedia/commons/thumb/f/fa/Microsoft_Azure.svg/75px-Microsoft_Azure.svg.png)](https://azure.microsoft.com/en-us/overview/open-source/)
-
-[Azure Credits for Open Source](https://opensource.microsoft.com/azure-credits)
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index fdb35d9b8..e14ef7d82 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -102,47 +102,6 @@ jobs:
       image: 'mysql:8.0'
       connectionString: 'server=localhost;port=3306;user id=mysqltest;password=test;database=conformance;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
 
-- job: azure_mysql_integration_tests
-  condition: ne('True', variables['System.PullRequest.IsFork'])
-  displayName: 'Azure MySQL Integration Tests'
-  pool:
-    vmimage: 'windows-latest'
-  steps:
-  - task: PowerShell@2
-    displayName: Create Database
-    inputs:
-      targetType: inline
-      script: "mysql -u$(Azure MySQL Root User) -p$(Azure MySQL Root Password) -h '$(Azure MySQL Host)' -e 'create schema mysqltest_$(Build.BuildId) collate utf8mb4_0900_ai_ci;'"
-  - task: UseDotNet@2
-    displayName: 'Install .NET'
-    inputs:
-      version: $(DotNetCoreSdkVersion)
-  - task: PowerShell@2
-    displayName: 'Copy Azure config'
-    inputs:
-      targetType: inline
-      script: Copy-Item -Path ".\.ci\config\config.ssl.json" -Destination ".\tests\IntegrationTests\config.json"
-  - task: DotNetCoreCLI@2
-    displayName: 'Restore packages'
-    inputs:
-      command: 'restore'
-  - task: DotNetCoreCLI@2
-    displayName: 'Integration tests (net8.0)'
-    inputs:
-      command: 'test'
-      projects: 'tests/IntegrationTests/IntegrationTests.csproj'
-      arguments: '-c Release -f net8.0 --no-restore'
-      testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'Azure', 'net8.0', 'SSL') }}
-    env:
-      DATA__UNSUPPORTEDFEATURES: 'CachingSha2Password,Ed25519,GlobalLog,KnownCertificateAuthority,QueryAttributes,RsaEncryption,Sha256Password,StreamingResults,Timeout,Tls11,Tls13,UnixDomainSocket,ZeroDateTime'
-      DATA__CONNECTIONSTRING: "$(AzureConnectionString);database=mysqltest_$(Build.BuildId);ssl mode=Required;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True"
-  - task: PowerShell@2
-    displayName: Drop Database
-    condition: always()
-    inputs:
-      targetType: inline
-      script: "mysql -u$(Azure MySQL Root User) -p$(Azure MySQL Root Password) -h '$(Azure MySQL Host)' -e 'drop schema if exists mysqltest_$(Build.BuildId);'"
-
 - job: windows_integration_tests_1
   displayName: 'Windows Integration Tests (Part 1)'
   pool:

From 06d1f8c001d38df9cd1c7b4244a586ba555c3330 Mon Sep 17 00:00:00 2001
From: rusher <diego.dupin@gmail.com>
Date: Mon, 15 Jul 2024 12:31:51 +0200
Subject: [PATCH 303/393] Avoids SET NAMES commands if not needed
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

for MariaDB since 15.1 (https://jira.mariadb.org/browse/MDEV-31609) followed session variables are sent in initial OK_Packet. This permits to avoids  initial query SET NAMES command, since charset is really know, defaulting to server corresponding default collation.

Signed-off-by: rusher <diego.dupin@gmail.com>
---
 .../Core/ConcatenatedCommandPayloadCreator.cs |  2 +-
 src/MySqlConnector/Core/ConnectionPool.cs     |  2 +-
 src/MySqlConnector/Core/Context.cs            | 29 +++++++
 src/MySqlConnector/Core/ResultSet.cs          | 16 ++--
 src/MySqlConnector/Core/ServerSession.cs      | 87 +++++++++----------
 .../Core/SingleCommandPayloadCreator.cs       |  4 +-
 src/MySqlConnector/Logging/Log.cs             |  4 +-
 src/MySqlConnector/MySqlConnection.cs         | 22 ++---
 .../Protocol/Payloads/OkPayload.cs            | 66 ++++++++------
 9 files changed, 138 insertions(+), 94 deletions(-)
 create mode 100644 src/MySqlConnector/Core/Context.cs

diff --git a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
index 20e0717f6..df92e081a 100644
--- a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
@@ -17,7 +17,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 
 		// ConcatenatedCommandPayloadCreator is only used by MySqlBatch, and MySqlBatchCommand doesn't expose attributes,
 		// so just write an empty attribute set if the server needs it.
-		if (commandListPosition.CommandAt(commandListPosition.CommandIndex).Connection!.Session.SupportsQueryAttributes)
+		if (commandListPosition.CommandAt(commandListPosition.CommandIndex).Connection!.Session.Context.SupportsQueryAttributes)
 		{
 			// attribute count
 			writer.WriteLengthEncodedInteger(0);
diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 17227b631..0fb5f2e4e 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -65,7 +65,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 				}
 				else
 				{
-					if (ConnectionSettings.ConnectionReset || session.DatabaseOverride is not null)
+					if (ConnectionSettings.ConnectionReset || !session.Context.IsInitialDatabase())
 					{
 						if (timeoutMilliseconds != 0)
 							session.SetTimeout(Math.Max(1, timeoutMilliseconds - Utility.GetElapsedMilliseconds(startingTimestamp)));
diff --git a/src/MySqlConnector/Core/Context.cs b/src/MySqlConnector/Core/Context.cs
new file mode 100644
index 000000000..74448d4f5
--- /dev/null
+++ b/src/MySqlConnector/Core/Context.cs
@@ -0,0 +1,29 @@
+using MySqlConnector.Protocol;
+
+namespace MySqlConnector.Core;
+
+internal sealed class Context
+{
+	public Context(ProtocolCapabilities protocolCapabilities, string? database, int connectionId)
+	{
+		SupportsDeprecateEof = (protocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
+		SupportsCachedPreparedMetadata = (protocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
+		SupportsQueryAttributes = (protocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
+		SupportsSessionTrack = (protocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
+		ConnectionId = connectionId;
+		Database = database;
+		m_initialDatabase = database;
+	}
+
+	public bool SupportsDeprecateEof { get; }
+	public bool SupportsQueryAttributes { get; }
+	public bool SupportsSessionTrack { get; }
+	public bool SupportsCachedPreparedMetadata { get; }
+	public string? ClientCharset { get; set; }
+
+	public string? Database { get; set; }
+	private readonly string? m_initialDatabase;
+	public bool IsInitialDatabase() => string.Equals(m_initialDatabase, Database, StringComparison.Ordinal);
+
+	public int ConnectionId { get; set; }
+}
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index a0887aa2c..8d40ee6fb 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -38,7 +38,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 				var firstByte = payload.HeaderByte;
 				if (firstByte == OkPayload.Signature)
 				{
-					var ok = OkPayload.Create(payload.Span, Session.SupportsDeprecateEof, Session.SupportsSessionTrack);
+					var ok = OkPayload.Create(payload.Span, Session.Context);
 
 					// if we've read a result set header then this is a SELECT statement, so we shouldn't overwrite RecordsAffected
 					// (which should be -1 for SELECT) unless the server reports a non-zero count
@@ -48,8 +48,6 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 					if (ok.LastInsertId != 0)
 						Command?.SetLastInsertedId((long) ok.LastInsertId);
 					WarningCount = ok.WarningCount;
-					if (ok.NewSchema is not null)
-						Connection.Session.DatabaseOverride = ok.NewSchema;
 					m_columnDefinitions = default;
 					State = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0
 						? ResultSetState.NoMoreData
@@ -109,7 +107,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 				}
 				else
 				{
-					var columnCountPacket = ColumnCountPayload.Create(payload.Span, Session.SupportsCachedPreparedMetadata);
+					var columnCountPacket = ColumnCountPayload.Create(payload.Span, Session.Context.SupportsCachedPreparedMetadata);
 					var columnCount = columnCountPacket.ColumnCount;
 					if (!columnCountPacket.MetadataFollows)
 					{
@@ -132,7 +130,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 						m_columnDefinitions = m_columnDefinitionPayloadCache.AsMemory(0, columnCount);
 
 						// if the server supports metadata caching but has re-sent it, something has changed since last prepare/execution and we need to update the columns
-						var preparedColumns = Session.SupportsCachedPreparedMetadata ? DataReader.LastUsedPreparedStatement?.Columns : null;
+						var preparedColumns = Session.Context.SupportsCachedPreparedMetadata ? DataReader.LastUsedPreparedStatement?.Columns : null;
 
 						for (var column = 0; column < columnCount; column++)
 						{
@@ -156,7 +154,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 						}
 					}
 
-					if (!Session.SupportsDeprecateEof)
+					if (!Session.Context.SupportsDeprecateEof)
 					{
 						payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 						_ = EofPayload.Create(payload.Span);
@@ -252,13 +250,13 @@ public async Task<bool> ReadAsync(IOBehavior ioBehavior, CancellationToken cance
 
 		if (payload.HeaderByte == EofPayload.Signature)
 		{
-			if (Session.SupportsDeprecateEof && OkPayload.IsOk(payload.Span, Session.SupportsDeprecateEof))
+			if (Session.Context.SupportsDeprecateEof && OkPayload.IsOk(payload.Span, Session.Context))
 			{
-				var ok = OkPayload.Create(payload.Span, Session.SupportsDeprecateEof, Session.SupportsSessionTrack);
+				var ok = OkPayload.Create(payload.Span, Session.Context);
 				BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
 				return null;
 			}
-			if (!Session.SupportsDeprecateEof && EofPayload.IsEof(payload))
+			if (!Session.Context.SupportsDeprecateEof && EofPayload.IsEof(payload))
 			{
 				var eof = EofPayload.Create(payload.Span);
 				BufferState = (eof.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index dec7faea3..70dcc3d28 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -44,6 +44,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 		m_activityTags = [];
 		DataReader = new();
 		Log.CreatedNewSession(m_logger, Id);
+		Context = new Context(0, null, 0);
 	}
 
 	public string Id { get; }
@@ -51,22 +52,19 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public bool SupportsPerQueryVariables => ServerVersion.IsMariaDb && ServerVersion.Version >= ServerVersions.MariaDbSupportsPerQueryVariables;
 	public int ActiveCommandId { get; private set; }
 	public int CancellationTimeout { get; private set; }
-	public int ConnectionId { get; set; }
 	public byte[]? AuthPluginData { get; set; }
 	public long CreatedTimestamp { get; }
 	public ConnectionPool? Pool { get; }
 	public int PoolGeneration { get; }
 	public long LastLeasedTimestamp { get; set; }
 	public long LastReturnedTimestamp { get; private set; }
-	public string? DatabaseOverride { get; set; }
+
 	public string HostName { get; private set; }
 	public IPEndPoint? IPEndPoint => m_tcpClient?.Client.RemoteEndPoint as IPEndPoint;
 	public string? UserID { get; private set; }
 	public WeakReference<MySqlConnection>? OwningConnection { get; set; }
-	public bool SupportsDeprecateEof { get; private set; }
-	public bool SupportsCachedPreparedMetadata { get; private set; }
-	public bool SupportsQueryAttributes { get; private set; }
-	public bool SupportsSessionTrack { get; private set; }
+	public Context Context { get; private set; }
+
 	public bool ProcAccessDenied { get; set; }
 	public ICollection<KeyValuePair<string, object?>> ActivityTags => m_activityTags;
 	public MySqlDataReader DataReader { get; set; }
@@ -241,7 +239,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
 					ColumnDefinitionPayload.Initialize(ref parameters[i], new(columnsAndParameters, columnsAndParametersSize, payloadLength));
 					columnsAndParametersSize += payloadLength;
 				}
-				if (!SupportsDeprecateEof)
+				if (!Context.SupportsDeprecateEof)
 				{
 					payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 					EofPayload.Create(payload.Span);
@@ -261,7 +259,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
 					ColumnDefinitionPayload.Initialize(ref columns[i], new(columnsAndParameters, columnsAndParametersSize, payloadLength));
 					columnsAndParametersSize += payloadLength;
 				}
-				if (!SupportsDeprecateEof)
+				if (!Context.SupportsDeprecateEof)
 				{
 					payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 					EofPayload.Create(payload.Span);
@@ -315,12 +313,12 @@ public void FinishQuerying()
 			// In order to handle this case, we issue a dummy query that will consume the pending cancellation.
 			// See https://bugs.mysql.com/bug.php?id=45679
 			Log.SendingSleepToClearPendingCancellation(m_logger, Id);
-			var payload = SupportsQueryAttributes ? s_sleepWithAttributesPayload : s_sleepNoAttributesPayload;
+			var payload = Context.SupportsQueryAttributes ? s_sleepWithAttributesPayload : s_sleepNoAttributesPayload;
 #pragma warning disable CA2012 // Safe because method completes synchronously
 			SendAsync(payload, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 			payload = ReceiveReplyAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 #pragma warning restore CA2012
-			OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, Context);
 		}
 
 		lock (m_lock)
@@ -340,8 +338,8 @@ public void FinishQuerying()
 		var activity = ActivitySourceHelper.StartActivity(name, m_activityTags);
 		if (activity is { IsAllDataRequested: true })
 		{
-			if (DatabaseOverride is not null)
-				activity.SetTag(ActivitySourceHelper.DatabaseNameTagName, DatabaseOverride);
+			if (!Context.IsInitialDatabase())
+				activity.SetTag(ActivitySourceHelper.DatabaseNameTagName, Context.Database);
 			if (tagName1 is not null)
 				activity.SetTag(tagName1, tagValue1);
 		}
@@ -454,7 +452,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			}
 
 			ServerVersion = new(initialHandshake.ServerVersion);
-			ConnectionId = initialHandshake.ConnectionId;
+			Context = new Context(initialHandshake.ProtocolCapabilities, cs.Database, initialHandshake.ConnectionId);
 			AuthPluginData = initialHandshake.AuthPluginData;
 			m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0;
 			CancellationTimeout = cs.CancellationTimeout;
@@ -462,22 +460,18 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 
 			// set activity tags
 			{
-				var connectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
+				var connectionId = Context.ConnectionId.ToString(CultureInfo.InvariantCulture);
 				m_activityTags[ActivitySourceHelper.DatabaseConnectionIdTagName] = connectionId;
 				if (activity is { IsAllDataRequested: true })
 					activity.SetTag(ActivitySourceHelper.DatabaseConnectionIdTagName, connectionId);
 			}
 
 			m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
-			SupportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
-			SupportsCachedPreparedMetadata = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
-			SupportsQueryAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
-			SupportsSessionTrack = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
 			var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0;
 			m_characterSet = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4GeneralCaseInsensitive : CharacterSet.Utf8Mb3GeneralCaseInsensitive;
 			m_setNamesPayload = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ?
-				(SupportsQueryAttributes ? s_setNamesUtf8mb4WithAttributesPayload : s_setNamesUtf8mb4NoAttributesPayload) :
-				(SupportsQueryAttributes ? s_setNamesUtf8WithAttributesPayload : s_setNamesUtf8NoAttributesPayload);
+				(Context.SupportsQueryAttributes ? s_setNamesUtf8mb4WithAttributesPayload : s_setNamesUtf8mb4NoAttributesPayload) :
+				(Context.SupportsQueryAttributes ? s_setNamesUtf8WithAttributesPayload : s_setNamesUtf8NoAttributesPayload);
 
 			// disable pipelining for RDS MySQL 5.7 (assuming Aurora); otherwise take it from the connection string or default to true
 			if (!cs.Pipelining.HasValue && ServerVersion.Version.Major == 5 && ServerVersion.Version.Minor == 7 && HostName.EndsWith(".rds.amazonaws.com", StringComparison.OrdinalIgnoreCase))
@@ -505,7 +499,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				}
 			}
 
-			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, SupportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, SupportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
+			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, Context.ConnectionId, m_useCompression, m_supportsConnectionAttributes, Context.SupportsDeprecateEof, Context.SupportsCachedPreparedMetadata, serverSupportsSsl, Context.SupportsSessionTrack, m_supportsPipelining, Context.SupportsQueryAttributes);
 
 			if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
 			{
@@ -532,16 +526,21 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			}
 
-			var ok = OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+			var ok = OkPayload.Create(payload.Span, Context);
 			var statusInfo = ok.StatusInfo;
 
 			if (m_useCompression)
 				m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
 
 			// set 'collation_connection' to the server default
-			await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
-			payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+			if (Context.ClientCharset == null || ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4
+				    ? !string.Equals(Context.ClientCharset, "utf8mb4", StringComparison.Ordinal)
+				    : !string.Equals(Context.ClientCharset, "utf8", StringComparison.Ordinal))
+			{
+				await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
+				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+				OkPayload.Verify(payload.Span, Context);
+			}
 
 			if (ShouldGetRealServerDetails(cs))
 				await GetRealServerDetailsAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
@@ -571,9 +570,9 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 			ClearPreparedStatements();
 
 			PayloadData payload;
-			if (DatabaseOverride is null &&
-				((!ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) ||
-				(ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.MariaDbSupportsResetConnection) >= 0)))
+			if (Context.IsInitialDatabase() &&
+			    ((!ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) ||
+			     (ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.MariaDbSupportsResetConnection) >= 0)))
 			{
 				if (m_supportsPipelining)
 				{
@@ -584,10 +583,10 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 
 					// read two OK replies
 					payload = await ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
-					OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+					OkPayload.Verify(payload.Span, Context);
 
 					payload = await ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
-					OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+					OkPayload.Verify(payload.Span, Context);
 
 					return true;
 				}
@@ -595,19 +594,19 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 				Log.SendingResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
 				await SendAsync(ResetConnectionPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-				OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+				OkPayload.Verify(payload.Span, Context);
 			}
 			else
 			{
 				// optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL)
-				if (DatabaseOverride is null)
+				if (Context.IsInitialDatabase())
 				{
 					Log.SendingChangeUserRequest(m_logger, Id, ServerVersion.OriginalString);
 				}
 				else
 				{
-					Log.SendingChangeUserRequestDueToChangedDatabase(m_logger, Id, DatabaseOverride);
-					DatabaseOverride = null;
+					Log.SendingChangeUserRequestDueToChangedDatabase(m_logger, Id, Context.Database!);
+					Context.Database = cs.Database;
 				}
 				var password = GetPassword(cs, connection);
 				var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData!, password);
@@ -619,13 +618,13 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 					Log.OptimisticReauthenticationFailed(m_logger, Id);
 					payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				}
-				OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+				OkPayload.Verify(payload.Span, Context);
 			}
 
 			// set 'collation_connection' to the server default
 			await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, Context);
 
 			return true;
 		}
@@ -684,7 +683,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 				// OK payload can be sent immediately (e.g., if password is empty) bypassing even the fast authentication path
-				if (OkPayload.IsOk(payload.Span, SupportsDeprecateEof))
+				if (OkPayload.IsOk(payload.Span, Context))
 					return payload;
 
 				var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload.Span);
@@ -824,7 +823,7 @@ public async ValueTask<bool> TryPingAsync(bool logInfo, IOBehavior ioBehavior, C
 			Log.PingingServer(m_logger, Id);
 			await SendAsync(PingPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
 			var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, Context);
 			Log.SuccessfullyPingedServer(m_logger, logInfo ? LogLevel.Information : LogLevel.Trace, Id);
 			return true;
 		}
@@ -1632,7 +1631,7 @@ private async Task GetRealServerDetailsAsync(IOBehavior ioBehavior, Cancellation
 		Log.DetectedProxy(m_logger, Id);
 		try
 		{
-			var payload = SupportsQueryAttributes ? s_selectConnectionIdVersionWithAttributesPayload : s_selectConnectionIdVersionNoAttributesPayload;
+			var payload = Context.SupportsQueryAttributes ? s_selectConnectionIdVersionWithAttributesPayload : s_selectConnectionIdVersionNoAttributesPayload;
 			await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			// column count: 2
@@ -1642,7 +1641,7 @@ private async Task GetRealServerDetailsAsync(IOBehavior ioBehavior, Cancellation
 			_ = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 			_ = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 
-			if (!SupportsDeprecateEof)
+			if (!Context.SupportsDeprecateEof)
 			{
 				payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 				_ = EofPayload.Create(payload.Span);
@@ -1662,15 +1661,15 @@ static void ReadRow(ReadOnlySpan<byte> span, out int? connectionId, out ServerVe
 
 			// OK/EOF payload
 			payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
-			if (OkPayload.IsOk(payload.Span, SupportsDeprecateEof))
-				OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
+			if (OkPayload.IsOk(payload.Span, Context))
+				OkPayload.Verify(payload.Span, Context);
 			else
 				EofPayload.Create(payload.Span);
 
 			if (connectionId is int newConnectionId && serverVersion is not null)
 			{
-				Log.ChangingConnectionId(m_logger, Id, ConnectionId, newConnectionId, ServerVersion.OriginalString, serverVersion.OriginalString);
-				ConnectionId = newConnectionId;
+				Log.ChangingConnectionId(m_logger, Id, Context.ConnectionId, newConnectionId, ServerVersion.OriginalString, serverVersion.OriginalString);
+				Context.ConnectionId = newConnectionId;
 				ServerVersion = serverVersion;
 			}
 		}
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index e6c2c641d..9da955d32 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -24,7 +24,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 			Log.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 
 			writer.Write((byte) CommandKind.Query);
-			var supportsQueryAttributes = command.Connection!.Session.SupportsQueryAttributes;
+			var supportsQueryAttributes = command.Connection!.Session.Context.SupportsQueryAttributes;
 			if (supportsQueryAttributes)
 			{
 				// attribute count
@@ -83,7 +83,7 @@ private static void WritePreparedStatement(IMySqlCommand command, PreparedStatem
 		Log.PreparingCommandPayloadWithId(command.Logger, command.Connection!.Session.Id, preparedStatement.StatementId, command.CommandText!);
 
 		var attributes = command.RawAttributes;
-		var supportsQueryAttributes = command.Connection!.Session.SupportsQueryAttributes;
+		var supportsQueryAttributes = command.Connection!.Session.Context.SupportsQueryAttributes;
 		writer.Write(preparedStatement.StatementId);
 
 		// NOTE: documentation is not updated yet, but due to bugs in MySQL Server 8.0.23-8.0.25, the PARAMETER_COUNT_AVAILABLE (0x08)
diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index 7cb3bd432..24421abd1 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -47,7 +47,7 @@ internal static partial class Log
 	public static partial void AutoDetectedAurora57(ILogger logger, string sessionId, string hostName);
 
 	[LoggerMessage(EventIds.SessionMadeConnection, LogLevel.Debug, "Session {SessionId} made connection; server version {ServerVersion}; connection ID {ConnectionId}; supports: compression {SupportsCompression}, attributes {SupportsAttributes}, deprecate EOF {SupportsDeprecateEof}, cached metadata {SupportsCachedMetadata}, SSL {SupportsSsl}, session track {SupportsSessionTrack}, pipelining {SupportsPipelining}, query attributes {SupportsQueryAttributes}")]
-	public static partial void SessionMadeConnection(ILogger logger, string sessionId, string serverVersion, int connectionId, bool supportsCompression, bool supportsAttributes, bool supportsDeprecateEof, bool supportsCachedMetadata, bool supportsSsl, bool supportsSessionTrack, bool supportsPipelining, bool supportsQueryAttributes);
+	public static partial void SessionMadeConnection(ILogger logger, string sessionId, string serverVersion, long connectionId, bool supportsCompression, bool supportsAttributes, bool supportsDeprecateEof, bool supportsCachedMetadata, bool supportsSsl, bool supportsSessionTrack, bool supportsPipelining, bool supportsQueryAttributes);
 
 	[LoggerMessage(EventIds.ServerDoesNotSupportSsl, LogLevel.Error, "Session {SessionId} requires SSL but server doesn't support it")]
 	public static partial void ServerDoesNotSupportSsl(ILogger logger, string sessionId);
@@ -184,7 +184,7 @@ internal static partial class Log
 	public static partial void DetectedProxy(ILogger logger, string sessionId);
 
 	[LoggerMessage(EventIds.ChangingConnectionId, LogLevel.Debug, "Session {SessionId} changing connection id from {OldConnectionId} to {ConnectionId} and server version from {OldServerVersion} to {ServerVersion}")]
-	public static partial void ChangingConnectionId(ILogger logger, string sessionId, int oldConnectionId, int connectionId, string oldServerVersion, string serverVersion);
+	public static partial void ChangingConnectionId(ILogger logger, string sessionId, long oldConnectionId, long connectionId, string oldServerVersion, string serverVersion);
 
 	[LoggerMessage(EventIds.FailedToGetConnectionId, LogLevel.Information, "Session {SessionId} failed to get CONNECTION_ID(), VERSION()")]
 	public static partial void FailedToGetConnectionId(ILogger logger, Exception exception, string sessionId);
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index e9b21042e..13b297d8c 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -152,7 +152,7 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
 		Log.StartingTransaction(m_transactionLogger, m_session!.Id);
 
 		// get the bytes for both payloads concatenated together (suitable for pipelining)
-		var startTransactionPayload = GetStartTransactionPayload(isolationLevel, isReadOnly, m_session.SupportsQueryAttributes);
+		var startTransactionPayload = GetStartTransactionPayload(isolationLevel, isReadOnly, m_session.Context.SupportsQueryAttributes);
 
 		if (GetInitializedConnectionSettings() is { UseCompression: false, Pipelining: not false })
 		{
@@ -161,10 +161,10 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
 
 			// read the two OK replies
 			var payload = await m_session.ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, m_session.Context);
 
 			payload = await m_session.ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, m_session.Context);
 		}
 		else
 		{
@@ -172,12 +172,12 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
 			await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(4, startTransactionPayload.Span[0])), ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, m_session.Context);
 
 			await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(8 + startTransactionPayload.Span[0], startTransactionPayload.Span[startTransactionPayload.Span[0] + 4])), ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
+			OkPayload.Verify(payload.Span, m_session.Context);
 		}
 
 		var transaction = new MySqlTransaction(this, isolationLevel, m_transactionLogger);
@@ -487,8 +487,10 @@ private async Task ChangeDatabaseAsync(IOBehavior ioBehavior, string databaseNam
 		using (var initDatabasePayload = InitDatabasePayload.Create(databaseName))
 			await m_session!.SendAsync(initDatabasePayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 		var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-		OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
-		m_session.DatabaseOverride = databaseName;
+		OkPayload.Verify(payload.Span, m_session.Context);
+
+		// for non session tracking servers
+		m_session.Context.Database = databaseName;
 	}
 
 	public new MySqlCommand CreateCommand() => (MySqlCommand) base.CreateCommand();
@@ -603,7 +605,7 @@ public async ValueTask ResetConnectionAsync(CancellationToken cancellationToken
 		Log.ResettingConnection(m_logger, session.Id);
 		await session.SendAsync(ResetConnectionPayload.Instance, AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
 		var payload = await session.ReceiveReplyAsync(AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
-		OkPayload.Verify(payload.Span, session.SupportsDeprecateEof, session.SupportsSessionTrack);
+		OkPayload.Verify(payload.Span, session.Context);
 	}
 
 	[AllowNull]
@@ -626,7 +628,7 @@ public override string ConnectionString
 		}
 	}
 
-	public override string Database => m_session?.DatabaseOverride ?? GetConnectionSettings().Database;
+	public override string Database => m_session?.Context.Database ?? GetConnectionSettings().Database;
 
 	public override ConnectionState State => m_connectionState;
 
@@ -637,7 +639,7 @@ public override string ConnectionString
 	/// <summary>
 	/// The connection ID from MySQL Server.
 	/// </summary>
-	public int ServerThread => Session.ConnectionId;
+	public int ServerThread => Session.Context.ConnectionId;
 
 	/// <summary>
 	/// Gets or sets the delegate used to provide client certificates for connecting to a server.
diff --git a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
index d5d4a1aa2..6b9e15093 100644
--- a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
@@ -1,4 +1,6 @@
+using System.Globalization;
 using System.Text;
+using MySqlConnector.Core;
 using MySqlConnector.Protocol.Serialization;
 using MySqlConnector.Utilities;
 
@@ -11,7 +13,6 @@ internal sealed class OkPayload
 	public ServerStatus ServerStatus { get; }
 	public int WarningCount { get; }
 	public string? StatusInfo { get; }
-	public string? NewSchema { get; }
 
 	public const byte Signature = 0x00;
 
@@ -20,56 +21,51 @@ internal sealed class OkPayload
 	 * https://mariadb.com/kb/en/the-mariadb-library/resultset/
 	 * https://github.com/MariaDB/mariadb-connector-j/blob/5fa814ac6e1b4c9cb6d141bd221cbd5fc45c8a78/src/main/java/org/mariadb/jdbc/internal/com/read/resultset/SelectResultSet.java#L443-L444
 	 */
-	public static bool IsOk(ReadOnlySpan<byte> span, bool deprecateEof) =>
+	public static bool IsOk(ReadOnlySpan<byte> span, Context context) =>
 		span.Length > 0 &&
 			(span.Length > 6 && span[0] == Signature ||
-			 deprecateEof && span.Length < 0xFF_FFFF && span[0] == EofPayload.Signature);
+			 context.SupportsDeprecateEof && span.Length < 0xFF_FFFF && span[0] == EofPayload.Signature);
 
 	/// <summary>
 	/// Creates an <see cref="OkPayload"/> from the given <paramref name="span"/>, or throws <see cref="FormatException"/>
 	/// if the bytes do not represent a valid <see cref="OkPayload"/>.
 	/// </summary>
 	/// <param name="span">The bytes from which to read an OK packet.</param>
-	/// <param name="deprecateEof">Whether the <see cref="ProtocolCapabilities.DeprecateEof"/> flag was set on the connection.</param>
-	/// <param name="clientSessionTrack">Whether <see cref="ProtocolCapabilities.SessionTrack"/> flag was set on the connection.</param>
+	/// <param name="context">Current connection variables context</param>
 	/// <returns>A <see cref="OkPayload"/> with the contents of the OK packet.</returns>
 	/// <exception cref="FormatException">Thrown when the bytes are not a valid OK packet.</exception>
-	public static OkPayload Create(ReadOnlySpan<byte> span, bool deprecateEof, bool clientSessionTrack) =>
-		Read(span, deprecateEof, clientSessionTrack, true)!;
+	public static OkPayload Create(ReadOnlySpan<byte> span, Context context) =>
+		Read(span, context, true)!;
 
 	/// <summary>
 	/// Verifies that the bytes in the given <paramref name="span"/> form a valid <see cref="OkPayload"/>, or throws
 	/// <see cref="FormatException"/> if they do not.
 	/// </summary>
 	/// <param name="span">The bytes from which to read an OK packet.</param>
-	/// <param name="deprecateEof">Whether the <see cref="ProtocolCapabilities.DeprecateEof"/> flag was set on the connection.</param>
-	/// <param name="clientSessionTrack">Whether <see cref="ProtocolCapabilities.SessionTrack"/> flag was set on the connection.</param>
+	/// <param name="context">Current connection variables context</param>
 	/// <exception cref="FormatException">Thrown when the bytes are not a valid OK packet.</exception>
-	public static void Verify(ReadOnlySpan<byte> span, bool deprecateEof, bool clientSessionTrack) =>
-		Read(span, deprecateEof, clientSessionTrack, createPayload: false);
+	public static void Verify(ReadOnlySpan<byte> span, Context context) =>
+		Read(span, context, createPayload: false);
 
-	private static OkPayload? Read(ReadOnlySpan<byte> span, bool deprecateEof, bool clientSessionTrack, bool createPayload)
+	private static OkPayload? Read(ReadOnlySpan<byte> span, Context context, bool createPayload)
 	{
 		var reader = new ByteArrayReader(span);
 		var signature = reader.ReadByte();
-		if (signature != Signature && (!deprecateEof || signature != EofPayload.Signature))
+		if (signature != Signature && (!context.SupportsDeprecateEof || signature != EofPayload.Signature))
 			throw new FormatException($"Expected to read 0x00 or 0xFE but got 0x{signature:X2}");
 		var affectedRowCount = reader.ReadLengthEncodedInteger();
 		var lastInsertId = reader.ReadLengthEncodedInteger();
 		var serverStatus = (ServerStatus) reader.ReadUInt16();
 		var warningCount = (int) reader.ReadUInt16();
-		string? newSchema = null;
 		ReadOnlySpan<byte> statusBytes;
 
-		if (clientSessionTrack)
+		if (context.SupportsSessionTrack)
 		{
 			if (reader.BytesRemaining > 0)
 			{
 				statusBytes = reader.ReadLengthEncodedByteString(); // human-readable info
-
-				if ((serverStatus & ServerStatus.SessionStateChanged) == ServerStatus.SessionStateChanged && reader.BytesRemaining > 0)
+				while (reader.BytesRemaining > 0)
 				{
-					// implies ProtocolCapabilities.SessionTrack
 					var sessionStateChangeDataLength = checked((int) reader.ReadLengthEncodedInteger());
 					var endOffset = reader.Offset + sessionStateChangeDataLength;
 					while (reader.Offset < endOffset)
@@ -79,7 +75,28 @@ public static void Verify(ReadOnlySpan<byte> span, bool deprecateEof, bool clien
 						switch (kind)
 						{
 							case SessionTrackKind.Schema:
-								newSchema = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
+								context.Database = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
+								break;
+
+							case SessionTrackKind.SystemVariables:
+								var systemVariableOffset = reader.Offset + dataLength;
+								do
+								{
+									var variableSv = Encoding.ASCII.GetString(reader.ReadLengthEncodedByteString());
+									var lenSv = reader.ReadLengthEncodedIntegerOrNull();
+									var valueSv = lenSv == -1
+										? null
+										: Encoding.ASCII.GetString(reader.ReadByteString(lenSv));
+									switch (variableSv)
+									{
+										case "character_set_client":
+											context.ClientCharset = valueSv;
+											break;
+										case "connection_id":
+											context.ConnectionId = Convert.ToInt32(valueSv, CultureInfo.InvariantCulture);
+											break;
+									}
+								} while (reader.Offset < systemVariableOffset);
 								break;
 
 							default:
@@ -109,7 +126,7 @@ public static void Verify(ReadOnlySpan<byte> span, bool deprecateEof, bool clien
 		{
 			var statusInfo = statusBytes.Length == 0 ? null : Encoding.UTF8.GetString(statusBytes);
 
-			if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && statusInfo is null && newSchema is null)
+			if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && statusInfo is null)
 			{
 				if (serverStatus == ServerStatus.AutoCommit)
 					return s_autoCommitOk;
@@ -117,7 +134,7 @@ public static void Verify(ReadOnlySpan<byte> span, bool deprecateEof, bool clien
 					return s_autoCommitSessionStateChangedOk;
 			}
 
-			return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, statusInfo, newSchema);
+			return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, statusInfo);
 		}
 		else
 		{
@@ -125,16 +142,15 @@ public static void Verify(ReadOnlySpan<byte> span, bool deprecateEof, bool clien
 		}
 	}
 
-	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, string? statusInfo, string? newSchema)
+	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, string? statusInfo)
 	{
 		AffectedRowCount = affectedRowCount;
 		LastInsertId = lastInsertId;
 		ServerStatus = serverStatus;
 		WarningCount = warningCount;
 		StatusInfo = statusInfo;
-		NewSchema = newSchema;
 	}
 
-	private static readonly OkPayload s_autoCommitOk = new(0, 0, ServerStatus.AutoCommit, 0, null, null);
-	private static readonly OkPayload s_autoCommitSessionStateChangedOk = new(0, 0, ServerStatus.AutoCommit | ServerStatus.SessionStateChanged, 0, null, null);
+	private static readonly OkPayload s_autoCommitOk = new(0, 0, ServerStatus.AutoCommit, 0, null);
+	private static readonly OkPayload s_autoCommitSessionStateChangedOk = new(0, 0, ServerStatus.AutoCommit | ServerStatus.SessionStateChanged, 0, null);
 }

From 77ad1910007923da2a6f12244eaf01eb30c77dcd Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 16 Jul 2024 10:18:08 -0700
Subject: [PATCH 304/393] Make 'Context' immutable.

Move mutable properties back to ServerSession.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ConnectionPool.cs     |  2 +-
 src/MySqlConnector/Core/Context.cs            | 12 +----
 src/MySqlConnector/Core/ResultSet.cs          |  2 +
 src/MySqlConnector/Core/ServerSession.cs      | 44 +++++++++++--------
 src/MySqlConnector/MySqlConnection.cs         |  6 +--
 .../Protocol/Payloads/OkPayload.cs            | 25 +++++++----
 6 files changed, 50 insertions(+), 41 deletions(-)

diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 0fb5f2e4e..17227b631 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -65,7 +65,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 				}
 				else
 				{
-					if (ConnectionSettings.ConnectionReset || !session.Context.IsInitialDatabase())
+					if (ConnectionSettings.ConnectionReset || session.DatabaseOverride is not null)
 					{
 						if (timeoutMilliseconds != 0)
 							session.SetTimeout(Math.Max(1, timeoutMilliseconds - Utility.GetElapsedMilliseconds(startingTimestamp)));
diff --git a/src/MySqlConnector/Core/Context.cs b/src/MySqlConnector/Core/Context.cs
index 74448d4f5..c83452d08 100644
--- a/src/MySqlConnector/Core/Context.cs
+++ b/src/MySqlConnector/Core/Context.cs
@@ -4,26 +4,16 @@ namespace MySqlConnector.Core;
 
 internal sealed class Context
 {
-	public Context(ProtocolCapabilities protocolCapabilities, string? database, int connectionId)
+	public Context(ProtocolCapabilities protocolCapabilities)
 	{
 		SupportsDeprecateEof = (protocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
 		SupportsCachedPreparedMetadata = (protocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
 		SupportsQueryAttributes = (protocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
 		SupportsSessionTrack = (protocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
-		ConnectionId = connectionId;
-		Database = database;
-		m_initialDatabase = database;
 	}
 
 	public bool SupportsDeprecateEof { get; }
 	public bool SupportsQueryAttributes { get; }
 	public bool SupportsSessionTrack { get; }
 	public bool SupportsCachedPreparedMetadata { get; }
-	public string? ClientCharset { get; set; }
-
-	public string? Database { get; set; }
-	private readonly string? m_initialDatabase;
-	public bool IsInitialDatabase() => string.Equals(m_initialDatabase, Database, StringComparison.Ordinal);
-
-	public int ConnectionId { get; set; }
 }
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 8d40ee6fb..5853482f3 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -48,6 +48,8 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 					if (ok.LastInsertId != 0)
 						Command?.SetLastInsertedId((long) ok.LastInsertId);
 					WarningCount = ok.WarningCount;
+					if (ok.NewSchema is not null)
+						Connection.Session.DatabaseOverride = ok.NewSchema;
 					m_columnDefinitions = default;
 					State = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0
 						? ResultSetState.NoMoreData
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 70dcc3d28..38d18acfb 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -44,7 +44,7 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 		m_activityTags = [];
 		DataReader = new();
 		Log.CreatedNewSession(m_logger, Id);
-		Context = new Context(0, null, 0);
+		Context = new Context(default);
 	}
 
 	public string Id { get; }
@@ -52,12 +52,14 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public bool SupportsPerQueryVariables => ServerVersion.IsMariaDb && ServerVersion.Version >= ServerVersions.MariaDbSupportsPerQueryVariables;
 	public int ActiveCommandId { get; private set; }
 	public int CancellationTimeout { get; private set; }
+	public int ConnectionId { get; set; }
 	public byte[]? AuthPluginData { get; set; }
 	public long CreatedTimestamp { get; }
 	public ConnectionPool? Pool { get; }
 	public int PoolGeneration { get; }
 	public long LastLeasedTimestamp { get; set; }
 	public long LastReturnedTimestamp { get; private set; }
+	public string? DatabaseOverride { get; set; }
 
 	public string HostName { get; private set; }
 	public IPEndPoint? IPEndPoint => m_tcpClient?.Client.RemoteEndPoint as IPEndPoint;
@@ -338,8 +340,8 @@ public void FinishQuerying()
 		var activity = ActivitySourceHelper.StartActivity(name, m_activityTags);
 		if (activity is { IsAllDataRequested: true })
 		{
-			if (!Context.IsInitialDatabase())
-				activity.SetTag(ActivitySourceHelper.DatabaseNameTagName, Context.Database);
+			if (DatabaseOverride is not null)
+				activity.SetTag(ActivitySourceHelper.DatabaseNameTagName, DatabaseOverride);
 			if (tagName1 is not null)
 				activity.SetTag(tagName1, tagValue1);
 		}
@@ -452,7 +454,8 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			}
 
 			ServerVersion = new(initialHandshake.ServerVersion);
-			Context = new Context(initialHandshake.ProtocolCapabilities, cs.Database, initialHandshake.ConnectionId);
+			ConnectionId = initialHandshake.ConnectionId;
+			Context = new Context(initialHandshake.ProtocolCapabilities);
 			AuthPluginData = initialHandshake.AuthPluginData;
 			m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0;
 			CancellationTimeout = cs.CancellationTimeout;
@@ -460,7 +463,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 
 			// set activity tags
 			{
-				var connectionId = Context.ConnectionId.ToString(CultureInfo.InvariantCulture);
+				var connectionId = ConnectionId.ToString(CultureInfo.InvariantCulture);
 				m_activityTags[ActivitySourceHelper.DatabaseConnectionIdTagName] = connectionId;
 				if (activity is { IsAllDataRequested: true })
 					activity.SetTag(ActivitySourceHelper.DatabaseConnectionIdTagName, connectionId);
@@ -499,7 +502,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				}
 			}
 
-			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, Context.ConnectionId, m_useCompression, m_supportsConnectionAttributes, Context.SupportsDeprecateEof, Context.SupportsCachedPreparedMetadata, serverSupportsSsl, Context.SupportsSessionTrack, m_supportsPipelining, Context.SupportsQueryAttributes);
+			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, Context.SupportsDeprecateEof, Context.SupportsCachedPreparedMetadata, serverSupportsSsl, Context.SupportsSessionTrack, m_supportsPipelining, Context.SupportsQueryAttributes);
 
 			if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
 			{
@@ -532,18 +535,23 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			if (m_useCompression)
 				m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
 
-			// set 'collation_connection' to the server default
-			if (Context.ClientCharset == null || ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4
-				    ? !string.Equals(Context.ClientCharset, "utf8mb4", StringComparison.Ordinal)
-				    : !string.Equals(Context.ClientCharset, "utf8", StringComparison.Ordinal))
+			if (ok.ClientCharacterSet != (ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? "utf8mb4" : "utf8"))
 			{
+				// set 'collation_connection' to the server default
 				await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 				OkPayload.Verify(payload.Span, Context);
 			}
 
 			if (ShouldGetRealServerDetails(cs))
+			{
 				await GetRealServerDetailsAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
+			}
+			else if (ok.ConnectionId is int newConnectionId && newConnectionId != ConnectionId)
+			{
+				Log.ChangingConnectionId(m_logger, Id, ConnectionId, newConnectionId, ServerVersion.OriginalString, ServerVersion.OriginalString);
+				ConnectionId = newConnectionId;
+			}
 
 			m_payloadHandler.ByteHandler.RemainingTimeout = Constants.InfiniteTimeout;
 			return statusInfo;
@@ -570,9 +578,9 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 			ClearPreparedStatements();
 
 			PayloadData payload;
-			if (Context.IsInitialDatabase() &&
-			    ((!ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) ||
-			     (ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.MariaDbSupportsResetConnection) >= 0)))
+			if (DatabaseOverride is null &&
+				((!ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.SupportsResetConnection) >= 0) ||
+				(ServerVersion.IsMariaDb && ServerVersion.Version.CompareTo(ServerVersions.MariaDbSupportsResetConnection) >= 0)))
 			{
 				if (m_supportsPipelining)
 				{
@@ -599,14 +607,14 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 			else
 			{
 				// optimistically hash the password with the challenge from the initial handshake (supported by MariaDB; doesn't appear to be supported by MySQL)
-				if (Context.IsInitialDatabase())
+				if (DatabaseOverride is null)
 				{
 					Log.SendingChangeUserRequest(m_logger, Id, ServerVersion.OriginalString);
 				}
 				else
 				{
-					Log.SendingChangeUserRequestDueToChangedDatabase(m_logger, Id, Context.Database!);
-					Context.Database = cs.Database;
+					Log.SendingChangeUserRequestDueToChangedDatabase(m_logger, Id, DatabaseOverride);
+					DatabaseOverride = null;
 				}
 				var password = GetPassword(cs, connection);
 				var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData!, password);
@@ -1668,8 +1676,8 @@ static void ReadRow(ReadOnlySpan<byte> span, out int? connectionId, out ServerVe
 
 			if (connectionId is int newConnectionId && serverVersion is not null)
 			{
-				Log.ChangingConnectionId(m_logger, Id, Context.ConnectionId, newConnectionId, ServerVersion.OriginalString, serverVersion.OriginalString);
-				Context.ConnectionId = newConnectionId;
+				Log.ChangingConnectionId(m_logger, Id, ConnectionId, newConnectionId, ServerVersion.OriginalString, serverVersion.OriginalString);
+				ConnectionId = newConnectionId;
 				ServerVersion = serverVersion;
 			}
 		}
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 13b297d8c..e63f1269f 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -490,7 +490,7 @@ private async Task ChangeDatabaseAsync(IOBehavior ioBehavior, string databaseNam
 		OkPayload.Verify(payload.Span, m_session.Context);
 
 		// for non session tracking servers
-		m_session.Context.Database = databaseName;
+		m_session.DatabaseOverride = databaseName;
 	}
 
 	public new MySqlCommand CreateCommand() => (MySqlCommand) base.CreateCommand();
@@ -628,7 +628,7 @@ public override string ConnectionString
 		}
 	}
 
-	public override string Database => m_session?.Context.Database ?? GetConnectionSettings().Database;
+	public override string Database => m_session?.DatabaseOverride ?? GetConnectionSettings().Database;
 
 	public override ConnectionState State => m_connectionState;
 
@@ -639,7 +639,7 @@ public override string ConnectionString
 	/// <summary>
 	/// The connection ID from MySQL Server.
 	/// </summary>
-	public int ServerThread => Session.Context.ConnectionId;
+	public int ServerThread => Session.ConnectionId;
 
 	/// <summary>
 	/// Gets or sets the delegate used to provide client certificates for connecting to a server.
diff --git a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
index 6b9e15093..4c31bba76 100644
--- a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
@@ -13,6 +13,9 @@ internal sealed class OkPayload
 	public ServerStatus ServerStatus { get; }
 	public int WarningCount { get; }
 	public string? StatusInfo { get; }
+	public string? NewSchema { get; }
+	public string? ClientCharacterSet { get; }
+	public int? ConnectionId { get; }
 
 	public const byte Signature = 0x00;
 
@@ -57,6 +60,9 @@ public static void Verify(ReadOnlySpan<byte> span, Context context) =>
 		var lastInsertId = reader.ReadLengthEncodedInteger();
 		var serverStatus = (ServerStatus) reader.ReadUInt16();
 		var warningCount = (int) reader.ReadUInt16();
+		string? newSchema = null;
+		string? clientCharacterSet = null;
+		int? connectionId = null;
 		ReadOnlySpan<byte> statusBytes;
 
 		if (context.SupportsSessionTrack)
@@ -75,7 +81,7 @@ public static void Verify(ReadOnlySpan<byte> span, Context context) =>
 						switch (kind)
 						{
 							case SessionTrackKind.Schema:
-								context.Database = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
+								newSchema = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
 								break;
 
 							case SessionTrackKind.SystemVariables:
@@ -90,10 +96,10 @@ public static void Verify(ReadOnlySpan<byte> span, Context context) =>
 									switch (variableSv)
 									{
 										case "character_set_client":
-											context.ClientCharset = valueSv;
+											clientCharacterSet = valueSv;
 											break;
 										case "connection_id":
-											context.ConnectionId = Convert.ToInt32(valueSv, CultureInfo.InvariantCulture);
+											connectionId = Convert.ToInt32(valueSv, CultureInfo.InvariantCulture);
 											break;
 									}
 								} while (reader.Offset < systemVariableOffset);
@@ -126,7 +132,7 @@ public static void Verify(ReadOnlySpan<byte> span, Context context) =>
 		{
 			var statusInfo = statusBytes.Length == 0 ? null : Encoding.UTF8.GetString(statusBytes);
 
-			if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && statusInfo is null)
+			if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && statusInfo is null && newSchema is null && clientCharacterSet is null && connectionId is null)
 			{
 				if (serverStatus == ServerStatus.AutoCommit)
 					return s_autoCommitOk;
@@ -134,7 +140,7 @@ public static void Verify(ReadOnlySpan<byte> span, Context context) =>
 					return s_autoCommitSessionStateChangedOk;
 			}
 
-			return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, statusInfo);
+			return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, statusInfo, newSchema, clientCharacterSet, connectionId);
 		}
 		else
 		{
@@ -142,15 +148,18 @@ public static void Verify(ReadOnlySpan<byte> span, Context context) =>
 		}
 	}
 
-	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, string? statusInfo)
+	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, string? statusInfo, string? newSchema, string? clientCharacterSet, int? connectionId)
 	{
 		AffectedRowCount = affectedRowCount;
 		LastInsertId = lastInsertId;
 		ServerStatus = serverStatus;
 		WarningCount = warningCount;
 		StatusInfo = statusInfo;
+		NewSchema = newSchema;
+		ClientCharacterSet = clientCharacterSet;
+		ConnectionId = connectionId;
 	}
 
-	private static readonly OkPayload s_autoCommitOk = new(0, 0, ServerStatus.AutoCommit, 0, null);
-	private static readonly OkPayload s_autoCommitSessionStateChangedOk = new(0, 0, ServerStatus.AutoCommit | ServerStatus.SessionStateChanged, 0, null);
+	private static readonly OkPayload s_autoCommitOk = new(0, 0, ServerStatus.AutoCommit, 0, default, default, default, default);
+	private static readonly OkPayload s_autoCommitSessionStateChangedOk = new(0, 0, ServerStatus.AutoCommit | ServerStatus.SessionStateChanged, 0, default, default, default, default);
 }

From b19f1ee1eed604983c87e42c6232813fbb5ecc76 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 16 Jul 2024 10:21:19 -0700
Subject: [PATCH 305/393] Revert change of connection ID from int to long.

This is still tracked as an Int32 internally so there seems to be no need to log it as an Int64.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Logging/Log.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index 24421abd1..7cb3bd432 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -47,7 +47,7 @@ internal static partial class Log
 	public static partial void AutoDetectedAurora57(ILogger logger, string sessionId, string hostName);
 
 	[LoggerMessage(EventIds.SessionMadeConnection, LogLevel.Debug, "Session {SessionId} made connection; server version {ServerVersion}; connection ID {ConnectionId}; supports: compression {SupportsCompression}, attributes {SupportsAttributes}, deprecate EOF {SupportsDeprecateEof}, cached metadata {SupportsCachedMetadata}, SSL {SupportsSsl}, session track {SupportsSessionTrack}, pipelining {SupportsPipelining}, query attributes {SupportsQueryAttributes}")]
-	public static partial void SessionMadeConnection(ILogger logger, string sessionId, string serverVersion, long connectionId, bool supportsCompression, bool supportsAttributes, bool supportsDeprecateEof, bool supportsCachedMetadata, bool supportsSsl, bool supportsSessionTrack, bool supportsPipelining, bool supportsQueryAttributes);
+	public static partial void SessionMadeConnection(ILogger logger, string sessionId, string serverVersion, int connectionId, bool supportsCompression, bool supportsAttributes, bool supportsDeprecateEof, bool supportsCachedMetadata, bool supportsSsl, bool supportsSessionTrack, bool supportsPipelining, bool supportsQueryAttributes);
 
 	[LoggerMessage(EventIds.ServerDoesNotSupportSsl, LogLevel.Error, "Session {SessionId} requires SSL but server doesn't support it")]
 	public static partial void ServerDoesNotSupportSsl(ILogger logger, string sessionId);
@@ -184,7 +184,7 @@ internal static partial class Log
 	public static partial void DetectedProxy(ILogger logger, string sessionId);
 
 	[LoggerMessage(EventIds.ChangingConnectionId, LogLevel.Debug, "Session {SessionId} changing connection id from {OldConnectionId} to {ConnectionId} and server version from {OldServerVersion} to {ServerVersion}")]
-	public static partial void ChangingConnectionId(ILogger logger, string sessionId, long oldConnectionId, long connectionId, string oldServerVersion, string serverVersion);
+	public static partial void ChangingConnectionId(ILogger logger, string sessionId, int oldConnectionId, int connectionId, string oldServerVersion, string serverVersion);
 
 	[LoggerMessage(EventIds.FailedToGetConnectionId, LogLevel.Information, "Session {SessionId} failed to get CONNECTION_ID(), VERSION()")]
 	public static partial void FailedToGetConnectionId(ILogger logger, Exception exception, string sessionId);

From 8466783f5647063092fc54dc47a43e2a3e19e16a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 16 Jul 2024 10:30:50 -0700
Subject: [PATCH 306/393] Replace Context class with IServerCapabilities
 interface.

This avoids an unnecessary small allocation per ServerSession.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Core/ConcatenatedCommandPayloadCreator.cs |  2 +-
 src/MySqlConnector/Core/Context.cs            | 19 -------
 .../Core/IServerCapabilities.cs               |  7 +++
 src/MySqlConnector/Core/ResultSet.cs          | 14 ++---
 src/MySqlConnector/Core/ServerSession.cs      | 55 ++++++++++---------
 .../Core/SingleCommandPayloadCreator.cs       |  4 +-
 src/MySqlConnector/MySqlConnection.cs         | 14 ++---
 .../Protocol/Payloads/OkPayload.cs            | 22 ++++----
 8 files changed, 64 insertions(+), 73 deletions(-)
 delete mode 100644 src/MySqlConnector/Core/Context.cs
 create mode 100644 src/MySqlConnector/Core/IServerCapabilities.cs

diff --git a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
index df92e081a..20e0717f6 100644
--- a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
@@ -17,7 +17,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 
 		// ConcatenatedCommandPayloadCreator is only used by MySqlBatch, and MySqlBatchCommand doesn't expose attributes,
 		// so just write an empty attribute set if the server needs it.
-		if (commandListPosition.CommandAt(commandListPosition.CommandIndex).Connection!.Session.Context.SupportsQueryAttributes)
+		if (commandListPosition.CommandAt(commandListPosition.CommandIndex).Connection!.Session.SupportsQueryAttributes)
 		{
 			// attribute count
 			writer.WriteLengthEncodedInteger(0);
diff --git a/src/MySqlConnector/Core/Context.cs b/src/MySqlConnector/Core/Context.cs
deleted file mode 100644
index c83452d08..000000000
--- a/src/MySqlConnector/Core/Context.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-using MySqlConnector.Protocol;
-
-namespace MySqlConnector.Core;
-
-internal sealed class Context
-{
-	public Context(ProtocolCapabilities protocolCapabilities)
-	{
-		SupportsDeprecateEof = (protocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
-		SupportsCachedPreparedMetadata = (protocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
-		SupportsQueryAttributes = (protocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
-		SupportsSessionTrack = (protocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
-	}
-
-	public bool SupportsDeprecateEof { get; }
-	public bool SupportsQueryAttributes { get; }
-	public bool SupportsSessionTrack { get; }
-	public bool SupportsCachedPreparedMetadata { get; }
-}
diff --git a/src/MySqlConnector/Core/IServerCapabilities.cs b/src/MySqlConnector/Core/IServerCapabilities.cs
new file mode 100644
index 000000000..233e63cd0
--- /dev/null
+++ b/src/MySqlConnector/Core/IServerCapabilities.cs
@@ -0,0 +1,7 @@
+namespace MySqlConnector.Core;
+
+internal interface IServerCapabilities
+{
+	bool SupportsDeprecateEof { get; }
+	bool SupportsSessionTrack { get; }
+}
diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 5853482f3..2b93879ca 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -38,7 +38,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 				var firstByte = payload.HeaderByte;
 				if (firstByte == OkPayload.Signature)
 				{
-					var ok = OkPayload.Create(payload.Span, Session.Context);
+					var ok = OkPayload.Create(payload.Span, Session);
 
 					// if we've read a result set header then this is a SELECT statement, so we shouldn't overwrite RecordsAffected
 					// (which should be -1 for SELECT) unless the server reports a non-zero count
@@ -109,7 +109,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 				}
 				else
 				{
-					var columnCountPacket = ColumnCountPayload.Create(payload.Span, Session.Context.SupportsCachedPreparedMetadata);
+					var columnCountPacket = ColumnCountPayload.Create(payload.Span, Session.SupportsCachedPreparedMetadata);
 					var columnCount = columnCountPacket.ColumnCount;
 					if (!columnCountPacket.MetadataFollows)
 					{
@@ -132,7 +132,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 						m_columnDefinitions = m_columnDefinitionPayloadCache.AsMemory(0, columnCount);
 
 						// if the server supports metadata caching but has re-sent it, something has changed since last prepare/execution and we need to update the columns
-						var preparedColumns = Session.Context.SupportsCachedPreparedMetadata ? DataReader.LastUsedPreparedStatement?.Columns : null;
+						var preparedColumns = Session.SupportsCachedPreparedMetadata ? DataReader.LastUsedPreparedStatement?.Columns : null;
 
 						for (var column = 0; column < columnCount; column++)
 						{
@@ -156,7 +156,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 						}
 					}
 
-					if (!Session.Context.SupportsDeprecateEof)
+					if (!Session.SupportsDeprecateEof)
 					{
 						payload = await Session.ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 						_ = EofPayload.Create(payload.Span);
@@ -252,13 +252,13 @@ public async Task<bool> ReadAsync(IOBehavior ioBehavior, CancellationToken cance
 
 		if (payload.HeaderByte == EofPayload.Signature)
 		{
-			if (Session.Context.SupportsDeprecateEof && OkPayload.IsOk(payload.Span, Session.Context))
+			if (Session.SupportsDeprecateEof && OkPayload.IsOk(payload.Span, Session))
 			{
-				var ok = OkPayload.Create(payload.Span, Session.Context);
+				var ok = OkPayload.Create(payload.Span, Session);
 				BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
 				return null;
 			}
-			if (!Session.Context.SupportsDeprecateEof && EofPayload.IsEof(payload))
+			if (!Session.SupportsDeprecateEof && EofPayload.IsEof(payload))
 			{
 				var eof = EofPayload.Create(payload.Span);
 				BufferState = (eof.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 38d18acfb..e05b2d6cf 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -23,7 +23,7 @@ namespace MySqlConnector.Core;
 
 #pragma warning disable CA1001 // Types that own disposable fields should be disposable
 
-internal sealed partial class ServerSession
+internal sealed partial class ServerSession : IServerCapabilities
 {
 	public ServerSession(ILogger logger)
 		: this(logger, null, 0, Interlocked.Increment(ref s_lastId))
@@ -44,7 +44,6 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 		m_activityTags = [];
 		DataReader = new();
 		Log.CreatedNewSession(m_logger, Id);
-		Context = new Context(default);
 	}
 
 	public string Id { get; }
@@ -60,13 +59,14 @@ public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, i
 	public long LastLeasedTimestamp { get; set; }
 	public long LastReturnedTimestamp { get; private set; }
 	public string? DatabaseOverride { get; set; }
-
 	public string HostName { get; private set; }
 	public IPEndPoint? IPEndPoint => m_tcpClient?.Client.RemoteEndPoint as IPEndPoint;
 	public string? UserID { get; private set; }
 	public WeakReference<MySqlConnection>? OwningConnection { get; set; }
-	public Context Context { get; private set; }
-
+	public bool SupportsDeprecateEof { get; private set; }
+	public bool SupportsCachedPreparedMetadata { get; private set; }
+	public bool SupportsQueryAttributes { get; private set; }
+	public bool SupportsSessionTrack { get; private set; }
 	public bool ProcAccessDenied { get; set; }
 	public ICollection<KeyValuePair<string, object?>> ActivityTags => m_activityTags;
 	public MySqlDataReader DataReader { get; set; }
@@ -241,7 +241,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
 					ColumnDefinitionPayload.Initialize(ref parameters[i], new(columnsAndParameters, columnsAndParametersSize, payloadLength));
 					columnsAndParametersSize += payloadLength;
 				}
-				if (!Context.SupportsDeprecateEof)
+				if (!SupportsDeprecateEof)
 				{
 					payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 					EofPayload.Create(payload.Span);
@@ -261,7 +261,7 @@ public async Task PrepareAsync(IMySqlCommand command, IOBehavior ioBehavior, Can
 					ColumnDefinitionPayload.Initialize(ref columns[i], new(columnsAndParameters, columnsAndParametersSize, payloadLength));
 					columnsAndParametersSize += payloadLength;
 				}
-				if (!Context.SupportsDeprecateEof)
+				if (!SupportsDeprecateEof)
 				{
 					payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 					EofPayload.Create(payload.Span);
@@ -315,12 +315,12 @@ public void FinishQuerying()
 			// In order to handle this case, we issue a dummy query that will consume the pending cancellation.
 			// See https://bugs.mysql.com/bug.php?id=45679
 			Log.SendingSleepToClearPendingCancellation(m_logger, Id);
-			var payload = Context.SupportsQueryAttributes ? s_sleepWithAttributesPayload : s_sleepNoAttributesPayload;
+			var payload = SupportsQueryAttributes ? s_sleepWithAttributesPayload : s_sleepNoAttributesPayload;
 #pragma warning disable CA2012 // Safe because method completes synchronously
 			SendAsync(payload, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 			payload = ReceiveReplyAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 #pragma warning restore CA2012
-			OkPayload.Verify(payload.Span, Context);
+			OkPayload.Verify(payload.Span, this);
 		}
 
 		lock (m_lock)
@@ -455,7 +455,6 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 
 			ServerVersion = new(initialHandshake.ServerVersion);
 			ConnectionId = initialHandshake.ConnectionId;
-			Context = new Context(initialHandshake.ProtocolCapabilities);
 			AuthPluginData = initialHandshake.AuthPluginData;
 			m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0;
 			CancellationTimeout = cs.CancellationTimeout;
@@ -470,11 +469,15 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			}
 
 			m_supportsConnectionAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ConnectionAttributes) != 0;
+			SupportsDeprecateEof = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.DeprecateEof) != 0;
+			SupportsCachedPreparedMetadata = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) != 0;
+			SupportsQueryAttributes = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.QueryAttributes) != 0;
+			SupportsSessionTrack = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SessionTrack) != 0;
 			var serverSupportsSsl = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Ssl) != 0;
 			m_characterSet = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4GeneralCaseInsensitive : CharacterSet.Utf8Mb3GeneralCaseInsensitive;
 			m_setNamesPayload = ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ?
-				(Context.SupportsQueryAttributes ? s_setNamesUtf8mb4WithAttributesPayload : s_setNamesUtf8mb4NoAttributesPayload) :
-				(Context.SupportsQueryAttributes ? s_setNamesUtf8WithAttributesPayload : s_setNamesUtf8NoAttributesPayload);
+				(SupportsQueryAttributes ? s_setNamesUtf8mb4WithAttributesPayload : s_setNamesUtf8mb4NoAttributesPayload) :
+				(SupportsQueryAttributes ? s_setNamesUtf8WithAttributesPayload : s_setNamesUtf8NoAttributesPayload);
 
 			// disable pipelining for RDS MySQL 5.7 (assuming Aurora); otherwise take it from the connection string or default to true
 			if (!cs.Pipelining.HasValue && ServerVersion.Version.Major == 5 && ServerVersion.Version.Minor == 7 && HostName.EndsWith(".rds.amazonaws.com", StringComparison.OrdinalIgnoreCase))
@@ -502,7 +505,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				}
 			}
 
-			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, Context.SupportsDeprecateEof, Context.SupportsCachedPreparedMetadata, serverSupportsSsl, Context.SupportsSessionTrack, m_supportsPipelining, Context.SupportsQueryAttributes);
+			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, SupportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, SupportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
 
 			if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
 			{
@@ -529,7 +532,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			}
 
-			var ok = OkPayload.Create(payload.Span, Context);
+			var ok = OkPayload.Create(payload.Span, this);
 			var statusInfo = ok.StatusInfo;
 
 			if (m_useCompression)
@@ -540,7 +543,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				// set 'collation_connection' to the server default
 				await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-				OkPayload.Verify(payload.Span, Context);
+				OkPayload.Verify(payload.Span, this);
 			}
 
 			if (ShouldGetRealServerDetails(cs))
@@ -591,10 +594,10 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 
 					// read two OK replies
 					payload = await ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
-					OkPayload.Verify(payload.Span, Context);
+					OkPayload.Verify(payload.Span, this);
 
 					payload = await ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
-					OkPayload.Verify(payload.Span, Context);
+					OkPayload.Verify(payload.Span, this);
 
 					return true;
 				}
@@ -602,7 +605,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 				Log.SendingResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
 				await SendAsync(ResetConnectionPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-				OkPayload.Verify(payload.Span, Context);
+				OkPayload.Verify(payload.Span, this);
 			}
 			else
 			{
@@ -626,13 +629,13 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 					Log.OptimisticReauthenticationFailed(m_logger, Id);
 					payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				}
-				OkPayload.Verify(payload.Span, Context);
+				OkPayload.Verify(payload.Span, this);
 			}
 
 			// set 'collation_connection' to the server default
 			await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, Context);
+			OkPayload.Verify(payload.Span, this);
 
 			return true;
 		}
@@ -691,7 +694,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 				// OK payload can be sent immediately (e.g., if password is empty) bypassing even the fast authentication path
-				if (OkPayload.IsOk(payload.Span, Context))
+				if (OkPayload.IsOk(payload.Span, this))
 					return payload;
 
 				var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload.Span);
@@ -831,7 +834,7 @@ public async ValueTask<bool> TryPingAsync(bool logInfo, IOBehavior ioBehavior, C
 			Log.PingingServer(m_logger, Id);
 			await SendAsync(PingPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
 			var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, Context);
+			OkPayload.Verify(payload.Span, this);
 			Log.SuccessfullyPingedServer(m_logger, logInfo ? LogLevel.Information : LogLevel.Trace, Id);
 			return true;
 		}
@@ -1639,7 +1642,7 @@ private async Task GetRealServerDetailsAsync(IOBehavior ioBehavior, Cancellation
 		Log.DetectedProxy(m_logger, Id);
 		try
 		{
-			var payload = Context.SupportsQueryAttributes ? s_selectConnectionIdVersionWithAttributesPayload : s_selectConnectionIdVersionNoAttributesPayload;
+			var payload = SupportsQueryAttributes ? s_selectConnectionIdVersionWithAttributesPayload : s_selectConnectionIdVersionNoAttributesPayload;
 			await SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			// column count: 2
@@ -1649,7 +1652,7 @@ private async Task GetRealServerDetailsAsync(IOBehavior ioBehavior, Cancellation
 			_ = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 			_ = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 
-			if (!Context.SupportsDeprecateEof)
+			if (!SupportsDeprecateEof)
 			{
 				payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 				_ = EofPayload.Create(payload.Span);
@@ -1669,8 +1672,8 @@ static void ReadRow(ReadOnlySpan<byte> span, out int? connectionId, out ServerVe
 
 			// OK/EOF payload
 			payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
-			if (OkPayload.IsOk(payload.Span, Context))
-				OkPayload.Verify(payload.Span, Context);
+			if (OkPayload.IsOk(payload.Span, this))
+				OkPayload.Verify(payload.Span, this);
 			else
 				EofPayload.Create(payload.Span);
 
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index 9da955d32..e6c2c641d 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -24,7 +24,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 			Log.PreparingCommandPayload(command.Logger, command.Connection!.Session.Id, command.CommandText!);
 
 			writer.Write((byte) CommandKind.Query);
-			var supportsQueryAttributes = command.Connection!.Session.Context.SupportsQueryAttributes;
+			var supportsQueryAttributes = command.Connection!.Session.SupportsQueryAttributes;
 			if (supportsQueryAttributes)
 			{
 				// attribute count
@@ -83,7 +83,7 @@ private static void WritePreparedStatement(IMySqlCommand command, PreparedStatem
 		Log.PreparingCommandPayloadWithId(command.Logger, command.Connection!.Session.Id, preparedStatement.StatementId, command.CommandText!);
 
 		var attributes = command.RawAttributes;
-		var supportsQueryAttributes = command.Connection!.Session.Context.SupportsQueryAttributes;
+		var supportsQueryAttributes = command.Connection!.Session.SupportsQueryAttributes;
 		writer.Write(preparedStatement.StatementId);
 
 		// NOTE: documentation is not updated yet, but due to bugs in MySQL Server 8.0.23-8.0.25, the PARAMETER_COUNT_AVAILABLE (0x08)
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index e63f1269f..7a835d202 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -152,7 +152,7 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
 		Log.StartingTransaction(m_transactionLogger, m_session!.Id);
 
 		// get the bytes for both payloads concatenated together (suitable for pipelining)
-		var startTransactionPayload = GetStartTransactionPayload(isolationLevel, isReadOnly, m_session.Context.SupportsQueryAttributes);
+		var startTransactionPayload = GetStartTransactionPayload(isolationLevel, isReadOnly, m_session.SupportsQueryAttributes);
 
 		if (GetInitializedConnectionSettings() is { UseCompression: false, Pipelining: not false })
 		{
@@ -161,10 +161,10 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
 
 			// read the two OK replies
 			var payload = await m_session.ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, m_session.Context);
+			OkPayload.Verify(payload.Span, m_session);
 
 			payload = await m_session.ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, m_session.Context);
+			OkPayload.Verify(payload.Span, m_session);
 		}
 		else
 		{
@@ -172,12 +172,12 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
 			await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(4, startTransactionPayload.Span[0])), ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, m_session.Context);
+			OkPayload.Verify(payload.Span, m_session);
 
 			await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(8 + startTransactionPayload.Span[0], startTransactionPayload.Span[startTransactionPayload.Span[0] + 4])), ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-			OkPayload.Verify(payload.Span, m_session.Context);
+			OkPayload.Verify(payload.Span, m_session);
 		}
 
 		var transaction = new MySqlTransaction(this, isolationLevel, m_transactionLogger);
@@ -487,7 +487,7 @@ private async Task ChangeDatabaseAsync(IOBehavior ioBehavior, string databaseNam
 		using (var initDatabasePayload = InitDatabasePayload.Create(databaseName))
 			await m_session!.SendAsync(initDatabasePayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 		var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-		OkPayload.Verify(payload.Span, m_session.Context);
+		OkPayload.Verify(payload.Span, m_session);
 
 		// for non session tracking servers
 		m_session.DatabaseOverride = databaseName;
@@ -605,7 +605,7 @@ public async ValueTask ResetConnectionAsync(CancellationToken cancellationToken
 		Log.ResettingConnection(m_logger, session.Id);
 		await session.SendAsync(ResetConnectionPayload.Instance, AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
 		var payload = await session.ReceiveReplyAsync(AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
-		OkPayload.Verify(payload.Span, session.Context);
+		OkPayload.Verify(payload.Span, session);
 	}
 
 	[AllowNull]
diff --git a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
index 4c31bba76..8abc850cc 100644
--- a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
@@ -24,37 +24,37 @@ internal sealed class OkPayload
 	 * https://mariadb.com/kb/en/the-mariadb-library/resultset/
 	 * https://github.com/MariaDB/mariadb-connector-j/blob/5fa814ac6e1b4c9cb6d141bd221cbd5fc45c8a78/src/main/java/org/mariadb/jdbc/internal/com/read/resultset/SelectResultSet.java#L443-L444
 	 */
-	public static bool IsOk(ReadOnlySpan<byte> span, Context context) =>
+	public static bool IsOk(ReadOnlySpan<byte> span, IServerCapabilities serverCapabilities) =>
 		span.Length > 0 &&
 			(span.Length > 6 && span[0] == Signature ||
-			 context.SupportsDeprecateEof && span.Length < 0xFF_FFFF && span[0] == EofPayload.Signature);
+			 serverCapabilities.SupportsDeprecateEof && span.Length < 0xFF_FFFF && span[0] == EofPayload.Signature);
 
 	/// <summary>
 	/// Creates an <see cref="OkPayload"/> from the given <paramref name="span"/>, or throws <see cref="FormatException"/>
 	/// if the bytes do not represent a valid <see cref="OkPayload"/>.
 	/// </summary>
 	/// <param name="span">The bytes from which to read an OK packet.</param>
-	/// <param name="context">Current connection variables context</param>
+	/// <param name="serverCapabilities">The server capabilities.</param>
 	/// <returns>A <see cref="OkPayload"/> with the contents of the OK packet.</returns>
 	/// <exception cref="FormatException">Thrown when the bytes are not a valid OK packet.</exception>
-	public static OkPayload Create(ReadOnlySpan<byte> span, Context context) =>
-		Read(span, context, true)!;
+	public static OkPayload Create(ReadOnlySpan<byte> span, IServerCapabilities serverCapabilities) =>
+		Read(span, serverCapabilities, true)!;
 
 	/// <summary>
 	/// Verifies that the bytes in the given <paramref name="span"/> form a valid <see cref="OkPayload"/>, or throws
 	/// <see cref="FormatException"/> if they do not.
 	/// </summary>
 	/// <param name="span">The bytes from which to read an OK packet.</param>
-	/// <param name="context">Current connection variables context</param>
+	/// <param name="serverCapabilities">The server capabilities.</param>
 	/// <exception cref="FormatException">Thrown when the bytes are not a valid OK packet.</exception>
-	public static void Verify(ReadOnlySpan<byte> span, Context context) =>
-		Read(span, context, createPayload: false);
+	public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCapabilities) =>
+		Read(span, serverCapabilities, createPayload: false);
 
-	private static OkPayload? Read(ReadOnlySpan<byte> span, Context context, bool createPayload)
+	private static OkPayload? Read(ReadOnlySpan<byte> span, IServerCapabilities serverCapabilities, bool createPayload)
 	{
 		var reader = new ByteArrayReader(span);
 		var signature = reader.ReadByte();
-		if (signature != Signature && (!context.SupportsDeprecateEof || signature != EofPayload.Signature))
+		if (signature != Signature && (!serverCapabilities.SupportsDeprecateEof || signature != EofPayload.Signature))
 			throw new FormatException($"Expected to read 0x00 or 0xFE but got 0x{signature:X2}");
 		var affectedRowCount = reader.ReadLengthEncodedInteger();
 		var lastInsertId = reader.ReadLengthEncodedInteger();
@@ -65,7 +65,7 @@ public static void Verify(ReadOnlySpan<byte> span, Context context) =>
 		int? connectionId = null;
 		ReadOnlySpan<byte> statusBytes;
 
-		if (context.SupportsSessionTrack)
+		if (serverCapabilities.SupportsSessionTrack)
 		{
 			if (reader.BytesRemaining > 0)
 			{

From 3326a0ec06a8b32776271a7c4de103e8088b422a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 16 Jul 2024 19:23:42 -0700
Subject: [PATCH 307/393] Reduce string allocations when parsing variable
 names.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Protocol/Payloads/OkPayload.cs            | 27 +++++++++----------
 1 file changed, 12 insertions(+), 15 deletions(-)

diff --git a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
index 8abc850cc..95e77959c 100644
--- a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
@@ -1,4 +1,4 @@
-using System.Globalization;
+using System.Buffers.Text;
 using System.Text;
 using MySqlConnector.Core;
 using MySqlConnector.Protocol.Serialization;
@@ -85,24 +85,21 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 								break;
 
 							case SessionTrackKind.SystemVariables:
-								var systemVariableOffset = reader.Offset + dataLength;
+								var systemVariablesEndOffset = reader.Offset + dataLength;
 								do
 								{
-									var variableSv = Encoding.ASCII.GetString(reader.ReadLengthEncodedByteString());
-									var lenSv = reader.ReadLengthEncodedIntegerOrNull();
-									var valueSv = lenSv == -1
-										? null
-										: Encoding.ASCII.GetString(reader.ReadByteString(lenSv));
-									switch (variableSv)
+									var systemVariableName = reader.ReadLengthEncodedByteString();
+									var systemVariableValueLength = reader.ReadLengthEncodedIntegerOrNull();
+									var systemVariableValue = systemVariableValueLength == -1 ? default : reader.ReadByteString(systemVariableValueLength);
+									if (systemVariableName.SequenceEqual("character_set_client"u8) && systemVariableValueLength != 0)
 									{
-										case "character_set_client":
-											clientCharacterSet = valueSv;
-											break;
-										case "connection_id":
-											connectionId = Convert.ToInt32(valueSv, CultureInfo.InvariantCulture);
-											break;
+										clientCharacterSet = Encoding.ASCII.GetString(systemVariableValue);
 									}
-								} while (reader.Offset < systemVariableOffset);
+									else if (systemVariableName.SequenceEqual("connection_id"u8))
+									{
+										connectionId = Utf8Parser.TryParse(systemVariableValue, out int parsedConnectionId, out var bytesConsumed) && bytesConsumed == systemVariableValue.Length ? parsedConnectionId : default(int?);
+									}
+								} while (reader.Offset < systemVariablesEndOffset);
 								break;
 
 							default:

From 3f50c14f734b23025d98f0d090416cec2a1940cf Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 16 Jul 2024 20:49:24 -0700
Subject: [PATCH 308/393] Ensure all server variables specify the right
 character set.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ServerSession.cs      |  5 ++-
 .../Protocol/Payloads/OkPayload.cs            | 39 ++++++++++++++-----
 2 files changed, 33 insertions(+), 11 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index e05b2d6cf..dcaf1e69f 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -538,7 +538,8 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			if (m_useCompression)
 				m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
 
-			if (ok.ClientCharacterSet != (ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? "utf8mb4" : "utf8"))
+			// send 'SET NAMES' to set the character set and collation unless the server reports that it's already using the desired character set (e.g., MariaDB >= 11.5)
+			if (ok.NewCharacterSet != (ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4Binary : CharacterSet.Utf8Mb3Binary))
 			{
 				// set 'collation_connection' to the server default
 				await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
@@ -550,7 +551,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			{
 				await GetRealServerDetailsAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
 			}
-			else if (ok.ConnectionId is int newConnectionId && newConnectionId != ConnectionId)
+			else if (ok.NewConnectionId is int newConnectionId && newConnectionId != ConnectionId)
 			{
 				Log.ChangingConnectionId(m_logger, Id, ConnectionId, newConnectionId, ServerVersion.OriginalString, ServerVersion.OriginalString);
 				ConnectionId = newConnectionId;
diff --git a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
index 95e77959c..50be1db79 100644
--- a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
@@ -14,8 +14,8 @@ internal sealed class OkPayload
 	public int WarningCount { get; }
 	public string? StatusInfo { get; }
 	public string? NewSchema { get; }
-	public string? ClientCharacterSet { get; }
-	public int? ConnectionId { get; }
+	public CharacterSet? NewCharacterSet { get; }
+	public int? NewConnectionId { get; }
 
 	public const byte Signature = 0x00;
 
@@ -61,7 +61,9 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 		var serverStatus = (ServerStatus) reader.ReadUInt16();
 		var warningCount = (int) reader.ReadUInt16();
 		string? newSchema = null;
-		string? clientCharacterSet = null;
+		CharacterSet clientCharacterSet = default;
+		CharacterSet connectionCharacterSet = default;
+		CharacterSet resultsCharacterSet = default;
 		int? connectionId = null;
 		ReadOnlySpan<byte> statusBytes;
 
@@ -93,7 +95,21 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 									var systemVariableValue = systemVariableValueLength == -1 ? default : reader.ReadByteString(systemVariableValueLength);
 									if (systemVariableName.SequenceEqual("character_set_client"u8) && systemVariableValueLength != 0)
 									{
-										clientCharacterSet = Encoding.ASCII.GetString(systemVariableValue);
+										clientCharacterSet = systemVariableValue.SequenceEqual("utf8mb4"u8) ? CharacterSet.Utf8Mb4Binary :
+											systemVariableValue.SequenceEqual("utf8"u8) ? CharacterSet.Utf8Mb3Binary :
+											CharacterSet.None;
+									}
+									else if (systemVariableName.SequenceEqual("character_set_connection"u8) && systemVariableValueLength != 0)
+									{
+										connectionCharacterSet = systemVariableValue.SequenceEqual("utf8mb4"u8) ? CharacterSet.Utf8Mb4Binary :
+											systemVariableValue.SequenceEqual("utf8"u8) ? CharacterSet.Utf8Mb3Binary :
+											CharacterSet.None;
+									}
+									else if (systemVariableName.SequenceEqual("character_set_results"u8) && systemVariableValueLength != 0)
+									{
+										resultsCharacterSet = systemVariableValue.SequenceEqual("utf8mb4"u8) ? CharacterSet.Utf8Mb4Binary :
+											systemVariableValue.SequenceEqual("utf8"u8) ? CharacterSet.Utf8Mb3Binary :
+											CharacterSet.None;
 									}
 									else if (systemVariableName.SequenceEqual("connection_id"u8))
 									{
@@ -129,7 +145,12 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 		{
 			var statusInfo = statusBytes.Length == 0 ? null : Encoding.UTF8.GetString(statusBytes);
 
-			if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && statusInfo is null && newSchema is null && clientCharacterSet is null && connectionId is null)
+			// detect the connection character set as utf8mb4 (or utf8) if all three system variables are set to the same value
+			var characterSet = clientCharacterSet == CharacterSet.Utf8Mb4Binary && connectionCharacterSet == CharacterSet.Utf8Mb4Binary && resultsCharacterSet == CharacterSet.Utf8Mb4Binary ? CharacterSet.Utf8Mb4Binary :
+				clientCharacterSet == CharacterSet.Utf8Mb3Binary && connectionCharacterSet == CharacterSet.Utf8Mb3Binary && resultsCharacterSet == CharacterSet.Utf8Mb3Binary ? CharacterSet.Utf8Mb3Binary :
+				CharacterSet.None;
+
+			if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && statusInfo is null && newSchema is null && clientCharacterSet is CharacterSet.None && connectionId is null)
 			{
 				if (serverStatus == ServerStatus.AutoCommit)
 					return s_autoCommitOk;
@@ -137,7 +158,7 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 					return s_autoCommitSessionStateChangedOk;
 			}
 
-			return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, statusInfo, newSchema, clientCharacterSet, connectionId);
+			return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, statusInfo, newSchema, characterSet, connectionId);
 		}
 		else
 		{
@@ -145,7 +166,7 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 		}
 	}
 
-	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, string? statusInfo, string? newSchema, string? clientCharacterSet, int? connectionId)
+	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, string? statusInfo, string? newSchema, CharacterSet newCharacterSet, int? connectionId)
 	{
 		AffectedRowCount = affectedRowCount;
 		LastInsertId = lastInsertId;
@@ -153,8 +174,8 @@ private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serve
 		WarningCount = warningCount;
 		StatusInfo = statusInfo;
 		NewSchema = newSchema;
-		ClientCharacterSet = clientCharacterSet;
-		ConnectionId = connectionId;
+		NewCharacterSet = newCharacterSet;
+		NewConnectionId = connectionId;
 	}
 
 	private static readonly OkPayload s_autoCommitOk = new(0, 0, ServerStatus.AutoCommit, 0, default, default, default, default);

From f5eebefe234087b20399584cb018d0705224ca3f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 17 Jul 2024 10:47:51 -0700
Subject: [PATCH 309/393] Don't parse the rest of the OK packet if we're not
 returning it.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Protocol/Payloads/OkPayload.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
index 50be1db79..a08a3195d 100644
--- a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
@@ -69,7 +69,7 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 
 		if (serverCapabilities.SupportsSessionTrack)
 		{
-			if (reader.BytesRemaining > 0)
+			if (reader.BytesRemaining > 0 && createPayload)
 			{
 				statusBytes = reader.ReadLengthEncodedByteString(); // human-readable info
 				while (reader.BytesRemaining > 0)

From cb2e8e75c2a04bc7918c671dd04f1e074e05b1eb Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 17 Jul 2024 20:26:08 -0700
Subject: [PATCH 310/393] Add `--pull always` option to Docker command line.

---
 tests/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/README.md b/tests/README.md
index c863a4a2b..bd5267639 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,7 @@ It also verifies that MySqlConnector and MySQL Connector/NET (MySql.Data) have s
 
 The tests require a MySQL server. The simplest way to run one is with [Docker](https://www.docker.com/community-edition):
 
-    docker run -d --rm --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.0 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
+    docker run -d --rm --pull always --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.0 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
     docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes'; CREATE USER 'caching-sha2-user'@'%' IDENTIFIED WITH caching_sha2_password BY 'Cach!ng-Sh@2-Pa55'; GRANT ALL PRIVILEGES ON *.* TO 'caching-sha2-user'@'%';"
 
 Copy the file `IntegrationTests/config.json.example` to `IntegrationTests/config.json`, then edit

From 6e45dcd141563ec3122ecd1289ca87993885e1ac Mon Sep 17 00:00:00 2001
From: diego dupin <diego.dupin@gmail.com>
Date: Sun, 28 Jul 2024 02:50:06 +0200
Subject: [PATCH 311/393] Rewrite server redirection logic. (#1499)

This is based on https://jira.mariadb.org/browse/MDEV-15935: first OK Packet can contain variable 'redirect_url' using "mariadb/mysql://[{user}[:{password}]@]{host}[:{port}]/[{db}[?{opt1}={value1}[&{opt2}={value2}]]]" format.

Introduce IConnectionPoolMetadata to unify the code between pooled and non-pooled sessions. Standardise all redirection logging to use the MySqlConnection logger, whether pooled or non-pooled.

Drop support for redirection format used by Azure Database for MySQL Single Server (which is now deprecated).

Signed-off-by: rusher <diego.dupin@gmail.com>
Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
Co-authored-by: Bradley Grainger <bgrainger@gmail.com>
---
 .ci/config/config.compression+ssl.json        |   2 +-
 .ci/config/config.compression.json            |   2 +-
 .ci/config/config.json                        |   2 +-
 .ci/config/config.ssl.json                    |   2 +-
 azure-pipelines.yml                           |  16 +-
 src/MySqlConnector/Core/ConnectionPool.cs     |  90 ++------
 src/MySqlConnector/Core/ConnectionSettings.cs |   8 +-
 .../Core/IConnectionPoolMetadata.cs           |  26 +++
 .../Core/NonPooledConnectionPoolMetadata.cs   |  13 ++
 src/MySqlConnector/Core/ServerSession.cs      |  89 ++++++--
 src/MySqlConnector/Logging/Log.cs             |  20 +-
 src/MySqlConnector/MySqlConnection.cs         |  21 +-
 .../Protocol/Payloads/OkPayload.cs            |  20 +-
 src/MySqlConnector/Utilities/Utility.cs       |  79 +++----
 tests/IntegrationTests/RedirectionTests.cs    | 194 ++++++++++++++++++
 tests/IntegrationTests/ServerFeatures.cs      |   5 +
 tests/MySqlConnector.Tests/UtilityTests.cs    |  46 ++---
 17 files changed, 418 insertions(+), 217 deletions(-)
 create mode 100644 src/MySqlConnector/Core/IConnectionPoolMetadata.cs
 create mode 100644 src/MySqlConnector/Core/NonPooledConnectionPoolMetadata.cs
 create mode 100644 tests/IntegrationTests/RedirectionTests.cs

diff --git a/.ci/config/config.compression+ssl.json b/.ci/config/config.compression+ssl.json
index 8a11c95bf..71cd817a0 100644
--- a/.ci/config/config.compression+ssl.json
+++ b/.ci/config/config.compression+ssl.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "RsaEncryption,CachingSha2Password,Tls12,Tls13,UuidToBin",
+      "UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,UuidToBin",
       "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
       "CertificatesPath": "../../../../.ci/server/certs"
diff --git a/.ci/config/config.compression.json b/.ci/config/config.compression.json
index f42f53ab6..bf1073a12 100644
--- a/.ci/config/config.compression.json
+++ b/.ci/config/config.compression.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket,ZeroDateTime",
+      "UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,UnixDomainSocket,ZeroDateTime",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
diff --git a/.ci/config/config.json b/.ci/config/config.json
index 183b2299c..035c05855 100644
--- a/.ci/config/config.json
+++ b/.ci/config/config.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket,ZeroDateTime",
+      "UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,UnixDomainSocket,ZeroDateTime",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
diff --git a/.ci/config/config.ssl.json b/.ci/config/config.ssl.json
index 84261b1be..705e0a168 100644
--- a/.ci/config/config.ssl.json
+++ b/.ci/config/config.ssl.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "RsaEncryption,CachingSha2Password,Tls12,Tls13,UuidToBin",
+      "UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,UuidToBin",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV",
       "CertificatesPath": "../../../../.ci/server/certs"
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index e14ef7d82..47aa77a39 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -136,7 +136,7 @@ jobs:
       arguments: '-c Release --no-restore'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net472/net8.0', 'No SSL') }}
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True'
 
 - job: windows_integration_tests_2
@@ -174,7 +174,7 @@ jobs:
       arguments: '-c Release --no-restore'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net6.0', 'No SSL') }}
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,Tls11,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
 
 - job: linux_integration_tests
@@ -187,27 +187,27 @@ jobs:
       'MySQL 8.0':
         image: 'mysql:8.0'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,ZeroDateTime'
       'MySQL 8.4':
         image: 'mysql:8.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,ZeroDateTime'
       'MySQL 9.0':
         image: 'mysql:9.0'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,StreamingResults,Tls11,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,ZeroDateTime'
       'MariaDB 10.6':
         image: 'mariadb:10.6'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin,Redirection'
       'MariaDB 10.11':
         image: 'mariadb:10.11'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin,Redirection'
       'MariaDB 11.4':
         image: 'mariadb:11.4'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin,Redirection'
   steps:
   - template: '.ci/integration-tests-steps.yml'
     parameters:
diff --git a/src/MySqlConnector/Core/ConnectionPool.cs b/src/MySqlConnector/Core/ConnectionPool.cs
index 17227b631..a11391cd5 100644
--- a/src/MySqlConnector/Core/ConnectionPool.cs
+++ b/src/MySqlConnector/Core/ConnectionPool.cs
@@ -8,10 +8,16 @@
 
 namespace MySqlConnector.Core;
 
-internal sealed class ConnectionPool : IDisposable
+internal sealed class ConnectionPool : IConnectionPoolMetadata, IDisposable
 {
 	public int Id { get; }
 
+	ConnectionPool? IConnectionPoolMetadata.ConnectionPool => this;
+
+	int IConnectionPoolMetadata.Generation => m_generation;
+
+	int IConnectionPoolMetadata.GetNewSessionId() => Interlocked.Increment(ref m_lastSessionId); 
+
 	public string? Name { get; }
 
 	public ConnectionSettings ConnectionSettings { get; }
@@ -95,6 +101,7 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 						m_leasedSessions.Add(session.Id, session);
 						leasedSessionsCountPooled = m_leasedSessions.Count;
 					}
+
 					MetricsReporter.AddUsed(this);
 					ActivitySourceHelper.CopyTags(session.ActivityTags, activity);
 					Log.ReturningPooledSession(m_logger, Id, session.Id, leasedSessionsCountPooled);
@@ -106,7 +113,8 @@ public async ValueTask<ServerSession> GetSessionAsync(MySqlConnection connection
 			}
 
 			// create a new session
-			session = await ConnectSessionAsync(connection, s_createdNewSession, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+			session = await ServerSession.ConnectAndRedirectAsync(m_connectionLogger, m_logger, this, ConnectionSettings, m_loadBalancer,
+				connection, s_createdNewSession, startingTimestamp, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
 			AdjustHostConnectionCount(session, 1);
 			session.OwningConnection = new(connection);
 			int leasedSessionsCountNew;
@@ -402,7 +410,8 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh
 
 			try
 			{
-				var session = await ConnectSessionAsync(connection, s_createdToReachMinimumPoolSize, Stopwatch.GetTimestamp(), null, ioBehavior, cancellationToken).ConfigureAwait(false);
+				var session = await ServerSession.ConnectAndRedirectAsync(m_connectionLogger, m_logger, this, ConnectionSettings, m_loadBalancer,
+					connection, s_createdToReachMinimumPoolSize, Stopwatch.GetTimestamp(), null, ioBehavior, cancellationToken).ConfigureAwait(false);
 				AdjustHostConnectionCount(session, 1);
 				lock (m_sessions)
 					_ = m_sessions.AddFirst(session);
@@ -416,81 +425,6 @@ private async Task CreateMinimumPooledSessions(MySqlConnection connection, IOBeh
 		}
 	}
 
-	private async ValueTask<ServerSession> ConnectSessionAsync(MySqlConnection connection, Action<ILogger, int, string, Exception?> logMessage, long startingTimestamp, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
-	{
-		var session = new ServerSession(m_connectionLogger, this, m_generation, Interlocked.Increment(ref m_lastSessionId));
-		if (m_logger.IsEnabled(LogLevel.Debug))
-			logMessage(m_logger, Id, session.Id, null);
-		string? statusInfo;
-		try
-		{
-			statusInfo = await session.ConnectAsync(ConnectionSettings, connection, startingTimestamp, m_loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
-		}
-		catch (Exception)
-		{
-			await session.DisposeAsync(ioBehavior, default).ConfigureAwait(false);
-			throw;
-		}
-
-		Exception? redirectionException = null;
-		if (statusInfo is not null && statusInfo.StartsWith("Location: mysql://", StringComparison.Ordinal))
-		{
-			// server redirection string has the format "Location: mysql://{host}:{port}/user={userId}[&ttl={ttl}]"
-			Log.HasServerRedirectionHeader(m_logger, session.Id, statusInfo);
-
-			if (ConnectionSettings.ServerRedirectionMode == MySqlServerRedirectionMode.Disabled)
-			{
-				Log.ServerRedirectionIsDisabled(m_logger, Id);
-			}
-			else if (Utility.TryParseRedirectionHeader(statusInfo, out var host, out var port, out var user))
-			{
-				if (host != ConnectionSettings.HostNames![0] || port != ConnectionSettings.Port || user != ConnectionSettings.UserID)
-				{
-					var redirectedSettings = ConnectionSettings.CloneWith(host, port, user);
-					Log.OpeningNewConnection(m_logger, Id, host, port, user);
-					var redirectedSession = new ServerSession(m_connectionLogger, this, m_generation, Interlocked.Increment(ref m_lastSessionId));
-					try
-					{
-						_ = await redirectedSession.ConnectAsync(redirectedSettings, connection, startingTimestamp, m_loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
-					}
-					catch (Exception ex)
-					{
-						Log.FailedToConnectRedirectedSession(m_logger, ex, Id, redirectedSession.Id);
-						redirectionException = ex;
-					}
-
-					if (redirectionException is null)
-					{
-						Log.ClosingSessionToUseRedirectedSession(m_logger, Id, session.Id, redirectedSession.Id);
-						await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-						return redirectedSession;
-					}
-					else
-					{
-						try
-						{
-							await redirectedSession.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
-						}
-						catch (Exception)
-						{
-						}
-					}
-				}
-				else
-				{
-					Log.SessionAlreadyConnectedToServer(m_logger, session.Id);
-				}
-			}
-		}
-
-		if (ConnectionSettings.ServerRedirectionMode == MySqlServerRedirectionMode.Required)
-		{
-			Log.RequiresServerRedirection(m_logger, Id);
-			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support redirection", redirectionException);
-		}
-		return session;
-	}
-
 	public static ConnectionPool? CreatePool(string connectionString, MySqlConnectorLoggingConfiguration loggingConfiguration, string? name)
 	{
 		// parse connection string and check for 'Pooling' setting; return 'null' if pooling is disabled
diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index c93e16004..8b365833c 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -270,8 +270,12 @@ public int ConnectionTimeoutMilliseconds
 
 	private ConnectionSettings(ConnectionSettings other, string host, int port, string userId)
 	{
-		ConnectionStringBuilder = other.ConnectionStringBuilder;
-		ConnectionString = other.ConnectionString;
+		ConnectionStringBuilder = new MySqlConnectionStringBuilder(other.ConnectionString);
+		ConnectionStringBuilder.Port = (uint)port;
+		ConnectionStringBuilder.Server = host;
+		ConnectionStringBuilder.UserID = userId;
+
+		ConnectionString = ConnectionStringBuilder.ConnectionString;
 
 		ConnectionProtocol = MySqlConnectionProtocol.Sockets;
 		HostNames = [host];
diff --git a/src/MySqlConnector/Core/IConnectionPoolMetadata.cs b/src/MySqlConnector/Core/IConnectionPoolMetadata.cs
new file mode 100644
index 000000000..64f8dd31b
--- /dev/null
+++ b/src/MySqlConnector/Core/IConnectionPoolMetadata.cs
@@ -0,0 +1,26 @@
+namespace MySqlConnector.Core;
+
+internal interface IConnectionPoolMetadata
+{
+	/// <summary>
+	/// Returns the <see cref="ConnectionPool"/> this <see cref="IConnectionPoolMetadata"/> is associated with,
+	/// or <c>null</c> if it represents a non-pooled connection.
+	/// </summary>
+	ConnectionPool? ConnectionPool { get; }
+
+	/// <summary>
+	/// Returns the ID of the connection pool, or 0 if this is a non-pooled connection.
+	/// </summary>
+	int Id { get; }
+
+	/// <summary>
+	/// Returns the generation of the connection pool, or 0 if this is a non-pooled connection.
+	/// </summary>
+	int Generation { get; }
+
+	/// <summary>
+	/// Returns a new session ID.
+	/// </summary>
+	/// <returns>A new session ID.</returns>
+	int GetNewSessionId();
+}
diff --git a/src/MySqlConnector/Core/NonPooledConnectionPoolMetadata.cs b/src/MySqlConnector/Core/NonPooledConnectionPoolMetadata.cs
new file mode 100644
index 000000000..20d296722
--- /dev/null
+++ b/src/MySqlConnector/Core/NonPooledConnectionPoolMetadata.cs
@@ -0,0 +1,13 @@
+namespace MySqlConnector.Core;
+
+internal sealed class NonPooledConnectionPoolMetadata : IConnectionPoolMetadata
+{
+	public static IConnectionPoolMetadata Instance { get; } = new NonPooledConnectionPoolMetadata();
+
+	public ConnectionPool? ConnectionPool => null;
+	public int Id => 0;
+	public int Generation => 0;
+	public int GetNewSessionId() => Interlocked.Increment(ref m_lastId);
+
+	private int m_lastId;
+}
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index dcaf1e69f..dcd64d009 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -25,21 +25,16 @@ namespace MySqlConnector.Core;
 
 internal sealed partial class ServerSession : IServerCapabilities
 {
-	public ServerSession(ILogger logger)
-		: this(logger, null, 0, Interlocked.Increment(ref s_lastId))
-	{
-	}
-
-	public ServerSession(ILogger logger, ConnectionPool? pool, int poolGeneration, int id)
+	public ServerSession(ILogger logger, IConnectionPoolMetadata pool)
 	{
 		m_logger = logger;
 		m_lock = new();
 		m_payloadCache = new();
-		Id = (pool?.Id ?? 0) + "." + id;
+		Id = pool.Id + "." + pool.GetNewSessionId();
 		ServerVersion = ServerVersion.Empty;
 		CreatedTimestamp = Stopwatch.GetTimestamp();
-		Pool = pool;
-		PoolGeneration = poolGeneration;
+		Pool = pool.ConnectionPool;
+		PoolGeneration = pool.Generation;
 		HostName = "";
 		m_activityTags = [];
 		DataReader = new();
@@ -391,7 +386,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			m_state = State.Closed;
 	}
 
-	public async Task<string?> ConnectAsync(ConnectionSettings cs, MySqlConnection connection, long startingTimestamp, ILoadBalancer? loadBalancer, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	private async Task<string?> ConnectAsync(ConnectionSettings cs, MySqlConnection connection, long startingTimestamp, ILoadBalancer? loadBalancer, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		try
 		{
@@ -533,7 +528,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			}
 
 			var ok = OkPayload.Create(payload.Span, this);
-			var statusInfo = ok.StatusInfo;
+			var redirectionUrl = ok.RedirectionUrl;
 
 			if (m_useCompression)
 				m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
@@ -558,7 +553,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			}
 
 			m_payloadHandler.ByteHandler.RemainingTimeout = Constants.InfiniteTimeout;
-			return statusInfo;
+			return redirectionUrl;
 		}
 		catch (ArgumentException ex)
 		{
@@ -572,6 +567,75 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 		}
 	}
 
+	public static async ValueTask<ServerSession> ConnectAndRedirectAsync(ILogger connectionLogger, ILogger poolLogger, IConnectionPoolMetadata pool, ConnectionSettings cs, ILoadBalancer? loadBalancer, MySqlConnection connection, Action<ILogger, int, string, Exception?>? logMessage, long startingTimestamp, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	{
+		var session = new ServerSession(connectionLogger, pool);
+		if (logMessage is not null && poolLogger.IsEnabled(LogLevel.Debug))
+			logMessage(poolLogger, pool.Id, session.Id, null);
+
+		string? redirectionUrl;
+		try
+		{
+			redirectionUrl = await session.ConnectAsync(cs, connection, startingTimestamp, loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+		}
+		catch (Exception)
+		{
+			await session.DisposeAsync(ioBehavior, default).ConfigureAwait(false);
+			throw;
+		}
+
+		Exception? redirectionException = null;
+		if (redirectionUrl is not null)
+		{
+			Log.HasServerRedirectionHeader(connectionLogger, session.Id, redirectionUrl);
+			if (cs.ServerRedirectionMode == MySqlServerRedirectionMode.Disabled)
+			{
+				Log.ServerRedirectionIsDisabled(connectionLogger, session.Id);
+				return session;
+			}
+
+			if (Utility.TryParseRedirectionHeader(redirectionUrl, cs.UserID, out var host, out var port, out var user))
+			{
+				if (host != cs.HostNames![0] || port != cs.Port || user != cs.UserID)
+				{
+					var redirectedSettings = cs.CloneWith(host, port, user);
+					Log.OpeningNewConnection(connectionLogger, session.Id, host, port, user);
+					var redirectedSession = new ServerSession(connectionLogger, pool);
+					try
+					{
+						await redirectedSession.ConnectAsync(redirectedSettings, connection, startingTimestamp, loadBalancer, activity, ioBehavior, cancellationToken).ConfigureAwait(false);
+						Log.ClosingSessionToUseRedirectedSession(connectionLogger, session.Id, redirectedSession.Id);
+						await session.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+						return redirectedSession;
+					}
+					catch (Exception ex)
+					{
+						redirectionException = ex;
+						Log.FailedToConnectRedirectedSession(connectionLogger, ex, session.Id, redirectedSession.Id);
+						try
+						{
+							await redirectedSession.DisposeAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+						}
+						catch (Exception)
+						{
+						}
+					}
+				}
+				else
+				{
+					Log.SessionAlreadyConnectedToServer(connectionLogger, session.Id);
+				}
+			}
+		}
+
+		if (cs.ServerRedirectionMode == MySqlServerRedirectionMode.Required)
+		{
+			Log.RequiresServerRedirection(connectionLogger, session.Id);
+			throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "Server does not support redirection", redirectionException);
+		}
+		return session;
+	}
+
 	public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConnection connection, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		VerifyState(State.Connected);
@@ -1922,7 +1986,6 @@ protected override void OnStatementBegin(int index)
 	private static readonly PayloadData s_sleepWithAttributesPayload = QueryPayload.Create(true, "SELECT SLEEP(0) INTO @\uE001MySqlConnector\uE001Sleep;"u8);
 	private static readonly PayloadData s_selectConnectionIdVersionNoAttributesPayload = QueryPayload.Create(false, "SELECT CONNECTION_ID(), VERSION();"u8);
 	private static readonly PayloadData s_selectConnectionIdVersionWithAttributesPayload = QueryPayload.Create(true, "SELECT CONNECTION_ID(), VERSION();"u8);
-	private static int s_lastId;
 
 	private readonly ILogger m_logger;
 	private readonly object m_lock;
diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index 7cb3bd432..57b10a088 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -405,23 +405,23 @@ internal static partial class Log
 	[LoggerMessage(EventIds.HasServerRedirectionHeader, LogLevel.Trace, "Session {SessionId} has server redirection header {Header}")]
 	public static partial void HasServerRedirectionHeader(ILogger logger, string sessionId, string header);
 
-	[LoggerMessage(EventIds.ServerRedirectionIsDisabled, LogLevel.Trace, "Pool {PoolId} server redirection is disabled; ignoring redirection")]
-	public static partial void ServerRedirectionIsDisabled(ILogger logger, int poolId);
+	[LoggerMessage(EventIds.ServerRedirectionIsDisabled, LogLevel.Trace, "Session {SessionId} server redirection is disabled; ignoring redirection")]
+	public static partial void ServerRedirectionIsDisabled(ILogger logger, string sessionId);
 
-	[LoggerMessage(EventIds.OpeningNewConnection, LogLevel.Debug, "Pool {PoolId} opening new connection to {Host}:{Port} as {User}")]
-	public static partial void OpeningNewConnection(ILogger logger, int poolId, string host, int port, string user);
+	[LoggerMessage(EventIds.OpeningNewConnection, LogLevel.Debug, "Session {SessionId} opening new connection to {Host}:{Port} as {User}")]
+	public static partial void OpeningNewConnection(ILogger logger, string sessionId, string host, int port, string user);
 
-	[LoggerMessage(EventIds.FailedToConnectRedirectedSession, LogLevel.Information, "Pool {PoolId} failed to connect redirected session {SessionId}")]
-	public static partial void FailedToConnectRedirectedSession(ILogger logger, Exception ex, int poolId, string sessionId);
+	[LoggerMessage(EventIds.FailedToConnectRedirectedSession, LogLevel.Information, "Session {SessionId} failed to connect redirected session {RedirectedSessionId}")]
+	public static partial void FailedToConnectRedirectedSession(ILogger logger, Exception ex, string sessionId, string redirectedSessionId);
 
-	[LoggerMessage(EventIds.ClosingSessionToUseRedirectedSession, LogLevel.Trace, "Pool {PoolId} closing session {SessionId} to use redirected session {RedirectedSessionId} instead")]
-	public static partial void ClosingSessionToUseRedirectedSession(ILogger logger, int poolId, string sessionId, string redirectedSessionId);
+	[LoggerMessage(EventIds.ClosingSessionToUseRedirectedSession, LogLevel.Trace, "Closing session {SessionId} to use redirected session {RedirectedSessionId} instead")]
+	public static partial void ClosingSessionToUseRedirectedSession(ILogger logger, string sessionId, string redirectedSessionId);
 
 	[LoggerMessage(EventIds.SessionAlreadyConnectedToServer, LogLevel.Trace, "Session {SessionId} is already connected to this server; ignoring redirection")]
 	public static partial void SessionAlreadyConnectedToServer(ILogger logger, string sessionId);
 
-	[LoggerMessage(EventIds.RequiresServerRedirection, LogLevel.Error, "Pool {PoolId} requires server redirection but server doesn't support it")]
-	public static partial void RequiresServerRedirection(ILogger logger, int poolId);
+	[LoggerMessage(EventIds.RequiresServerRedirection, LogLevel.Error, "Session {SessionId} requires server redirection but server doesn't support it")]
+	public static partial void RequiresServerRedirection(ILogger logger, string sessionId);
 
 	[LoggerMessage(EventIds.CreatedPoolWillNotBeUsed, LogLevel.Debug, "Pool {PoolId} was created but will not be used (due to race)")]
 	public static partial void CreatedPoolWillNotBeUsed(ILogger logger, int poolId);
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 7a835d202..571af0e51 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -3,6 +3,7 @@
 #if NET6_0_OR_GREATER
 using System.Globalization;
 #endif
+using System.Net;
 using System.Net.Security;
 using System.Net.Sockets;
 using System.Security.Authentication;
@@ -1062,22 +1063,10 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 				// only "fail over" and "random" load balancers supported without connection pooling
 				var loadBalancer = connectionSettings.LoadBalance == MySqlLoadBalance.Random && connectionSettings.HostNames!.Count > 1 ?
 					RandomLoadBalancer.Instance : FailOverLoadBalancer.Instance;
-
-				var session = new ServerSession(m_logger)
-				{
-					OwningConnection = new WeakReference<MySqlConnection>(this),
-				};
+				var session = await ServerSession.ConnectAndRedirectAsync(m_logger, m_logger, NonPooledConnectionPoolMetadata.Instance, connectionSettings, loadBalancer, this, null, startingTimestamp, null, actualIOBehavior, connectToken).ConfigureAwait(false);
+				session.OwningConnection = new WeakReference<MySqlConnection>(this);
 				Log.CreatedNonPooledSession(m_logger, session.Id);
-				try
-				{
-					_ = await session.ConnectAsync(connectionSettings, this, startingTimestamp, loadBalancer, activity, actualIOBehavior, connectToken).ConfigureAwait(false);
-					return session;
-				}
-				catch (Exception)
-				{
-					await session.DisposeAsync(actualIOBehavior, default).ConfigureAwait(false);
-					throw;
-				}
+				return session;
 			}
 		}
 		catch (OperationCanceledException) when (timeoutSource?.IsCancellationRequested is true)
@@ -1114,6 +1103,8 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 
 	internal SslProtocols SslProtocol => m_session!.SslProtocol;
 
+	internal IPEndPoint? SessionEndPoint => m_session!.IPEndPoint;
+
 	internal void SetState(ConnectionState newState)
 	{
 		if (m_connectionState != newState)
diff --git a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
index a08a3195d..37db8862f 100644
--- a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
@@ -16,6 +16,7 @@ internal sealed class OkPayload
 	public string? NewSchema { get; }
 	public CharacterSet? NewCharacterSet { get; }
 	public int? NewConnectionId { get; }
+	public string? RedirectionUrl { get; }
 
 	public const byte Signature = 0x00;
 
@@ -64,6 +65,7 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 		CharacterSet clientCharacterSet = default;
 		CharacterSet connectionCharacterSet = default;
 		CharacterSet resultsCharacterSet = default;
+		string? redirectionUrl = default;
 		int? connectionId = null;
 		ReadOnlySpan<byte> statusBytes;
 
@@ -115,6 +117,13 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 									{
 										connectionId = Utf8Parser.TryParse(systemVariableValue, out int parsedConnectionId, out var bytesConsumed) && bytesConsumed == systemVariableValue.Length ? parsedConnectionId : default(int?);
 									}
+									else if (systemVariableName.SequenceEqual("redirect_url"u8))
+									{
+										if (systemVariableValue.Length > 0)
+										{
+											redirectionUrl = Encoding.UTF8.GetString(systemVariableValue);
+										}
+									}
 								} while (reader.Offset < systemVariablesEndOffset);
 								break;
 
@@ -150,7 +159,7 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 				clientCharacterSet == CharacterSet.Utf8Mb3Binary && connectionCharacterSet == CharacterSet.Utf8Mb3Binary && resultsCharacterSet == CharacterSet.Utf8Mb3Binary ? CharacterSet.Utf8Mb3Binary :
 				CharacterSet.None;
 
-			if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && statusInfo is null && newSchema is null && clientCharacterSet is CharacterSet.None && connectionId is null)
+			if (affectedRowCount == 0 && lastInsertId == 0 && warningCount == 0 && statusInfo is null && newSchema is null && clientCharacterSet is CharacterSet.None && connectionId is null && redirectionUrl is null)
 			{
 				if (serverStatus == ServerStatus.AutoCommit)
 					return s_autoCommitOk;
@@ -158,7 +167,7 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 					return s_autoCommitSessionStateChangedOk;
 			}
 
-			return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, statusInfo, newSchema, characterSet, connectionId);
+			return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, statusInfo, newSchema, characterSet, connectionId, redirectionUrl);
 		}
 		else
 		{
@@ -166,7 +175,7 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 		}
 	}
 
-	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, string? statusInfo, string? newSchema, CharacterSet newCharacterSet, int? connectionId)
+	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, string? statusInfo, string? newSchema, CharacterSet newCharacterSet, int? connectionId, string? redirectionUrl)
 	{
 		AffectedRowCount = affectedRowCount;
 		LastInsertId = lastInsertId;
@@ -176,8 +185,9 @@ private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serve
 		NewSchema = newSchema;
 		NewCharacterSet = newCharacterSet;
 		NewConnectionId = connectionId;
+		RedirectionUrl = redirectionUrl;
 	}
 
-	private static readonly OkPayload s_autoCommitOk = new(0, 0, ServerStatus.AutoCommit, 0, default, default, default, default);
-	private static readonly OkPayload s_autoCommitSessionStateChangedOk = new(0, 0, ServerStatus.AutoCommit | ServerStatus.SessionStateChanged, 0, default, default, default, default);
+	private static readonly OkPayload s_autoCommitOk = new(0, 0, ServerStatus.AutoCommit, 0, default, default, default, default, default);
+	private static readonly OkPayload s_autoCommitSessionStateChangedOk = new(0, 0, ServerStatus.AutoCommit | ServerStatus.SessionStateChanged, 0, default, default, default, default, default);
 }
diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index d1d3ce02b..63f07c8c6 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -336,68 +336,45 @@ public static void Resize<T>([NotNull] ref ResizableArray<T>? resizableArray, in
 		resizableArray.DoResize(newLength);
 	}
 
-	public static bool TryParseRedirectionHeader(string header, out string host, out int port, out string user)
+	public static bool TryParseRedirectionHeader(string redirectUrl, string initialUser, out string host, out int port, out string user)
 	{
 		host = "";
 		port = 0;
 		user = "";
 
-		if (!header.StartsWith("Location: mysql://", StringComparison.Ordinal) || header.Length < 22)
+		// "mariadb/mysql://[{user}[:{password}]@]{host}[:{port}]/[{db}[?{opt1}={value1}[&{opt2}={value2}]]]']"
+		if (!redirectUrl.StartsWith("mysql://", StringComparison.Ordinal) && !redirectUrl.StartsWith("mariadb://", StringComparison.Ordinal))
 			return false;
 
-		bool isCommunityFormat;
-		int portIndex;
-		if (header[18] == '[')
-		{
-			// Community protocol:
-			// Location: mysql://[redirectedHostName]:redirectedPort/?user=redirectedUser&ttl=%d\n
-			isCommunityFormat = true;
-
-			var hostIndex = 19;
-			var closeSquareBracketIndex = header.IndexOf(']', hostIndex);
-			if (closeSquareBracketIndex == -1)
-				return false;
-
-			host = header[hostIndex..closeSquareBracketIndex];
-			if (header.Length <= closeSquareBracketIndex + 2)
-				return false;
-			if (header[closeSquareBracketIndex + 1] != ':')
-				return false;
-			portIndex = closeSquareBracketIndex + 2;
-		}
-		else
+		try
 		{
-			// Azure protocol:
-			// Location: mysql://redirectedHostName:redirectedPort/user=redirectedUser&ttl=%d (where ttl is optional)
-			isCommunityFormat = false;
-
-			var hostIndex = 18;
-			var colonIndex = header.IndexOf(':', hostIndex);
-			if (colonIndex == -1)
-				return false;
+			var uri = new Uri(redirectUrl);
+			host = uri.Host;
+			if (string.IsNullOrEmpty(host)) return false;
+			if (host.StartsWith('[') && host.EndsWith("]", StringComparison.InvariantCulture)) host = host.Substring(1, host.Length - 2);
+
+			port = uri.Port;
+			user = Uri.UnescapeDataString(uri.UserInfo.Split(':')[0]);
+			if (string.IsNullOrEmpty(user) && !string.IsNullOrEmpty(uri.Query))
+			{
+				// query format "?{opt1}={value1}[&{opt2}={value2}]"
+				var q = uri.Query.Substring(1);
+				foreach (var token in q.Split('&'))
+				{
+					if (token.StartsWith("user=", StringComparison.InvariantCulture))
+					{
+						user = Uri.UnescapeDataString(token.Substring(5));
+					}
+				}
+			}
 
-			host = header[hostIndex..colonIndex];
-			portIndex = colonIndex + 1;
+			if (string.IsNullOrEmpty(user)) user = initialUser;
+			return true;
 		}
-
-		var userIndex = header.IndexOf(isCommunityFormat ? "/?user=" : "/user=", StringComparison.Ordinal);
-		if (userIndex == -1)
-			return false;
-
-#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
-		if (!int.TryParse(header.AsSpan(portIndex, userIndex - portIndex), out port) || port <= 0)
-#else
-		if (!int.TryParse(header[portIndex..userIndex], out port) || port <= 0)
-#endif
+		catch (UriFormatException)
+		{
 			return false;
-
-		userIndex += isCommunityFormat ? 7 : 6;
-		var ampersandIndex = header.IndexOf('&', userIndex);
-		var newlineIndex = header.IndexOf('\n', userIndex);
-		var terminatorIndex = ampersandIndex == -1 ? (newlineIndex == -1 ? header.Length : newlineIndex) :
-			(newlineIndex == -1 ? ampersandIndex : Math.Min(ampersandIndex, newlineIndex));
-		user = header[userIndex..terminatorIndex];
-		return user.Length != 0;
+		}
 	}
 
 	public static TimeSpan ParseTimeSpan(ReadOnlySpan<byte> value)
diff --git a/tests/IntegrationTests/RedirectionTests.cs b/tests/IntegrationTests/RedirectionTests.cs
new file mode 100644
index 000000000..892f1eb7f
--- /dev/null
+++ b/tests/IntegrationTests/RedirectionTests.cs
@@ -0,0 +1,194 @@
+#if !MYSQL_DATA
+using System.Globalization;
+using System.Net;
+using System.Net.Sockets;
+
+namespace IntegrationTests;
+
+public class RedirectionTests : IClassFixture<DatabaseFixture>, IDisposable
+{
+	public RedirectionTests(DatabaseFixture database)
+	{
+		m_database = database;
+		m_database.Connection.Open();
+	}
+
+	public void Dispose()
+	{
+		m_database.Connection.Close();
+	}
+
+	[SkippableFact(ServerFeatures.Redirection)]
+	public void RedirectionTest()
+	{
+		StartProxy();
+
+		// wait for proxy to launch
+		Thread.Sleep(50);
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		var initialServer = csb.Server;
+		var initialPort = csb.Port;
+		m_database.Connection.Execute($"set @@global.redirect_url=\"mariadb://{initialServer}:{initialPort}\"");
+
+		try
+		{
+			// changing to proxy port
+			csb.Server = "localhost";
+			csb.Port = (uint)proxy.ListenPort;
+			csb.ServerRedirectionMode = MySqlServerRedirectionMode.Preferred;
+
+			// ensure that connection has been redirected
+			using (var db = new MySqlConnection(csb.ConnectionString))
+			{
+				db.Open();
+				using (var cmd = db.CreateCommand())
+				{
+					cmd.CommandText = "SELECT 1";
+					cmd.ExecuteNonQuery();
+				}
+
+				Assert.Equal((int) initialPort, db.SessionEndPoint!.Port);
+				db.Close();
+			}
+
+			// ensure that connection has been redirected with Required
+			csb.ServerRedirectionMode = MySqlServerRedirectionMode.Required;
+			using (var db = new MySqlConnection(csb.ConnectionString))
+			{
+				db.Open();
+				using (var cmd = db.CreateCommand())
+				{
+					cmd.CommandText = "SELECT 1";
+					cmd.ExecuteNonQuery();
+				}
+
+				Assert.Equal((int) initialPort, db.SessionEndPoint!.Port);
+				db.Close();
+			}
+
+			// ensure that redirection is not done
+			csb.ServerRedirectionMode = MySqlServerRedirectionMode.Disabled;
+			using (var db = new MySqlConnection(csb.ConnectionString))
+			{
+				db.Open();
+				using (var cmd = db.CreateCommand())
+				{
+					cmd.CommandText = "SELECT 1";
+					cmd.ExecuteNonQuery();
+				}
+
+				Assert.Equal(proxy.ListenPort, db.SessionEndPoint!.Port);
+				db.Close();
+			}
+
+		} finally{
+			m_database.Connection.Execute(
+				$"set @@global.redirect_url=\"\"");
+		}
+		MySqlConnection.ClearAllPools();
+		// ensure that when required, throwing error if no redirection
+		csb.ServerRedirectionMode = MySqlServerRedirectionMode.Required;
+		using (var db = new MySqlConnection(csb.ConnectionString))
+		{
+			try
+			{
+				db.Open();
+				Assert.Fail("must have thrown error");
+			}
+			catch (MySqlException ex)
+			{
+				Assert.Equal((int) MySqlErrorCode.UnableToConnectToHost, ex.Number);
+			}
+		}
+
+		StopProxy();
+	}
+
+    protected void StartProxy()
+    {
+	    var csb = AppConfig.CreateConnectionStringBuilder();
+	    proxy = new ServerConfiguration( csb.Server, (int)csb.Port );
+	    Thread serverThread = new Thread( ServerThread );
+	    serverThread.Start( proxy );
+    }
+
+    protected void StopProxy()
+    {
+	    proxy.RunServer = false;
+	    proxy.ServerSocket.Close();
+    }
+
+    private class ServerConfiguration {
+
+        public IPAddress RemoteAddress;
+        public int    RemotePort;
+        public int    ListenPort;
+        public Socket ServerSocket;
+        public ServerConfiguration(String remoteAddress, int remotePort) {
+			var ipHostEntry = Dns.GetHostEntry(remoteAddress);
+			RemoteAddress = ipHostEntry.AddressList[0];
+            RemotePort    = remotePort;
+            ListenPort    = 0;
+        }
+        public bool RunServer = true;
+    }
+
+    private static void ServerThread(Object configObj) {
+        ServerConfiguration config = (ServerConfiguration)configObj;
+        Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+
+        serverSocket.Bind( new IPEndPoint( IPAddress.Any, 0 ) );
+        serverSocket.Listen(1);
+        config.ListenPort = ((IPEndPoint) serverSocket.LocalEndPoint).Port;
+        config.ServerSocket = serverSocket;
+        while( config.RunServer ) {
+			try
+			{
+				Socket client = serverSocket.Accept();
+				Thread clientThread = new Thread(ClientThread);
+				clientThread.Start(new ClientContext() { Config = config, Client = client });
+			}
+			catch (SocketException) when (!config.RunServer)
+			{
+				return;
+			}
+        }
+    }
+
+    private class ClientContext {
+        public ServerConfiguration Config;
+        public Socket              Client;
+    }
+
+    private static void ClientThread(Object contextObj) {
+        ClientContext context = (ClientContext)contextObj;
+        Socket              client = context.Client;
+        ServerConfiguration config = context.Config;
+        IPEndPoint remoteEndPoint = new IPEndPoint( config.RemoteAddress, config.RemotePort );
+        Socket remote = new Socket( remoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+        remote.Connect( remoteEndPoint );
+        Byte[] buffer = new Byte[4096];
+        for(;;) {
+	        if (!config.RunServer)
+	        {
+		        remote.Close();
+		        client.Close();
+		        return;
+	        }
+            if( client.Available > 0 ) {
+                var count = client.Receive( buffer );
+                if( count == 0 ) return;
+                remote.Send( buffer, count, SocketFlags.None );
+            }
+            if( remote.Available > 0 ) {
+                var count = remote.Receive( buffer );
+                if( count == 0 ) return;
+                client.Send( buffer, count, SocketFlags.None );
+            }
+        }
+    }
+
+	readonly DatabaseFixture m_database;
+	private ServerConfiguration proxy;
+}
+#endif
diff --git a/tests/IntegrationTests/ServerFeatures.cs b/tests/IntegrationTests/ServerFeatures.cs
index cefe5d377..ac4fa4863 100644
--- a/tests/IntegrationTests/ServerFeatures.cs
+++ b/tests/IntegrationTests/ServerFeatures.cs
@@ -35,4 +35,9 @@ public enum ServerFeatures
 	/// A "SLEEP" command produces a result set when it is cancelled, not an error payload.
 	/// </summary>
 	CancelSleepSuccessfully = 0x40_0000,
+
+	/// <summary>
+	/// Server permit redirection, available on first OK_Packet
+	/// </summary>
+	Redirection = 0x80_0000,
 }
diff --git a/tests/MySqlConnector.Tests/UtilityTests.cs b/tests/MySqlConnector.Tests/UtilityTests.cs
index 57cdcc1b1..8040c2284 100644
--- a/tests/MySqlConnector.Tests/UtilityTests.cs
+++ b/tests/MySqlConnector.Tests/UtilityTests.cs
@@ -8,21 +8,17 @@ namespace MySqlConnector.Tests;
 public class UtilityTests
 {
 	[Theory]
-	[InlineData("Location: mysql://host.example.com:1234/user=user@host", "host.example.com", 1234, "user@host")]
-	[InlineData("Location: mysql://host.example.com:1234/user=user@host\n", "host.example.com", 1234, "user@host")]
-	[InlineData("Location: mysql://host.example.com:1234/user=user@host&ttl=60", "host.example.com", 1234, "user@host")]
-	[InlineData("Location: mysql://host.example.com:1234/user=user@host&ttl=60\n", "host.example.com", 1234, "user@host")]
-	[InlineData("Location: mysql://[host.example.com]:1234/?user=abcd", "host.example.com", 1234, "abcd")]
-	[InlineData("Location: mysql://[host.example.com]:1234/?user=abcd\n", "host.example.com", 1234, "abcd")]
-	[InlineData("Location: mysql://[host.example.com]:1234/?user=abcd&ttl=60", "host.example.com", 1234, "abcd")]
-	[InlineData("Location: mysql://[host.example.com]:1234/?user=abcd&ttl=60\n", "host.example.com", 1234, "abcd")]
-	[InlineData("Location: mysql://[2001:4860:4860::8888]:1234/?user=abcd", "2001:4860:4860::8888", 1234, "abcd")]
-	[InlineData("Location: mysql://[2001:4860:4860::8888]:1234/?user=abcd\n", "2001:4860:4860::8888", 1234, "abcd")]
-	[InlineData("Location: mysql://[2001:4860:4860::8888]:1234/?user=abcd&ttl=60", "2001:4860:4860::8888", 1234, "abcd")]
-	[InlineData("Location: mysql://[2001:4860:4860::8888]:1234/?user=abcd&ttl=60\n", "2001:4860:4860::8888", 1234, "abcd")]
+	[InlineData("mariadb://host.example.com:1234/?user=user@host", "host.example.com", 1234, "user@host")]
+	[InlineData("mariadb://user%40host:password@host.example.com:1234/", "host.example.com", 1234, "user@host")]
+	[InlineData("mariadb://host.example.com:1234/?user=user@host&ttl=60", "host.example.com", 1234, "user@host")]
+	[InlineData("mariadb://someuser:password@host.example.com:1234/?user=user@host&ttl=60\n", "host.example.com", 1234, "someuser")]
+	[InlineData("mysql://[2001:4860:4860::8888]:1234/?user=abcd", "2001:4860:4860::8888", 1234, "abcd")]
+	[InlineData("mysql://[2001:4860:4860::8888]:1234/?user=abcd\n", "2001:4860:4860::8888", 1234, "abcd")]
+	[InlineData("mysql://[2001:4860:4860::8888]:1234/?user=abcd&ttl=60", "2001:4860:4860::8888", 1234, "abcd")]
+	[InlineData("mysql://[2001:4860:4860::8888]:1234/?user=abcd&ttl=60\n", "2001:4860:4860::8888", 1234, "abcd")]
 	public void ParseRedirectionHeader(string input, string expectedHost, int expectedPort, string expectedUser)
 	{
-		Assert.True(Utility.TryParseRedirectionHeader(input, out var host, out var port, out var user));
+		Assert.True(Utility.TryParseRedirectionHeader(input, null, out var host, out var port, out var user));
 		Assert.Equal(expectedHost, host);
 		Assert.Equal(expectedPort, port);
 		Assert.Equal(expectedUser, user);
@@ -30,26 +26,14 @@ public void ParseRedirectionHeader(string input, string expectedHost, int expect
 
 	[Theory]
 	[InlineData("")]
-	[InlineData("Location: mysql")]
-	[InlineData("Location: mysql://host.example.com")]
-	[InlineData("Location: mysql://host.example.com:")]
-	[InlineData("Location: mysql://[host.example.com")]
-	[InlineData("Location: mysql://[host.example.com]")]
-	[InlineData("Location: mysql://[host.example.com]:")]
-	[InlineData("Location: mysql://host.example.com:123")]
-	[InlineData("Location: mysql://host.example.com:123/")]
-	[InlineData("Location: mysql://[host.example.com]:123")]
-	[InlineData("Location: mysql://[host.example.com]:123/")]
-	[InlineData("Location: mysql://host.example.com:/user=")]
-	[InlineData("Location: mysql://host.example.com:123/user=")]
-	[InlineData("Location: mysql://[host.example.com]:123/?user=")]
-	[InlineData("Location: mysql://host.example.com:/user=user@host")]
-	[InlineData("Location: mysql://host.example.com:-1/user=user@host")]
-	[InlineData("Location: mysql://host.example.com:0/user=user@host")]
-	[InlineData("Location: mysql://[host.example.com]:123/user=abcd")]
+	[InlineData("not formated")]
+	[InlineData("mysql")]
+	[InlineData("mysql://[host.example.com")]
+	[InlineData("mysql://host.example.com:-1/user=user@host")]
+	[InlineData("mysql://[host.example.com]:123/user=abcd")]
 	public void ParseRedirectionHeaderFails(string input)
 	{
-		Assert.False(Utility.TryParseRedirectionHeader(input, out _, out _, out _));
+		Assert.False(Utility.TryParseRedirectionHeader(input, null, out _, out _, out _));
 	}
 
 	[Theory]

From 1e009ea0ed0be2007a44b902fd1ff3590f0b14a6 Mon Sep 17 00:00:00 2001
From: diego dupin <diego.dupin@gmail.com>
Date: Tue, 30 Jul 2024 00:49:24 +0200
Subject: [PATCH 312/393] Support zero-configuration TLS. (#1500)

Implements the client side of https://jira.mariadb.org/browse/MDEV-31855.

Since MariaDB 11.4.1, TLS use has greatly been simplified. Connector side doesn't require TLS configuration anymore, even with self-signed certificates. Clients now validate SSL certificates using client password (using seed and server certificate SHA256 thumbprint).

Limitations:
 * only possible when using mysql_native_password/client_ed25519 authentication
 * password is required

See https://mariadb.org/mission-impossible-zero-configuration-ssl/.

Make Ed25519AuthenticationPlugin.Install threadsafe.

Signed-off-by: rusher <diego.dupin@gmail.com>
Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
Co-authored-by: Bradley Grainger <bgrainger@gmail.com>
---
 .ci/config/config.compression+ssl.json        |   2 +-
 .ci/config/config.compression.json            |   2 +-
 .ci/config/config.json                        |   2 +-
 .ci/config/config.ssl.json                    |   2 +-
 azure-pipelines.yml                           |  16 +-
 .../Ed25519AuthenticationPlugin.cs            |  30 +++-
 .../Authentication/IAuthenticationPlugin.cs   |  16 ++
 src/MySqlConnector/Core/ServerSession.cs      | 146 +++++++++++++++++-
 src/MySqlConnector/Logging/EventIds.cs        |   5 +
 src/MySqlConnector/Logging/Log.cs             |  17 ++
 .../Protocol/Payloads/OkPayload.cs            |   6 +-
 .../Serialization/AuthenticationUtility.cs    |  11 +-
 .../IntegrationTests/MySqlDataSourceTests.cs  |   2 +-
 tests/IntegrationTests/ServerFeatures.cs      |   7 +-
 tests/IntegrationTests/SslTests.cs            |  38 ++++-
 tests/README.md                               |   2 +
 16 files changed, 269 insertions(+), 35 deletions(-)

diff --git a/.ci/config/config.compression+ssl.json b/.ci/config/config.compression+ssl.json
index 71cd817a0..f7d4263c8 100644
--- a/.ci/config/config.compression+ssl.json
+++ b/.ci/config/config.compression+ssl.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,UuidToBin",
+      "UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin",
       "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
       "CertificatesPath": "../../../../.ci/server/certs"
diff --git a/.ci/config/config.compression.json b/.ci/config/config.compression.json
index bf1073a12..09326f1f6 100644
--- a/.ci/config/config.compression.json
+++ b/.ci/config/config.compression.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,UnixDomainSocket,ZeroDateTime",
+      "UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
diff --git a/.ci/config/config.json b/.ci/config/config.json
index 035c05855..bc38f605a 100644
--- a/.ci/config/config.json
+++ b/.ci/config/config.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,UnixDomainSocket,ZeroDateTime",
+      "UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
diff --git a/.ci/config/config.ssl.json b/.ci/config/config.ssl.json
index 705e0a168..a7511faa4 100644
--- a/.ci/config/config.ssl.json
+++ b/.ci/config/config.ssl.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,UuidToBin",
+      "UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV",
       "CertificatesPath": "../../../../.ci/server/certs"
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 47aa77a39..5e57f867e 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -62,7 +62,7 @@ jobs:
       arguments: 'tests\IntegrationTests\IntegrationTests.csproj -c MySqlData'
       testRunTitle: 'MySql.Data integration tests'
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,TlsFingerprintValidation,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=root;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600'
       DATA__CERTIFICATESPATH: '$(Build.Repository.LocalPath)\.ci\server\certs\'
       DATA__MYSQLBULKLOADERLOCALCSVFILE: '$(Build.Repository.LocalPath)\tests\TestData\LoadData_UTF8_BOM_Unix.CSV'
@@ -136,7 +136,7 @@ jobs:
       arguments: '-c Release --no-restore'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net472/net8.0', 'No SSL') }}
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True'
 
 - job: windows_integration_tests_2
@@ -174,7 +174,7 @@ jobs:
       arguments: '-c Release --no-restore'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net6.0', 'No SSL') }}
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
 
 - job: linux_integration_tests
@@ -187,23 +187,23 @@ jobs:
       'MySQL 8.0':
         image: 'mysql:8.0'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
       'MySQL 8.4':
         image: 'mysql:8.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
       'MySQL 9.0':
         image: 'mysql:9.0'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
       'MariaDB 10.6':
         image: 'mariadb:10.6'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin,Redirection'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
       'MariaDB 10.11':
         image: 'mariadb:10.11'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin,Redirection'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
       'MariaDB 11.4':
         image: 'mariadb:11.4'
         connectionStringExtra: ''
diff --git a/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs b/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs
index 117953366..f2ea62c3e 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs
@@ -1,6 +1,7 @@
 using System;
 using System.Security.Cryptography;
 using System.Text;
+using System.Threading;
 using Chaos.NaCl.Internal.Ed25519Ref10;
 
 namespace MySqlConnector.Authentication.Ed25519;
@@ -9,7 +10,7 @@ namespace MySqlConnector.Authentication.Ed25519;
 /// Provides an implementation of the <c>client_ed25519</c> authentication plugin for MariaDB.
 /// </summary>
 /// <remarks>See <a href="https://mariadb.com/kb/en/library/authentication-plugin-ed25519/">Authentication Plugin - ed25519</a>.</remarks>
-public sealed class Ed25519AuthenticationPlugin : IAuthenticationPlugin
+public sealed class Ed25519AuthenticationPlugin : IAuthenticationPlugin2
 {
 	/// <summary>
 	/// Registers the Ed25519 authentication plugin with MySqlConnector. You must call this method once before
@@ -17,11 +18,8 @@ public sealed class Ed25519AuthenticationPlugin : IAuthenticationPlugin
 	/// </summary>
 	public static void Install()
 	{
-		if (!s_isInstalled)
-		{
+		if (Interlocked.CompareExchange(ref s_isInstalled, 1, 0) == 0)
 			AuthenticationPlugins.Register(new Ed25519AuthenticationPlugin());
-			s_isInstalled = true;
-		}
 	}
 
 	/// <summary>
@@ -33,6 +31,21 @@ public static void Install()
 	/// Creates the authentication response.
 	/// </summary>
 	public byte[] CreateResponse(string password, ReadOnlySpan<byte> authenticationData)
+	{
+		CreateResponseAndHash(password, authenticationData, out _, out var authenticationResponse);
+		return authenticationResponse;
+	}
+
+	/// <summary>
+	/// Creates the Ed25519 password hash.
+	/// </summary>
+	public byte[] CreatePasswordHash(string password, ReadOnlySpan<byte> authenticationData)
+	{
+		CreateResponseAndHash(password, authenticationData, out var passwordHash, out _);
+		return passwordHash;
+	}
+
+	private static void CreateResponseAndHash(string password, ReadOnlySpan<byte> authenticationData, out byte[] passwordHash, out byte[] authenticationResponse)
 	{
 		// Java reference: https://github.com/MariaDB/mariadb-connector-j/blob/master/src/main/java/org/mariadb/jdbc/internal/com/send/authentication/Ed25519PasswordPlugin.java
 		// C reference:  https://github.com/MariaDB/server/blob/592fe954ef82be1bc08b29a8e54f7729eb1e1343/plugin/auth_ed25519/ref10/sign.c#L7
@@ -111,6 +124,9 @@ public byte[] CreateResponse(string password, ReadOnlySpan<byte> authenticationD
 		GroupOperations.ge_scalarmult_base(out var A, az, 0);
 		GroupOperations.ge_p3_tobytes(sm, 32, ref A);
 
+		passwordHash = new byte[32];
+		Array.Copy(sm, 32, passwordHash, 0, 32);
+
 		/*** Java
 			nonce = scalar.reduce(nonce);
 			GroupElement elementRvalue = spec.getB().scalarMultiply(nonce);
@@ -154,12 +170,12 @@ public byte[] CreateResponse(string password, ReadOnlySpan<byte> authenticationD
 
 		var result = new byte[64];
 		Buffer.BlockCopy(sm, 0, result, 0, result.Length);
-		return result;
+		authenticationResponse = result;
 	}
 
 	private Ed25519AuthenticationPlugin()
 	{
 	}
 
-	private static bool s_isInstalled;
+	private static int s_isInstalled;
 }
diff --git a/src/MySqlConnector/Authentication/IAuthenticationPlugin.cs b/src/MySqlConnector/Authentication/IAuthenticationPlugin.cs
index 063bd4c8b..6dfface4e 100644
--- a/src/MySqlConnector/Authentication/IAuthenticationPlugin.cs
+++ b/src/MySqlConnector/Authentication/IAuthenticationPlugin.cs
@@ -20,3 +20,19 @@ public interface IAuthenticationPlugin
 	/// <returns>The authentication response.</returns>
 	byte[] CreateResponse(string password, ReadOnlySpan<byte> authenticationData);
 }
+
+/// <summary>
+/// <see cref="IAuthenticationPlugin2"/> is an extension to <see cref="IAuthenticationPlugin"/> that returns a hash of the client's password.
+/// </summary>
+public interface IAuthenticationPlugin2 : IAuthenticationPlugin
+{
+	/// <summary>
+	/// Hashes the client's password (e.g., for TLS certificate fingerprint verification).
+	/// </summary>
+	/// <param name="password">The client's password.</param>
+	/// <param name="authenticationData">The authentication data supplied by the server; this is the <code>auth method data</code>
+	/// from the <a href="https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest">Authentication
+	/// Method Switch Request Packet</a>.</param>
+	/// <returns>The authentication-method-specific hash of the client's password.</returns>
+	byte[] CreatePasswordHash(string password, ReadOnlySpan<byte> authenticationData);
+}
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index dcd64d009..69a288345 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -438,13 +438,13 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			var initialHandshake = InitialHandshakePayload.Create(payload.Span);
 
 			// if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
-			var authPluginName = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0 ? initialHandshake.AuthPluginName! :
+			m_currentAuthenticationMethod = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0 ? initialHandshake.AuthPluginName! :
 				(initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" :
 				"mysql_native_password";
-			Log.ServerSentAuthPluginName(m_logger, Id, authPluginName);
-			if (authPluginName is not "mysql_native_password" and not "sha256_password" and not "caching_sha2_password")
+			Log.ServerSentAuthPluginName(m_logger, Id, m_currentAuthenticationMethod);
+			if (m_currentAuthenticationMethod is not "mysql_native_password" and not "sha256_password" and not "caching_sha2_password")
 			{
-				Log.UnsupportedAuthenticationMethod(m_logger, Id, authPluginName);
+				Log.UnsupportedAuthenticationMethod(m_logger, Id, m_currentAuthenticationMethod);
 				throw new NotSupportedException($"Authentication method '{initialHandshake.AuthPluginName}' is not supported.");
 			}
 
@@ -528,6 +528,46 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			}
 
 			var ok = OkPayload.Create(payload.Span, this);
+
+			if (m_sslPolicyErrors != SslPolicyErrors.None)
+			{
+				// SSL would normally have thrown error, but this was suppressed in ValidateRemoteCertificate; now we need to verify the server certificate
+				// pass only if :
+				// * connection method is MitM-proof (e.g. unix socket)
+				// * auth plugin is MitM-proof and check SHA2(user's hashed password, scramble, certificate fingerprint)
+				// see https://mariadb.org/mission-impossible-zero-configuration-ssl/
+				var ignoreCertificateError = false;
+
+				if (cs.ConnectionProtocol == MySqlConnectionProtocol.UnixSocket)
+				{
+					Log.CertificateErrorUnixSocket(m_logger, Id, m_sslPolicyErrors);
+					ignoreCertificateError = true;
+				}
+				else if (string.IsNullOrEmpty(password))
+				{
+					// there is no shared secret that can be used to validate the certificate
+					Log.CertificateErrorNoPassword(m_logger, Id, m_sslPolicyErrors);
+				}
+				else if (ValidateFingerprint(ok.StatusInfo, initialHandshake.AuthPluginData.AsSpan(0, 20), password))
+				{
+					Log.CertificateErrorValidThumbprint(m_logger, Id, m_sslPolicyErrors);
+					ignoreCertificateError = true;
+				}
+
+				if (!ignoreCertificateError)
+				{
+					ShutdownSocket();
+					HostName = "";
+					lock (m_lock)
+						m_state = State.Failed;
+
+					// throw a MySqlException with an AuthenticationException InnerException to mimic what would have happened if ValidateRemoteCertificate returned false
+					var innerException = new AuthenticationException($"The remote certificate was rejected due to the following error: {m_sslPolicyErrors}");
+					Log.CouldNotInitializeTlsConnection(m_logger, innerException, Id);
+					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, "SSL Authentication Error", innerException);
+				}
+			}
+
 			var redirectionUrl = ok.RedirectionUrl;
 
 			if (m_useCompression)
@@ -567,6 +607,70 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 		}
 	}
 
+	/// <summary>
+	/// Validate SSL validation hash (from OK packet).
+	/// </summary>
+	/// <param name="validationHash">The validation hash received from the server.</param>
+	/// <param name="challenge">The auth plugin data from the initial handshake.</param>
+	/// <param name="password">The user's password.</param>
+	/// <returns><c>true</c> if the validation hash matches the locally-computed value; otherwise, <c>false</c>.</returns>
+	private bool ValidateFingerprint(byte[]? validationHash, ReadOnlySpan<byte> challenge, string password)
+	{
+		// expect 0x01 followed by 64 hex characters giving a SHA2 hash
+		if (validationHash?.Length != 65 || validationHash[0] != 1)
+			return false;
+
+		byte[]? passwordHashResult = null;
+		switch (m_currentAuthenticationMethod)
+		{
+			case "mysql_native_password":
+				passwordHashResult = AuthenticationUtility.HashPassword([], password, onlyHashPassword: true);
+				break;
+
+			case "client_ed25519":
+				AuthenticationPlugins.TryGetPlugin(m_currentAuthenticationMethod, out var ed25519Plugin);
+				if (ed25519Plugin is IAuthenticationPlugin2 plugin2)
+					passwordHashResult = plugin2.CreatePasswordHash(password, challenge);
+				break;
+		}
+		if (passwordHashResult is null)
+			return false;
+
+		Span<byte> combined = stackalloc byte[32 + challenge.Length + passwordHashResult.Length];
+		passwordHashResult.CopyTo(combined);
+		challenge.CopyTo(combined[passwordHashResult.Length..]);
+		m_remoteCertificateSha2Thumbprint!.CopyTo(combined[(passwordHashResult.Length + challenge.Length)..]);
+
+		Span<byte> hashBytes = stackalloc byte[32];
+#if NET5_0_OR_GREATER
+		SHA256.TryHashData(combined, hashBytes, out _);
+#else
+		using var sha256 = SHA256.Create();
+		sha256.TryComputeHash(combined, hashBytes, out _);
+#endif
+
+		Span<byte> serverHash = combined[0..32];
+		return TryConvertFromHexString(validationHash.AsSpan(1), serverHash) && serverHash.SequenceEqual(hashBytes);
+
+		static bool TryConvertFromHexString(ReadOnlySpan<byte> hexChars, Span<byte> data)
+		{
+			ReadOnlySpan<byte> hexDigits = "0123456789ABCDEFabcdef"u8;
+			for (var i = 0; i < hexChars.Length; i += 2)
+			{
+				var high = hexDigits.IndexOf(hexChars[i]);
+				var low = hexDigits.IndexOf(hexChars[i + 1]);
+				if (high == -1 || low == -1)
+					return false;
+				if (high > 15)
+					high -= 6;
+				if (low > 15)
+					low -= 6;
+				data[i / 2] = (byte) ((high << 4) | low);
+			}
+			return true;
+		}
+	}
+
 	public static async ValueTask<ServerSession> ConnectAndRedirectAsync(ILogger connectionLogger, ILogger poolLogger, IConnectionPoolMetadata pool, ConnectionSettings cs, ILoadBalancer? loadBalancer, MySqlConnection connection, Action<ILogger, int, string, Exception?>? logMessage, long startingTimestamp, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		var session = new ServerSession(connectionLogger, pool);
@@ -729,6 +833,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 		// if the server didn't support the hashed password; rehash with the new challenge
 		var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload.Span);
 		Log.SwitchingToAuthenticationMethod(m_logger, Id, switchRequest.Name);
+		m_currentAuthenticationMethod = switchRequest.Name;
 		switch (switchRequest.Name)
 		{
 			case "mysql_native_password":
@@ -1485,6 +1590,21 @@ caCertificateChain is not null &&
 			if (cs.SslMode == MySqlSslMode.VerifyCA)
 				rcbPolicyErrors &= ~SslPolicyErrors.RemoteCertificateNameMismatch;
 
+			if (rcbCertificate is X509Certificate2 cert2)
+			{
+				// saving sha256 thumbprint and SSL errors until thumbprint validation
+#if NET7_0_OR_GREATER
+				m_remoteCertificateSha2Thumbprint = SHA256.HashData(cert2.RawDataMemory.Span);
+#elif NET5_0_OR_GREATER
+				m_remoteCertificateSha2Thumbprint = SHA256.HashData(cert2.RawData);
+#else
+				using var sha256 = SHA256.Create();
+				m_remoteCertificateSha2Thumbprint = sha256.ComputeHash(cert2.RawData);
+#endif
+				m_sslPolicyErrors = rcbPolicyErrors;
+				return true;
+			}
+
 			return rcbPolicyErrors == SslPolicyErrors.None;
 		}
 
@@ -1558,11 +1678,22 @@ await sslStream.AuthenticateAsClientAsync(clientAuthenticationOptions.TargetHost
 			m_payloadHandler!.ByteHandler = sslByteHandler;
 			m_isSecureConnection = true;
 			m_sslStream = sslStream;
+			if (m_sslPolicyErrors != SslPolicyErrors.None)
+			{
 #if NETCOREAPP3_0_OR_GREATER
-			Log.ConnectedTlsBasic(m_logger, Id, sslStream.SslProtocol, sslStream.NegotiatedCipherSuite);
+				Log.ConnectedTlsBasicPreliminary(m_logger, Id, m_sslPolicyErrors, sslStream.SslProtocol, sslStream.NegotiatedCipherSuite);
 #else
-			Log.ConnectedTlsDetailed(m_logger, Id, sslStream.SslProtocol, sslStream.CipherAlgorithm, sslStream.HashAlgorithm, sslStream.KeyExchangeAlgorithm, sslStream.KeyExchangeStrength);
+				Log.ConnectedTlsDetailedPreliminary(m_logger, Id, m_sslPolicyErrors, sslStream.SslProtocol, sslStream.CipherAlgorithm, sslStream.HashAlgorithm, sslStream.KeyExchangeAlgorithm, sslStream.KeyExchangeStrength);
 #endif
+			}
+			else
+			{
+#if NETCOREAPP3_0_OR_GREATER
+				Log.ConnectedTlsBasic(m_logger, Id, sslStream.SslProtocol, sslStream.NegotiatedCipherSuite);
+#else
+				Log.ConnectedTlsDetailed(m_logger, Id, sslStream.SslProtocol, sslStream.CipherAlgorithm, sslStream.HashAlgorithm, sslStream.KeyExchangeAlgorithm, sslStream.KeyExchangeStrength);
+#endif
+			}
 		}
 		catch (Exception ex)
 		{
@@ -2006,4 +2137,7 @@ protected override void OnStatementBegin(int index)
 	private PayloadData m_setNamesPayload;
 	private byte[]? m_pipelinedResetConnectionBytes;
 	private Dictionary<string, PreparedStatements>? m_preparedStatements;
+	private string? m_currentAuthenticationMethod;
+	private byte[]? m_remoteCertificateSha2Thumbprint;
+	private SslPolicyErrors m_sslPolicyErrors;
 }
diff --git a/src/MySqlConnector/Logging/EventIds.cs b/src/MySqlConnector/Logging/EventIds.cs
index 568acdcf3..de87e8590 100644
--- a/src/MySqlConnector/Logging/EventIds.cs
+++ b/src/MySqlConnector/Logging/EventIds.cs
@@ -81,6 +81,11 @@ internal static class EventIds
 	public const int CreatingConnectionAttributes = 2153;
 	public const int ObtainingPasswordViaProvidePasswordCallback = 2154;
 	public const int FailedToObtainPassword = 2155;
+	public const int ConnectedTlsBasicPreliminary = 2156;
+	public const int ConnectedTlsDetailedPreliminary = 2157;
+	public const int CertificateErrorUnixSocket = 2158;
+	public const int CertificateErrorNoPassword = 2159;
+	public const int CertificateErrorValidThumbprint = 2160;
 
 	// Command execution events, 2200-2299
 	public const int CannotExecuteNewCommandInState = 2200;
diff --git a/src/MySqlConnector/Logging/Log.cs b/src/MySqlConnector/Logging/Log.cs
index 57b10a088..e9b4f88bc 100644
--- a/src/MySqlConnector/Logging/Log.cs
+++ b/src/MySqlConnector/Logging/Log.cs
@@ -201,6 +201,23 @@ internal static partial class Log
 	[LoggerMessage(EventIds.FailedToObtainPassword, LogLevel.Error, "Session {SessionId} failed to obtain password via ProvidePasswordCallback: {ExceptionMessage}")]
 	public static partial void FailedToObtainPassword(ILogger logger, Exception exception, string sessionId, string exceptionMessage);
 
+#if NETCOREAPP3_0_OR_GREATER
+	[LoggerMessage(EventIds.ConnectedTlsBasicPreliminary, LogLevel.Debug, "Session {SessionId} provisionally connected TLS with error {SslPolicyErrors} using {SslProtocol} and {NegotiatedCipherSuite}")]
+	public static partial void ConnectedTlsBasicPreliminary(ILogger logger, string sessionId, SslPolicyErrors sslPolicyErrors, SslProtocols sslProtocol, TlsCipherSuite negotiatedCipherSuite);
+#endif
+
+	[LoggerMessage(EventIds.ConnectedTlsDetailedPreliminary, LogLevel.Debug, "Session {SessionId} provisionally connected TLS with error {SslPolicyErrors} using {SslProtocol}, {CipherAlgorithm}, {HashAlgorithm}, {KeyExchangeAlgorithm}, {KeyExchangeStrength}")]
+	public static partial void ConnectedTlsDetailedPreliminary(ILogger logger, string sessionId, SslPolicyErrors sslPolicyErrors, SslProtocols sslProtocol, CipherAlgorithmType cipherAlgorithm, HashAlgorithmType hashAlgorithm, ExchangeAlgorithmType keyExchangeAlgorithm, int keyExchangeStrength);
+
+	[LoggerMessage(EventIds.CertificateErrorUnixSocket, LogLevel.Trace, "Session {SessionId} ignoring remote certificate error {SslPolicyErrors} due to Unix socket connection")]
+	public static partial void CertificateErrorUnixSocket(ILogger logger, string sessionId, SslPolicyErrors sslPolicyErrors);
+
+	[LoggerMessage(EventIds.CertificateErrorNoPassword, LogLevel.Trace, "Session {SessionId} acknowledging remote certificate error {SslPolicyErrors} due to passwordless connection")]
+	public static partial void CertificateErrorNoPassword(ILogger logger, string sessionId, SslPolicyErrors sslPolicyErrors);
+
+	[LoggerMessage(EventIds.CertificateErrorValidThumbprint, LogLevel.Trace, "Session {SessionId} ignoring remote certificate error {SslPolicyErrors} due to valid signature in OK packet")]
+	public static partial void CertificateErrorValidThumbprint(ILogger logger, string sessionId, SslPolicyErrors sslPolicyErrors);
+
 	[LoggerMessage(EventIds.IgnoringCancellationForCommand, LogLevel.Trace, "Ignoring cancellation for closed connection or invalid command {CommandId}")]
 	public static partial void IgnoringCancellationForCommand(ILogger logger, int commandId);
 
diff --git a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
index 37db8862f..b1bc70139 100644
--- a/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/OkPayload.cs
@@ -12,7 +12,7 @@ internal sealed class OkPayload
 	public ulong LastInsertId { get; }
 	public ServerStatus ServerStatus { get; }
 	public int WarningCount { get; }
-	public string? StatusInfo { get; }
+	public byte[]? StatusInfo { get; }
 	public string? NewSchema { get; }
 	public CharacterSet? NewCharacterSet { get; }
 	public int? NewConnectionId { get; }
@@ -152,7 +152,7 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 
 		if (createPayload)
 		{
-			var statusInfo = statusBytes.Length == 0 ? null : Encoding.UTF8.GetString(statusBytes);
+			var statusInfo = statusBytes.Length == 0 ? null : statusBytes.ToArray();
 
 			// detect the connection character set as utf8mb4 (or utf8) if all three system variables are set to the same value
 			var characterSet = clientCharacterSet == CharacterSet.Utf8Mb4Binary && connectionCharacterSet == CharacterSet.Utf8Mb4Binary && resultsCharacterSet == CharacterSet.Utf8Mb4Binary ? CharacterSet.Utf8Mb4Binary :
@@ -175,7 +175,7 @@ public static void Verify(ReadOnlySpan<byte> span, IServerCapabilities serverCap
 		}
 	}
 
-	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, string? statusInfo, string? newSchema, CharacterSet newCharacterSet, int? connectionId, string? redirectionUrl)
+	private OkPayload(ulong affectedRowCount, ulong lastInsertId, ServerStatus serverStatus, int warningCount, byte[]? statusInfo, string? newSchema, CharacterSet newCharacterSet, int? connectionId, string? redirectionUrl)
 	{
 		AffectedRowCount = affectedRowCount;
 		LastInsertId = lastInsertId;
diff --git a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
index fc0301a90..659d2350d 100644
--- a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
@@ -25,25 +25,27 @@ public static byte[] GetNullTerminatedPasswordBytes(string password)
 	}
 
 	public static byte[] CreateAuthenticationResponse(ReadOnlySpan<byte> challenge, string password) =>
-		string.IsNullOrEmpty(password) ? [] : HashPassword(challenge, password);
+		string.IsNullOrEmpty(password) ? [] : HashPassword(challenge, password, onlyHashPassword: false);
 
 	/// <summary>
 	/// Hashes a password with the "Secure Password Authentication" method.
 	/// </summary>
 	/// <param name="challenge">The 20-byte random challenge (from the "auth-plugin-data" in the initial handshake).</param>
 	/// <param name="password">The password to hash.</param>
+	/// <param name="onlyHashPassword">If true, <paramref name="challenge"/> is ignored and only the twice-hashed password
+	/// is returned, instead of performing the full "secure password authentication" algorithm that XORs the hashed password against
+	/// a hash derived from the challenge.</param>
 	/// <returns>A 20-byte password hash.</returns>
 	/// <remarks>See <a href="https://dev.mysql.com/doc/internals/en/secure-password-authentication.html">Secure Password Authentication</a>.</remarks>
 #if NET5_0_OR_GREATER
 	[SkipLocalsInit]
 #endif
-	public static byte[] HashPassword(ReadOnlySpan<byte> challenge, string password)
+	public static byte[] HashPassword(ReadOnlySpan<byte> challenge, string password, bool onlyHashPassword)
 	{
 #if !NET5_0_OR_GREATER
 		using var sha1 = SHA1.Create();
 #endif
 		Span<byte> combined = stackalloc byte[40];
-		challenge.CopyTo(combined);
 
 		var passwordByteCount = Encoding.UTF8.GetByteCount(password);
 		Span<byte> passwordBytes = stackalloc byte[passwordByteCount];
@@ -56,7 +58,10 @@ public static byte[] HashPassword(ReadOnlySpan<byte> challenge, string password)
 		sha1.TryComputeHash(passwordBytes, hashedPassword, out _);
 		sha1.TryComputeHash(hashedPassword, combined[20..], out _);
 #endif
+		if (onlyHashPassword)
+			return combined[20..].ToArray();
 
+		challenge[..20].CopyTo(combined);
 		Span<byte> xorBytes = stackalloc byte[20];
 #if NET5_0_OR_GREATER
 		SHA1.TryHashData(combined, xorBytes, out _);
diff --git a/tests/IntegrationTests/MySqlDataSourceTests.cs b/tests/IntegrationTests/MySqlDataSourceTests.cs
index dcd8d7f2e..c753fe97c 100644
--- a/tests/IntegrationTests/MySqlDataSourceTests.cs
+++ b/tests/IntegrationTests/MySqlDataSourceTests.cs
@@ -147,7 +147,7 @@ public async Task ConnectSslRemoteCertificateValidationCallback(MySqlSslMode ssl
 
 		using var dataSource = builder.Build();
 		using var connection = dataSource.CreateConnection();
-		if (expectedSuccess)
+		if (expectedSuccess || AppConfig.SupportedFeatures.HasFlag(ServerFeatures.TlsFingerprintValidation))
 			await connection.OpenAsync();
 		else
 			await Assert.ThrowsAsync<MySqlException>(connection.OpenAsync);
diff --git a/tests/IntegrationTests/ServerFeatures.cs b/tests/IntegrationTests/ServerFeatures.cs
index ac4fa4863..120b541bf 100644
--- a/tests/IntegrationTests/ServerFeatures.cs
+++ b/tests/IntegrationTests/ServerFeatures.cs
@@ -37,7 +37,12 @@ public enum ServerFeatures
 	CancelSleepSuccessfully = 0x40_0000,
 
 	/// <summary>
-	/// Server permit redirection, available on first OK_Packet
+	/// Server permits redirection (sent as a server variable in first OK packet).
 	/// </summary>
 	Redirection = 0x80_0000,
+
+	/// <summary>
+	/// Server provides hash of TLS certificate in first OK packet.
+	/// </summary>
+	TlsFingerprintValidation = 0x100_0000,
 }
diff --git a/tests/IntegrationTests/SslTests.cs b/tests/IntegrationTests/SslTests.cs
index e22c034fb..6c428f386 100644
--- a/tests/IntegrationTests/SslTests.cs
+++ b/tests/IntegrationTests/SslTests.cs
@@ -181,7 +181,10 @@ public async Task ConnectSslBadCaCertificate()
 		csb.SslMode = MySqlSslMode.VerifyCA;
 		csb.SslCa = Path.Combine(AppConfig.CertsPath, "non-ca-client-cert.pem");
 		using var connection = new MySqlConnection(csb.ConnectionString);
-		await Assert.ThrowsAsync<MySqlException>(async () => await connection.OpenAsync());
+		if (AppConfig.SupportedFeatures.HasFlag(ServerFeatures.TlsFingerprintValidation))
+			await connection.OpenAsync();
+		else
+			await Assert.ThrowsAsync<MySqlException>(async () => await connection.OpenAsync());
 	}
 
 #if !MYSQL_DATA
@@ -198,13 +201,44 @@ public async Task ConnectSslRemoteCertificateValidationCallback(MySqlSslMode ssl
 		using var connection = new MySqlConnection(csb.ConnectionString);
 		connection.RemoteCertificateValidationCallback = (s, c, h, e) => true;
 
-		if (expectedSuccess)
+		if (expectedSuccess || AppConfig.SupportedFeatures.HasFlag(ServerFeatures.TlsFingerprintValidation))
 			await connection.OpenAsync();
 		else
 			await Assert.ThrowsAsync<MySqlException>(async () => await connection.OpenAsync());
 	}
 #endif
 
+	[SkippableFact(ServerFeatures.TlsFingerprintValidation)]
+	public async Task ConnectZeroConfigurationSslNative()
+	{
+		// permit connection without any Ssl configuration.
+		// reference https://mariadb.org/mission-impossible-zero-configuration-ssl/
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		csb.CertificateFile = null;
+		csb.SslMode = MySqlSslMode.VerifyFull;
+		csb.SslCa = "";
+		csb.UserID = "ssltest";
+		csb.Password = "test";
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+	}
+
+#if !MYSQL_DATA
+	[SkippableFact(ServerFeatures.TlsFingerprintValidation | ServerFeatures.Ed25519)]
+	public async Task ConnectZeroConfigurationSslEd25519()
+	{
+		MySqlConnector.Authentication.Ed25519.Ed25519AuthenticationPlugin.Install();
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		csb.CertificateFile = null;
+		csb.SslMode = MySqlSslMode.VerifyFull;
+		csb.SslCa = "";
+		csb.UserID = "ed25519user";
+		csb.Password = "Ed255!9";
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+	}
+#endif
+
 	[SkippableFact(ConfigSettings.RequiresSsl)]
 	public async Task ConnectSslTlsVersion()
 	{
diff --git a/tests/README.md b/tests/README.md
index bd5267639..da0fcb0ab 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -27,6 +27,7 @@ Otherwise, set the following options appropriately:
   * `ErrorCodes`: server returns error codes in error packet (some MySQL proxies do not)
   * `Json`: the `JSON` data type (MySQL 5.7 and later)
   * `LargePackets`: large packets (over 4MB)
+  * `Redirection`: server supports sending redirection information in a server variable in the first OK packet
   * `RoundDateTime`: server rounds `datetime` values to the specified precision (not implemented in MariaDB)
   * `RsaEncryption`: server supports RSA public key encryption (for `sha256_password` and `caching_sha2_password`)
   * `SessionTrack`: server supports `CLIENT_SESSION_TRACK` capability (MySQL 5.7 and later)
@@ -36,6 +37,7 @@ Otherwise, set the following options appropriately:
   * `Tls11`: server supports TLS 1.1
   * `Tls12`: server supports TLS 1.2
   * `Tls13`: server supports TLS 1.3
+  * `TlsFingerprintValidation`: server provides a hash of the TLS certificate fingerprint in the first OK packet
   * `UnixDomainSocket`: server is accessible via a Unix domain socket
   * `UuidToBin`: server supports `UUID_TO_BIN` (MySQL 8.0 and later)
 

From 5151359726dcc394d727a1ad37c7dd95f5eb46ca Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 13 Aug 2024 21:17:57 -0700
Subject: [PATCH 313/393] Update to .NET 9 Preview 7.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ServerSession.cs | 15 +++++++++++++--
 tests/IntegrationTests/SslTests.cs       |  8 ++++++++
 2 files changed, 21 insertions(+), 2 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 170e1c64d..9434092a8 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -7,6 +7,7 @@
 using System.Net.Security;
 using System.Net.Sockets;
 using System.Reflection;
+using System.Runtime.ConstrainedExecution;
 using System.Security.Authentication;
 using System.Security.Cryptography;
 using System.Security.Cryptography.X509Certificates;
@@ -1280,7 +1281,11 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 		{
 			try
 			{
+#if NET9_0_OR_GREATER
+				var certificate = X509CertificateLoader.LoadPkcs12FromFile(cs.CertificateFile, cs.CertificatePassword, X509KeyStorageFlags.MachineKeySet);
+#else
 				var certificate = new X509Certificate2(cs.CertificateFile, cs.CertificatePassword, X509KeyStorageFlags.MachineKeySet);
+#endif
 				if (!certificate.HasPrivateKey)
 				{
 					certificate.Dispose();
@@ -1352,7 +1357,9 @@ private async Task InitSslAsync(ProtocolCapabilities serverCapabilities, Connect
 					{
 						// load the certificate at this index; note that 'new X509Certificate' stops at the end of the first certificate it loads
 						Log.LoadingCaCertificate(m_logger, Id, index);
-#if NET5_0_OR_GREATER
+#if NET9_0_OR_GREATER
+						var caCertificate = X509CertificateLoader.LoadCertificate(certificateBytes.AsSpan(index, (nextIndex == -1 ? certificateBytes.Length : nextIndex) - index));
+#elif NET5_0_OR_GREATER
 						var caCertificate = new X509Certificate2(certificateBytes.AsSpan(index, (nextIndex == -1 ? certificateBytes.Length : nextIndex) - index), default(ReadOnlySpan<char>), X509KeyStorageFlags.MachineKeySet);
 #else
 						var caCertificate = new X509Certificate2(Utility.ArraySlice(certificateBytes, index, (nextIndex == -1 ? certificateBytes.Length : nextIndex) - index), default(string), X509KeyStorageFlags.MachineKeySet);
@@ -1522,7 +1529,11 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 				// Schannel has a bug where ephemeral keys can't be loaded: https://github.com/dotnet/runtime/issues/23749#issuecomment-485947319
 				// The workaround is to export the key (which may make it "Perphemeral"): https://github.com/dotnet/runtime/issues/23749#issuecomment-739895373
 				var oldCertificate = m_clientCertificate;
+#if NET9_0_OR_GREATER
+				m_clientCertificate = X509CertificateLoader.LoadPkcs12(m_clientCertificate.Export(X509ContentType.Pkcs12, default(string?)), null);
+#else
 				m_clientCertificate = new X509Certificate2(m_clientCertificate.Export(X509ContentType.Pkcs12));
+#endif
 				oldCertificate.Dispose();
 			}
 			return [m_clientCertificate];
@@ -1593,7 +1604,7 @@ X509CertificateCollection LoadCertificate(string sslKeyFile, string sslCertifica
 				throw new MySqlException("Could not load the client key from " + sslCertificateFile, ex);
 			}
 #endif
-		}
+			}
 	}
 
 #if !NETCOREAPP2_1_OR_GREATER && !NETSTANDARD2_1_OR_GREATER
diff --git a/tests/IntegrationTests/SslTests.cs b/tests/IntegrationTests/SslTests.cs
index 96f0421ed..344bd051e 100644
--- a/tests/IntegrationTests/SslTests.cs
+++ b/tests/IntegrationTests/SslTests.cs
@@ -64,7 +64,11 @@ public async Task ConnectSslClientCertificateCallback(string certificateFile, st
 		using var connection = new MySqlConnection(csb.ConnectionString);
 		connection.ProvideClientCertificatesCallback = x =>
 		{
+#if NET9_0_OR_GREATER
+			x.Add(X509CertificateLoader.LoadPkcs12FromFile(certificateFilePath, certificateFilePassword));
+#else
 			x.Add(new X509Certificate2(certificateFilePath, certificateFilePassword));
+#endif
 			return default;
 		};
 
@@ -119,7 +123,11 @@ public async Task ConnectSslClientCertificateFromCertificateStore(string certFil
 		// Create a mock of certificate store
 		var store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
 		store.Open(OpenFlags.ReadWrite);
+#if NET9_0_OR_GREATER
+		var certificate = X509CertificateLoader.LoadPkcs12FromFile(Path.Combine(AppConfig.CertsPath, certFile), null);
+#else
 		var certificate = new X509Certificate2(Path.Combine(AppConfig.CertsPath, certFile));
+#endif
 		store.Add(certificate);
 
 		var csb = AppConfig.CreateConnectionStringBuilder();

From ed0fad8a9e247911ce453bbbcb0cb0bcd595ca73 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 16 Aug 2024 20:45:23 -0700
Subject: [PATCH 314/393] Fix potential NullReferenceException. Fixes #1506

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/MySqlConnection.cs | 19 ++++++++++---------
 1 file changed, 10 insertions(+), 9 deletions(-)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 571af0e51..4e3663e66 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -879,13 +879,14 @@ internal ServerSession Session
 
 	internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 	{
-		if (m_session?.Id is not string sessionId || State != ConnectionState.Open || m_session?.TryStartCancel(command) is not true)
+		// NOTE: read and cache m_session in a local variable because it can be set to null by CloseAsync on another thread
+		if (m_session is not { } session || State != ConnectionState.Open || !session.TryStartCancel(command))
 		{
 			Log.IgnoringCancellationForCommand(m_logger, commandId);
 			return;
 		}
 
-		Log.CommandHasBeenCanceled(m_logger, commandId, sessionId, isCancel ? "Cancel()" : "command timeout");
+		Log.CommandHasBeenCanceled(m_logger, commandId, session.Id, isCancel ? "Cancel()" : "command timeout");
 		try
 		{
 			// open a dedicated connection to the server to kill the active query
@@ -894,12 +895,12 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 				AutoEnlist = false,
 				Pooling = false,
 			};
-			if (m_session.IPEndPoint is { Address: { } ipAddress, Port: { } port })
+			if (session.IPEndPoint is { Address: { } ipAddress, Port: { } port })
 			{
 				csb.Server = ipAddress.ToString();
 				csb.Port = (uint) port;
 			}
-			csb.UserID = m_session.UserID;
+			csb.UserID = session.UserID;
 			var cancellationTimeout = GetConnectionSettings().CancellationTimeout;
 			csb.ConnectionTimeout = cancellationTimeout < 1 ? 3u : (uint) cancellationTimeout;
 
@@ -912,20 +913,20 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 #endif
 			using var killCommand = new MySqlCommand(killQuerySql, connection);
 			killCommand.CommandTimeout = cancellationTimeout < 1 ? 3 : cancellationTimeout;
-			m_session?.DoCancel(command, killCommand);
+			session.DoCancel(command, killCommand);
 		}
 		catch (InvalidOperationException ex)
 		{
 			// ignore a rare race condition where the connection is open at the beginning of the method, but closed by the time
 			// KILL QUERY is executed: https://github.com/mysql-net/MySqlConnector/issues/1002
-			Log.IgnoringCancellationForClosedConnection(m_logger, ex, sessionId);
-			m_session?.AbortCancel(command);
+			Log.IgnoringCancellationForClosedConnection(m_logger, ex, session.Id);
+			session.AbortCancel(command);
 		}
 		catch (MySqlException ex)
 		{
 			// cancelling the query failed; setting the state back to 'Querying' will allow another call to 'Cancel' to try again
-			Log.CancelingCommandFailed(m_logger, ex, sessionId, command.CommandId);
-			m_session?.AbortCancel(command);
+			Log.CancelingCommandFailed(m_logger, ex, session.Id, command.CommandId);
+			session.AbortCancel(command);
 		}
 	}
 

From 3ae8fc1bc0d6ceb477a84b495b08fda2f58e04fb Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 24 Aug 2024 12:38:37 -0700
Subject: [PATCH 315/393] Remove unnecessary usings.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ServerSession.cs                     | 1 -
 src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs | 1 -
 tests/IntegrationTests/RedirectionTests.cs                   | 1 -
 3 files changed, 3 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index b38860da5..d6ed98905 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -7,7 +7,6 @@
 using System.Net.Security;
 using System.Net.Sockets;
 using System.Reflection;
-using System.Runtime.ConstrainedExecution;
 using System.Security.Authentication;
 using System.Security.Cryptography;
 using System.Security.Cryptography.X509Certificates;
diff --git a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
index 77e20d081..259f3f963 100644
--- a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
@@ -1,6 +1,5 @@
 using System.Buffers;
 using MySqlConnector.Protocol.Payloads;
-using MySqlConnector.Utilities;
 
 namespace MySqlConnector.Protocol.Serialization;
 
diff --git a/tests/IntegrationTests/RedirectionTests.cs b/tests/IntegrationTests/RedirectionTests.cs
index 892f1eb7f..f5f374eb9 100644
--- a/tests/IntegrationTests/RedirectionTests.cs
+++ b/tests/IntegrationTests/RedirectionTests.cs
@@ -1,5 +1,4 @@
 #if !MYSQL_DATA
-using System.Globalization;
 using System.Net;
 using System.Net.Sockets;
 

From 788107c70aaf22513afdb0835efe37ee512ca868 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 31 Aug 2024 08:42:02 -0700
Subject: [PATCH 316/393] Provide logger to new MySqlConnection. Fixes #1510

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/MySqlConnection.cs | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 4e3663e66..2d1b30531 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -1178,12 +1178,10 @@ private async Task DoCloseAsync(bool changeState, IOBehavior ioBehavior)
 			// This connection is being closed, so create a new MySqlConnection that will own the ServerSession
 			// (which remains open). This ensures the ServerSession always has a valid OwningConnection (even
 			// if 'this' is GCed.
-			var connection = new MySqlConnection
+			var connection = new MySqlConnection(this, m_dataSource, m_connectionString, hasBeenOpened: true)
 			{
-				m_connectionString = m_connectionString,
 				m_connectionSettings = m_connectionSettings,
 				m_connectionState = m_connectionState,
-				m_hasBeenOpened = true,
 			};
 			connection.TakeSessionFrom(this);
 

From f4bba7f1fb6093d4bd3a2f9606b53c7cd6fae886 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 2 Oct 2024 19:24:49 -0700
Subject: [PATCH 317/393] Bump
 advanced-security/component-detection-dependency-submission-action. (#1513)

Bumps [advanced-security/component-detection-dependency-submission-action](https://github.com/advanced-security/component-detection-dependency-submission-action) from 0.0.2 to 0.0.3.
- [Release notes](https://github.com/advanced-security/component-detection-dependency-submission-action/releases)
- [Commits](https://github.com/advanced-security/component-detection-dependency-submission-action/compare/v0.0.2...v0.0.3)

---
updated-dependencies:
- dependency-name: advanced-security/component-detection-dependency-submission-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4cc77bfca..4a9b14ef0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -27,4 +27,4 @@ jobs:
       run: dotnet build --configuration Release --no-restore
 
     - name: Component detection
-      uses: advanced-security/component-detection-dependency-submission-action@v0.0.2
+      uses: advanced-security/component-detection-dependency-submission-action@v0.0.3

From 8107efe38700db5b708a9c776719e5074594e299 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 2 Oct 2024 19:46:13 -0700
Subject: [PATCH 318/393] Add three new Connector/NET bugs.

---
 docs/content/tutorials/migrating-from-connector-net.md | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 967fd5c65..ae3ca6cd7 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -254,6 +254,9 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector.
 * [#115155](https://bugs.mysql.com/bug.php?id=115155): MySql.Data executes `SET NAMES` using an unsupported value
 * [#115168](https://bugs.mysql.com/bug.php?id=115168): `MySqlDataReader.GetX` performs culture-sensitive conversions
 * [#115222](https://bugs.mysql.com/bug.php?id=115222): `MySqlPool.ClearAsync` is not thread-safe
+* [#115937](https://bugs.mysql.com/bug.php?id=115937): Transaction isn't released after I/O timeout
+* [#116146](https://bugs.mysql.com/bug.php?id=116146): `MySqlConnection.OpenAsync` throws exception when `UseCompression = true`
+* [#116257](https://bugs.mysql.com/bug.php?id=116257): `NullReferenceException` thrown when using prepared statement under OpenTelemetry
 
 ### Fixed First by MySqlConnector
 

From f74ce0bfdacc2d88d58a735fd5f5abcf15264ba9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 4 Oct 2024 20:37:50 -0700
Subject: [PATCH 319/393] Implement core code to support multiple compression
 methods.

---
 src/MySqlConnector/Core/CompressionMethod.cs   |  8 ++++++++
 src/MySqlConnector/Core/ServerSession.cs       | 18 +++++++++++-------
 src/MySqlConnector/MySqlConnection.cs          |  2 ++
 src/MySqlConnector/MySqlConnector.csproj       |  2 ++
 src/MySqlConnector/MySqlDataSource.cs          |  8 ++++++--
 src/MySqlConnector/MySqlDataSourceBuilder.cs   |  6 +++++-
 src/MySqlConnector/Plugins/ZstandardPlugin.cs  |  9 +++++++++
 .../Payloads/HandshakeResponse41Payload.cs     | 17 +++++++++++------
 .../Protocol/ProtocolCapabilities.cs           | 12 ++++++++++++
 9 files changed, 66 insertions(+), 16 deletions(-)
 create mode 100644 src/MySqlConnector/Core/CompressionMethod.cs
 create mode 100644 src/MySqlConnector/Plugins/ZstandardPlugin.cs

diff --git a/src/MySqlConnector/Core/CompressionMethod.cs b/src/MySqlConnector/Core/CompressionMethod.cs
new file mode 100644
index 000000000..bd16884dd
--- /dev/null
+++ b/src/MySqlConnector/Core/CompressionMethod.cs
@@ -0,0 +1,8 @@
+namespace MySqlConnector.Core;
+
+internal enum CompressionMethod
+{
+	None,
+	Zlib,
+	Zstandard,
+}
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 69a288345..25e36f5a9 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -451,7 +451,9 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			ServerVersion = new(initialHandshake.ServerVersion);
 			ConnectionId = initialHandshake.ConnectionId;
 			AuthPluginData = initialHandshake.AuthPluginData;
-			m_useCompression = cs.UseCompression && (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0;
+			m_compressionMethod = !cs.UseCompression ? CompressionMethod.None :
+				((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.ZstandardCompressionAlgorithm) != 0 && connection.ZstandardPlugin is not null) ? CompressionMethod.Zstandard :
+				((initialHandshake.ProtocolCapabilities & ProtocolCapabilities.Compress) != 0) ? CompressionMethod.Zlib : CompressionMethod.None;
 			CancellationTimeout = cs.CancellationTimeout;
 			UserID = cs.UserID;
 
@@ -483,7 +485,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			else
 			{
 				// pipelining is not currently compatible with compression
-				m_supportsPipelining = !cs.UseCompression && cs.Pipelining is not false;
+				m_supportsPipelining = m_compressionMethod == CompressionMethod.None && cs.Pipelining is not false;
 
 				// for pipelining, concatenate reset connection and SET NAMES query into one buffer
 				if (m_supportsPipelining)
@@ -500,7 +502,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				}
 			}
 
-			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_useCompression, m_supportsConnectionAttributes, SupportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, SupportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
+			Log.SessionMadeConnection(m_logger, Id, ServerVersion.OriginalString, ConnectionId, m_compressionMethod != CompressionMethod.None, m_supportsConnectionAttributes, SupportsDeprecateEof, SupportsCachedPreparedMetadata, serverSupportsSsl, SupportsSessionTrack, m_supportsPipelining, SupportsQueryAttributes);
 
 			if (cs.SslMode != MySqlSslMode.None && (cs.SslMode != MySqlSslMode.Preferred || serverSupportsSsl))
 			{
@@ -517,7 +519,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				cs.ConnectionAttributes = CreateConnectionAttributes(cs.ApplicationName);
 
 			var password = GetPassword(cs, connection);
-			using (var handshakeResponsePayload = HandshakeResponse41Payload.Create(initialHandshake, cs, password, m_useCompression, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null))
+			using (var handshakeResponsePayload = HandshakeResponse41Payload.Create(initialHandshake, cs, password, m_compressionMethod, connection.ZstandardPlugin?.CompressionLevel, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null))
 				await SendReplyAsync(handshakeResponsePayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
@@ -570,8 +572,10 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 
 			var redirectionUrl = ok.RedirectionUrl;
 
-			if (m_useCompression)
+			if (m_compressionMethod == CompressionMethod.Zlib)
 				m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
+			else if (m_compressionMethod == CompressionMethod.Zstandard)
+				m_payloadHandler = connection.ZstandardPlugin!.CreatePayloadHandler(m_payloadHandler.ByteHandler);
 
 			// send 'SET NAMES' to set the character set and collation unless the server reports that it's already using the desired character set (e.g., MariaDB >= 11.5)
 			if (ok.NewCharacterSet != (ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4Binary : CharacterSet.Utf8Mb3Binary))
@@ -1632,7 +1636,7 @@ caCertificateChain is not null &&
 
 		var checkCertificateRevocation = cs.SslMode == MySqlSslMode.VerifyFull;
 
-		using (var initSsl = HandshakeResponse41Payload.CreateWithSsl(serverCapabilities, cs, m_useCompression, m_characterSet))
+		using (var initSsl = HandshakeResponse41Payload.CreateWithSsl(serverCapabilities, cs, m_compressionMethod, m_characterSet))
 			await SendReplyAsync(initSsl, ioBehavior, cancellationToken).ConfigureAwait(false);
 
 		var clientAuthenticationOptions = new SslClientAuthenticationOptions
@@ -2129,7 +2133,7 @@ protected override void OnStatementBegin(int index)
 	private SslStream? m_sslStream;
 	private X509Certificate2? m_clientCertificate;
 	private IPayloadHandler? m_payloadHandler;
-	private bool m_useCompression;
+	private CompressionMethod m_compressionMethod;
 	private bool m_isSecureConnection;
 	private bool m_supportsConnectionAttributes;
 	private bool m_supportsPipelining;
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 2d1b30531..bb6cbe795 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -11,6 +11,7 @@
 using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
 using MySqlConnector.Logging;
+using MySqlConnector.Plugins;
 using MySqlConnector.Protocol;
 using MySqlConnector.Protocol.Payloads;
 using MySqlConnector.Protocol.Serialization;
@@ -979,6 +980,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 
 	internal MySqlTransaction? CurrentTransaction { get; set; }
 	internal MySqlConnectorLoggingConfiguration LoggingConfiguration { get; }
+	internal ZstandardPlugin? ZstandardPlugin { get; set; }
 	internal bool AllowLoadLocalInfile => GetInitializedConnectionSettings().AllowLoadLocalInfile;
 	internal bool AllowUserVariables => GetInitializedConnectionSettings().AllowUserVariables;
 	internal bool AllowZeroDateTime => GetInitializedConnectionSettings().AllowZeroDateTime;
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 4cb3425f5..db3ec187d 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -35,6 +35,8 @@
 
   <ItemGroup>
     <InternalsVisibleTo Include="MySqlConnector.DependencyInjection.Tests" Key="00240000048000001402000006020000002400005253413100100000010001000521c81bf0f0ec7b261bb89bb583611d3767205d542c16c9353e317455acf612d3ec3dd03b77e7e6fda1aa8f15c58576d90dae0fb9f4fd4bd48709ae199b8c771963fa67d70b35f7ed2fbb6c60423935adfae0606716ea6ce31a1fcd56fdb206fc0c3b1205ec6ba56fb20c14c42105a601ddd0bfaea7207d535b29a39ffe82f00880f4f64f86e6bcf26eb5242a133bad9d7a32e3126036b68b13b413ce4097dfc18d9a5b1e494f1aed54dc84d7089fd0d931a49e679fdc7c8f07a5121df38ec27c2c9993a8f8f136b2937849aed32aef7324a5b7e482dc2eb693c7988f6074e82e75a41dd001587be4d79108588b25d40ed9aeb30ff921edaf509c94f71428e48219ba940f5f10c061421dc0c006e09feadec30df20b2d13d02c3ce4ceb32b6fbefd254288d45f3bb2c425b197e19699d7efdfc7aba5dd45b727bc98abd866d2f6e69e33a64e4b5a5ab1e4d749266c7bf285550da9fb036f10eff76b697de9c5ed8de4a3cdbca1174543540bed6c3a95641cfdacbac834896639f8a75ed1fb9cfd9983d83d0b43b76bd3894bd2b3da0dd23d1e0362985217f087acce1a7f56546c214890acae8fc60e27890ff31c38578f85e220342061a1a5c867362a14aafdffa003dc13af064f5f860d1757883ea5237feed3a6228c86200062bd88f5592d5c399ef270a562d458ae8eac5eaa382b5bcc3f64298cc34b4598f0b33d7943b8" />
+    <InternalsVisibleTo Include="MySqlConnector.Encryption" Key="0024000004800000940000000602000000240000525341310004000001000100e57cf9527549be513974d12b6730ff1db99b10a1498bb424ef0a14f0441f7043c5159f51629255107b314c710bf29463e1cfa4585732f560c3ef25207c7dfa57004d28927078a28b0ab7fa534403c18f2d5aaeda506a3d276c26b3bcc62f7fc8efe9aaf3e68b14b839ad10f10f42db834d170c96b1da9fc463231afe73bbaabe" />
+    <InternalsVisibleTo Include="MySqlConnector.Zstandard" Key="0024000004800000940000000602000000240000525341310004000001000100e57cf9527549be513974d12b6730ff1db99b10a1498bb424ef0a14f0441f7043c5159f51629255107b314c710bf29463e1cfa4585732f560c3ef25207c7dfa57004d28927078a28b0ab7fa534403c18f2d5aaeda506a3d276c26b3bcc62f7fc8efe9aaf3e68b14b839ad10f10f42db834d170c96b1da9fc463231afe73bbaabe" />
     <InternalsVisibleTo Include="MySqlConnector.Tests" Key="00240000048000001402000006020000002400005253413100100000010001000521c81bf0f0ec7b261bb89bb583611d3767205d542c16c9353e317455acf612d3ec3dd03b77e7e6fda1aa8f15c58576d90dae0fb9f4fd4bd48709ae199b8c771963fa67d70b35f7ed2fbb6c60423935adfae0606716ea6ce31a1fcd56fdb206fc0c3b1205ec6ba56fb20c14c42105a601ddd0bfaea7207d535b29a39ffe82f00880f4f64f86e6bcf26eb5242a133bad9d7a32e3126036b68b13b413ce4097dfc18d9a5b1e494f1aed54dc84d7089fd0d931a49e679fdc7c8f07a5121df38ec27c2c9993a8f8f136b2937849aed32aef7324a5b7e482dc2eb693c7988f6074e82e75a41dd001587be4d79108588b25d40ed9aeb30ff921edaf509c94f71428e48219ba940f5f10c061421dc0c006e09feadec30df20b2d13d02c3ce4ceb32b6fbefd254288d45f3bb2c425b197e19699d7efdfc7aba5dd45b727bc98abd866d2f6e69e33a64e4b5a5ab1e4d749266c7bf285550da9fb036f10eff76b697de9c5ed8de4a3cdbca1174543540bed6c3a95641cfdacbac834896639f8a75ed1fb9cfd9983d83d0b43b76bd3894bd2b3da0dd23d1e0362985217f087acce1a7f56546c214890acae8fc60e27890ff31c38578f85e220342061a1a5c867362a14aafdffa003dc13af064f5f860d1757883ea5237feed3a6228c86200062bd88f5592d5c399ef270a562d458ae8eac5eaa382b5bcc3f64298cc34b4598f0b33d7943b8" />
     <InternalsVisibleTo Include="IntegrationTests" Key="00240000048000001402000006020000002400005253413100100000010001000521c81bf0f0ec7b261bb89bb583611d3767205d542c16c9353e317455acf612d3ec3dd03b77e7e6fda1aa8f15c58576d90dae0fb9f4fd4bd48709ae199b8c771963fa67d70b35f7ed2fbb6c60423935adfae0606716ea6ce31a1fcd56fdb206fc0c3b1205ec6ba56fb20c14c42105a601ddd0bfaea7207d535b29a39ffe82f00880f4f64f86e6bcf26eb5242a133bad9d7a32e3126036b68b13b413ce4097dfc18d9a5b1e494f1aed54dc84d7089fd0d931a49e679fdc7c8f07a5121df38ec27c2c9993a8f8f136b2937849aed32aef7324a5b7e482dc2eb693c7988f6074e82e75a41dd001587be4d79108588b25d40ed9aeb30ff921edaf509c94f71428e48219ba940f5f10c061421dc0c006e09feadec30df20b2d13d02c3ce4ceb32b6fbefd254288d45f3bb2c425b197e19699d7efdfc7aba5dd45b727bc98abd866d2f6e69e33a64e4b5a5ab1e4d749266c7bf285550da9fb036f10eff76b697de9c5ed8de4a3cdbca1174543540bed6c3a95641cfdacbac834896639f8a75ed1fb9cfd9983d83d0b43b76bd3894bd2b3da0dd23d1e0362985217f087acce1a7f56546c214890acae8fc60e27890ff31c38578f85e220342061a1a5c867362a14aafdffa003dc13af064f5f860d1757883ea5237feed3a6228c86200062bd88f5592d5c399ef270a562d458ae8eac5eaa382b5bcc3f64298cc34b4598f0b33d7943b8" />
     <Using Include="System.Data" />
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index a975b60c2..0c4372fd4 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -3,6 +3,7 @@
 using Microsoft.Extensions.Logging;
 using MySqlConnector.Core;
 using MySqlConnector.Logging;
+using MySqlConnector.Plugins;
 using MySqlConnector.Protocol.Serialization;
 
 namespace MySqlConnector;
@@ -18,7 +19,7 @@ public sealed class MySqlDataSource : DbDataSource
 	/// <param name="connectionString">The connection string for the MySQL Server. This parameter is required.</param>
 	/// <exception cref="ArgumentNullException">Thrown if <paramref name="connectionString"/> is <c>null</c>.</exception>
 	public MySqlDataSource(string connectionString)
-		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, default, default)
+		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, default, default, default)
 	{
 	}
 
@@ -29,7 +30,8 @@ internal MySqlDataSource(string connectionString,
 		RemoteCertificateValidationCallback? remoteCertificateValidationCallback,
 		Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? periodicPasswordProvider,
 		TimeSpan periodicPasswordProviderSuccessRefreshInterval,
-		TimeSpan periodicPasswordProviderFailureRefreshInterval)
+		TimeSpan periodicPasswordProviderFailureRefreshInterval,
+		ZstandardPlugin? zstandardPlugin)
 	{
 		m_connectionString = connectionString;
 		LoggingConfiguration = loggingConfiguration;
@@ -37,6 +39,7 @@ internal MySqlDataSource(string connectionString,
 		m_clientCertificatesCallback = clientCertificatesCallback;
 		m_remoteCertificateValidationCallback = remoteCertificateValidationCallback;
 		m_logger = loggingConfiguration.DataSourceLogger;
+		m_zstandardPlugin = zstandardPlugin;
 
 		Pool = ConnectionPool.CreatePool(m_connectionString, LoggingConfiguration, name);
 		m_id = Interlocked.Increment(ref s_lastId);
@@ -221,6 +224,7 @@ private string ProvidePasswordFromInitialRefreshTask(MySqlProvidePasswordContext
 	private readonly Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? m_periodicPasswordProvider;
 	private readonly TimeSpan m_periodicPasswordProviderSuccessRefreshInterval;
 	private readonly TimeSpan m_periodicPasswordProviderFailureRefreshInterval;
+	private readonly ZstandardPlugin? m_zstandardPlugin;
 	private readonly MySqlProvidePasswordContext? m_providePasswordContext;
 	private readonly CancellationTokenSource? m_passwordProviderTimerCancellationTokenSource;
 	private readonly Timer? m_passwordProviderTimer;
diff --git a/src/MySqlConnector/MySqlDataSourceBuilder.cs b/src/MySqlConnector/MySqlDataSourceBuilder.cs
index fbe902b40..e1d15d183 100644
--- a/src/MySqlConnector/MySqlDataSourceBuilder.cs
+++ b/src/MySqlConnector/MySqlDataSourceBuilder.cs
@@ -2,6 +2,7 @@
 using System.Security.Cryptography.X509Certificates;
 using Microsoft.Extensions.Logging;
 using MySqlConnector.Logging;
+using MySqlConnector.Plugins;
 
 namespace MySqlConnector;
 
@@ -102,7 +103,8 @@ public MySqlDataSource Build()
 			m_remoteCertificateValidationCallback,
 			m_periodicPasswordProvider,
 			m_periodicPasswordProviderSuccessRefreshInterval,
-			m_periodicPasswordProviderFailureRefreshInterval
+			m_periodicPasswordProviderFailureRefreshInterval,
+			ZstandardPlugin
 			);
 	}
 
@@ -111,6 +113,8 @@ public MySqlDataSource Build()
 	/// </summary>
 	public MySqlConnectionStringBuilder ConnectionStringBuilder { get; }
 
+	internal ZstandardPlugin? ZstandardPlugin { get; set; }
+
 	private ILoggerFactory? m_loggerFactory;
 	private string? m_name;
 	private Func<X509CertificateCollection, ValueTask>? m_clientCertificatesCallback;
diff --git a/src/MySqlConnector/Plugins/ZstandardPlugin.cs b/src/MySqlConnector/Plugins/ZstandardPlugin.cs
new file mode 100644
index 000000000..3696067c3
--- /dev/null
+++ b/src/MySqlConnector/Plugins/ZstandardPlugin.cs
@@ -0,0 +1,9 @@
+using MySqlConnector.Protocol.Serialization;
+
+namespace MySqlConnector.Plugins;
+
+internal abstract class ZstandardPlugin
+{
+	public abstract IPayloadHandler CreatePayloadHandler(IByteHandler byteHandler);
+	public abstract int CompressionLevel { get; }
+}
diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
index 4628d1507..609fcfd45 100644
--- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
@@ -5,7 +5,7 @@ namespace MySqlConnector.Protocol.Payloads;
 
 internal static class HandshakeResponse41Payload
 {
-	private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, bool useCompression, CharacterSet characterSet, ProtocolCapabilities additionalCapabilities = 0)
+	private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, CompressionMethod compressionMethod, CharacterSet characterSet, ProtocolCapabilities additionalCapabilities = 0)
 	{
 		var writer = new ByteBufferWriter();
 
@@ -22,10 +22,11 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 			(cs.AllowLoadLocalInfile ? ProtocolCapabilities.LocalFiles : 0) |
 			(string.IsNullOrWhiteSpace(cs.Database) ? 0 : ProtocolCapabilities.ConnectWithDatabase) |
 			(cs.UseAffectedRows ? 0 : ProtocolCapabilities.FoundRows) |
-			(useCompression ? ProtocolCapabilities.Compress : ProtocolCapabilities.None) |
+			(compressionMethod == CompressionMethod.Zlib ? ProtocolCapabilities.Compress : ProtocolCapabilities.None) |
 			(serverCapabilities & ProtocolCapabilities.ConnectionAttributes) |
 			(serverCapabilities & ProtocolCapabilities.SessionTrack) |
 			(serverCapabilities & ProtocolCapabilities.DeprecateEof) |
+			(compressionMethod == CompressionMethod.Zstandard ? ProtocolCapabilities.ZstandardCompressionAlgorithm : 0) |
 			(serverCapabilities & ProtocolCapabilities.QueryAttributes) |
 			(serverCapabilities & ProtocolCapabilities.MariaDbCacheMetadata) |
 			additionalCapabilities;
@@ -51,13 +52,13 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 		return writer;
 	}
 
-	public static PayloadData CreateWithSsl(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, bool useCompression, CharacterSet characterSet) =>
-		CreateCapabilitiesPayload(serverCapabilities, cs, useCompression, characterSet, ProtocolCapabilities.Ssl).ToPayloadData();
+	public static PayloadData CreateWithSsl(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, CompressionMethod compressionMethod, CharacterSet characterSet) =>
+		CreateCapabilitiesPayload(serverCapabilities, cs, compressionMethod, characterSet, ProtocolCapabilities.Ssl).ToPayloadData();
 
-	public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSettings cs, string password, bool useCompression, CharacterSet characterSet, byte[]? connectionAttributes)
+	public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSettings cs, string password, CompressionMethod compressionMethod, int? compressionLevel, CharacterSet characterSet, byte[]? connectionAttributes)
 	{
 		// TODO: verify server capabilities
-		var writer = CreateCapabilitiesPayload(handshake.ProtocolCapabilities, cs, useCompression, characterSet);
+		var writer = CreateCapabilitiesPayload(handshake.ProtocolCapabilities, cs, compressionMethod, characterSet);
 		writer.WriteNullTerminatedString(cs.UserID);
 		var authenticationResponse = AuthenticationUtility.CreateAuthenticationResponse(handshake.AuthPluginData, password);
 		writer.Write((byte) authenticationResponse.Length);
@@ -72,6 +73,10 @@ public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSe
 		if (connectionAttributes is not null)
 			writer.Write(connectionAttributes);
 
+		// Zstandard compression level
+		if (compressionMethod == CompressionMethod.Zstandard)
+			writer.Write((byte) (compressionLevel ?? 10));
+
 		return writer.ToPayloadData();
 	}
 }
diff --git a/src/MySqlConnector/Protocol/ProtocolCapabilities.cs b/src/MySqlConnector/Protocol/ProtocolCapabilities.cs
index aca015562..9351dcc85 100644
--- a/src/MySqlConnector/Protocol/ProtocolCapabilities.cs
+++ b/src/MySqlConnector/Protocol/ProtocolCapabilities.cs
@@ -128,6 +128,18 @@ internal enum ProtocolCapabilities : ulong
 	/// </summary>
 	DeprecateEof = 0x100_0000,
 
+	/// <summary>
+	/// The client can handle optional metadata information in the resultset.
+	/// </summary>
+	/// <remarks>Corresponds to CLIENT_OPTIONAL_RESULTSET_METADATA.</remarks>
+	ClientOptionalResultsetMetadata = 0x200_0000,
+
+	/// <summary>
+	/// The client supports the Zstandard compression algorithm.
+	/// </summary>
+	/// <remarks>Corresponds to CLIENT_ZSTD_COMPRESSION_ALGORITHM.</remarks>
+	ZstandardCompressionAlgorithm = 0x400_0000,
+
 	/// <summary>
 	/// Supports query attributes (CLIENT_QUERY_ATTRIBUTES).
 	/// </summary>

From 9d17f7fc7cc2349a6bebb4efe82d7051b2b07e6a Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Fri, 4 Oct 2024 20:16:34 -0700
Subject: [PATCH 320/393] Use correct hostname when cancelling query. Fixes
 #1514

Connect directly to the cancelled command's session's IP address but maintain the hostname for SSL certificate validation.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ConnectionSettings.cs | 11 ++++++++---
 src/MySqlConnector/Core/ServerSession.cs      | 15 +++++++++++----
 src/MySqlConnector/MySqlConnection.cs         | 15 +++++++++++++--
 3 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/src/MySqlConnector/Core/ConnectionSettings.cs b/src/MySqlConnector/Core/ConnectionSettings.cs
index 8b365833c..f43162fbe 100644
--- a/src/MySqlConnector/Core/ConnectionSettings.cs
+++ b/src/MySqlConnector/Core/ConnectionSettings.cs
@@ -1,6 +1,7 @@
 #if NETCOREAPP3_0_OR_GREATER
 using System.Net.Security;
 #endif
+using System.Net;
 using System.Security.Authentication;
 using MySqlConnector.Utilities;
 
@@ -150,7 +151,9 @@ public ConnectionSettings(MySqlConnectionStringBuilder csb)
 		static int ToSigned(uint value) => value >= int.MaxValue ? int.MaxValue : (int) value;
 	}
 
-	public ConnectionSettings CloneWith(string host, int port, string userId) => new(this, host, port, userId);
+	public ConnectionSettings CloneWith(string host, int port, string userId) => new(this, host, port, userId, null);
+
+	public ConnectionSettings CloneWith(IPAddress ipAddress) => new(this, HostNames![0], Port, UserID, ipAddress);
 
 	private static MySqlGuidFormat GetEffectiveGuidFormat(MySqlGuidFormat guidFormat, bool oldGuids)
 	{
@@ -182,6 +185,7 @@ private static MySqlGuidFormat GetEffectiveGuidFormat(MySqlGuidFormat guidFormat
 	public string ConnectionString { get; }
 	public MySqlConnectionProtocol ConnectionProtocol { get; }
 	public IReadOnlyList<string>? HostNames { get; }
+	public IPAddress? IPAddress { get; }
 	public MySqlLoadBalance LoadBalance { get; }
 	public int Port { get; }
 	public string PipeName { get; }
@@ -268,10 +272,10 @@ public int ConnectionTimeoutMilliseconds
 		}
 	}
 
-	private ConnectionSettings(ConnectionSettings other, string host, int port, string userId)
+	private ConnectionSettings(ConnectionSettings other, string host, int port, string userId, IPAddress? ipAddress)
 	{
 		ConnectionStringBuilder = new MySqlConnectionStringBuilder(other.ConnectionString);
-		ConnectionStringBuilder.Port = (uint)port;
+		ConnectionStringBuilder.Port = (uint) port;
 		ConnectionStringBuilder.Server = host;
 		ConnectionStringBuilder.UserID = userId;
 
@@ -279,6 +283,7 @@ private ConnectionSettings(ConnectionSettings other, string host, int port, stri
 
 		ConnectionProtocol = MySqlConnectionProtocol.Sockets;
 		HostNames = [host];
+		IPAddress = ipAddress;
 		LoadBalance = other.LoadBalance;
 		Port = port;
 		PipeName = other.PipeName;
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 25e36f5a9..93112e16f 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1164,13 +1164,20 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 			IPAddress[] ipAddresses;
 			try
 			{
-				ipAddresses = ioBehavior == IOBehavior.Asynchronous
+				if (cs.IPAddress is { } ipAddress)
+				{
+					ipAddresses = [ipAddress];
+				}
+				else
+				{
+					ipAddresses = ioBehavior == IOBehavior.Asynchronous
 #if NET6_0_OR_GREATER
-					? await Dns.GetHostAddressesAsync(hostName, cancellationToken).ConfigureAwait(false)
+						? await Dns.GetHostAddressesAsync(hostName, cancellationToken).ConfigureAwait(false)
 #else
-					? await Dns.GetHostAddressesAsync(hostName).ConfigureAwait(false)
+						? await Dns.GetHostAddressesAsync(hostName).ConfigureAwait(false)
 #endif
-					: Dns.GetHostAddresses(hostName);
+						: Dns.GetHostAddresses(hostName);
+				}
 			}
 			catch (SocketException ex)
 			{
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index bb6cbe795..b98c330f7 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -896,16 +896,27 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 				AutoEnlist = false,
 				Pooling = false,
 			};
-			if (session.IPEndPoint is { Address: { } ipAddress, Port: { } port })
+
+			// connect directly to the session's IP address to ensure we're cancelling the query on the right server (in a load-balanced scenario)
+			IPAddress? ipAddress = null;
+			if (session.IPEndPoint is { Address: { } sessionIpAddress, Port: { } port })
 			{
-				csb.Server = ipAddress.ToString();
+				// set the hostname to the existing session's hostname (for SSL validation)
+				csb.Server = session.HostName;
 				csb.Port = (uint) port;
+				ipAddress = sessionIpAddress;
 			}
 			csb.UserID = session.UserID;
 			var cancellationTimeout = GetConnectionSettings().CancellationTimeout;
 			csb.ConnectionTimeout = cancellationTimeout < 1 ? 3u : (uint) cancellationTimeout;
 
+			// forcibly set the IP address the new connection should use
+			var connectionSettings = new ConnectionSettings(csb);
+			if (ipAddress is not null)
+				connectionSettings = connectionSettings.CloneWith(ipAddress);
+
 			using var connection = CloneWith(csb.ConnectionString);
+			connection.m_connectionSettings = connectionSettings;
 			connection.Open();
 #if NET6_0_OR_GREATER
 			var killQuerySql = string.Create(CultureInfo.InvariantCulture, $"KILL QUERY {command.Connection!.ServerThread}");

From e93cbf349c8b86ce0fc578e274f30ccf105f56ad Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 5 Oct 2024 12:15:08 -0700
Subject: [PATCH 321/393] Update artifact build tasks.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .ci/build-steps.yml                | 25 +++++++++++++++----------
 .ci/conformance-test-steps.yml     |  3 ++-
 .ci/integration-test-steps.yml     |  5 +++--
 .ci/mysqlconnector-tests-steps.yml |  6 ++++--
 4 files changed, 24 insertions(+), 15 deletions(-)

diff --git a/.ci/build-steps.yml b/.ci/build-steps.yml
index 384c6aed7..b3d6d0895 100644
--- a/.ci/build-steps.yml
+++ b/.ci/build-steps.yml
@@ -30,10 +30,11 @@ steps:
     arguments: '-c Release -f net9.0 --no-build tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
-- task: PublishPipelineArtifact@0
+- task: PublishPipelineArtifact@1
   inputs:
-    artifactName: 'MySqlConnector.Tests-9.0-$(Agent.OS)'
+    artifact: 'MySqlConnector.Tests-9.0-$(Agent.OS)'
     targetPath: 'artifacts/publish/MySqlConnector.Tests/release_net9.0'
+    publishLocation: 'pipeline'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish Conformance.Tests'
@@ -42,10 +43,11 @@ steps:
     arguments: '-c Release -f net9.0 --no-build tests/Conformance.Tests/Conformance.Tests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
-- task: PublishPipelineArtifact@0
+- task: PublishPipelineArtifact@1
   inputs:
-    artifactName: 'Conformance.Tests-9.0-$(Agent.OS)'
+    artifact: 'Conformance.Tests-9.0-$(Agent.OS)'
     targetPath: 'artifacts/publish/Conformance.Tests/release_net9.0'
+    publishLocation: 'pipeline'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish MySqlConnector.DependencyInjection.Tests'
@@ -54,10 +56,11 @@ steps:
     arguments: '-c Release -f net9.0 --no-build tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
-- task: PublishPipelineArtifact@0
+- task: PublishPipelineArtifact@1
   inputs:
-    artifactName: 'MySqlConnector.DependencyInjection.Tests-9.0-$(Agent.OS)'
+    artifact: 'MySqlConnector.DependencyInjection.Tests-9.0-$(Agent.OS)'
     targetPath: 'artifacts/publish/MySqlConnector.DependencyInjection.Tests/release_net9.0'
+    publishLocation: 'pipeline'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish IntegrationTests (9.0)'
@@ -66,10 +69,11 @@ steps:
     arguments: '-c Release -f net9.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
-- task: PublishPipelineArtifact@0
+- task: PublishPipelineArtifact@1
   inputs:
-    artifactName: 'IntegrationTests-net9.0-$(Agent.OS)'
+    artifact: 'IntegrationTests-net9.0-$(Agent.OS)'
     targetPath: 'artifacts/publish/IntegrationTests/release_net9.0'
+    publishLocation: 'pipeline'
 
 - task: DotNetCoreCLI@2
   displayName: 'Publish IntegrationTests (8.0)'
@@ -78,7 +82,8 @@ steps:
     arguments: '-c Release -f net8.0 --no-build tests/IntegrationTests/IntegrationTests.csproj'
     publishWebProjects: false
     zipAfterPublish: false
-- task: PublishPipelineArtifact@0
+- task: PublishPipelineArtifact@1
   inputs:
-    artifactName: 'IntegrationTests-net8.0-$(Agent.OS)'
+    artifact: 'IntegrationTests-net8.0-$(Agent.OS)'
     targetPath: 'artifacts/publish/IntegrationTests/release_net8.0'
+    publishLocation: 'pipeline'
diff --git a/.ci/conformance-test-steps.yml b/.ci/conformance-test-steps.yml
index 349663215..87cdf035e 100644
--- a/.ci/conformance-test-steps.yml
+++ b/.ci/conformance-test-steps.yml
@@ -7,9 +7,10 @@ steps:
   condition: always()
   inputs:
     contents: '**/*.trx'
-- task: DownloadPipelineArtifact@0
+- task: DownloadPipelineArtifact@2
   condition: always()
   inputs:
+    buildType: 'current'
     artifactName: 'Conformance.Tests-9.0-$(Agent.OS)'
     targetPath: '$(Build.BinariesDirectory)/9.0'
 - task: DotNetCoreCLI@2
diff --git a/.ci/integration-test-steps.yml b/.ci/integration-test-steps.yml
index c4c4d3135..f45f7ac9c 100644
--- a/.ci/integration-test-steps.yml
+++ b/.ci/integration-test-steps.yml
@@ -10,9 +10,10 @@ steps:
   condition: always()
   inputs:
     contents: '**/*.trx'
-- task: DownloadPipelineArtifact@0
+- task: DownloadPipelineArtifact@2
   condition: always()
   inputs:
+    buildType: 'current'
     artifactName: ${{ format('IntegrationTests-{0}-$(Agent.OS)', parameters.platform) }}
     targetPath: ${{ format('$(Build.BinariesDirectory)/{0}', parameters.platform) }}
 - task: DotNetCoreCLI@2
@@ -35,4 +36,4 @@ steps:
     testResultsFormat: VSTest
     testResultsFiles: '**/*.trx'
     testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', parameters.image, parameters.platform, parameters.description) }}
-    failTaskOnFailedTests: true
\ No newline at end of file
+    failTaskOnFailedTests: true
diff --git a/.ci/mysqlconnector-tests-steps.yml b/.ci/mysqlconnector-tests-steps.yml
index ef0e6a095..0ddc01fc9 100644
--- a/.ci/mysqlconnector-tests-steps.yml
+++ b/.ci/mysqlconnector-tests-steps.yml
@@ -4,8 +4,9 @@ steps:
   inputs:
     version: $(DotNetCoreSdkVersion)
     includePreviewVersions: true
-- task: DownloadPipelineArtifact@0
+- task: DownloadPipelineArtifact@2
   inputs:
+    buildType: 'current'
     artifactName: 'MySqlConnector.Tests-9.0-$(Agent.OS)'
     targetPath: $(System.DefaultWorkingDirectory)
 - task: DotNetCoreCLI@2
@@ -14,8 +15,9 @@ steps:
     command: 'custom'
     custom: 'vstest'
     arguments: 'MySqlConnector.Tests.dll /logger:trx'
-- task: DownloadPipelineArtifact@0
+- task: DownloadPipelineArtifact@2
   inputs:
+    buildType: 'current'
     artifactName: 'MySqlConnector.DependencyInjection.Tests-9.0-$(Agent.OS)'
     targetPath: $(System.DefaultWorkingDirectory)
 - task: DotNetCoreCLI@2

From 6010787cff0d72dd5a942243f612c06c68effda8 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 5 Oct 2024 13:43:21 -0700
Subject: [PATCH 322/393] Remove TABLESPACES schema. Fixes #1477

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 docs/content/overview/schema-collections.md   |  3 +--
 src/MySqlConnector/Core/SchemaProvider.g.cs   | 25 -------------------
 .../SchemaCollections.yaml                    | 22 ----------------
 3 files changed, 1 insertion(+), 49 deletions(-)

diff --git a/docs/content/overview/schema-collections.md b/docs/content/overview/schema-collections.md
index d8a4178b4..36ba177ca 100644
--- a/docs/content/overview/schema-collections.md
+++ b/docs/content/overview/schema-collections.md
@@ -1,6 +1,6 @@
 ---
-lastmod: 2023-12-15
 date: 2021-04-24
+lastmod: 2024-10-05
 title: Schema Collections
 customtitle: "Supported Schema Collections"
 description: Information about the schemas supported by MySqlConnection.GetSchema.
@@ -41,7 +41,6 @@ weight: 80
 * `Tables`—[information about tables](../schema/tables/)
 * `TableConstraints`
 * `TablePrivileges`
-* `TableSpaces`
 * `Triggers`
 * `UserPrivileges`
 * `Views`
diff --git a/src/MySqlConnector/Core/SchemaProvider.g.cs b/src/MySqlConnector/Core/SchemaProvider.g.cs
index b3575bc83..adacc5473 100644
--- a/src/MySqlConnector/Core/SchemaProvider.g.cs
+++ b/src/MySqlConnector/Core/SchemaProvider.g.cs
@@ -67,8 +67,6 @@ public async ValueTask<DataTable> GetSchemaAsync(IOBehavior ioBehavior, string c
 			await FillTableConstraintsAsync(ioBehavior, dataTable, "TableConstraints", restrictionValues, cancellationToken).ConfigureAwait(false);
 		else if (string.Equals(collectionName, "TablePrivileges", StringComparison.OrdinalIgnoreCase))
 			await FillTablePrivilegesAsync(ioBehavior, dataTable, "TablePrivileges", restrictionValues, cancellationToken).ConfigureAwait(false);
-		else if (string.Equals(collectionName, "TableSpaces", StringComparison.OrdinalIgnoreCase))
-			await FillTableSpacesAsync(ioBehavior, dataTable, "TableSpaces", restrictionValues, cancellationToken).ConfigureAwait(false);
 		else if (string.Equals(collectionName, "Triggers", StringComparison.OrdinalIgnoreCase))
 			await FillTriggersAsync(ioBehavior, dataTable, "Triggers", restrictionValues, cancellationToken).ConfigureAwait(false);
 		else if (string.Equals(collectionName, "UserPrivileges", StringComparison.OrdinalIgnoreCase))
@@ -125,7 +123,6 @@ private Task FillMetaDataCollectionsAsync(IOBehavior ioBehavior, DataTable dataT
 		dataTable.Rows.Add("Tables", 4, 3);
 		dataTable.Rows.Add("TableConstraints", 0, 3);
 		dataTable.Rows.Add("TablePrivileges", 0, 0);
-		dataTable.Rows.Add("TableSpaces", 0, 0);
 		dataTable.Rows.Add("Triggers", 0, 3);
 		dataTable.Rows.Add("UserPrivileges", 0, 0);
 		dataTable.Rows.Add("Views", 0, 3);
@@ -757,28 +754,6 @@ private async Task FillTablePrivilegesAsync(IOBehavior ioBehavior, DataTable dat
 		await FillDataTableAsync(ioBehavior, dataTable, "TABLE_PRIVILEGES", null, cancellationToken).ConfigureAwait(false);
 	}
 
-	private async Task FillTableSpacesAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
-	{
-		if (restrictionValues is not null && restrictionValues.Length > 0)
-			throw new ArgumentException("restrictionValues is not supported for schema 'TableSpaces'.", nameof(restrictionValues));
-
-		dataTable.TableName = tableName;
-		dataTable.Columns.AddRange(
-		[
-			new("TABLESPACE_NAME", typeof(string)),
-			new("ENGINE", typeof(string)),
-			new("TABLESPACE_TYPE", typeof(string)),
-			new("LOGFILE_GROUP_NAME", typeof(string)),
-			new("EXTENT_SIZE", typeof(long)),
-			new("AUTOEXTEND_SIZE", typeof(long)),
-			new("MAXIMUM_SIZE", typeof(long)),
-			new("NODEGROUP_ID", typeof(long)),
-			new("TABLESPACE_COMMENT", typeof(string)),
-		]);
-
-		await FillDataTableAsync(ioBehavior, dataTable, "TABLESPACES", null, cancellationToken).ConfigureAwait(false);
-	}
-
 	private async Task FillTriggersAsync(IOBehavior ioBehavior, DataTable dataTable, string tableName, string?[]? restrictionValues, CancellationToken cancellationToken)
 	{
 		if (restrictionValues is not null && restrictionValues.Length > 0)
diff --git a/tools/SchemaCollectionGenerator/SchemaCollections.yaml b/tools/SchemaCollectionGenerator/SchemaCollections.yaml
index 770166cae..47c464b7c 100644
--- a/tools/SchemaCollectionGenerator/SchemaCollections.yaml
+++ b/tools/SchemaCollectionGenerator/SchemaCollections.yaml
@@ -652,28 +652,6 @@
   - name: IS_GRANTABLE
     type: string
 
-- name: TableSpaces
-  table: TABLESPACES
-  columns:
-  - name: TABLESPACE_NAME
-    type: string
-  - name: ENGINE
-    type: string
-  - name: TABLESPACE_TYPE
-    type: string
-  - name: LOGFILE_GROUP_NAME
-    type: string
-  - name: EXTENT_SIZE
-    type: long
-  - name: AUTOEXTEND_SIZE
-    type: long
-  - name: MAXIMUM_SIZE
-    type: long
-  - name: NODEGROUP_ID
-    type: long
-  - name: TABLESPACE_COMMENT
-    type: string
-
 - name: Triggers
   table: TRIGGERS
   identifierPartCount: 3

From 0ce83d750cd5b73539f6ff18d6853304aa9609e7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 5 Oct 2024 17:19:37 -0700
Subject: [PATCH 323/393] Change variable name to avoid PUA character. Fixes
 #1502

This should improve compatibility with servers that don't accept Unicode characters in identifiers.
---
 src/MySqlConnector/Core/ServerSession.cs                | 4 ++--
 tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index d6d613ed6..3ec67f156 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -2131,8 +2131,8 @@ protected override void OnStatementBegin(int index)
 	private static readonly PayloadData s_setNamesUtf8mb4NoAttributesPayload = QueryPayload.Create(false, "SET NAMES utf8mb4;"u8);
 	private static readonly PayloadData s_setNamesUtf8WithAttributesPayload = QueryPayload.Create(true, "SET NAMES utf8;"u8);
 	private static readonly PayloadData s_setNamesUtf8mb4WithAttributesPayload = QueryPayload.Create(true, "SET NAMES utf8mb4;"u8);
-	private static readonly PayloadData s_sleepNoAttributesPayload = QueryPayload.Create(false, "SELECT SLEEP(0) INTO @\uE001MySqlConnector\uE001Sleep;"u8);
-	private static readonly PayloadData s_sleepWithAttributesPayload = QueryPayload.Create(true, "SELECT SLEEP(0) INTO @\uE001MySqlConnector\uE001Sleep;"u8);
+	private static readonly PayloadData s_sleepNoAttributesPayload = QueryPayload.Create(false, "SELECT SLEEP(0) INTO @__MySqlConnector__Sleep;"u8);
+	private static readonly PayloadData s_sleepWithAttributesPayload = QueryPayload.Create(true, "SELECT SLEEP(0) INTO @__MySqlConnector__Sleep;"u8);
 	private static readonly PayloadData s_selectConnectionIdVersionNoAttributesPayload = QueryPayload.Create(false, "SELECT CONNECTION_ID(), VERSION();"u8);
 	private static readonly PayloadData s_selectConnectionIdVersionWithAttributesPayload = QueryPayload.Create(true, "SELECT CONNECTION_ID(), VERSION();"u8);
 
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
index 2d1447779..6081d7535 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
@@ -158,7 +158,7 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 								m_server.CancelQuery(connectionId);
 								await SendAsync(stream, 1, WriteOk);
 							}
-							else if (query == "SELECT SLEEP(0) INTO @\uE001MySqlConnector\uE001Sleep;")
+							else if (query == "SELECT SLEEP(0) INTO @__MySqlConnector__Sleep;")
 							{
 								var wasSet = CancelQueryEvent.Wait(0, token);
 								await SendAsync(stream, 1, WriteOk);

From 34621d421526f76bff1c819d3186bebde5edfb46 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 5 Oct 2024 18:06:59 -0700
Subject: [PATCH 324/393] Use RawDataMemory in .NET 7.

---
 src/MySqlConnector/Core/ServerSession.cs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 3ec67f156..5d3e09dfd 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1595,7 +1595,11 @@ caCertificateChain is not null &&
 					var rootCertificate = caCertificateChain.ChainElements[^1].Certificate;
 					foreach (var sslCaCertificate in caCertificateChain.ChainPolicy.ExtraStore)
 					{
+#if NET7_0_OR_GREATER
+						if (rootCertificate.RawDataMemory.Span.SequenceEqual(sslCaCertificate.RawDataMemory.Span))
+#else
 						if (rootCertificate.RawData.AsSpan().SequenceEqual(sslCaCertificate.RawData))
+#endif
 						{
 							rcbPolicyErrors &= ~SslPolicyErrors.RemoteCertificateChainErrors;
 							break;

From 633a65b3263ed1f3a3e67605ef2e93dcd9b24907 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 9 Oct 2024 07:18:27 -0700
Subject: [PATCH 325/393] Update NuGet packages for tests.

---
 Directory.Packages.props                             | 12 ++++++------
 .../MySqlParameterAppendBinaryTests.cs               |  2 +-
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 961777baa..9cd40df8a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -11,26 +11,26 @@
     <PackageVersion Include="log4net" Version="2.0.10" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
-    <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.0" />
+    <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
-    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
+    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
     <PackageVersion Include="MySql.Data" Version="9.0.0" />
     <PackageVersion Include="NLog" Version="4.5.0" />
     <PackageVersion Include="Serilog" Version="2.6.0" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
-    <PackageVersion Include="System.Text.Json" Version="8.0.4" />
+    <PackageVersion Include="System.Text.Json" Version="8.0.5" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageVersion Include="xunit" Version="2.9.0" />
+    <PackageVersion Include="xunit" Version="2.9.2" />
     <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
-    <PackageVersion Include="YamlDotNet" Version="15.3.0" />
+    <PackageVersion Include="YamlDotNet" Version="16.1.3" />
   </ItemGroup>
 
   <ItemGroup>
     <GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
-    <GlobalPackageReference Include="MinVer" Version="5.0.0" />
+    <GlobalPackageReference Include="MinVer" Version="6.0.0" />
   </ItemGroup>
 
 </Project>
diff --git a/tests/MySqlConnector.Tests/MySqlParameterAppendBinaryTests.cs b/tests/MySqlConnector.Tests/MySqlParameterAppendBinaryTests.cs
index 948332773..d5a9a3835 100644
--- a/tests/MySqlConnector.Tests/MySqlParameterAppendBinaryTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterAppendBinaryTests.cs
@@ -22,6 +22,6 @@ public void WriteBinaryEnumType(object value, MySqlDbType expectedMySqlDbType, b
 
 		Assert.Equal(parameter.MySqlDbType, expectedMySqlDbType);
 		Assert.Equal(writer.Position, expectedBinary.Length);
-		Assert.Equal(writer.ArraySegment, expectedBinary);
+		Assert.Equal(writer.ArraySegment.ToArray(), expectedBinary);
 	}
 }

From b78e43f8aad7dcb30713c6724a68ea4f5f4dbdb4 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 13 Oct 2024 16:32:00 -0700
Subject: [PATCH 326/393] Add ConnectionOpenedCallback. Fixes #1508 (#1515)

This differs from the existing StateChanged event in that:

- it supports an async callback
- it's only invoked when a connection is opened
- it provides information about new vs existing and whether the connection was reset

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ServerSession.cs      |   5 +
 src/MySqlConnector/MySqlConnection.cs         |  20 +++
 .../MySqlConnectionOpenedCallback.cs          |   9 ++
 .../MySqlConnectionOpenedConditions.cs        |  23 +++
 .../MySqlConnectionOpenedContext.cs           |  23 +++
 src/MySqlConnector/MySqlDataSource.cs         |   8 +-
 src/MySqlConnector/MySqlDataSourceBuilder.cs  |  15 +-
 .../IntegrationTests/TransactionScopeTests.cs |  32 ++++
 .../ConnectionOpenedCallbackTests.cs          | 140 ++++++++++++++++++
 .../MySqlConnector.Tests.csproj               |   2 +-
 10 files changed, 273 insertions(+), 4 deletions(-)
 create mode 100644 src/MySqlConnector/MySqlConnectionOpenedCallback.cs
 create mode 100644 src/MySqlConnector/MySqlConnectionOpenedConditions.cs
 create mode 100644 src/MySqlConnector/MySqlConnectionOpenedContext.cs
 create mode 100644 tests/MySqlConnector.Tests/ConnectionOpenedCallbackTests.cs

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 5d3e09dfd..8338a9bb6 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -65,10 +65,12 @@ public ServerSession(ILogger logger, IConnectionPoolMetadata pool)
 	public bool ProcAccessDenied { get; set; }
 	public ICollection<KeyValuePair<string, object?>> ActivityTags => m_activityTags;
 	public MySqlDataReader DataReader { get; set; }
+	public MySqlConnectionOpenedConditions Conditions { get; private set; }
 
 	public ValueTask ReturnToPoolAsync(IOBehavior ioBehavior, MySqlConnection? owningConnection)
 	{
 		Log.ReturningToPool(m_logger, Id, Pool?.Id ?? 0);
+		Conditions = MySqlConnectionOpenedConditions.None;
 		LastReturnedTimestamp = Stopwatch.GetTimestamp();
 		if (Pool is null)
 			return default;
@@ -414,6 +416,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				}
 			}
 
+			Conditions = MySqlConnectionOpenedConditions.New;
 			var connected = cs.ConnectionProtocol switch
 			{
 				MySqlConnectionProtocol.Sockets => await OpenTcpSocketAsync(cs, loadBalancer ?? throw new ArgumentNullException(nameof(loadBalancer)), activity, ioBehavior, cancellationToken).ConfigureAwait(false),
@@ -747,6 +750,7 @@ public static async ValueTask<ServerSession> ConnectAndRedirectAsync(ILogger con
 	public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConnection connection, IOBehavior ioBehavior, CancellationToken cancellationToken)
 	{
 		VerifyState(State.Connected);
+		Conditions |= MySqlConnectionOpenedConditions.Reset;
 
 		try
 		{
@@ -829,6 +833,7 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 			Log.IgnoringFailureInTryResetConnectionAsync(m_logger, ex, Id, "SocketException");
 		}
 
+		Conditions &= ~MySqlConnectionOpenedConditions.Reset;
 		return false;
 	}
 
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index a037a3eed..e7c0989de 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -551,6 +551,13 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 					ActivitySourceHelper.CopyTags(m_session!.ActivityTags, activity);
 					m_hasBeenOpened = true;
 					SetState(ConnectionState.Open);
+
+					if (ConnectionOpenedCallback is { } autoEnlistConnectionOpenedCallback)
+					{
+						cancellationToken.ThrowIfCancellationRequested();
+						await autoEnlistConnectionOpenedCallback(new(this, MySqlConnectionOpenedConditions.None), cancellationToken).ConfigureAwait(false);
+					}
+
 					return;
 				}
 			}
@@ -582,6 +589,12 @@ internal async Task OpenAsync(IOBehavior? ioBehavior, CancellationToken cancella
 
 			if (m_connectionSettings.AutoEnlist && System.Transactions.Transaction.Current is not null)
 				EnlistTransaction(System.Transactions.Transaction.Current);
+
+			if (ConnectionOpenedCallback is { } connectionOpenedCallback)
+			{
+				cancellationToken.ThrowIfCancellationRequested();
+				await connectionOpenedCallback(new(this, m_session.Conditions), cancellationToken).ConfigureAwait(false);
+			}
 		}
 		catch (Exception ex) when (activity is { IsAllDataRequested: true })
 		{
@@ -917,6 +930,11 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 
 			using var connection = CloneWith(csb.ConnectionString);
 			connection.m_connectionSettings = connectionSettings;
+
+			// clear the callback because this is not intended to be a user-visible MySqlConnection that will execute setup logic; it's a
+			// non-pooled connection that will execute "KILL QUERY" then immediately be closed
+			connection.ConnectionOpenedCallback = null;
+
 			connection.Open();
 #if NET6_0_OR_GREATER
 			var killQuerySql = string.Create(CultureInfo.InvariantCulture, $"KILL QUERY {command.Connection!.ServerThread}");
@@ -992,6 +1010,7 @@ internal void Cancel(ICancellableCommand command, int commandId, bool isCancel)
 	internal MySqlTransaction? CurrentTransaction { get; set; }
 	internal MySqlConnectorLoggingConfiguration LoggingConfiguration { get; }
 	internal ZstandardPlugin? ZstandardPlugin { get; set; }
+	internal MySqlConnectionOpenedCallback? ConnectionOpenedCallback { get; set; }
 	internal bool AllowLoadLocalInfile => GetInitializedConnectionSettings().AllowLoadLocalInfile;
 	internal bool AllowUserVariables => GetInitializedConnectionSettings().AllowUserVariables;
 	internal bool AllowZeroDateTime => GetInitializedConnectionSettings().AllowZeroDateTime;
@@ -1142,6 +1161,7 @@ private MySqlConnection(MySqlConnection other, MySqlDataSource? dataSource, stri
 		ProvideClientCertificatesCallback = other.ProvideClientCertificatesCallback;
 		ProvidePasswordCallback = other.ProvidePasswordCallback;
 		RemoteCertificateValidationCallback = other.RemoteCertificateValidationCallback;
+		ConnectionOpenedCallback = other.ConnectionOpenedCallback;
 	}
 
 	private void VerifyNotDisposed()
diff --git a/src/MySqlConnector/MySqlConnectionOpenedCallback.cs b/src/MySqlConnector/MySqlConnectionOpenedCallback.cs
new file mode 100644
index 000000000..c1b9c3941
--- /dev/null
+++ b/src/MySqlConnector/MySqlConnectionOpenedCallback.cs
@@ -0,0 +1,9 @@
+namespace MySqlConnector;
+
+/// <summary>
+/// A callback that is invoked when a new <see cref="MySqlConnection"/> is opened.
+/// </summary>
+/// <param name="context">A <see cref="MySqlConnectionOpenedContext"/> giving information about the connection being opened.</param>
+/// <param name="cancellationToken">A <see cref="CancellationToken"/> that can be used to cancel the asynchronous operation.</param>
+/// <returns>A <see cref="ValueTask"/> representing the result of the possibly-asynchronous operation.</returns>
+public delegate ValueTask MySqlConnectionOpenedCallback(MySqlConnectionOpenedContext context, CancellationToken cancellationToken);
diff --git a/src/MySqlConnector/MySqlConnectionOpenedConditions.cs b/src/MySqlConnector/MySqlConnectionOpenedConditions.cs
new file mode 100644
index 000000000..49c4c670a
--- /dev/null
+++ b/src/MySqlConnector/MySqlConnectionOpenedConditions.cs
@@ -0,0 +1,23 @@
+﻿namespace MySqlConnector;
+
+/// <summary>
+/// Bitflags giving the conditions under which a connection was opened.
+/// </summary>
+[Flags]
+public enum MySqlConnectionOpenedConditions
+{
+	/// <summary>
+	/// No specific conditions apply. This value may be used when an existing pooled connection is reused without being reset.
+	/// </summary>
+	None = 0,
+
+	/// <summary>
+	/// A new physical connection to a MySQL Server was opened. This value is mutually exclusive with <see cref="Reset"/>.
+	/// </summary>
+	New = 1,
+
+	/// <summary>
+	/// An existing pooled connection to a MySQL Server was reset. This value is mutually exclusive with <see cref="New"/>.
+	/// </summary>
+	Reset = 2,
+}
diff --git a/src/MySqlConnector/MySqlConnectionOpenedContext.cs b/src/MySqlConnector/MySqlConnectionOpenedContext.cs
new file mode 100644
index 000000000..46fe9adf3
--- /dev/null
+++ b/src/MySqlConnector/MySqlConnectionOpenedContext.cs
@@ -0,0 +1,23 @@
+namespace MySqlConnector;
+
+/// <summary>
+/// Contains information passed to <see cref="MySqlConnectionOpenedCallback"/> when a new <see cref="MySqlConnection"/> is opened.
+/// </summary>
+public sealed class MySqlConnectionOpenedContext
+{
+	/// <summary>
+	/// The <see cref="MySqlConnection"/> that was opened.
+	/// </summary>
+	public MySqlConnection Connection { get; }
+
+	/// <summary>
+	/// Bitflags giving the conditions under which a connection was opened.
+	/// </summary>
+	public MySqlConnectionOpenedConditions Conditions { get; }
+
+	internal MySqlConnectionOpenedContext(MySqlConnection connection, MySqlConnectionOpenedConditions conditions)
+	{
+		Connection = connection;
+		Conditions = conditions;
+	}
+}
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index 0c4372fd4..56e9718af 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -19,7 +19,7 @@ public sealed class MySqlDataSource : DbDataSource
 	/// <param name="connectionString">The connection string for the MySQL Server. This parameter is required.</param>
 	/// <exception cref="ArgumentNullException">Thrown if <paramref name="connectionString"/> is <c>null</c>.</exception>
 	public MySqlDataSource(string connectionString)
-		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, default, default, default)
+		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, default, default, default, default)
 	{
 	}
 
@@ -31,7 +31,8 @@ internal MySqlDataSource(string connectionString,
 		Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? periodicPasswordProvider,
 		TimeSpan periodicPasswordProviderSuccessRefreshInterval,
 		TimeSpan periodicPasswordProviderFailureRefreshInterval,
-		ZstandardPlugin? zstandardPlugin)
+		ZstandardPlugin? zstandardPlugin,
+		MySqlConnectionOpenedCallback? connectionOpenedCallback)
 	{
 		m_connectionString = connectionString;
 		LoggingConfiguration = loggingConfiguration;
@@ -40,6 +41,7 @@ internal MySqlDataSource(string connectionString,
 		m_remoteCertificateValidationCallback = remoteCertificateValidationCallback;
 		m_logger = loggingConfiguration.DataSourceLogger;
 		m_zstandardPlugin = zstandardPlugin;
+		m_connectionOpenedCallback = connectionOpenedCallback;
 
 		Pool = ConnectionPool.CreatePool(m_connectionString, LoggingConfiguration, name);
 		m_id = Interlocked.Increment(ref s_lastId);
@@ -142,6 +144,7 @@ protected override DbConnection CreateDbConnection()
 			ProvideClientCertificatesCallback = m_clientCertificatesCallback,
 			ProvidePasswordCallback = m_providePasswordCallback,
 			RemoteCertificateValidationCallback = m_remoteCertificateValidationCallback,
+			ConnectionOpenedCallback = m_connectionOpenedCallback,
 		};
 	}
 
@@ -225,6 +228,7 @@ private string ProvidePasswordFromInitialRefreshTask(MySqlProvidePasswordContext
 	private readonly TimeSpan m_periodicPasswordProviderSuccessRefreshInterval;
 	private readonly TimeSpan m_periodicPasswordProviderFailureRefreshInterval;
 	private readonly ZstandardPlugin? m_zstandardPlugin;
+	private readonly MySqlConnectionOpenedCallback? m_connectionOpenedCallback;
 	private readonly MySqlProvidePasswordContext? m_providePasswordContext;
 	private readonly CancellationTokenSource? m_passwordProviderTimerCancellationTokenSource;
 	private readonly Timer? m_passwordProviderTimer;
diff --git a/src/MySqlConnector/MySqlDataSourceBuilder.cs b/src/MySqlConnector/MySqlDataSourceBuilder.cs
index e1d15d183..4bceec180 100644
--- a/src/MySqlConnector/MySqlDataSourceBuilder.cs
+++ b/src/MySqlConnector/MySqlDataSourceBuilder.cs
@@ -89,6 +89,17 @@ public MySqlDataSourceBuilder UseRemoteCertificateValidationCallback(RemoteCerti
 		return this;
 	}
 
+	/// <summary>
+	/// Adds a callback that is invoked when a new <see cref="MySqlConnection"/> is opened.
+	/// </summary>
+	/// <param name="callback">The callback to invoke.</param>
+	/// <returns>This builder, so that method calls can be chained.</returns>
+	public MySqlDataSourceBuilder UseConnectionOpenedCallback(MySqlConnectionOpenedCallback callback)
+	{
+		m_connectionOpenedCallback += callback;
+		return this;
+	}
+
 	/// <summary>
 	/// Builds a <see cref="MySqlDataSource"/> which is ready for use.
 	/// </summary>
@@ -104,7 +115,8 @@ public MySqlDataSource Build()
 			m_periodicPasswordProvider,
 			m_periodicPasswordProviderSuccessRefreshInterval,
 			m_periodicPasswordProviderFailureRefreshInterval,
-			ZstandardPlugin
+			ZstandardPlugin,
+			m_connectionOpenedCallback
 			);
 	}
 
@@ -122,4 +134,5 @@ public MySqlDataSource Build()
 	private Func<MySqlProvidePasswordContext, CancellationToken, ValueTask<string>>? m_periodicPasswordProvider;
 	private TimeSpan m_periodicPasswordProviderSuccessRefreshInterval;
 	private TimeSpan m_periodicPasswordProviderFailureRefreshInterval;
+	private MySqlConnectionOpenedCallback? m_connectionOpenedCallback;
 }
diff --git a/tests/IntegrationTests/TransactionScopeTests.cs b/tests/IntegrationTests/TransactionScopeTests.cs
index ffe14b594..5a808f193 100644
--- a/tests/IntegrationTests/TransactionScopeTests.cs
+++ b/tests/IntegrationTests/TransactionScopeTests.cs
@@ -886,6 +886,38 @@ public void Bug1348()
 
 		Assert.True(rollbacked, $"First branch transaction '{xid}1' not rolled back");
 	}
+
+	[Fact]
+	public void ConnectionOpenedCallbackAutoEnlistInTransaction()
+	{
+		var connectionOpenedCallbackCount = 0;
+		var connectionOpenedConditions = MySqlConnectionOpenedConditions.None;
+		using var dataSource = new MySqlDataSourceBuilder(AppConfig.ConnectionString)
+			.UseConnectionOpenedCallback((ctx, token) =>
+			{
+				connectionOpenedCallbackCount++;
+				connectionOpenedConditions = ctx.Conditions;
+				return default;
+			})
+			.Build();
+
+		using (var transactionScope = new TransactionScope())
+		{
+			using (var conn = dataSource.OpenConnection())
+			{
+				Assert.Equal(1, connectionOpenedCallbackCount);
+				Assert.Equal(MySqlConnectionOpenedConditions.New, connectionOpenedConditions);
+			}
+
+			using (var conn = dataSource.OpenConnection())
+			{
+				Assert.Equal(2, connectionOpenedCallbackCount);
+				Assert.Equal(MySqlConnectionOpenedConditions.None, connectionOpenedConditions);
+			}
+
+			transactionScope.Complete();
+		}
+	}
 #endif
 
 	readonly DatabaseFixture m_database;
diff --git a/tests/MySqlConnector.Tests/ConnectionOpenedCallbackTests.cs b/tests/MySqlConnector.Tests/ConnectionOpenedCallbackTests.cs
new file mode 100644
index 000000000..bf9b72e84
--- /dev/null
+++ b/tests/MySqlConnector.Tests/ConnectionOpenedCallbackTests.cs
@@ -0,0 +1,140 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MySqlConnector.Tests;
+
+public class ConnectionOpenedCallbackTests : IDisposable
+{
+	public ConnectionOpenedCallbackTests()
+	{
+		m_server = new();
+		m_server.Start();
+
+		m_csb = new MySqlConnectionStringBuilder()
+		{
+			Server = "localhost",
+			Port = (uint) m_server.Port,
+		};
+		m_dataSource = new MySqlDataSourceBuilder(m_csb.ConnectionString)
+			.UseConnectionOpenedCallback(OnConnectionOpenedAsync)
+			.Build();
+	}
+
+	public void Dispose()
+	{
+		m_dataSource.Dispose();
+		m_server.Stop();
+	}
+
+	[Fact]
+	public void CallbackIsInvoked()
+	{
+		using (var connection = m_dataSource.CreateConnection())
+		{
+			Assert.Equal(0, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.None, m_connectionOpenedConditions);
+
+			connection.Open();
+
+			Assert.Equal(1, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.New, m_connectionOpenedConditions);
+		}
+	}
+
+	[Fact]
+	public void CallbackIsInvokedForPooledConnection()
+	{
+		using (var connection = m_dataSource.CreateConnection())
+		{
+			Assert.Equal(0, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.None, m_connectionOpenedConditions);
+
+			connection.Open();
+
+			Assert.Equal(1, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.New, m_connectionOpenedConditions);
+		}
+
+		using (var connection = m_dataSource.OpenConnection())
+		{
+			Assert.Equal(2, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.Reset, m_connectionOpenedConditions);
+		}
+
+		using (var connection = m_dataSource.OpenConnection())
+		{
+			Assert.Equal(3, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.Reset, m_connectionOpenedConditions);
+		}
+	}
+
+	[Fact]
+	public void CallbackIsInvokedForNonPooledConnection()
+	{
+		var csb = new MySqlConnectionStringBuilder(m_csb.ConnectionString)
+		{
+			Pooling = false,
+		};
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UseConnectionOpenedCallback(OnConnectionOpenedAsync)
+			.Build();
+
+		using (var connection = dataSource.OpenConnection())
+		{
+			Assert.Equal(1, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.New, m_connectionOpenedConditions);
+		}
+
+		using (var connection = dataSource.OpenConnection())
+		{
+			Assert.Equal(2, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.New, m_connectionOpenedConditions);
+		}
+
+		using (var connection = dataSource.OpenConnection())
+		{
+			Assert.Equal(3, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.New, m_connectionOpenedConditions);
+		}
+	}
+
+	[Fact]
+	public void ConditionsForNonResetConnection()
+	{
+		var csb = new MySqlConnectionStringBuilder(m_csb.ConnectionString)
+		{
+			ConnectionReset = false,
+		};
+		using var dataSource = new MySqlDataSourceBuilder(csb.ConnectionString)
+			.UseConnectionOpenedCallback(OnConnectionOpenedAsync)
+			.Build();
+
+		using (var connection = dataSource.OpenConnection())
+		{
+			Assert.Equal(1, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.New, m_connectionOpenedConditions);
+		}
+		using (var connection = dataSource.OpenConnection())
+		{
+			Assert.Equal(2, m_connectionOpenedCount);
+			Assert.Equal(MySqlConnectionOpenedConditions.None, m_connectionOpenedConditions);
+		}
+	}
+
+	private ValueTask OnConnectionOpenedAsync(MySqlConnectionOpenedContext context, CancellationToken cancellationToken)
+	{
+		m_connectionOpenedCount++;
+		m_connectionOpenedConditions = context.Conditions;
+		return default;
+	}
+
+	private readonly FakeMySqlServer m_server;
+	private readonly MySqlConnectionStringBuilder m_csb;
+	private readonly MySqlDataSource m_dataSource;
+
+	private int m_connectionOpenedCount;
+	private MySqlConnectionOpenedConditions m_connectionOpenedConditions;
+}
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 5d14a2cce..8de95ae49 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -47,7 +47,7 @@
 
   <ItemGroup Condition=" '$(Configuration)' == 'MySqlData' ">
     <PackageReference Include="MySql.Data" />
-    <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ColumnCountPayloadTests.cs;ColumnReaderTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlConnectionTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterAppendBinaryTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
+    <Compile Remove="ByteBufferWriterTests.cs;CachedProcedureTests.cs;CancellationTests.cs;ColumnCountPayloadTests.cs;ColumnReaderTests.cs;ConnectionOpenedCallbackTests.cs;ConnectionTests.cs;FakeMySqlServer.cs;FakeMySqlServerConnection.cs;LoadBalancerTests.cs;MySqlConnectionTests.cs;MySqlDecimalTests.cs;MySqlExceptionTests.cs;MySqlParameterAppendBinaryTests.cs;MySqlParameterCollectionNameToIndexTests.cs;NormalizeTests.cs;ServerVersionTests.cs;StatementPreparerTests.cs;TypeMapperTests.cs;UtilityTests.cs" />
     <Compile Remove="Metrics\*.cs" />
     <Using Include="MySql.Data.MySqlClient" />
     <Using Include="MySql.Data.Types" />

From 3ab20447b006762bccdcc7ad2928c004ef6a4d06 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 13 Oct 2024 16:39:43 -0700
Subject: [PATCH 327/393] Update API documentation.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../CreatePasswordHash.md                     | 28 ++++++++++++++++++
 .../IAuthenticationPlugin2Type.md             | 25 ++++++++++++++++
 .../MySqlConnectionOpenedCallbackType.md      | 29 +++++++++++++++++++
 .../MySqlConnectionOpenedConditionsType.md    | 27 +++++++++++++++++
 .../Conditions.md                             | 20 +++++++++++++
 .../Connection.md                             | 20 +++++++++++++
 .../MySqlConnectionOpenedContextType.md       | 25 ++++++++++++++++
 .../UseConnectionOpenedCallback.md            | 28 ++++++++++++++++++
 .../MySqlDataSourceBuilderType.md             |  1 +
 docs/content/api/MySqlConnectorAssembly.md    |  4 +++
 .../MySqlConnectorAuthenticationNamespace.md  |  1 +
 docs/content/api/MySqlConnectorNamespace.md   |  3 ++
 12 files changed, 211 insertions(+)
 create mode 100644 docs/content/api/MySqlConnector.Authentication/IAuthenticationPlugin2/CreatePasswordHash.md
 create mode 100644 docs/content/api/MySqlConnector.Authentication/IAuthenticationPlugin2Type.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlConnectionOpenedCallbackType.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlConnectionOpenedConditionsType.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlConnectionOpenedContext/Conditions.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlConnectionOpenedContext/Connection.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlConnectionOpenedContextType.md
 create mode 100644 docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseConnectionOpenedCallback.md

diff --git a/docs/content/api/MySqlConnector.Authentication/IAuthenticationPlugin2/CreatePasswordHash.md b/docs/content/api/MySqlConnector.Authentication/IAuthenticationPlugin2/CreatePasswordHash.md
new file mode 100644
index 000000000..9bf5f1b62
--- /dev/null
+++ b/docs/content/api/MySqlConnector.Authentication/IAuthenticationPlugin2/CreatePasswordHash.md
@@ -0,0 +1,28 @@
+---
+title: IAuthenticationPlugin2.CreatePasswordHash method
+---
+
+# IAuthenticationPlugin2.CreatePasswordHash method
+
+Hashes the client's password (e.g., for TLS certificate fingerprint verification).
+
+```csharp
+public byte[] CreatePasswordHash(string password, ReadOnlySpan<byte> authenticationData)
+```
+
+| parameter | description |
+| --- | --- |
+| password | The client's password. |
+| authenticationData | The authentication data supplied by the server; this is the |
+
+## Return Value
+
+The authentication-method-specific hash of the client's password.
+
+## See Also
+
+* interface [IAuthenticationPlugin2](../../IAuthenticationPlugin2Type/)
+* namespace [MySqlConnector.Authentication](../../IAuthenticationPlugin2Type/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector.Authentication/IAuthenticationPlugin2Type.md b/docs/content/api/MySqlConnector.Authentication/IAuthenticationPlugin2Type.md
new file mode 100644
index 000000000..033bc42cc
--- /dev/null
+++ b/docs/content/api/MySqlConnector.Authentication/IAuthenticationPlugin2Type.md
@@ -0,0 +1,25 @@
+---
+title: IAuthenticationPlugin2
+---
+
+# IAuthenticationPlugin2 interface
+
+[`IAuthenticationPlugin2`](../IAuthenticationPlugin2Type/) is an extension to [`IAuthenticationPlugin`](../IAuthenticationPluginType/) that returns a hash of the client's password.
+
+```csharp
+public interface IAuthenticationPlugin2 : IAuthenticationPlugin
+```
+
+## Members
+
+| name | description |
+| --- | --- |
+| [CreatePasswordHash](../IAuthenticationPlugin2/CreatePasswordHash/)(…) | Hashes the client's password (e.g., for TLS certificate fingerprint verification). |
+
+## See Also
+
+* interface [IAuthenticationPlugin](../IAuthenticationPluginType/)
+* namespace [MySqlConnector.Authentication](../../MySqlConnectorAuthenticationNamespace/)
+* assembly [MySqlConnector](../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlConnectionOpenedCallbackType.md b/docs/content/api/MySqlConnector/MySqlConnectionOpenedCallbackType.md
new file mode 100644
index 000000000..7091e0a2e
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlConnectionOpenedCallbackType.md
@@ -0,0 +1,29 @@
+---
+title: MySqlConnectionOpenedCallback
+---
+
+# MySqlConnectionOpenedCallback delegate
+
+A callback that is invoked when a new [`MySqlConnection`](../MySqlConnectionType/) is opened.
+
+```csharp
+public delegate ValueTask MySqlConnectionOpenedCallback(MySqlConnectionOpenedContext context, 
+    CancellationToken cancellationToken);
+```
+
+| parameter | description |
+| --- | --- |
+| context | A [`MySqlConnectionOpenedContext`](../MySqlConnectionOpenedContextType/) giving information about the connection being opened. |
+| cancellationToken | A CancellationToken that can be used to cancel the asynchronous operation. |
+
+## Return Value
+
+A ValueTask representing the result of the possibly-asynchronous operation.
+
+## See Also
+
+* class [MySqlConnectionOpenedContext](../MySqlConnectionOpenedContextType/)
+* namespace [MySqlConnector](../../MySqlConnectorNamespace/)
+* assembly [MySqlConnector](../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlConnectionOpenedConditionsType.md b/docs/content/api/MySqlConnector/MySqlConnectionOpenedConditionsType.md
new file mode 100644
index 000000000..ca177011f
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlConnectionOpenedConditionsType.md
@@ -0,0 +1,27 @@
+---
+title: MySqlConnectionOpenedConditions
+---
+
+# MySqlConnectionOpenedConditions enumeration
+
+Bitflags giving the conditions under which a connection was opened.
+
+```csharp
+[Flags]
+public enum MySqlConnectionOpenedConditions
+```
+
+## Values
+
+| name | value | description |
+| --- | --- | --- |
+| None | `0x0` | No specific conditions apply. This value may be used when an existing pooled connection is reused without being reset. |
+| New | `0x1` | A new physical connection to a MySQL Server was opened. This value is mutually exclusive with Reset. |
+| Reset | `0x2` | An existing pooled connection to a MySQL Server was reset. This value is mutually exclusive with New. |
+
+## See Also
+
+* namespace [MySqlConnector](../../MySqlConnectorNamespace/)
+* assembly [MySqlConnector](../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlConnectionOpenedContext/Conditions.md b/docs/content/api/MySqlConnector/MySqlConnectionOpenedContext/Conditions.md
new file mode 100644
index 000000000..a62434adc
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlConnectionOpenedContext/Conditions.md
@@ -0,0 +1,20 @@
+---
+title: MySqlConnectionOpenedContext.Conditions property
+---
+
+# MySqlConnectionOpenedContext.Conditions property
+
+Bitflags giving the conditions under which a connection was opened.
+
+```csharp
+public MySqlConnectionOpenedConditions Conditions { get; }
+```
+
+## See Also
+
+* enum [MySqlConnectionOpenedConditions](../../MySqlConnectionOpenedConditionsType/)
+* class [MySqlConnectionOpenedContext](../../MySqlConnectionOpenedContextType/)
+* namespace [MySqlConnector](../../MySqlConnectionOpenedContextType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlConnectionOpenedContext/Connection.md b/docs/content/api/MySqlConnector/MySqlConnectionOpenedContext/Connection.md
new file mode 100644
index 000000000..70af5af5f
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlConnectionOpenedContext/Connection.md
@@ -0,0 +1,20 @@
+---
+title: MySqlConnectionOpenedContext.Connection property
+---
+
+# MySqlConnectionOpenedContext.Connection property
+
+The [`MySqlConnection`](../../MySqlConnectionType/) that was opened.
+
+```csharp
+public MySqlConnection Connection { get; }
+```
+
+## See Also
+
+* class [MySqlConnection](../../MySqlConnectionType/)
+* class [MySqlConnectionOpenedContext](../../MySqlConnectionOpenedContextType/)
+* namespace [MySqlConnector](../../MySqlConnectionOpenedContextType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlConnectionOpenedContextType.md b/docs/content/api/MySqlConnector/MySqlConnectionOpenedContextType.md
new file mode 100644
index 000000000..bf4cf0a59
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlConnectionOpenedContextType.md
@@ -0,0 +1,25 @@
+---
+title: MySqlConnectionOpenedContext
+---
+
+# MySqlConnectionOpenedContext class
+
+Contains information passed to [`MySqlConnectionOpenedCallback`](../MySqlConnectionOpenedCallbackType/) when a new [`MySqlConnection`](../MySqlConnectionType/) is opened.
+
+```csharp
+public sealed class MySqlConnectionOpenedContext
+```
+
+## Public Members
+
+| name | description |
+| --- | --- |
+| [Conditions](../MySqlConnectionOpenedContext/Conditions/) { get; } | Bitflags giving the conditions under which a connection was opened. |
+| [Connection](../MySqlConnectionOpenedContext/Connection/) { get; } | The [`MySqlConnection`](../MySqlConnectionType/) that was opened. |
+
+## See Also
+
+* namespace [MySqlConnector](../../MySqlConnectorNamespace/)
+* assembly [MySqlConnector](../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseConnectionOpenedCallback.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseConnectionOpenedCallback.md
new file mode 100644
index 000000000..f0a71550d
--- /dev/null
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilder/UseConnectionOpenedCallback.md
@@ -0,0 +1,28 @@
+---
+title: MySqlDataSourceBuilder.UseConnectionOpenedCallback method
+---
+
+# MySqlDataSourceBuilder.UseConnectionOpenedCallback method
+
+Adds a callback that is invoked when a new [`MySqlConnection`](../../MySqlConnectionType/) is opened.
+
+```csharp
+public MySqlDataSourceBuilder UseConnectionOpenedCallback(MySqlConnectionOpenedCallback callback)
+```
+
+| parameter | description |
+| --- | --- |
+| callback | The callback to invoke. |
+
+## Return Value
+
+This builder, so that method calls can be chained.
+
+## See Also
+
+* delegate [MySqlConnectionOpenedCallback](../../MySqlConnectionOpenedCallbackType/)
+* class [MySqlDataSourceBuilder](../../MySqlDataSourceBuilderType/)
+* namespace [MySqlConnector](../../MySqlDataSourceBuilderType/)
+* assembly [MySqlConnector](../../../MySqlConnectorAssembly/)
+
+<!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md b/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
index 6330bd5d0..e70fccbd3 100644
--- a/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
+++ b/docs/content/api/MySqlConnector/MySqlDataSourceBuilderType.md
@@ -18,6 +18,7 @@ public sealed class MySqlDataSourceBuilder
 | [ConnectionStringBuilder](../MySqlDataSourceBuilder/ConnectionStringBuilder/) { get; } | A [`MySqlConnectionStringBuilder`](../MySqlConnectionStringBuilderType/) that can be used to configure the connection string on this [`MySqlDataSourceBuilder`](../MySqlDataSourceBuilderType/). |
 | [Build](../MySqlDataSourceBuilder/Build/)() | Builds a [`MySqlDataSource`](../MySqlDataSourceType/) which is ready for use. |
 | [UseClientCertificatesCallback](../MySqlDataSourceBuilder/UseClientCertificatesCallback/)(…) | Sets the callback used to provide client certificates for connecting to a server. |
+| [UseConnectionOpenedCallback](../MySqlDataSourceBuilder/UseConnectionOpenedCallback/)(…) | Adds a callback that is invoked when a new [`MySqlConnection`](../MySqlConnectionType/) is opened. |
 | [UseLoggerFactory](../MySqlDataSourceBuilder/UseLoggerFactory/)(…) | Sets the ILoggerFactory that will be used for logging. |
 | [UseName](../MySqlDataSourceBuilder/UseName/)(…) | Sets the name of the [`MySqlDataSource`](../MySqlDataSourceType/) that will be created. |
 | [UsePeriodicPasswordProvider](../MySqlDataSourceBuilder/UsePeriodicPasswordProvider/)(…) | Configures a periodic password provider, which is automatically called by the data source at some regular interval. This is the recommended way to fetch a rotating access token. |
diff --git a/docs/content/api/MySqlConnectorAssembly.md b/docs/content/api/MySqlConnectorAssembly.md
index 2700276c9..f03a48bee 100644
--- a/docs/content/api/MySqlConnectorAssembly.md
+++ b/docs/content/api/MySqlConnectorAssembly.md
@@ -23,6 +23,9 @@ title: MySqlConnector
 | class [MySqlCommand](../MySqlConnector/MySqlCommandType/) | [`MySqlCommand`](../MySqlConnector/MySqlCommandType/) represents a SQL statement or stored procedure name to execute against a MySQL database. |
 | class [MySqlCommandBuilder](../MySqlConnector/MySqlCommandBuilderType/) |  |
 | class [MySqlConnection](../MySqlConnector/MySqlConnectionType/) | [`MySqlConnection`](../MySqlConnector/MySqlConnectionType/) represents a connection to a MySQL database. |
+| delegate [MySqlConnectionOpenedCallback](../MySqlConnector/MySqlConnectionOpenedCallbackType/) | A callback that is invoked when a new [`MySqlConnection`](../MySqlConnector/MySqlConnectionType/) is opened. |
+| [Flags] enum [MySqlConnectionOpenedConditions](../MySqlConnector/MySqlConnectionOpenedConditionsType/) | Bitflags giving the conditions under which a connection was opened. |
+| class [MySqlConnectionOpenedContext](../MySqlConnector/MySqlConnectionOpenedContextType/) | Contains information passed to [`MySqlConnectionOpenedCallback`](../MySqlConnector/MySqlConnectionOpenedCallbackType/) when a new [`MySqlConnection`](../MySqlConnector/MySqlConnectionType/) is opened. |
 | enum [MySqlConnectionProtocol](../MySqlConnector/MySqlConnectionProtocolType/) | Specifies the type of connection to make to the server. |
 | class [MySqlConnectionStringBuilder](../MySqlConnector/MySqlConnectionStringBuilderType/) | [`MySqlConnectionStringBuilder`](../MySqlConnector/MySqlConnectionStringBuilderType/) allows you to construct a MySQL connection string by setting properties on the builder then reading the ConnectionString property. |
 | class [MySqlConnectorFactory](../MySqlConnector/MySqlConnectorFactoryType/) | An implementation of DbProviderFactory that creates MySqlConnector objects. |
@@ -66,6 +69,7 @@ title: MySqlConnector
 | --- | --- |
 | static class [AuthenticationPlugins](../MySqlConnector.Authentication/AuthenticationPluginsType/) | A registry of known authentication plugins. |
 | interface [IAuthenticationPlugin](../MySqlConnector.Authentication/IAuthenticationPluginType/) | The primary interface implemented by an authentication plugin. |
+| interface [IAuthenticationPlugin2](../MySqlConnector.Authentication/IAuthenticationPlugin2Type/) | [`IAuthenticationPlugin2`](../MySqlConnector.Authentication/IAuthenticationPlugin2Type/) is an extension to [`IAuthenticationPlugin`](../MySqlConnector.Authentication/IAuthenticationPluginType/) that returns a hash of the client's password. |
 
 ## MySqlConnector.Logging namespace
 
diff --git a/docs/content/api/MySqlConnectorAuthenticationNamespace.md b/docs/content/api/MySqlConnectorAuthenticationNamespace.md
index fb40ce5dd..4f76be588 100644
--- a/docs/content/api/MySqlConnectorAuthenticationNamespace.md
+++ b/docs/content/api/MySqlConnectorAuthenticationNamespace.md
@@ -8,5 +8,6 @@ title: MySqlConnector.Authentication
 | --- | --- |
 | static class [AuthenticationPlugins](../MySqlConnector.Authentication/AuthenticationPluginsType/) | A registry of known authentication plugins. |
 | interface [IAuthenticationPlugin](../MySqlConnector.Authentication/IAuthenticationPluginType/) | The primary interface implemented by an authentication plugin. |
+| interface [IAuthenticationPlugin2](../MySqlConnector.Authentication/IAuthenticationPlugin2Type/) | [`IAuthenticationPlugin2`](../MySqlConnector.Authentication/IAuthenticationPlugin2Type/) is an extension to [`IAuthenticationPlugin`](../MySqlConnector.Authentication/IAuthenticationPluginType/) that returns a hash of the client's password. |
 
 <!-- DO NOT EDIT: generated by xmldocmd for MySqlConnector.dll -->
diff --git a/docs/content/api/MySqlConnectorNamespace.md b/docs/content/api/MySqlConnectorNamespace.md
index 0204e78d5..5c9eaa40b 100644
--- a/docs/content/api/MySqlConnectorNamespace.md
+++ b/docs/content/api/MySqlConnectorNamespace.md
@@ -21,6 +21,9 @@ title: MySqlConnector
 | class [MySqlCommand](../MySqlConnector/MySqlCommandType/) | [`MySqlCommand`](../MySqlConnector/MySqlCommandType/) represents a SQL statement or stored procedure name to execute against a MySQL database. |
 | class [MySqlCommandBuilder](../MySqlConnector/MySqlCommandBuilderType/) |  |
 | class [MySqlConnection](../MySqlConnector/MySqlConnectionType/) | [`MySqlConnection`](../MySqlConnector/MySqlConnectionType/) represents a connection to a MySQL database. |
+| delegate [MySqlConnectionOpenedCallback](../MySqlConnector/MySqlConnectionOpenedCallbackType/) | A callback that is invoked when a new [`MySqlConnection`](../MySqlConnector/MySqlConnectionType/) is opened. |
+| [Flags] enum [MySqlConnectionOpenedConditions](../MySqlConnector/MySqlConnectionOpenedConditionsType/) | Bitflags giving the conditions under which a connection was opened. |
+| class [MySqlConnectionOpenedContext](../MySqlConnector/MySqlConnectionOpenedContextType/) | Contains information passed to [`MySqlConnectionOpenedCallback`](../MySqlConnector/MySqlConnectionOpenedCallbackType/) when a new [`MySqlConnection`](../MySqlConnector/MySqlConnectionType/) is opened. |
 | enum [MySqlConnectionProtocol](../MySqlConnector/MySqlConnectionProtocolType/) | Specifies the type of connection to make to the server. |
 | class [MySqlConnectionStringBuilder](../MySqlConnector/MySqlConnectionStringBuilderType/) | [`MySqlConnectionStringBuilder`](../MySqlConnector/MySqlConnectionStringBuilderType/) allows you to construct a MySQL connection string by setting properties on the builder then reading the ConnectionString property. |
 | class [MySqlConnectorFactory](../MySqlConnector/MySqlConnectorFactoryType/) | An implementation of DbProviderFactory that creates MySqlConnector objects. |

From d41e47325c8f026816bed95013e2ebb2410ca0fd Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 13 Oct 2024 16:53:15 -0700
Subject: [PATCH 328/393] Release 2.4.0 Beta 1.

---
 docs/content/overview/version-history.md | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index de0e514dd..638f47f84 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -11,6 +11,20 @@ weight: 30
 
 # Version History
 
+### 2.4.0 Beta 1
+
+* Support .NET 9.0.
+* Add `MySqlDataSourceBuilder.UseConnectionOpenedCallback`: [#1508](https://github.com/mysql-net/MySqlConnector/issues/1508).
+* Rewrite server redirection logic based on latest MariaDB specification: [#1499](https://github.com/mysql-net/MySqlConnector/pull/1499).
+* Support `SET` data type in functions: [#1491](https://github.com/mysql-net/MySqlConnector/issues/1491).
+* Performance: Avoid `SET NAMES` commands when not necessary: [#1497](https://github.com/mysql-net/MySqlConnector/pull/1497).
+* Fix potential `NullReferenceException` in `MySqlConnection.Cancel`: [#1506](https://github.com/mysql-net/MySqlConnector/issues/1506).
+* Provide logger to new MySqlConnection: [#1510](https://github.com/mysql-net/MySqlConnector/issues/1510).
+* Use correct hostname when cancelling query: [#1514](https://github.com/mysql-net/MySqlConnector/issues/1514).
+* Remove `TABLESPACES` schema: [#1477](https://github.com/mysql-net/MySqlConnector/issues/1477).
+* Change variable name for better server compatibility: [#1502](https://github.com/mysql-net/MySqlConnector/issues/1502).
+* Thanks to [Diego Dupin](https://github.com/rusher) for contributions to this release.
+
 ### 2.3.7
 
 * Fix `NullReferenceException` in `MySqlDataReader.ActivateResultSet` and `MySqlDataReader.DisposeAsync`: [#1459](https://github.com/mysql-net/MySqlConnector/issues/1459).

From cf40051f062f2164bfb2209df8ab3288b50080eb Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 14 Oct 2024 08:46:05 -0700
Subject: [PATCH 329/393] Update dependencies.

Update logging dependencies only to the last patch version in the same minor version. The MySqlConnector packages that use these will probably never be updated, as that logging system is deprecated.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 Directory.Packages.props | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9cd40df8a..c56b73371 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -7,20 +7,20 @@
   <ItemGroup>
     <PackageVersion Include="AdoNet.Specification.Tests" Version="2.0.0-beta.2" />
     <PackageVersion Include="Dapper.StrongName" Version="2.1.35" />
-    <PackageVersion Include="IndexRange" Version="1.0.2" />
-    <PackageVersion Include="log4net" Version="2.0.10" />
+    <PackageVersion Include="IndexRange" Version="1.0.3" />
+    <PackageVersion Include="log4net" Version="2.0.17" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
-    <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
-    <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
-    <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.1" />
+    <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
+    <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
+    <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
     <PackageVersion Include="MySql.Data" Version="9.0.0" />
-    <PackageVersion Include="NLog" Version="4.5.0" />
-    <PackageVersion Include="Serilog" Version="2.6.0" />
+    <PackageVersion Include="NLog" Version="4.7.15" />
+    <PackageVersion Include="Serilog" Version="2.12.0" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
-    <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="7.0.2" />
+    <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
     <PackageVersion Include="System.Text.Json" Version="8.0.5" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
     <PackageVersion Include="xunit" Version="2.9.2" />

From 67f141f9529e81e87da753ece455e7982de2761b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 16 Oct 2024 09:47:26 -0700
Subject: [PATCH 330/393] Log into Docker before pulling an image.

This should help with rate limits.
---
 .ci/integration-tests-steps.yml | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/.ci/integration-tests-steps.yml b/.ci/integration-tests-steps.yml
index 7d7bd5d08..db26b23ed 100644
--- a/.ci/integration-tests-steps.yml
+++ b/.ci/integration-tests-steps.yml
@@ -5,6 +5,11 @@ parameters:
   DotNetCoreSdkVersion: ''
 
 steps:
+- task: Docker@2
+  displayName: Log in to Docker
+  inputs:
+    command: login
+    containerRegistry: DockerHub
 - bash: ${{ format('.ci/docker-run.sh {0} 3300 {1}', parameters.image, parameters.unsupportedFeatures) }}
   displayName: 'Start Docker container'
 - task: UseDotNet@2

From d98522e2181cc4596612daa5a47f32a69a383612 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 16 Oct 2024 13:36:52 -0700
Subject: [PATCH 331/393] Update to MySQL Server 9.1 and MySql.Data 9.1.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 Directory.Packages.props                               | 2 +-
 azure-pipelines.yml                                    | 4 ++--
 docs/content/home.md                                   | 2 +-
 docs/content/tutorials/migrating-from-connector-net.md | 2 +-
 tests/IntegrationTests/app.config                      | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index c56b73371..6e2efac78 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -16,7 +16,7 @@
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
-    <PackageVersion Include="MySql.Data" Version="9.0.0" />
+    <PackageVersion Include="MySql.Data" Version="9.1.0" />
     <PackageVersion Include="NLog" Version="4.7.15" />
     <PackageVersion Include="Serilog" Version="2.12.0" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index fd74685be..c53d407b4 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -176,8 +176,8 @@ jobs:
         image: 'mysql:8.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
-      'MySQL 9.0':
-        image: 'mysql:9.0'
+      'MySQL 9.1':
+        image: 'mysql:9.1'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
       'MariaDB 10.6':
diff --git a/docs/content/home.md b/docs/content/home.md
index 0c0f0b4e1..4cf363195 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -65,7 +65,7 @@ Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://m
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
 MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**) |
-MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), **9.0** | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
+MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.1**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
 ProxySQL | 2.x | Some [compatibility issues](https://github.com/search?q=repo%3Amysql-net%2FMySqlConnector+proxysql&type=issues)
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index ae3ca6cd7..65782f642 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -41,7 +41,7 @@ In a .NET Framework application, make the following `app.config` change to regis
     <!-- add name="MySQL Data Provider"
       invariant="MySql.Data.MySqlClient"
       description=".Net Framework Data Provider for MySQL"
-      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.0.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.1.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
     <add name="MySqlConnector"
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index e92393822..00cf3c1b7 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -4,7 +4,7 @@
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
       <remove invariant="MySql.Data.MySqlClient" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.0.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.1.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>

From 929dd40cdb787dfb5323d83fb3dbabaa9f3daa61 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 17 Oct 2024 06:59:55 -0700
Subject: [PATCH 332/393] Update tests instructions to mysql:9.1.

---
 tests/README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/README.md b/tests/README.md
index da0fcb0ab..a81ebc761 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,7 @@ It also verifies that MySqlConnector and MySQL Connector/NET (MySql.Data) have s
 
 The tests require a MySQL server. The simplest way to run one is with [Docker](https://www.docker.com/community-edition):
 
-    docker run -d --rm --pull always --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.0 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
+    docker run -d --rm --pull always --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.1 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
     docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes'; CREATE USER 'caching-sha2-user'@'%' IDENTIFIED WITH caching_sha2_password BY 'Cach!ng-Sh@2-Pa55'; GRANT ALL PRIVILEGES ON *.* TO 'caching-sha2-user'@'%';"
 
 Copy the file `IntegrationTests/config.json.example` to `IntegrationTests/config.json`, then edit

From 390d5791de25135d503feecdea111edafd07edbd Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 19 Oct 2024 19:09:19 -0700
Subject: [PATCH 333/393] Fix "Can't replace active reader". Fixes #1469

If an exception happens during MySqlConnection.Close(), the connection may be left in an invalid state, thinking it still has an open MySqlDataReader. Clear this field during cleanup to remove this invalid state.
---
 src/MySqlConnector/MySqlConnection.cs         |  3 ++
 tests/MySqlConnector.Tests/ConnectionTests.cs | 43 +++++++++++++++++++
 .../FakeMySqlServerConnection.cs              | 14 ++++++
 3 files changed, 60 insertions(+)

diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index e7c0989de..27219bb47 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -1248,6 +1248,9 @@ private async Task DoCloseAsync(bool changeState, IOBehavior ioBehavior)
 		{
 			if (m_session is not null)
 			{
+				// under exceptional circumstances, we may enter this code without having called FinishQuerying, which clears this field
+				m_activeReader = null;
+
 				if (GetInitializedConnectionSettings().Pooling)
 				{
 					await m_session.ReturnToPoolAsync(ioBehavior, this).ConfigureAwait(false);
diff --git a/tests/MySqlConnector.Tests/ConnectionTests.cs b/tests/MySqlConnector.Tests/ConnectionTests.cs
index 0c0833a1f..454c00242 100644
--- a/tests/MySqlConnector.Tests/ConnectionTests.cs
+++ b/tests/MySqlConnector.Tests/ConnectionTests.cs
@@ -234,6 +234,49 @@ public void ReadInfinity()
 		Assert.False(reader.Read());
 	}
 
+	[Fact]
+	public void ReplaceActiveReader()
+	{
+		var connection = new MySqlConnection(m_csb.ConnectionString);
+		connection.Open();
+		using (var command = connection.CreateCommand())
+		{
+			command.CommandText = "select disconnect";
+			command.CommandTimeout = 600;
+			var reader = command.ExecuteReader();
+			Assert.True(reader.Read());
+			Assert.Equal(1, reader.GetInt32(0));
+		}
+
+		try
+		{
+			connection.Close();
+		}
+		catch (MySqlEndOfStreamException)
+		{
+		}
+
+		connection.Open();
+		using (var command = connection.CreateCommand())
+		{
+			command.CommandText = "SELECT 1;";
+			var reader = command.ExecuteReader();
+			Assert.True(reader.Read());
+			Assert.Equal(1, reader.GetInt32(0));
+		}
+		connection.Close();
+
+		connection.Open();
+		using (var command = connection.CreateCommand())
+		{
+			command.CommandText = "SELECT 2;";
+			var reader = command.ExecuteReader();
+			Assert.True(reader.Read());
+			Assert.Equal(2, reader.GetInt32(0));
+		}
+		connection.Close();
+	}
+
 	private static async Task WaitForConditionAsync<T>(T expected, Func<T> getValue)
 	{
 		var sw = Stopwatch.StartNew();
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
index 6081d7535..f07e421c0 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
@@ -179,6 +179,20 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 								for (var packetIndex = 0; packetIndex < packets.Length; packetIndex++)
 									await SendAsync(stream, packetIndex + 1, x => x.Write(packets[packetIndex]));
 							}
+							else if (query == "select disconnect")
+							{
+								var packets = new[]
+								{
+								new byte[] { 1 }, // one column
+								[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x41, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x01, 0, 0x1F, 0, 0], // column definition (int)
+								[0xFE, 0, 0, 2, 0], // EOF
+								[1, 0x31], // 1
+							};
+								for (var packetIndex = 0; packetIndex < packets.Length; packetIndex++)
+									await SendAsync(stream, packetIndex + 1, x => x.Write(packets[packetIndex]));
+								stream.Close();
+								client.Close();
+							}
 							else
 							{
 								await SendAsync(stream, 1, x => WriteError(x, "Unhandled query: " + query));

From 4f3197298682d6a4ad85acb4de48c05ec1ec6f81 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 19 Oct 2024 20:35:28 -0700
Subject: [PATCH 334/393] Remove net7.0 target framework.

This .NET version has been EOL for six months.

Users of MySqlConnector.DependencyInjection who want to inject a DbDataSource will need to target .NET 8 or greater.
---
 docs/content/overview/installing.md                           | 4 ++--
 .../MySqlConnector.DependencyInjection.csproj                 | 2 +-
 src/MySqlConnector/CompatibilitySuppressions.xml              | 4 ++--
 src/MySqlConnector/MySqlConnector.csproj                      | 2 +-
 4 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/docs/content/overview/installing.md b/docs/content/overview/installing.md
index c29f637b1..c0e7e6205 100644
--- a/docs/content/overview/installing.md
+++ b/docs/content/overview/installing.md
@@ -30,11 +30,11 @@ Or, in Visual Studio, use the _NuGet Package Manager_ to browse for and install
     <AssemblyTitle>My Application</AssemblyTitle>
     <Description>A great application</Description>
     <OutputType>Exe</OutputType>
-    <TargetFramework>net7.0</TargetFramework>
+    <TargetFramework>net8.0</TargetFramework>
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="MySqlConnector" Version="2.2.5" />
+    <PackageReference Include="MySqlConnector" Version="2.3.7" />
   </ItemGroup>
 
 </Project>
diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
index 69cd4cea2..6a7f30b45 100644
--- a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
+++ b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>netstandard2.0;net7.0</TargetFrameworks>
+    <TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
     <EnablePackageValidation>false</EnablePackageValidation>
diff --git a/src/MySqlConnector/CompatibilitySuppressions.xml b/src/MySqlConnector/CompatibilitySuppressions.xml
index 7471076ba..fc096128b 100644
--- a/src/MySqlConnector/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector/CompatibilitySuppressions.xml
@@ -1,5 +1,5 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
-<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
+<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Suppression>
     <DiagnosticId>CP0001</DiagnosticId>
@@ -18,7 +18,7 @@
     <DiagnosticId>CP0001</DiagnosticId>
     <Target>T:System.Data.Common.DbDataSource</Target>
     <Left>lib/net6.0/MySqlConnector.dll</Left>
-    <Right>lib/net7.0/MySqlConnector.dll</Right>
+    <Right>lib/net8.0/MySqlConnector.dll</Right>
   </Suppression>
   <Suppression>
     <DiagnosticId>CP0002</DiagnosticId>
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index 769a9a5e6..ae92887e6 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;net471;net48;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
+    <TargetFrameworks>net462;net471;net48;netstandard2.0;netstandard2.1;net6.0;net8.0;net9.0</TargetFrameworks>
     <Description>A truly async MySQL ADO.NET provider, supporting MySQL Server, MariaDB, Amazon Aurora, Azure Database for MySQL, Google Cloud SQL, and more.</Description>
     <Copyright>Copyright 2016–2024 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>

From 7b9a76e66b479a68d3e023a425a8af896f9d36d1 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 19 Oct 2024 21:46:59 -0700
Subject: [PATCH 335/393] Release 2.4.0 Beta 2.

---
 docs/content/overview/version-history.md | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 638f47f84..e790b53a0 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -11,6 +11,15 @@ weight: 30
 
 # Version History
 
+### 2.4.0 Beta 2
+
+* Remove end-of-life .NET 7 target framework.
+  * Users who wish to use `DbDataSource` need to target .NET 8.0 or later.
+* Fix "Can't replace active reader": [#1469](https://github.com/mysql-net/MySqlConnector/issues/1469).
+* Update dependencies:
+  * Microsoft.Extensions.Logging.Abstractions from 7.0.1 to 8.0.2.
+  * System.Diagnostics.DiagnosticSource from 7.0.2 to 8.0.1.
+
 ### 2.4.0 Beta 1
 
 * Support .NET 9.0.

From 31ceca56da9b568ab418d0b5b345be323db712f3 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 19 Oct 2024 22:42:36 -0700
Subject: [PATCH 336/393] Add missing release note for 2.4.0 Beta 1.

---
 docs/content/overview/version-history.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index e790b53a0..c34770731 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -23,6 +23,7 @@ weight: 30
 ### 2.4.0 Beta 1
 
 * Support .NET 9.0.
+* Support [zero-configuration SSL](https://mariadb.org/mission-impossible-zero-configuration-ssl/) with MariaDB: [#1500](https://github.com/mysql-net/MySqlConnector/pull/1500).
 * Add `MySqlDataSourceBuilder.UseConnectionOpenedCallback`: [#1508](https://github.com/mysql-net/MySqlConnector/issues/1508).
 * Rewrite server redirection logic based on latest MariaDB specification: [#1499](https://github.com/mysql-net/MySqlConnector/pull/1499).
 * Support `SET` data type in functions: [#1491](https://github.com/mysql-net/MySqlConnector/issues/1491).

From 5ec770b4946592897d8e04c1a7f75c78915e8953 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 19 Oct 2024 22:53:16 -0700
Subject: [PATCH 337/393] Note possibly breaking changes in release notes.

---
 docs/content/overview/version-history.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index c34770731..61a39c500 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -13,10 +13,10 @@ weight: 30
 
 ### 2.4.0 Beta 2
 
-* Remove end-of-life .NET 7 target framework.
+* **Possibly breaking** Remove end-of-life .NET 7 target framework.
   * Users who wish to use `DbDataSource` need to target .NET 8.0 or later.
 * Fix "Can't replace active reader": [#1469](https://github.com/mysql-net/MySqlConnector/issues/1469).
-* Update dependencies:
+* **Possibly breaking** Update dependencies:
   * Microsoft.Extensions.Logging.Abstractions from 7.0.1 to 8.0.2.
   * System.Diagnostics.DiagnosticSource from 7.0.2 to 8.0.1.
 

From 02bcc0f2ed1c9d9e7206fda15a871e8ee26081b5 Mon Sep 17 00:00:00 2001
From: AlexKerman <kerman@ormfactory.com>
Date: Mon, 21 Oct 2024 13:09:30 +0500
Subject: [PATCH 338/393] Make table alias accessible from MySqlDbColumn

Signed-off-by: AlexKerman <kerman@ormfactory.com>
---
 src/MySqlConnector/MySqlDbColumn.cs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/MySqlConnector/MySqlDbColumn.cs b/src/MySqlConnector/MySqlDbColumn.cs
index 9710a8035..35843d54f 100644
--- a/src/MySqlConnector/MySqlDbColumn.cs
+++ b/src/MySqlConnector/MySqlDbColumn.cs
@@ -22,6 +22,7 @@ internal MySqlDbColumn(int ordinal, ColumnDefinitionPayload column, bool allowZe
 		BaseColumnName = column.PhysicalName;
 		BaseSchemaName = column.SchemaName;
 		BaseTableName = column.PhysicalTable;
+		TableName = column.Table;
 		ColumnName = column.Name;
 		ColumnOrdinal = ordinal;
 		ColumnSize = columnSize > int.MaxValue ? int.MaxValue : unchecked((int) columnSize);
@@ -50,5 +51,6 @@ internal MySqlDbColumn(int ordinal, ColumnDefinitionPayload column, bool allowZe
 		ProviderType = mySqlDbType;
 	}
 
+	public string TableName { get; }
 	public MySqlDbType ProviderType { get; }
 }

From fb239d69731b4aa6bfaa4c76aa85fef05ce617ee Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 27 Oct 2024 20:06:42 -0700
Subject: [PATCH 339/393] Add tests for MySqlDbColumn.TableName.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 tests/IntegrationTests/QueryTests.cs | 42 ++++++++++++++++++++++++++++
 1 file changed, 42 insertions(+)

diff --git a/tests/IntegrationTests/QueryTests.cs b/tests/IntegrationTests/QueryTests.cs
index 051e88c30..8e40848e2 100644
--- a/tests/IntegrationTests/QueryTests.cs
+++ b/tests/IntegrationTests/QueryTests.cs
@@ -933,6 +933,48 @@ public void GetColumnSchemaAfterNextResult()
 		Assert.False(reader.NextResult());
 		Assert.Empty(reader.GetColumnSchema());
 	}
+
+	[Fact]
+	public void GetColumnSchemaForTableAlias()
+	{
+		using var cmd = m_database.Connection.CreateCommand();
+		cmd.CommandText = """
+			drop table if exists column_schema_table2;
+			drop table if exists column_schema_table1;
+			create table column_schema_table1(id int not null primary key);
+			create table column_schema_table2(id int not null primary key,
+				table1_id1 int not null,
+				table1_id2 int not null,
+				foreign key (table1_id1) references column_schema_table1(id),
+				foreign key (table1_id2) references column_schema_table1(id));
+			insert into column_schema_table1(id) values(1),(2);
+			insert into column_schema_table2(id, table1_id1, table1_id2) values(3, 1, 2);
+			""";
+		cmd.ExecuteNonQuery();
+
+		cmd.CommandText = """
+			select t2.id as id2, t1a.id as ida, t1b.id as idb
+			from column_schema_table2 t2
+			join column_schema_table1 t1a on t2.table1_id1 = t1a.id
+			join column_schema_table1 t1b on t2.table1_id2 = t1b.id;
+			""";
+		using var reader = cmd.ExecuteReader();
+		Assert.True(reader.Read());
+		var schema = reader.GetColumnSchema();
+		Assert.Equal(3, schema.Count);
+		Assert.Equal("id2", schema[0].ColumnName);
+		Assert.Equal("ida", schema[1].ColumnName);
+		Assert.Equal("idb", schema[2].ColumnName);
+		Assert.Equal("id", schema[0].BaseColumnName);
+		Assert.Equal("id", schema[1].BaseColumnName);
+		Assert.Equal("id", schema[2].BaseColumnName);
+		Assert.Equal("column_schema_table2", schema[0].BaseTableName);
+		Assert.Equal("column_schema_table1", schema[1].BaseTableName);
+		Assert.Equal("column_schema_table1", schema[2].BaseTableName);
+		Assert.Equal("t2", ((MySqlDbColumn) schema[0]).TableName);
+		Assert.Equal("t1a", ((MySqlDbColumn) schema[1]).TableName);
+		Assert.Equal("t1b", ((MySqlDbColumn) schema[2]).TableName);
+	}
 #endif
 
 	private void UseReaderWithoutDisposingThread(object obj)

From 31c0852208fd28b6aa18383be64cefbf40e03825 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 27 Oct 2024 20:14:33 -0700
Subject: [PATCH 340/393] Add documentation comments for TableName.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/MySqlDbColumn.cs | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/MySqlConnector/MySqlDbColumn.cs b/src/MySqlConnector/MySqlDbColumn.cs
index 35843d54f..b55e9a02a 100644
--- a/src/MySqlConnector/MySqlDbColumn.cs
+++ b/src/MySqlConnector/MySqlDbColumn.cs
@@ -22,7 +22,6 @@ internal MySqlDbColumn(int ordinal, ColumnDefinitionPayload column, bool allowZe
 		BaseColumnName = column.PhysicalName;
 		BaseSchemaName = column.SchemaName;
 		BaseTableName = column.PhysicalTable;
-		TableName = column.Table;
 		ColumnName = column.Name;
 		ColumnOrdinal = ordinal;
 		ColumnSize = columnSize > int.MaxValue ? int.MaxValue : unchecked((int) columnSize);
@@ -49,8 +48,13 @@ internal MySqlDbColumn(int ordinal, ColumnDefinitionPayload column, bool allowZe
 		}
 		NumericScale = column.Decimals;
 		ProviderType = mySqlDbType;
+		TableName = column.Table;
 	}
 
-	public string TableName { get; }
 	public MySqlDbType ProviderType { get; }
+
+	/// <summary>
+	/// Gets the name of the table that the column belongs to. This will be the alias if the table is aliased in the query.
+	/// </summary>
+	public string TableName { get; }
 }

From 29d8e35119afe8f9b1c4d4b809614c907303751f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 2 Nov 2024 16:45:47 -0700
Subject: [PATCH 341/393] Fix potential race condition in FinishQuerying. Fixes
 #1341

If TryStartCancel were called between the two 'lock' blocks, the session could be put into an unexpected state. Instead, perform all state updates in the first 'lock' instead of deferring cleanup work. This should also improve efficiency for the common (not cancelled) path.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ServerSession.cs | 23 +++++++++++++++--------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 8338a9bb6..5350726da 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -296,14 +296,23 @@ public void StartQuerying(ICancellableCommand command)
 	public void FinishQuerying()
 	{
 		EnteringFinishQuerying(m_logger, Id, m_state);
-		bool clearConnection = false;
+		var clearConnection = false;
 		lock (m_lock)
 		{
+			// entering this method, we expect to be in Querying, CancelingQuery, or Failed state
 			if (m_state == State.CancelingQuery)
 			{
 				m_state = State.ClearingPendingCancellation;
 				clearConnection = true;
 			}
+			else
+			{
+				if (m_state is State.Querying)
+					m_state = State.Connected;
+				else
+					VerifyState(State.Failed);
+				ActiveCommandId = 0;
+			}
 		}
 
 		if (clearConnection)
@@ -318,15 +327,13 @@ public void FinishQuerying()
 			payload = ReceiveReplyAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
 #pragma warning restore CA2012
 			OkPayload.Verify(payload.Span, this);
-		}
 
-		lock (m_lock)
-		{
-			if (m_state is State.Querying or State.ClearingPendingCancellation)
+			lock (m_lock)
+			{
+				VerifyState(State.ClearingPendingCancellation);
 				m_state = State.Connected;
-			else
-				VerifyState(State.Failed);
-			ActiveCommandId = 0;
+				ActiveCommandId = 0;
+			}
 		}
 	}
 

From a593bcf4d9aa86cf28a6fdaea433c6a5c99d2d44 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 2 Nov 2024 19:40:03 -0700
Subject: [PATCH 342/393] Add NativeAOT test. Fixes #1265

---
 .github/workflows/ci.yml                      | 35 +++++++++++++++++--
 MySqlConnector.sln                            |  6 ++++
 .../MySqlConnector.DependencyInjection.csproj |  1 +
 .../MySqlConnectionStringBuilder.cs           |  3 ++
 src/MySqlConnector/MySqlConnector.csproj      |  1 +
 src/MySqlConnector/MySqlDataReader.cs         |  5 +++
 .../MySqlConnector.NativeAot.Tests.csproj     | 20 +++++++++++
 .../MySqlConnector.NativeAot.Tests/Program.cs | 26 ++++++++++++++
 8 files changed, 94 insertions(+), 3 deletions(-)
 create mode 100644 tests/MySqlConnector.NativeAot.Tests/MySqlConnector.NativeAot.Tests.csproj
 create mode 100644 tests/MySqlConnector.NativeAot.Tests/Program.cs

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 01ae0c1fb..aa7ec5123 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -5,14 +5,14 @@ on:
     branches: [ master, test ]
 
 jobs:
-  build:
-    name: Build
+  component-detection:
+    name: Component Detection
     runs-on: ubuntu-latest
     permissions:
       contents: write
 
     steps:
-    - name: Checkout repository
+    - name: Check out repository
       uses: actions/checkout@v4
 
     - name: Set up .NET
@@ -26,3 +26,32 @@ jobs:
 
     - name: Component detection
       uses: advanced-security/component-detection-dependency-submission-action@v0.0.3
+
+  native-aot:
+    name: Native AOT
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        tfm: [ 'net8.0', 'net9.0' ]
+    services:
+      mysql:
+        image: mysql:9.1
+        env:
+          MYSQL_DATABASE: testdb
+          MYSQL_ROOT_PASSWORD: pass
+        ports:
+          - 3306:3306
+          # Before continuing, verify the mysql container is reachable from the ubuntu host
+        options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
+    steps:
+      - name: Check out repository
+        uses: actions/checkout@v4
+
+      - name: Set up .NET
+        uses: actions/setup-dotnet@v4
+
+      - name: Publish
+        run: dotnet publish -c Release -r linux-x64 -f ${{ matrix.tfm }} tests/MySqlConnector.NativeAot.Tests/MySqlConnector.NativeAot.Tests.csproj
+
+      - name: Test Native AOT
+        run: ./artifacts/publish/MySqlConnector.NativeAot.Tests/release_${{ matrix.tfm }}_linux-x64/MySqlConnector.NativeAot.Tests
diff --git a/MySqlConnector.sln b/MySqlConnector.sln
index c3646c415..85c7e52ff 100644
--- a/MySqlConnector.sln
+++ b/MySqlConnector.sln
@@ -28,6 +28,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.DependencyIn
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.DependencyInjection.Tests", "tests\MySqlConnector.DependencyInjection.Tests\MySqlConnector.DependencyInjection.Tests.csproj", "{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}"
 EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySqlConnector.NativeAot.Tests", "tests\MySqlConnector.NativeAot.Tests\MySqlConnector.NativeAot.Tests.csproj", "{F3A1E4DF-A5CC-4447-A0FC-C6971218EEDE}"
+EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
 		Debug|Any CPU = Debug|Any CPU
@@ -82,6 +84,10 @@ Global
 		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Release|Any CPU.ActiveCfg = Release|Any CPU
 		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Release|Any CPU.Build.0 = Release|Any CPU
+		{F3A1E4DF-A5CC-4447-A0FC-C6971218EEDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{F3A1E4DF-A5CC-4447-A0FC-C6971218EEDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{F3A1E4DF-A5CC-4447-A0FC-C6971218EEDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{F3A1E4DF-A5CC-4447-A0FC-C6971218EEDE}.Release|Any CPU.Build.0 = Release|Any CPU
 	EndGlobalSection
 	GlobalSection(SolutionProperties) = preSolution
 		HideSolutionNode = FALSE
diff --git a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
index 6a7f30b45..69dbc32be 100644
--- a/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
+++ b/src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj
@@ -11,6 +11,7 @@
     <Authors>Bradley Grainger</Authors>
     <PackageReadmeFile>README.md</PackageReadmeFile>
     <PackageTags>mysqlconnector;dependency injection;iservicecollection</PackageTags>
+    <IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/src/MySqlConnector/MySqlConnectionStringBuilder.cs b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
index 352508326..762bcc639 100644
--- a/src/MySqlConnector/MySqlConnectionStringBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectionStringBuilder.cs
@@ -871,6 +871,9 @@ internal string GetConnectionString(bool includePassword)
 	/// Fills in <paramref name="propertyDescriptors"/> with information about the available properties on this object.
 	/// </summary>
 	/// <param name="propertyDescriptors">The collection of <see cref="PropertyDescriptor"/> objects to populate.</param>
+#if NET6_0_OR_GREATER
+	[RequiresUnreferencedCode("PropertyDescriptor's PropertyType cannot be statically discovered.")]
+#endif
 	protected override void GetProperties(Hashtable propertyDescriptors)
 	{
 		base.GetProperties(propertyDescriptors);
diff --git a/src/MySqlConnector/MySqlConnector.csproj b/src/MySqlConnector/MySqlConnector.csproj
index ae92887e6..023a6e28e 100644
--- a/src/MySqlConnector/MySqlConnector.csproj
+++ b/src/MySqlConnector/MySqlConnector.csproj
@@ -13,6 +13,7 @@
     <Nullable>enable</Nullable>
     <CheckEolTargetFramework>false</CheckEolTargetFramework>
     <ImplicitUsings>enable</ImplicitUsings>
+    <IsAotCompatible Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net7.0'))">true</IsAotCompatible>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 6490fc48b..800fafe47 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -1,6 +1,7 @@
 using System.Collections;
 using System.Collections.ObjectModel;
 using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using MySqlConnector.Core;
 using MySqlConnector.Logging;
@@ -245,6 +246,10 @@ public override long GetChars(int ordinal, long dataOffset, char[]? buffer, int
 	public override string GetDataTypeName(int ordinal) => GetResultSet().GetDataTypeName(ordinal);
 
 	public Type GetFieldType(string name) => GetFieldType(GetOrdinal(name));
+
+#if NET6_0_OR_GREATER
+	[UnconditionalSuppressMessage("ILLink", "IL2093", Justification = "This method is provided to implement the DbDataReader API. We do not want to retain all public methods of Types just used as sentinel values for field mapping.")]
+#endif
 	public override Type GetFieldType(int ordinal) => GetResultSet().GetFieldType(ordinal);
 
 	public override object GetValue(int ordinal) => GetResultSet().GetCurrentRow().GetValue(ordinal);
diff --git a/tests/MySqlConnector.NativeAot.Tests/MySqlConnector.NativeAot.Tests.csproj b/tests/MySqlConnector.NativeAot.Tests/MySqlConnector.NativeAot.Tests.csproj
new file mode 100644
index 000000000..3797dedb8
--- /dev/null
+++ b/tests/MySqlConnector.NativeAot.Tests/MySqlConnector.NativeAot.Tests.csproj
@@ -0,0 +1,20 @@
+<Project Sdk="Microsoft.NET.Sdk">
+
+  <PropertyGroup>
+    <OutputType>Exe</OutputType>
+    <TargetFrameworks>net8.0;net9.0</TargetFrameworks>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+    <PublishAot>true</PublishAot>
+    <InvariantGlobalization>true</InvariantGlobalization>
+    <StripSymbols>true</StripSymbols>
+    <UseSystemResourceKeys>true</UseSystemResourceKeys>
+    <IlcFoldIdenticalMethodBodies>true</IlcFoldIdenticalMethodBodies>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\..\src\MySqlConnector\MySqlConnector.csproj" />
+    <ProjectReference Include="..\..\src\MySqlConnector.DependencyInjection\MySqlConnector.DependencyInjection.csproj" />
+  </ItemGroup>
+
+</Project>
diff --git a/tests/MySqlConnector.NativeAot.Tests/Program.cs b/tests/MySqlConnector.NativeAot.Tests/Program.cs
new file mode 100644
index 000000000..f72c65d71
--- /dev/null
+++ b/tests/MySqlConnector.NativeAot.Tests/Program.cs
@@ -0,0 +1,26 @@
+using MySqlConnector;
+
+var connectionString = Environment.GetEnvironmentVariable("CONNECTION_STRING") ?? "Server=localhost;Username=root;Password=pass";
+
+await using var dataSource = new MySqlDataSourceBuilder(connectionString).Build();
+
+await using var conn = dataSource.CreateConnection();
+await conn.OpenAsync();
+await using var cmd = new MySqlCommand("SELECT 'Hello World'", conn);
+await using var reader = await cmd.ExecuteReaderAsync();
+if (!await reader.ReadAsync())
+	throw new Exception("ReadAsync returned false");
+
+var value = reader.GetFieldValue<string>(0);
+if (value != "Hello World")
+	throw new Exception($"Expected 'Hello World'; got '{value}'");
+
+var schema = reader.GetColumnSchema();
+if (schema.Count != 1)
+	throw new Exception($"Expected 1 column, got {schema.Count}");
+if (((MySqlDbColumn) schema[0]).ProviderType != MySqlDbType.VarChar)
+	throw new Exception($"Expected column type to be MySqlDbType.VarChar, got {((MySqlDbColumn) schema[0]).ProviderType}");
+if (reader.GetFieldType(0) != typeof(string))
+	throw new Exception($"Expected column type to be System.String, got {reader.GetFieldType(0)}");
+
+Console.WriteLine("Success");

From 21c07f6442e106cc8b4e1396917e29b478eb052e Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 5 Nov 2024 05:08:53 -0800
Subject: [PATCH 343/393] Use a lock file for NuGet restore.

---
 Directory.Build.props                         |    1 +
 Directory.Packages.props                      |    2 +-
 .../packages.lock.json                        |  267 +++
 .../packages.lock.json                        |  212 +++
 .../packages.lock.json                        |  143 ++
 .../packages.lock.json                        |  279 ++++
 .../packages.lock.json                        |  279 ++++
 .../packages.lock.json                        |  320 ++++
 src/MySqlConnector/packages.lock.json         |  808 +++++++++
 tests/Conformance.Tests/packages.lock.json    |  192 +++
 tests/IntegrationTests/packages.lock.json     | 1481 +++++++++++++++++
 .../packages.lock.json                        |  169 ++
 .../packages.lock.json                        |  141 ++
 tests/MySqlConnector.Tests/packages.lock.json |  358 ++++
 .../packages.lock.json                        |   39 +
 15 files changed, 4690 insertions(+), 1 deletion(-)
 create mode 100644 src/MySqlConnector.Authentication.Ed25519/packages.lock.json
 create mode 100644 src/MySqlConnector.DependencyInjection/packages.lock.json
 create mode 100644 src/MySqlConnector.Logging.Microsoft.Extensions.Logging/packages.lock.json
 create mode 100644 src/MySqlConnector.Logging.NLog/packages.lock.json
 create mode 100644 src/MySqlConnector.Logging.Serilog/packages.lock.json
 create mode 100644 src/MySqlConnector.Logging.log4net/packages.lock.json
 create mode 100644 src/MySqlConnector/packages.lock.json
 create mode 100644 tests/Conformance.Tests/packages.lock.json
 create mode 100644 tests/IntegrationTests/packages.lock.json
 create mode 100644 tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
 create mode 100644 tests/MySqlConnector.NativeAot.Tests/packages.lock.json
 create mode 100644 tests/MySqlConnector.Tests/packages.lock.json
 create mode 100644 tools/SchemaCollectionGenerator/packages.lock.json

diff --git a/Directory.Build.props b/Directory.Build.props
index 10bca8b48..ff778ea9a 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -9,6 +9,7 @@
     <NuGetAudit>true</NuGetAudit>
     <NuGetAuditLevel>low</NuGetAuditLevel>
     <NuGetAuditMode>all</NuGetAuditMode>
+    <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 6e2efac78..e46e0df6a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -1,7 +1,7 @@
 <Project>
   <PropertyGroup>
     <ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
-    <CentralPackageTransitivePinningEnabled>false</CentralPackageTransitivePinningEnabled>
+    <CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/src/MySqlConnector.Authentication.Ed25519/packages.lock.json b/src/MySqlConnector.Authentication.Ed25519/packages.lock.json
new file mode 100644
index 000000000..c4c1e88fd
--- /dev/null
+++ b/src/MySqlConnector.Authentication.Ed25519/packages.lock.json
@@ -0,0 +1,267 @@
+{
+  "version": 2,
+  "dependencies": {
+    ".NETFramework,Version=v4.6.2": {
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.5.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    },
+    ".NETStandard,Version=v2.0": {
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "NETStandard.Library": {
+        "type": "Direct",
+        "requested": "[2.0.3, )",
+        "resolved": "2.0.3",
+        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0"
+        }
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.NETCore.Platforms": {
+        "type": "Transitive",
+        "resolved": "1.1.0",
+        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.4.0",
+        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/MySqlConnector.DependencyInjection/packages.lock.json b/src/MySqlConnector.DependencyInjection/packages.lock.json
new file mode 100644
index 000000000..c7119724a
--- /dev/null
+++ b/src/MySqlConnector.DependencyInjection/packages.lock.json
@@ -0,0 +1,212 @@
+{
+  "version": 2,
+  "dependencies": {
+    ".NETStandard,Version=v2.0": {
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "NETStandard.Library": {
+        "type": "Direct",
+        "requested": "[2.0.3, )",
+        "resolved": "2.0.3",
+        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0"
+        }
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.NETCore.Platforms": {
+        "type": "Transitive",
+        "resolved": "1.1.0",
+        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.4.0",
+        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    },
+    "net8.0": {
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      },
+      "Microsoft.NET.ILLink.Tasks": {
+        "type": "Direct",
+        "requested": "[8.0.10, )",
+        "resolved": "8.0.10",
+        "contentHash": "xT8jYjlroY7SLbGtoV9vUTVW/TPgodL4Egc31a444Xe0TMytLZ3UlKQ0kxMZsy/CrWsFB6wtKnSG1SsXcWreew=="
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/packages.lock.json b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/packages.lock.json
new file mode 100644
index 000000000..fbfb4a12b
--- /dev/null
+++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/packages.lock.json
@@ -0,0 +1,143 @@
+{
+  "version": 2,
+  "dependencies": {
+    ".NETStandard,Version=v2.0": {
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "NETStandard.Library": {
+        "type": "Direct",
+        "requested": "[2.0.3, )",
+        "resolved": "2.0.3",
+        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0"
+        }
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.NETCore.Platforms": {
+        "type": "Transitive",
+        "resolved": "1.1.0",
+        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.4.0",
+        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/MySqlConnector.Logging.NLog/packages.lock.json b/src/MySqlConnector.Logging.NLog/packages.lock.json
new file mode 100644
index 000000000..1c8d67f69
--- /dev/null
+++ b/src/MySqlConnector.Logging.NLog/packages.lock.json
@@ -0,0 +1,279 @@
+{
+  "version": 2,
+  "dependencies": {
+    ".NETFramework,Version=v4.6.2": {
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "NLog": {
+        "type": "Direct",
+        "requested": "[4.7.15, )",
+        "resolved": "4.7.15",
+        "contentHash": "vXMmsK17hXnMsKd8BdCrJzvb8JMT0+cPgBGRfnqcXIb6y8b6RMIZexO4hMhQgifcTHgU7dbubvCR5A5wX6Ne+A=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.5.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    },
+    ".NETStandard,Version=v2.0": {
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "NETStandard.Library": {
+        "type": "Direct",
+        "requested": "[2.0.3, )",
+        "resolved": "2.0.3",
+        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0"
+        }
+      },
+      "NLog": {
+        "type": "Direct",
+        "requested": "[4.7.15, )",
+        "resolved": "4.7.15",
+        "contentHash": "vXMmsK17hXnMsKd8BdCrJzvb8JMT0+cPgBGRfnqcXIb6y8b6RMIZexO4hMhQgifcTHgU7dbubvCR5A5wX6Ne+A=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.NETCore.Platforms": {
+        "type": "Transitive",
+        "resolved": "1.1.0",
+        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.4.0",
+        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/MySqlConnector.Logging.Serilog/packages.lock.json b/src/MySqlConnector.Logging.Serilog/packages.lock.json
new file mode 100644
index 000000000..4083d5c77
--- /dev/null
+++ b/src/MySqlConnector.Logging.Serilog/packages.lock.json
@@ -0,0 +1,279 @@
+{
+  "version": 2,
+  "dependencies": {
+    ".NETFramework,Version=v4.6.2": {
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "Serilog": {
+        "type": "Direct",
+        "requested": "[2.12.0, )",
+        "resolved": "2.12.0",
+        "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.5.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    },
+    ".NETStandard,Version=v2.0": {
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "NETStandard.Library": {
+        "type": "Direct",
+        "requested": "[2.0.3, )",
+        "resolved": "2.0.3",
+        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0"
+        }
+      },
+      "Serilog": {
+        "type": "Direct",
+        "requested": "[2.12.0, )",
+        "resolved": "2.12.0",
+        "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.NETCore.Platforms": {
+        "type": "Transitive",
+        "resolved": "1.1.0",
+        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.4.0",
+        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/MySqlConnector.Logging.log4net/packages.lock.json b/src/MySqlConnector.Logging.log4net/packages.lock.json
new file mode 100644
index 000000000..05fb71c2d
--- /dev/null
+++ b/src/MySqlConnector.Logging.log4net/packages.lock.json
@@ -0,0 +1,320 @@
+{
+  "version": 2,
+  "dependencies": {
+    ".NETFramework,Version=v4.6.2": {
+      "log4net": {
+        "type": "Direct",
+        "requested": "[2.0.17, )",
+        "resolved": "2.0.17",
+        "contentHash": "qnnDf/ubJzwm2i1xH7nRMjEDoD+ctse7nZDqb+p7L1PvZc6ykpMoEesWr1/9hFqlsbII2v9e8yyQHJhoDQh7ZA=="
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.5.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    },
+    ".NETStandard,Version=v2.0": {
+      "log4net": {
+        "type": "Direct",
+        "requested": "[2.0.17, )",
+        "resolved": "2.0.17",
+        "contentHash": "qnnDf/ubJzwm2i1xH7nRMjEDoD+ctse7nZDqb+p7L1PvZc6ykpMoEesWr1/9hFqlsbII2v9e8yyQHJhoDQh7ZA==",
+        "dependencies": {
+          "System.Configuration.ConfigurationManager": "4.5.0"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "NETStandard.Library": {
+        "type": "Direct",
+        "requested": "[2.0.3, )",
+        "resolved": "2.0.3",
+        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0"
+        }
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.NETCore.Platforms": {
+        "type": "Transitive",
+        "resolved": "1.1.0",
+        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Configuration.ConfigurationManager": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "UIFvaFfuKhLr9u5tWMxmVoDPkFeD+Qv8gUuap4aZgVGYSYMdERck4OhLN/2gulAc0nYTEigWXSJNNWshrmxnng==",
+        "dependencies": {
+          "System.Security.Cryptography.ProtectedData": "4.5.0",
+          "System.Security.Permissions": "4.5.0"
+        }
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.4.0",
+        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "System.Security.AccessControl": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "vW8Eoq0TMyz5vAG/6ce483x/CP83fgm4SJe5P8Tb1tZaobcvPrbMEL7rhH1DRdrYbbb6F0vq3OlzmK0Pkwks5A==",
+        "dependencies": {
+          "System.Security.Principal.Windows": "4.5.0"
+        }
+      },
+      "System.Security.Cryptography.ProtectedData": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "wLBKzFnDCxP12VL9ANydSYhk59fC4cvOr9ypYQLPnAj48NQIhqnjdD2yhP8yEKyBJEjERWS9DisKL7rX5eU25Q==",
+        "dependencies": {
+          "System.Memory": "4.5.0"
+        }
+      },
+      "System.Security.Permissions": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "9gdyuARhUR7H+p5CjyUB/zPk7/Xut3wUSP8NJQB6iZr8L3XUXTMdoLeVAg9N4rqF8oIpE7MpdqHdDHQ7XgJe0g==",
+        "dependencies": {
+          "System.Security.AccessControl": "4.5.0"
+        }
+      },
+      "System.Security.Principal.Windows": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "U77HfRXlZlOeIXd//Yoj6Jnk8AXlbeisf1oq1os+hxOGVnuG+lGSfGqTwTZBoORFF6j/0q7HXIl8cqwQ9aUGqQ=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/src/MySqlConnector/packages.lock.json b/src/MySqlConnector/packages.lock.json
new file mode 100644
index 000000000..5fed9ca01
--- /dev/null
+++ b/src/MySqlConnector/packages.lock.json
@@ -0,0 +1,808 @@
+{
+  "version": 2,
+  "dependencies": {
+    ".NETFramework,Version=v4.6.2": {
+      "IndexRange": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "tqgj3LuQHDH0OfGZamyiW1i9fhpny5piqvI+8Y7IIaRly+Bhqv5Z7zSlcIwA/DlYPS/cBmVwb+J4VGEFFqmnfQ==",
+        "dependencies": {
+          "System.ValueTuple": "4.5.0"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "Direct",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.5.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "System.ValueTuple": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      }
+    },
+    ".NETFramework,Version=v4.7.1": {
+      "IndexRange": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "tqgj3LuQHDH0OfGZamyiW1i9fhpny5piqvI+8Y7IIaRly+Bhqv5Z7zSlcIwA/DlYPS/cBmVwb+J4VGEFFqmnfQ=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "Direct",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.5.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      }
+    },
+    ".NETFramework,Version=v4.8": {
+      "IndexRange": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "tqgj3LuQHDH0OfGZamyiW1i9fhpny5piqvI+8Y7IIaRly+Bhqv5Z7zSlcIwA/DlYPS/cBmVwb+J4VGEFFqmnfQ=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "Direct",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.5.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      }
+    },
+    ".NETStandard,Version=v2.0": {
+      "IndexRange": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "tqgj3LuQHDH0OfGZamyiW1i9fhpny5piqvI+8Y7IIaRly+Bhqv5Z7zSlcIwA/DlYPS/cBmVwb+J4VGEFFqmnfQ=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "NETStandard.Library": {
+        "type": "Direct",
+        "requested": "[2.0.3, )",
+        "resolved": "2.0.3",
+        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0"
+        }
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "Direct",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.NETCore.Platforms": {
+        "type": "Transitive",
+        "resolved": "1.1.0",
+        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.4.0",
+        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      }
+    },
+    ".NETStandard,Version=v2.1": {
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.4.0",
+        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      }
+    },
+    "net6.0": {
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Diagnostics.DiagnosticSource": "8.0.1"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      }
+    },
+    "net8.0": {
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      },
+      "Microsoft.NET.ILLink.Tasks": {
+        "type": "Direct",
+        "requested": "[8.0.10, )",
+        "resolved": "8.0.10",
+        "contentHash": "xT8jYjlroY7SLbGtoV9vUTVW/TPgodL4Egc31a444Xe0TMytLZ3UlKQ0kxMZsy/CrWsFB6wtKnSG1SsXcWreew=="
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      }
+    },
+    "net9.0": {
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      },
+      "Microsoft.NET.ILLink.Tasks": {
+        "type": "Direct",
+        "requested": "[9.0.0-rc.2.24473.5, )",
+        "resolved": "9.0.0-rc.2.24473.5",
+        "contentHash": "mXyCgKqBIo/KOpY09FqjOU8d+LME2qowzzvBd1/viYmSpAKYfQ2w1WDOlh0FniY4xnrB/wmDiAo4frJvfCqT0w=="
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/Conformance.Tests/packages.lock.json b/tests/Conformance.Tests/packages.lock.json
new file mode 100644
index 000000000..c74127043
--- /dev/null
+++ b/tests/Conformance.Tests/packages.lock.json
@@ -0,0 +1,192 @@
+{
+  "version": 2,
+  "dependencies": {
+    "net9.0": {
+      "AdoNet.Specification.Tests": {
+        "type": "Direct",
+        "requested": "[2.0.0-beta.2, )",
+        "resolved": "2.0.0-beta.2",
+        "contentHash": "W64bxMUv/RH3Bs65HJrw/N3opKDvxpnei5BqVtaGaNyeGaimYnngATFPpMCw0ljBpmMAx7b+mWvdEXl40hoNUA==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies": "1.0.3",
+          "Xunit.SkippableFact": "1.4.13",
+          "xunit": "2.5.3",
+          "xunit.extensibility.execution": "2.5.3"
+        }
+      },
+      "Microsoft.NET.Test.Sdk": {
+        "type": "Direct",
+        "requested": "[17.11.1, )",
+        "resolved": "17.11.1",
+        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "dependencies": {
+          "Microsoft.CodeCoverage": "17.11.1",
+          "Microsoft.TestPlatform.TestHost": "17.11.1"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "xunit": {
+        "type": "Direct",
+        "requested": "[2.9.2, )",
+        "resolved": "2.9.2",
+        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "dependencies": {
+          "xunit.analyzers": "1.16.0",
+          "xunit.assert": "2.9.2",
+          "xunit.core": "[2.9.2]"
+        }
+      },
+      "xunit.runner.visualstudio": {
+        "type": "Direct",
+        "requested": "[2.8.2, )",
+        "resolved": "2.8.2",
+        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.CodeCoverage": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+      },
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
+        }
+      },
+      "Microsoft.NETFramework.ReferenceAssemblies.net461": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "Microsoft.TestPlatform.ObjectModel": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "dependencies": {
+          "System.Reflection.Metadata": "1.6.0"
+        }
+      },
+      "Microsoft.TestPlatform.TestHost": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "dependencies": {
+          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Newtonsoft.Json": "13.0.1"
+        }
+      },
+      "Newtonsoft.Json": {
+        "type": "Transitive",
+        "resolved": "13.0.1",
+        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
+      },
+      "System.Reflection.Metadata": {
+        "type": "Transitive",
+        "resolved": "1.6.0",
+        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
+      },
+      "Validation": {
+        "type": "Transitive",
+        "resolved": "2.4.18",
+        "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw=="
+      },
+      "xunit.abstractions": {
+        "type": "Transitive",
+        "resolved": "2.0.3",
+        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
+      },
+      "xunit.analyzers": {
+        "type": "Transitive",
+        "resolved": "1.16.0",
+        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+      },
+      "xunit.assert": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+      },
+      "xunit.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]",
+          "xunit.extensibility.execution": "[2.9.2]"
+        }
+      },
+      "xunit.extensibility.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "dependencies": {
+          "xunit.abstractions": "2.0.3"
+        }
+      },
+      "xunit.extensibility.execution": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]"
+        }
+      },
+      "Xunit.SkippableFact": {
+        "type": "Transitive",
+        "resolved": "1.4.13",
+        "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==",
+        "dependencies": {
+          "Validation": "2.4.18",
+          "xunit.extensibility.execution": "2.4.0"
+        }
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/IntegrationTests/packages.lock.json b/tests/IntegrationTests/packages.lock.json
new file mode 100644
index 000000000..cacaafd6a
--- /dev/null
+++ b/tests/IntegrationTests/packages.lock.json
@@ -0,0 +1,1481 @@
+{
+  "version": 2,
+  "dependencies": {
+    ".NETFramework,Version=v4.6.2": {
+      "Dapper.StrongName": {
+        "type": "Direct",
+        "requested": "[2.1.35, )",
+        "resolved": "2.1.35",
+        "contentHash": "DkQpZMhT6Tk/ZrN1cksQ7lQx3BMFL6Eq2oq5VLlZxqnDBJhOUo6qSwFrI5E0xssdDLxt91mPHlxLqPH4tylLNw=="
+      },
+      "Microsoft.Extensions.Configuration.Binder": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.EnvironmentVariables": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Json": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1",
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "System.Text.Json": "8.0.5"
+        }
+      },
+      "Microsoft.NET.Test.Sdk": {
+        "type": "Direct",
+        "requested": "[17.11.1, )",
+        "resolved": "17.11.1",
+        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "dependencies": {
+          "Microsoft.CodeCoverage": "17.11.1"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "System.Text.Json": {
+        "type": "Direct",
+        "requested": "[8.0.5, )",
+        "resolved": "8.0.5",
+        "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Buffers": "4.5.1",
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
+          "System.Text.Encodings.Web": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4",
+          "System.ValueTuple": "4.5.0"
+        }
+      },
+      "xunit": {
+        "type": "Direct",
+        "requested": "[2.9.2, )",
+        "resolved": "2.9.2",
+        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "dependencies": {
+          "xunit.analyzers": "1.16.0",
+          "xunit.assert": "2.9.2",
+          "xunit.core": "[2.9.2]"
+        }
+      },
+      "xunit.runner.visualstudio": {
+        "type": "Direct",
+        "requested": "[2.8.2, )",
+        "resolved": "2.8.2",
+        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ==",
+        "dependencies": {
+          "Microsoft.TestPlatform.ObjectModel": "17.10.0"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.CodeCoverage": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+      },
+      "Microsoft.Extensions.Configuration": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "8.0.0",
+          "System.ValueTuple": "4.5.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.FileExtensions": {
+        "type": "Transitive",
+        "resolved": "8.0.1",
+        "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileProviders.Physical": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileProviders.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileProviders.Physical": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==",
+        "dependencies": {
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileSystemGlobbing": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileSystemGlobbing": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
+      },
+      "Microsoft.Extensions.Primitives": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "Microsoft.TestPlatform.ObjectModel": {
+        "type": "Transitive",
+        "resolved": "17.10.0",
+        "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==",
+        "dependencies": {
+          "System.Reflection.Metadata": "1.6.0"
+        }
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Collections.Immutable": {
+        "type": "Transitive",
+        "resolved": "1.5.0",
+        "contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.5.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+      },
+      "System.Reflection.Metadata": {
+        "type": "Transitive",
+        "resolved": "1.6.0",
+        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==",
+        "dependencies": {
+          "System.Collections.Immutable": "1.5.0"
+        }
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "System.Text.Encodings.Web": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.ValueTuple": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
+      },
+      "xunit.abstractions": {
+        "type": "Transitive",
+        "resolved": "2.0.3",
+        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
+      },
+      "xunit.analyzers": {
+        "type": "Transitive",
+        "resolved": "1.16.0",
+        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+      },
+      "xunit.assert": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+      },
+      "xunit.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]",
+          "xunit.extensibility.execution": "[2.9.2]"
+        }
+      },
+      "xunit.extensibility.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "dependencies": {
+          "xunit.abstractions": "2.0.3"
+        }
+      },
+      "xunit.extensibility.execution": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]"
+        }
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "mysqlconnector.authentication.ed25519": {
+        "type": "Project",
+        "dependencies": {
+          "MySqlConnector": "[1.0.0, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    },
+    ".NETFramework,Version=v4.8.1": {
+      "Dapper.StrongName": {
+        "type": "Direct",
+        "requested": "[2.1.35, )",
+        "resolved": "2.1.35",
+        "contentHash": "DkQpZMhT6Tk/ZrN1cksQ7lQx3BMFL6Eq2oq5VLlZxqnDBJhOUo6qSwFrI5E0xssdDLxt91mPHlxLqPH4tylLNw=="
+      },
+      "Microsoft.Extensions.Configuration.Binder": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.EnvironmentVariables": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Json": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1",
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "System.Text.Json": "8.0.5"
+        }
+      },
+      "Microsoft.NET.Test.Sdk": {
+        "type": "Direct",
+        "requested": "[17.11.1, )",
+        "resolved": "17.11.1",
+        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "dependencies": {
+          "Microsoft.CodeCoverage": "17.11.1"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "System.Text.Json": {
+        "type": "Direct",
+        "requested": "[8.0.5, )",
+        "resolved": "8.0.5",
+        "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Buffers": "4.5.1",
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
+          "System.Text.Encodings.Web": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4",
+          "System.ValueTuple": "4.5.0"
+        }
+      },
+      "xunit": {
+        "type": "Direct",
+        "requested": "[2.9.2, )",
+        "resolved": "2.9.2",
+        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "dependencies": {
+          "xunit.analyzers": "1.16.0",
+          "xunit.assert": "2.9.2",
+          "xunit.core": "[2.9.2]"
+        }
+      },
+      "xunit.runner.visualstudio": {
+        "type": "Direct",
+        "requested": "[2.8.2, )",
+        "resolved": "2.8.2",
+        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ==",
+        "dependencies": {
+          "Microsoft.TestPlatform.ObjectModel": "17.10.0"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.CodeCoverage": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+      },
+      "Microsoft.Extensions.Configuration": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "8.0.0",
+          "System.ValueTuple": "4.5.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.FileExtensions": {
+        "type": "Transitive",
+        "resolved": "8.0.1",
+        "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileProviders.Physical": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileProviders.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileProviders.Physical": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==",
+        "dependencies": {
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileSystemGlobbing": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileSystemGlobbing": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
+      },
+      "Microsoft.Extensions.Primitives": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "Microsoft.TestPlatform.ObjectModel": {
+        "type": "Transitive",
+        "resolved": "17.10.0",
+        "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==",
+        "dependencies": {
+          "System.Reflection.Metadata": "1.6.0"
+        }
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Collections.Immutable": {
+        "type": "Transitive",
+        "resolved": "1.5.0",
+        "contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.5.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+      },
+      "System.Reflection.Metadata": {
+        "type": "Transitive",
+        "resolved": "1.6.0",
+        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==",
+        "dependencies": {
+          "System.Collections.Immutable": "1.5.0"
+        }
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "System.Text.Encodings.Web": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.ValueTuple": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
+      },
+      "xunit.abstractions": {
+        "type": "Transitive",
+        "resolved": "2.0.3",
+        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
+      },
+      "xunit.analyzers": {
+        "type": "Transitive",
+        "resolved": "1.16.0",
+        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+      },
+      "xunit.assert": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+      },
+      "xunit.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]",
+          "xunit.extensibility.execution": "[2.9.2]"
+        }
+      },
+      "xunit.extensibility.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "dependencies": {
+          "xunit.abstractions": "2.0.3"
+        }
+      },
+      "xunit.extensibility.execution": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]"
+        }
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "mysqlconnector.authentication.ed25519": {
+        "type": "Project",
+        "dependencies": {
+          "MySqlConnector": "[1.0.0, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    },
+    "net6.0": {
+      "Dapper.StrongName": {
+        "type": "Direct",
+        "requested": "[2.1.35, )",
+        "resolved": "2.1.35",
+        "contentHash": "DkQpZMhT6Tk/ZrN1cksQ7lQx3BMFL6Eq2oq5VLlZxqnDBJhOUo6qSwFrI5E0xssdDLxt91mPHlxLqPH4tylLNw=="
+      },
+      "Microsoft.Extensions.Configuration.Binder": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.EnvironmentVariables": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Json": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1",
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "System.Text.Json": "8.0.5"
+        }
+      },
+      "Microsoft.NET.Test.Sdk": {
+        "type": "Direct",
+        "requested": "[17.11.1, )",
+        "resolved": "17.11.1",
+        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "dependencies": {
+          "Microsoft.CodeCoverage": "17.11.1",
+          "Microsoft.TestPlatform.TestHost": "17.11.1"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "System.Text.Json": {
+        "type": "Direct",
+        "requested": "[8.0.5, )",
+        "resolved": "8.0.5",
+        "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
+          "System.Text.Encodings.Web": "8.0.0"
+        }
+      },
+      "xunit": {
+        "type": "Direct",
+        "requested": "[2.9.2, )",
+        "resolved": "2.9.2",
+        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "dependencies": {
+          "xunit.analyzers": "1.16.0",
+          "xunit.assert": "2.9.2",
+          "xunit.core": "[2.9.2]"
+        }
+      },
+      "xunit.runner.visualstudio": {
+        "type": "Direct",
+        "requested": "[2.8.2, )",
+        "resolved": "2.8.2",
+        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.CodeCoverage": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+      },
+      "Microsoft.Extensions.Configuration": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.FileExtensions": {
+        "type": "Transitive",
+        "resolved": "8.0.1",
+        "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileProviders.Physical": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileProviders.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileProviders.Physical": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==",
+        "dependencies": {
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileSystemGlobbing": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileSystemGlobbing": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
+      },
+      "Microsoft.Extensions.Primitives": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "Microsoft.TestPlatform.ObjectModel": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "dependencies": {
+          "System.Reflection.Metadata": "1.6.0"
+        }
+      },
+      "Microsoft.TestPlatform.TestHost": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "dependencies": {
+          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Newtonsoft.Json": "13.0.1"
+        }
+      },
+      "Newtonsoft.Json": {
+        "type": "Transitive",
+        "resolved": "13.0.1",
+        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
+      },
+      "System.Reflection.Metadata": {
+        "type": "Transitive",
+        "resolved": "1.6.0",
+        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "System.Text.Encodings.Web": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "xunit.abstractions": {
+        "type": "Transitive",
+        "resolved": "2.0.3",
+        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
+      },
+      "xunit.analyzers": {
+        "type": "Transitive",
+        "resolved": "1.16.0",
+        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+      },
+      "xunit.assert": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+      },
+      "xunit.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]",
+          "xunit.extensibility.execution": "[2.9.2]"
+        }
+      },
+      "xunit.extensibility.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "dependencies": {
+          "xunit.abstractions": "2.0.3"
+        }
+      },
+      "xunit.extensibility.execution": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]"
+        }
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )"
+        }
+      },
+      "mysqlconnector.authentication.ed25519": {
+        "type": "Project",
+        "dependencies": {
+          "MySqlConnector": "[1.0.0, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Diagnostics.DiagnosticSource": "8.0.1"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      }
+    },
+    "net8.0": {
+      "Dapper.StrongName": {
+        "type": "Direct",
+        "requested": "[2.1.35, )",
+        "resolved": "2.1.35",
+        "contentHash": "DkQpZMhT6Tk/ZrN1cksQ7lQx3BMFL6Eq2oq5VLlZxqnDBJhOUo6qSwFrI5E0xssdDLxt91mPHlxLqPH4tylLNw=="
+      },
+      "Microsoft.Extensions.Configuration.Binder": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.EnvironmentVariables": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Json": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1",
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.NET.Test.Sdk": {
+        "type": "Direct",
+        "requested": "[17.11.1, )",
+        "resolved": "17.11.1",
+        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "dependencies": {
+          "Microsoft.CodeCoverage": "17.11.1",
+          "Microsoft.TestPlatform.TestHost": "17.11.1"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "System.Text.Json": {
+        "type": "Direct",
+        "requested": "[8.0.5, )",
+        "resolved": "8.0.5",
+        "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg=="
+      },
+      "xunit": {
+        "type": "Direct",
+        "requested": "[2.9.2, )",
+        "resolved": "2.9.2",
+        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "dependencies": {
+          "xunit.analyzers": "1.16.0",
+          "xunit.assert": "2.9.2",
+          "xunit.core": "[2.9.2]"
+        }
+      },
+      "xunit.runner.visualstudio": {
+        "type": "Direct",
+        "requested": "[2.8.2, )",
+        "resolved": "2.8.2",
+        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.CodeCoverage": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+      },
+      "Microsoft.Extensions.Configuration": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.FileExtensions": {
+        "type": "Transitive",
+        "resolved": "8.0.1",
+        "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileProviders.Physical": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileProviders.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileProviders.Physical": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==",
+        "dependencies": {
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileSystemGlobbing": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileSystemGlobbing": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
+      },
+      "Microsoft.Extensions.Primitives": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "Microsoft.TestPlatform.ObjectModel": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "dependencies": {
+          "System.Reflection.Metadata": "1.6.0"
+        }
+      },
+      "Microsoft.TestPlatform.TestHost": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "dependencies": {
+          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Newtonsoft.Json": "13.0.1"
+        }
+      },
+      "Newtonsoft.Json": {
+        "type": "Transitive",
+        "resolved": "13.0.1",
+        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
+      },
+      "System.Reflection.Metadata": {
+        "type": "Transitive",
+        "resolved": "1.6.0",
+        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
+      },
+      "xunit.abstractions": {
+        "type": "Transitive",
+        "resolved": "2.0.3",
+        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
+      },
+      "xunit.analyzers": {
+        "type": "Transitive",
+        "resolved": "1.16.0",
+        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+      },
+      "xunit.assert": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+      },
+      "xunit.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]",
+          "xunit.extensibility.execution": "[2.9.2]"
+        }
+      },
+      "xunit.extensibility.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "dependencies": {
+          "xunit.abstractions": "2.0.3"
+        }
+      },
+      "xunit.extensibility.execution": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]"
+        }
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
+        }
+      },
+      "mysqlconnector.authentication.ed25519": {
+        "type": "Project",
+        "dependencies": {
+          "MySqlConnector": "[1.0.0, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      }
+    },
+    "net9.0": {
+      "Dapper.StrongName": {
+        "type": "Direct",
+        "requested": "[2.1.35, )",
+        "resolved": "2.1.35",
+        "contentHash": "DkQpZMhT6Tk/ZrN1cksQ7lQx3BMFL6Eq2oq5VLlZxqnDBJhOUo6qSwFrI5E0xssdDLxt91mPHlxLqPH4tylLNw=="
+      },
+      "Microsoft.Extensions.Configuration.Binder": {
+        "type": "Direct",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.EnvironmentVariables": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Json": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1",
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0"
+        }
+      },
+      "Microsoft.NET.Test.Sdk": {
+        "type": "Direct",
+        "requested": "[17.11.1, )",
+        "resolved": "17.11.1",
+        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "dependencies": {
+          "Microsoft.CodeCoverage": "17.11.1",
+          "Microsoft.TestPlatform.TestHost": "17.11.1"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "System.Text.Json": {
+        "type": "Direct",
+        "requested": "[8.0.5, )",
+        "resolved": "8.0.5",
+        "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg=="
+      },
+      "xunit": {
+        "type": "Direct",
+        "requested": "[2.9.2, )",
+        "resolved": "2.9.2",
+        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "dependencies": {
+          "xunit.analyzers": "1.16.0",
+          "xunit.assert": "2.9.2",
+          "xunit.core": "[2.9.2]"
+        }
+      },
+      "xunit.runner.visualstudio": {
+        "type": "Direct",
+        "requested": "[2.8.2, )",
+        "resolved": "2.8.2",
+        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.CodeCoverage": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+      },
+      "Microsoft.Extensions.Configuration": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.Configuration.FileExtensions": {
+        "type": "Transitive",
+        "resolved": "8.0.1",
+        "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "8.0.0",
+          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileProviders.Physical": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileProviders.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileProviders.Physical": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==",
+        "dependencies": {
+          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
+          "Microsoft.Extensions.FileSystemGlobbing": "8.0.0",
+          "Microsoft.Extensions.Primitives": "8.0.0"
+        }
+      },
+      "Microsoft.Extensions.FileSystemGlobbing": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
+      },
+      "Microsoft.Extensions.Primitives": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "Microsoft.TestPlatform.ObjectModel": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "dependencies": {
+          "System.Reflection.Metadata": "1.6.0"
+        }
+      },
+      "Microsoft.TestPlatform.TestHost": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "dependencies": {
+          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Newtonsoft.Json": "13.0.1"
+        }
+      },
+      "Newtonsoft.Json": {
+        "type": "Transitive",
+        "resolved": "13.0.1",
+        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
+      },
+      "System.Reflection.Metadata": {
+        "type": "Transitive",
+        "resolved": "1.6.0",
+        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
+      },
+      "xunit.abstractions": {
+        "type": "Transitive",
+        "resolved": "2.0.3",
+        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
+      },
+      "xunit.analyzers": {
+        "type": "Transitive",
+        "resolved": "1.16.0",
+        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+      },
+      "xunit.assert": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+      },
+      "xunit.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]",
+          "xunit.extensibility.execution": "[2.9.2]"
+        }
+      },
+      "xunit.extensibility.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "dependencies": {
+          "xunit.abstractions": "2.0.3"
+        }
+      },
+      "xunit.extensibility.execution": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]"
+        }
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
+        }
+      },
+      "mysqlconnector.authentication.ed25519": {
+        "type": "Project",
+        "dependencies": {
+          "MySqlConnector": "[1.0.0, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json b/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
new file mode 100644
index 000000000..3cf8566e4
--- /dev/null
+++ b/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
@@ -0,0 +1,169 @@
+{
+  "version": 2,
+  "dependencies": {
+    "net9.0": {
+      "Microsoft.Extensions.DependencyInjection": {
+        "type": "Direct",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      },
+      "Microsoft.NET.Test.Sdk": {
+        "type": "Direct",
+        "requested": "[17.11.1, )",
+        "resolved": "17.11.1",
+        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "dependencies": {
+          "Microsoft.CodeCoverage": "17.11.1",
+          "Microsoft.TestPlatform.TestHost": "17.11.1"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "xunit": {
+        "type": "Direct",
+        "requested": "[2.9.2, )",
+        "resolved": "2.9.2",
+        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "dependencies": {
+          "xunit.analyzers": "1.16.0",
+          "xunit.assert": "2.9.2",
+          "xunit.core": "[2.9.2]"
+        }
+      },
+      "xunit.runner.visualstudio": {
+        "type": "Direct",
+        "requested": "[2.8.2, )",
+        "resolved": "2.8.2",
+        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.CodeCoverage": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "Microsoft.TestPlatform.ObjectModel": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "dependencies": {
+          "System.Reflection.Metadata": "1.6.0"
+        }
+      },
+      "Microsoft.TestPlatform.TestHost": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "dependencies": {
+          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Newtonsoft.Json": "13.0.1"
+        }
+      },
+      "Newtonsoft.Json": {
+        "type": "Transitive",
+        "resolved": "13.0.1",
+        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
+      },
+      "System.Reflection.Metadata": {
+        "type": "Transitive",
+        "resolved": "1.6.0",
+        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
+      },
+      "xunit.abstractions": {
+        "type": "Transitive",
+        "resolved": "2.0.3",
+        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
+      },
+      "xunit.analyzers": {
+        "type": "Transitive",
+        "resolved": "1.16.0",
+        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+      },
+      "xunit.assert": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+      },
+      "xunit.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]",
+          "xunit.extensibility.execution": "[2.9.2]"
+        }
+      },
+      "xunit.extensibility.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "dependencies": {
+          "xunit.abstractions": "2.0.3"
+        }
+      },
+      "xunit.extensibility.execution": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]"
+        }
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
+        }
+      },
+      "mysqlconnector.dependencyinjection": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "[8.0.2, )",
+          "MySqlConnector": "[1.0.0, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
new file mode 100644
index 000000000..729a2ef26
--- /dev/null
+++ b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
@@ -0,0 +1,141 @@
+{
+  "version": 2,
+  "dependencies": {
+    "net8.0": {
+      "Microsoft.DotNet.ILCompiler": {
+        "type": "Direct",
+        "requested": "[8.0.10, )",
+        "resolved": "8.0.10",
+        "contentHash": "iThS5LNCcLlFkUlnxCJMgMSQ6s1BgbZo9ri11DDgNxDn8StwgFVUQJ8O5N/7S037jcS1md8tWp7Zu2YconIlag=="
+      },
+      "Microsoft.NET.ILLink.Tasks": {
+        "type": "Direct",
+        "requested": "[8.0.10, )",
+        "resolved": "8.0.10",
+        "contentHash": "xT8jYjlroY7SLbGtoV9vUTVW/TPgodL4Egc31a444Xe0TMytLZ3UlKQ0kxMZsy/CrWsFB6wtKnSG1SsXcWreew=="
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
+        }
+      },
+      "mysqlconnector.dependencyinjection": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "[8.0.2, )",
+          "MySqlConnector": "[1.0.0, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      }
+    },
+    "net9.0": {
+      "Microsoft.DotNet.ILCompiler": {
+        "type": "Direct",
+        "requested": "[9.0.0-rc.2.24473.5, )",
+        "resolved": "9.0.0-rc.2.24473.5",
+        "contentHash": "+4KH+fbuWXX+I1nM+iP84N0nnorwxY3N8SrNLDNdWdblCPcWXSZnnst/IS+NEHTavnoTErwFNwk7IP8rPrNAQA=="
+      },
+      "Microsoft.NET.ILLink.Tasks": {
+        "type": "Direct",
+        "requested": "[9.0.0-rc.2.24473.5, )",
+        "resolved": "9.0.0-rc.2.24473.5",
+        "contentHash": "mXyCgKqBIo/KOpY09FqjOU8d+LME2qowzzvBd1/viYmSpAKYfQ2w1WDOlh0FniY4xnrB/wmDiAo4frJvfCqT0w=="
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
+        }
+      },
+      "mysqlconnector.dependencyinjection": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "[8.0.2, )",
+          "MySqlConnector": "[1.0.0, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tests/MySqlConnector.Tests/packages.lock.json b/tests/MySqlConnector.Tests/packages.lock.json
new file mode 100644
index 000000000..f9ff016c5
--- /dev/null
+++ b/tests/MySqlConnector.Tests/packages.lock.json
@@ -0,0 +1,358 @@
+{
+  "version": 2,
+  "dependencies": {
+    ".NETFramework,Version=v4.8.1": {
+      "Microsoft.NET.Test.Sdk": {
+        "type": "Direct",
+        "requested": "[17.11.1, )",
+        "resolved": "17.11.1",
+        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "dependencies": {
+          "Microsoft.CodeCoverage": "17.11.1"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "xunit": {
+        "type": "Direct",
+        "requested": "[2.9.2, )",
+        "resolved": "2.9.2",
+        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "dependencies": {
+          "xunit.analyzers": "1.16.0",
+          "xunit.assert": "2.9.2",
+          "xunit.core": "[2.9.2]"
+        }
+      },
+      "xunit.runner.visualstudio": {
+        "type": "Direct",
+        "requested": "[2.8.2, )",
+        "resolved": "2.8.2",
+        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ==",
+        "dependencies": {
+          "Microsoft.TestPlatform.ObjectModel": "17.10.0"
+        }
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "dependencies": {
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.CodeCoverage": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "Microsoft.TestPlatform.ObjectModel": {
+        "type": "Transitive",
+        "resolved": "17.10.0",
+        "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==",
+        "dependencies": {
+          "System.Reflection.Metadata": "1.6.0"
+        }
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.Collections.Immutable": {
+        "type": "Transitive",
+        "resolved": "1.5.0",
+        "contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ=="
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Numerics.Vectors": "4.5.0",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      },
+      "System.Numerics.Vectors": {
+        "type": "Transitive",
+        "resolved": "4.5.0",
+        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
+      },
+      "System.Reflection.Metadata": {
+        "type": "Transitive",
+        "resolved": "1.6.0",
+        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==",
+        "dependencies": {
+          "System.Collections.Immutable": "1.5.0"
+        }
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "xunit.abstractions": {
+        "type": "Transitive",
+        "resolved": "2.0.3",
+        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
+      },
+      "xunit.analyzers": {
+        "type": "Transitive",
+        "resolved": "1.16.0",
+        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+      },
+      "xunit.assert": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+      },
+      "xunit.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]",
+          "xunit.extensibility.execution": "[2.9.2]"
+        }
+      },
+      "xunit.extensibility.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "dependencies": {
+          "xunit.abstractions": "2.0.3"
+        }
+      },
+      "xunit.extensibility.execution": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]"
+        }
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
+          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
+          "System.Threading.Tasks.Extensions": "[4.5.4, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "System.Buffers": "4.5.1",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Memory": "4.5.5"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.1, )",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
+        "dependencies": {
+          "System.Memory": "4.5.5",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
+        "dependencies": {
+          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
+        }
+      }
+    },
+    "net9.0": {
+      "Microsoft.NET.Test.Sdk": {
+        "type": "Direct",
+        "requested": "[17.11.1, )",
+        "resolved": "17.11.1",
+        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "dependencies": {
+          "Microsoft.CodeCoverage": "17.11.1",
+          "Microsoft.TestPlatform.TestHost": "17.11.1"
+        }
+      },
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "xunit": {
+        "type": "Direct",
+        "requested": "[2.9.2, )",
+        "resolved": "2.9.2",
+        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "dependencies": {
+          "xunit.analyzers": "1.16.0",
+          "xunit.assert": "2.9.2",
+          "xunit.core": "[2.9.2]"
+        }
+      },
+      "xunit.runner.visualstudio": {
+        "type": "Direct",
+        "requested": "[2.8.2, )",
+        "resolved": "2.8.2",
+        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.CodeCoverage": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "Microsoft.TestPlatform.ObjectModel": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "dependencies": {
+          "System.Reflection.Metadata": "1.6.0"
+        }
+      },
+      "Microsoft.TestPlatform.TestHost": {
+        "type": "Transitive",
+        "resolved": "17.11.1",
+        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "dependencies": {
+          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Newtonsoft.Json": "13.0.1"
+        }
+      },
+      "Newtonsoft.Json": {
+        "type": "Transitive",
+        "resolved": "13.0.1",
+        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
+      },
+      "System.Reflection.Metadata": {
+        "type": "Transitive",
+        "resolved": "1.6.0",
+        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
+      },
+      "xunit.abstractions": {
+        "type": "Transitive",
+        "resolved": "2.0.3",
+        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
+      },
+      "xunit.analyzers": {
+        "type": "Transitive",
+        "resolved": "1.16.0",
+        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+      },
+      "xunit.assert": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+      },
+      "xunit.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]",
+          "xunit.extensibility.execution": "[2.9.2]"
+        }
+      },
+      "xunit.extensibility.core": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "dependencies": {
+          "xunit.abstractions": "2.0.3"
+        }
+      },
+      "xunit.extensibility.execution": {
+        "type": "Transitive",
+        "resolved": "2.9.2",
+        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "dependencies": {
+          "xunit.extensibility.core": "[2.9.2]"
+        }
+      },
+      "mysqlconnector": {
+        "type": "Project",
+        "dependencies": {
+          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "CentralTransitive",
+        "requested": "[8.0.2, )",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/tools/SchemaCollectionGenerator/packages.lock.json b/tools/SchemaCollectionGenerator/packages.lock.json
new file mode 100644
index 000000000..4180ad51e
--- /dev/null
+++ b/tools/SchemaCollectionGenerator/packages.lock.json
@@ -0,0 +1,39 @@
+{
+  "version": 2,
+  "dependencies": {
+    "net9.0": {
+      "Microsoft.SourceLink.GitHub": {
+        "type": "Direct",
+        "requested": "[8.0.0, )",
+        "resolved": "8.0.0",
+        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
+        "dependencies": {
+          "Microsoft.Build.Tasks.Git": "8.0.0",
+          "Microsoft.SourceLink.Common": "8.0.0"
+        }
+      },
+      "MinVer": {
+        "type": "Direct",
+        "requested": "[6.0.0, )",
+        "resolved": "6.0.0",
+        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
+      },
+      "YamlDotNet": {
+        "type": "Direct",
+        "requested": "[16.1.3, )",
+        "resolved": "16.1.3",
+        "contentHash": "gtHGiDvU9VTtWte8f0thIM38cL1oowOjStKpeAEKKfA+Rc4AvekJzqFDZiiPcc4kw00ZiwR4OTJS56L16q98DQ=="
+      },
+      "Microsoft.Build.Tasks.Git": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
+      },
+      "Microsoft.SourceLink.Common": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      }
+    }
+  }
+}
\ No newline at end of file

From 5eb1bf13d5bd70c9855994dbb6167b3d5922b71e Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 5 Nov 2024 05:53:15 -0800
Subject: [PATCH 344/393] Make LogManager.Provider [Obsolete]. Fixes #1397

---
 .../MySqlConnectorLoggingExtensions.cs                           | 1 +
 src/MySqlConnector/Logging/MySqlConnectorLogManager.cs           | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnectorLoggingExtensions.cs b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnectorLoggingExtensions.cs
index 98c90db85..b5c59efe0 100644
--- a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnectorLoggingExtensions.cs
+++ b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnectorLoggingExtensions.cs
@@ -5,6 +5,7 @@ namespace MySqlConnector.Logging;
 
 public static class MySqlConnectorLoggingExtensions
 {
+	[Obsolete("Use UseLoggerFactory or AddMySqlDataSource instead. See https://mysqlconnector.net/diagnostics/logging/.")]
 	public static IServiceProvider UseMySqlConnectorLogging(this IServiceProvider services)
 	{
 		var loggerFactory = (ILoggerFactory) services.GetService(typeof(ILoggerFactory));
diff --git a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
index da0ce1963..6f02b4f5e 100644
--- a/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
+++ b/src/MySqlConnector/Logging/MySqlConnectorLogManager.cs
@@ -12,6 +12,7 @@ public static class MySqlConnectorLogManager
 	/// be set once, and must be set before any other library methods are used.
 	/// </summary>
 #pragma warning disable CA1044 // Properties should not be write only
+	[Obsolete("Use UseLoggerFactory or AddMySqlDataSource instead. See https://mysqlconnector.net/diagnostics/logging/.")]
 	public static IMySqlConnectorLoggerProvider Provider
 	{
 		set

From edb00d8861a0c4e931ead829de415fe1df034e35 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 12 Nov 2024 11:49:40 -0800
Subject: [PATCH 345/393] Update to .NET 9.0.100 SDK.

---
 global.json                                   |  2 +-
 .../packages.lock.json                        | 10 ++--
 src/MySqlConnector/packages.lock.json         | 22 +++++---
 .../packages.lock.json                        | 56 +++++++++++++++----
 4 files changed, 66 insertions(+), 24 deletions(-)

diff --git a/global.json b/global.json
index 746f46e86..653245229 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,5 @@
 {
   "sdk": {
-    "version": "9.0"
+    "version": "9.0.100"
   }
 }
diff --git a/src/MySqlConnector.DependencyInjection/packages.lock.json b/src/MySqlConnector.DependencyInjection/packages.lock.json
index c7119724a..0bc3f6cfe 100644
--- a/src/MySqlConnector.DependencyInjection/packages.lock.json
+++ b/src/MySqlConnector.DependencyInjection/packages.lock.json
@@ -139,6 +139,7 @@
         }
       }
     },
+    ".NETStandard,Version=v2.0/win-x64": {},
     "net8.0": {
       "Microsoft.Extensions.DependencyInjection.Abstractions": {
         "type": "Direct",
@@ -148,9 +149,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.10, )",
-        "resolved": "8.0.10",
-        "contentHash": "xT8jYjlroY7SLbGtoV9vUTVW/TPgodL4Egc31a444Xe0TMytLZ3UlKQ0kxMZsy/CrWsFB6wtKnSG1SsXcWreew=="
+        "requested": "[8.0.11, )",
+        "resolved": "8.0.11",
+        "contentHash": "zk5lnZrYJgtuJG8L4v17Ej8rZ3PUcR2iweNV08BaO5LbYHIi2wNaVNcJoLxvqgQdnjLlKnCCfVGLDr6QHeAarQ=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -207,6 +208,7 @@
           "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
         }
       }
-    }
+    },
+    "net8.0/win-x64": {}
   }
 }
\ No newline at end of file
diff --git a/src/MySqlConnector/packages.lock.json b/src/MySqlConnector/packages.lock.json
index 5fed9ca01..f0321532d 100644
--- a/src/MySqlConnector/packages.lock.json
+++ b/src/MySqlConnector/packages.lock.json
@@ -131,6 +131,7 @@
         }
       }
     },
+    ".NETFramework,Version=v4.6.2/win-x64": {},
     ".NETFramework,Version=v4.7.1": {
       "IndexRange": {
         "type": "Direct",
@@ -253,6 +254,7 @@
         }
       }
     },
+    ".NETFramework,Version=v4.7.1/win-x64": {},
     ".NETFramework,Version=v4.8": {
       "IndexRange": {
         "type": "Direct",
@@ -375,6 +377,7 @@
         }
       }
     },
+    ".NETFramework,Version=v4.8/win-x64": {},
     ".NETStandard,Version=v2.0": {
       "IndexRange": {
         "type": "Direct",
@@ -511,6 +514,7 @@
         }
       }
     },
+    ".NETStandard,Version=v2.0/win-x64": {},
     ".NETStandard,Version=v2.1": {
       "Microsoft.Extensions.Logging.Abstractions": {
         "type": "Direct",
@@ -606,6 +610,7 @@
         "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
       }
     },
+    ".NETStandard,Version=v2.1/win-x64": {},
     "net6.0": {
       "Microsoft.Extensions.Logging.Abstractions": {
         "type": "Direct",
@@ -678,6 +683,7 @@
         "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
       }
     },
+    "net6.0/win-x64": {},
     "net8.0": {
       "Microsoft.Extensions.Logging.Abstractions": {
         "type": "Direct",
@@ -690,9 +696,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.10, )",
-        "resolved": "8.0.10",
-        "contentHash": "xT8jYjlroY7SLbGtoV9vUTVW/TPgodL4Egc31a444Xe0TMytLZ3UlKQ0kxMZsy/CrWsFB6wtKnSG1SsXcWreew=="
+        "requested": "[8.0.11, )",
+        "resolved": "8.0.11",
+        "contentHash": "zk5lnZrYJgtuJG8L4v17Ej8rZ3PUcR2iweNV08BaO5LbYHIi2wNaVNcJoLxvqgQdnjLlKnCCfVGLDr6QHeAarQ=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -741,6 +747,7 @@
         "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
       }
     },
+    "net8.0/win-x64": {},
     "net9.0": {
       "Microsoft.Extensions.Logging.Abstractions": {
         "type": "Direct",
@@ -753,9 +760,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[9.0.0-rc.2.24473.5, )",
-        "resolved": "9.0.0-rc.2.24473.5",
-        "contentHash": "mXyCgKqBIo/KOpY09FqjOU8d+LME2qowzzvBd1/viYmSpAKYfQ2w1WDOlh0FniY4xnrB/wmDiAo4frJvfCqT0w=="
+        "requested": "[9.0.0, )",
+        "resolved": "9.0.0",
+        "contentHash": "zAwp213evC3UkimtVXRb+Dlgc/40QG145nmZDtp2LO9zJJMfrp+i/87BnXN7tRXEA4liyzdFkjqG1HE8/RPb4A=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -803,6 +810,7 @@
         "resolved": "8.0.2",
         "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
       }
-    }
+    },
+    "net9.0/win-x64": {}
   }
 }
\ No newline at end of file
diff --git a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
index 729a2ef26..d9b34f596 100644
--- a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
+++ b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
@@ -4,15 +4,15 @@
     "net8.0": {
       "Microsoft.DotNet.ILCompiler": {
         "type": "Direct",
-        "requested": "[8.0.10, )",
-        "resolved": "8.0.10",
-        "contentHash": "iThS5LNCcLlFkUlnxCJMgMSQ6s1BgbZo9ri11DDgNxDn8StwgFVUQJ8O5N/7S037jcS1md8tWp7Zu2YconIlag=="
+        "requested": "[8.0.11, )",
+        "resolved": "8.0.11",
+        "contentHash": "9SWRKJzFrE07m77G/9jSR1lqdlPmKCsQX1UflZEFBGYzmOZ53sjLEEcAiWtnWY0QJ750h2ahciOPO8oVjztQ0g=="
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.10, )",
-        "resolved": "8.0.10",
-        "contentHash": "xT8jYjlroY7SLbGtoV9vUTVW/TPgodL4Egc31a444Xe0TMytLZ3UlKQ0kxMZsy/CrWsFB6wtKnSG1SsXcWreew=="
+        "requested": "[8.0.11, )",
+        "resolved": "8.0.11",
+        "contentHash": "zk5lnZrYJgtuJG8L4v17Ej8rZ3PUcR2iweNV08BaO5LbYHIi2wNaVNcJoLxvqgQdnjLlKnCCfVGLDr6QHeAarQ=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -69,18 +69,34 @@
         }
       }
     },
+    "net8.0/win-x64": {
+      "Microsoft.DotNet.ILCompiler": {
+        "type": "Direct",
+        "requested": "[8.0.11, )",
+        "resolved": "8.0.11",
+        "contentHash": "9SWRKJzFrE07m77G/9jSR1lqdlPmKCsQX1UflZEFBGYzmOZ53sjLEEcAiWtnWY0QJ750h2ahciOPO8oVjztQ0g==",
+        "dependencies": {
+          "runtime.win-x64.Microsoft.DotNet.ILCompiler": "8.0.11"
+        }
+      },
+      "runtime.win-x64.Microsoft.DotNet.ILCompiler": {
+        "type": "Transitive",
+        "resolved": "8.0.11",
+        "contentHash": "rrxOObwOfOZ7IqKpd4rRZqjIWrgCrLv3XzkU5FusWtgwoaUu1/8QJGcbog/RJzgl40Yt6vZpFFbWtFazeseapg=="
+      }
+    },
     "net9.0": {
       "Microsoft.DotNet.ILCompiler": {
         "type": "Direct",
-        "requested": "[9.0.0-rc.2.24473.5, )",
-        "resolved": "9.0.0-rc.2.24473.5",
-        "contentHash": "+4KH+fbuWXX+I1nM+iP84N0nnorwxY3N8SrNLDNdWdblCPcWXSZnnst/IS+NEHTavnoTErwFNwk7IP8rPrNAQA=="
+        "requested": "[9.0.0, )",
+        "resolved": "9.0.0",
+        "contentHash": "bbnlV2PbUmCQ8Ndpx0kJaicLyV28IU+4IzyctQLL57+DxrHurYr2qsJrC8+yD44Q0DyPfv2oM168c1Tk6Bxbmg=="
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[9.0.0-rc.2.24473.5, )",
-        "resolved": "9.0.0-rc.2.24473.5",
-        "contentHash": "mXyCgKqBIo/KOpY09FqjOU8d+LME2qowzzvBd1/viYmSpAKYfQ2w1WDOlh0FniY4xnrB/wmDiAo4frJvfCqT0w=="
+        "requested": "[9.0.0, )",
+        "resolved": "9.0.0",
+        "contentHash": "zAwp213evC3UkimtVXRb+Dlgc/40QG145nmZDtp2LO9zJJMfrp+i/87BnXN7tRXEA4liyzdFkjqG1HE8/RPb4A=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -136,6 +152,22 @@
           "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
         }
       }
+    },
+    "net9.0/win-x64": {
+      "Microsoft.DotNet.ILCompiler": {
+        "type": "Direct",
+        "requested": "[9.0.0, )",
+        "resolved": "9.0.0",
+        "contentHash": "bbnlV2PbUmCQ8Ndpx0kJaicLyV28IU+4IzyctQLL57+DxrHurYr2qsJrC8+yD44Q0DyPfv2oM168c1Tk6Bxbmg==",
+        "dependencies": {
+          "runtime.win-x64.Microsoft.DotNet.ILCompiler": "9.0.0"
+        }
+      },
+      "runtime.win-x64.Microsoft.DotNet.ILCompiler": {
+        "type": "Transitive",
+        "resolved": "9.0.0",
+        "contentHash": "oDIioFi3rPT79AS5TSEwU/nlqINeJwxG5yKG/BLY57an5PV+btlPBGJgOjI3d8/BdTpKmsd9pdavm/4ENq5M/A=="
+      }
     }
   }
 }
\ No newline at end of file

From 76f8c519f746d65b1847a5bf5f168a2744589958 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 12 Nov 2024 12:14:22 -0800
Subject: [PATCH 346/393] Release 2.4.0.

---
 docs/content/overview/version-history.md | 23 ++++++++++++-----------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/docs/content/overview/version-history.md b/docs/content/overview/version-history.md
index 61a39c500..be5a12b1f 100644
--- a/docs/content/overview/version-history.md
+++ b/docs/content/overview/version-history.md
@@ -11,29 +11,30 @@ weight: 30
 
 # Version History
 
-### 2.4.0 Beta 2
+### 2.4.0
 
+* Support .NET 9.0.
+* **Possibly breaking** Mark `MySqlConnectorLogManager.Provider` as `[Obsolete]`: [#1397](https://github.com/mysql-net/MySqlConnector/issues/1397).
 * **Possibly breaking** Remove end-of-life .NET 7 target framework.
   * Users who wish to use `DbDataSource` need to target .NET 8.0 or later.
-* Fix "Can't replace active reader": [#1469](https://github.com/mysql-net/MySqlConnector/issues/1469).
 * **Possibly breaking** Update dependencies:
   * Microsoft.Extensions.Logging.Abstractions from 7.0.1 to 8.0.2.
   * System.Diagnostics.DiagnosticSource from 7.0.2 to 8.0.1.
-
-### 2.4.0 Beta 1
-
-* Support .NET 9.0.
-* Support [zero-configuration SSL](https://mariadb.org/mission-impossible-zero-configuration-ssl/) with MariaDB: [#1500](https://github.com/mysql-net/MySqlConnector/pull/1500).
+* Support trimming and native AOT: [#1265](https://github.com/mysql-net/MySqlConnector/issues/1265).
 * Add `MySqlDataSourceBuilder.UseConnectionOpenedCallback`: [#1508](https://github.com/mysql-net/MySqlConnector/issues/1508).
-* Rewrite server redirection logic based on latest MariaDB specification: [#1499](https://github.com/mysql-net/MySqlConnector/pull/1499).
+* Add `MySqlDbColumn.TableName`: [#1521](https://github.com/mysql-net/MySqlConnector/issues/1521).
+* Support [zero-configuration SSL](https://mariadb.org/mission-impossible-zero-configuration-ssl/) with MariaDB: [#1500](https://github.com/mysql-net/MySqlConnector/pull/1500).
 * Support `SET` data type in functions: [#1491](https://github.com/mysql-net/MySqlConnector/issues/1491).
+* Remove `TABLESPACES` schema: [#1477](https://github.com/mysql-net/MySqlConnector/issues/1477).
+* Rewrite server redirection logic based on latest MariaDB specification: [#1499](https://github.com/mysql-net/MySqlConnector/pull/1499).
+* Change variable name for better server compatibility: [#1502](https://github.com/mysql-net/MySqlConnector/issues/1502).
 * Performance: Avoid `SET NAMES` commands when not necessary: [#1497](https://github.com/mysql-net/MySqlConnector/pull/1497).
+* Fix "Can't replace active reader": [#1469](https://github.com/mysql-net/MySqlConnector/issues/1469).
 * Fix potential `NullReferenceException` in `MySqlConnection.Cancel`: [#1506](https://github.com/mysql-net/MySqlConnector/issues/1506).
+* Fix `InvalidOperationException` in `FinishQuerying`: [#1341](https://github.com/mysql-net/MySqlConnector/issues/1341).
 * Provide logger to new MySqlConnection: [#1510](https://github.com/mysql-net/MySqlConnector/issues/1510).
 * Use correct hostname when cancelling query: [#1514](https://github.com/mysql-net/MySqlConnector/issues/1514).
-* Remove `TABLESPACES` schema: [#1477](https://github.com/mysql-net/MySqlConnector/issues/1477).
-* Change variable name for better server compatibility: [#1502](https://github.com/mysql-net/MySqlConnector/issues/1502).
-* Thanks to [Diego Dupin](https://github.com/rusher) for contributions to this release.
+* Thanks to [Alex Kerman](https://github.com/AlexKerman) and [Diego Dupin](https://github.com/rusher) for contributions to this release.
 
 ### 2.3.7
 

From a55200a066b1833fe742f9ac731cafbf07d04c55 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Wed, 27 Nov 2024 16:37:26 +0000
Subject: [PATCH 347/393] Bump
 advanced-security/component-detection-dependency-submission-action

Bumps [advanced-security/component-detection-dependency-submission-action](https://github.com/advanced-security/component-detection-dependency-submission-action) from 0.0.3 to 0.0.4.
- [Release notes](https://github.com/advanced-security/component-detection-dependency-submission-action/releases)
- [Commits](https://github.com/advanced-security/component-detection-dependency-submission-action/compare/v0.0.3...v0.0.4)

---
updated-dependencies:
- dependency-name: advanced-security/component-detection-dependency-submission-action
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index aa7ec5123..6329153ca 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,7 +25,7 @@ jobs:
       run: dotnet build --configuration Release --no-restore
 
     - name: Component detection
-      uses: advanced-security/component-detection-dependency-submission-action@v0.0.3
+      uses: advanced-security/component-detection-dependency-submission-action@v0.0.4
 
   native-aot:
     name: Native AOT

From cfd95c00ec1d0fe03233e3c2a3ada229f4a8014f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 27 Nov 2024 10:57:47 -0800
Subject: [PATCH 348/393] Update NuGet lock files.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../packages.lock.json                        |  4 +--
 src/MySqlConnector/packages.lock.json         | 10 +-----
 .../packages.lock.json                        | 32 -------------------
 3 files changed, 2 insertions(+), 44 deletions(-)

diff --git a/src/MySqlConnector.DependencyInjection/packages.lock.json b/src/MySqlConnector.DependencyInjection/packages.lock.json
index 0bc3f6cfe..a4d61d7a7 100644
--- a/src/MySqlConnector.DependencyInjection/packages.lock.json
+++ b/src/MySqlConnector.DependencyInjection/packages.lock.json
@@ -139,7 +139,6 @@
         }
       }
     },
-    ".NETStandard,Version=v2.0/win-x64": {},
     "net8.0": {
       "Microsoft.Extensions.DependencyInjection.Abstractions": {
         "type": "Direct",
@@ -208,7 +207,6 @@
           "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
         }
       }
-    },
-    "net8.0/win-x64": {}
+    }
   }
 }
\ No newline at end of file
diff --git a/src/MySqlConnector/packages.lock.json b/src/MySqlConnector/packages.lock.json
index f0321532d..529ea2ee7 100644
--- a/src/MySqlConnector/packages.lock.json
+++ b/src/MySqlConnector/packages.lock.json
@@ -131,7 +131,6 @@
         }
       }
     },
-    ".NETFramework,Version=v4.6.2/win-x64": {},
     ".NETFramework,Version=v4.7.1": {
       "IndexRange": {
         "type": "Direct",
@@ -254,7 +253,6 @@
         }
       }
     },
-    ".NETFramework,Version=v4.7.1/win-x64": {},
     ".NETFramework,Version=v4.8": {
       "IndexRange": {
         "type": "Direct",
@@ -377,7 +375,6 @@
         }
       }
     },
-    ".NETFramework,Version=v4.8/win-x64": {},
     ".NETStandard,Version=v2.0": {
       "IndexRange": {
         "type": "Direct",
@@ -514,7 +511,6 @@
         }
       }
     },
-    ".NETStandard,Version=v2.0/win-x64": {},
     ".NETStandard,Version=v2.1": {
       "Microsoft.Extensions.Logging.Abstractions": {
         "type": "Direct",
@@ -610,7 +606,6 @@
         "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
       }
     },
-    ".NETStandard,Version=v2.1/win-x64": {},
     "net6.0": {
       "Microsoft.Extensions.Logging.Abstractions": {
         "type": "Direct",
@@ -683,7 +678,6 @@
         "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
       }
     },
-    "net6.0/win-x64": {},
     "net8.0": {
       "Microsoft.Extensions.Logging.Abstractions": {
         "type": "Direct",
@@ -747,7 +741,6 @@
         "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
       }
     },
-    "net8.0/win-x64": {},
     "net9.0": {
       "Microsoft.Extensions.Logging.Abstractions": {
         "type": "Direct",
@@ -810,7 +803,6 @@
         "resolved": "8.0.2",
         "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
       }
-    },
-    "net9.0/win-x64": {}
+    }
   }
 }
\ No newline at end of file
diff --git a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
index d9b34f596..a77830d51 100644
--- a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
+++ b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
@@ -69,22 +69,6 @@
         }
       }
     },
-    "net8.0/win-x64": {
-      "Microsoft.DotNet.ILCompiler": {
-        "type": "Direct",
-        "requested": "[8.0.11, )",
-        "resolved": "8.0.11",
-        "contentHash": "9SWRKJzFrE07m77G/9jSR1lqdlPmKCsQX1UflZEFBGYzmOZ53sjLEEcAiWtnWY0QJ750h2ahciOPO8oVjztQ0g==",
-        "dependencies": {
-          "runtime.win-x64.Microsoft.DotNet.ILCompiler": "8.0.11"
-        }
-      },
-      "runtime.win-x64.Microsoft.DotNet.ILCompiler": {
-        "type": "Transitive",
-        "resolved": "8.0.11",
-        "contentHash": "rrxOObwOfOZ7IqKpd4rRZqjIWrgCrLv3XzkU5FusWtgwoaUu1/8QJGcbog/RJzgl40Yt6vZpFFbWtFazeseapg=="
-      }
-    },
     "net9.0": {
       "Microsoft.DotNet.ILCompiler": {
         "type": "Direct",
@@ -152,22 +136,6 @@
           "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
         }
       }
-    },
-    "net9.0/win-x64": {
-      "Microsoft.DotNet.ILCompiler": {
-        "type": "Direct",
-        "requested": "[9.0.0, )",
-        "resolved": "9.0.0",
-        "contentHash": "bbnlV2PbUmCQ8Ndpx0kJaicLyV28IU+4IzyctQLL57+DxrHurYr2qsJrC8+yD44Q0DyPfv2oM168c1Tk6Bxbmg==",
-        "dependencies": {
-          "runtime.win-x64.Microsoft.DotNet.ILCompiler": "9.0.0"
-        }
-      },
-      "runtime.win-x64.Microsoft.DotNet.ILCompiler": {
-        "type": "Transitive",
-        "resolved": "9.0.0",
-        "contentHash": "oDIioFi3rPT79AS5TSEwU/nlqINeJwxG5yKG/BLY57an5PV+btlPBGJgOjI3d8/BdTpKmsd9pdavm/4ENq5M/A=="
-      }
     }
   }
 }
\ No newline at end of file

From 0602473add830a7a05199328d4b6cd1a8cf39840 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 27 Nov 2024 11:01:25 -0800
Subject: [PATCH 349/393] Update logging documentation with migration
 instructions.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 docs/content/diagnostics/logging.md | 61 +++++++++++++++++++++++++++++
 1 file changed, 61 insertions(+)

diff --git a/docs/content/diagnostics/logging.md b/docs/content/diagnostics/logging.md
index 384980ca0..f6f7d6f02 100644
--- a/docs/content/diagnostics/logging.md
+++ b/docs/content/diagnostics/logging.md
@@ -54,6 +54,67 @@ This allows your endpoints to get injected with MySQL connections which log to t
 
 ## Global Logging
 
+### Migrating from Global Logging
+
+<blockquote class="highlight">
+In MySqlConnector 2.4.0, the <tt>MySqlConnectorLogManager.Provider</tt> API has been deprecated.
+Follow these instructions to migrate to the new logging API.
+</blockquote>
+
+With the deprecated logging framework, you may have had code that looked like this:
+
+```csharp
+log4net.Config.XmlConfigurator.Configure();
+MySqlConnectorLogManager.Provider = new Log4netLoggerProvider();
+using var connection = new MySqlConnection(connectionString);
+```
+
+To migrate to the new logging API, you will need to:
+
+* Create a `LoggerFactory`
+* Connect that `LoggerFactory` to your desired logging framework
+* Create a `MySqlDataSource` configurated with that `LoggerFactory`
+* Create new `MySqlConnection` objects using `MySqlDataSource.CreateConnection()` (or `OpenConnection[Async]()`).
+
+This will look like the following, depending on your exact configuration:
+
+```csharp
+// create a LoggerFactory and configure it with the desired logging framework
+// use ONLY ONE of the "Add" methods below, depending on your logging framework
+var loggerFactory = LoggerFactory.Create(builder =>
+  // if you just want console logging
+  builder.AddConsole();
+
+  // connect to log4net via Microsoft.Extensions.Logging.Log4Net.AspNetCore
+  builder.AddLog4Net(new Log4NetProviderOptions
+  {
+    UseWebOrAppConfig = true, // set this if you're storing your settings in Web.config instead of log4net.config
+    ExternalConfigurationSetup = true, // set this instead if you're initializing log4net yourself
+    // see other options at https://github.com/huorswords/Microsoft.Extensions.Logging.Log4Net.AspNetCore
+  }));
+
+  // connect to NLog via NLog.Extensions.Logging
+  builder.AddNLog();
+
+  // connect to Serilog via Serilog.Extensions.Logging
+  builder.AddSerilog(dispose: true);
+);
+
+// now create a MySqlDataSource and configure it with the LoggerFactory
+using var dataSource = new MySqlDataSourceBuilder(yourConnectionString)
+  .UseLoggerFactory(loggerFactory)
+  .Build();
+
+// create all MySqlConnection objects via the MySqlDataSource, not directly
+// DON'T: using var connection = new MySqlConnection(yourConnectionString);
+using var connection = dataSource.CreateConnection();
+
+// you can also create open connections
+using var connection = await dataSource.OpenConnectionAsync();
+```
+
+### Deprecated Logging Framework
+
 <blockquote class="warning">
 The following information is for MySqlConnector versions prior to 2.3.0.
 The global logging interface that is described is deprecated and shouldn't be used in new code.

From a2cab931446ee2817c574f255dee3cd0a54c9761 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 28 Nov 2024 15:30:44 -0800
Subject: [PATCH 350/393] Use System.Threading.Lock under .NET 9.0.

---
 src/MySqlConnector/Core/ServerSession.cs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 5350726da..fdead8006 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -2153,7 +2153,11 @@ protected override void OnStatementBegin(int index)
 	private static readonly PayloadData s_selectConnectionIdVersionWithAttributesPayload = QueryPayload.Create(true, "SELECT CONNECTION_ID(), VERSION();"u8);
 
 	private readonly ILogger m_logger;
+#if NET9_0_OR_GREATER
+	private readonly Lock m_lock;
+#else
 	private readonly object m_lock;
+#endif
 	private readonly ArraySegmentHolder<byte> m_payloadCache;
 	private readonly ActivityTagsCollection m_activityTags;
 	private State m_state;

From ce10e208fe3b16bbad6959025f9ad51505aa2916 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 28 Nov 2024 15:58:22 -0800
Subject: [PATCH 351/393] Add "txt" highlighting to Markdown blocks.

This ensures it's styled with a differently-coloured background.
---
 docs/content/tutorials/connect-to-mysql.md | 2 +-
 docs/content/tutorials/dapper.md           | 2 +-
 docs/content/tutorials/efcore.md           | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/content/tutorials/connect-to-mysql.md b/docs/content/tutorials/connect-to-mysql.md
index 6e10af7bd..1a23fed24 100644
--- a/docs/content/tutorials/connect-to-mysql.md
+++ b/docs/content/tutorials/connect-to-mysql.md
@@ -18,7 +18,7 @@ This tutorial will teach you how to connect to MySQL from .NET Core using C#.
 First, install the [MySqlConnector NuGet package](https://www.nuget.org/packages/MySqlConnector/). From
 a command prompt, run:
 
-```
+```txt
 dotnet add package MySqlConnector
 ```
 
diff --git a/docs/content/tutorials/dapper.md b/docs/content/tutorials/dapper.md
index 8a727901a..549b67a4a 100644
--- a/docs/content/tutorials/dapper.md
+++ b/docs/content/tutorials/dapper.md
@@ -32,7 +32,7 @@ You will need to install the following NuGet packages:
 
 Build your connection string by substituting the appropriate values in this template:
 
-```
+```txt
 Server=YOURSERVER; User ID=YOURUSERID; Password=YOURPASSWORD; Database=YOURDATABASE
 ```
 
diff --git a/docs/content/tutorials/efcore.md b/docs/content/tutorials/efcore.md
index 45ef27a54..d37e42c1f 100644
--- a/docs/content/tutorials/efcore.md
+++ b/docs/content/tutorials/efcore.md
@@ -22,7 +22,7 @@ It uses MySqlConnector to provide the core database connectivity.
 In this tutorial, we'll use a console application to demonstrate the core concepts.
 Create a console application, then add the Pomelo.EntityFrameworkCore.MySql NuGet package:
 
-```
+```txt
 dotnet new console -o EFCoreMySQL
 cd EFCoreMySQL
 dotnet add package Pomelo.EntityFrameworkCore.MySql
@@ -32,7 +32,7 @@ dotnet add package Pomelo.EntityFrameworkCore.MySql
 
 Build your connection string by substituting the appropriate values in this template:
 
-```
+```txt
 Server=YOURSERVER; User ID=YOURUSERID; Password=YOURPASSWORD; Database=YOURDATABASE
 ```
 

From 44fc7dfae4beb570da80ac62d1e3bc9138cfa72d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 18 Dec 2024 20:04:15 -0800
Subject: [PATCH 352/393] Fix link to Keyed Services documentation.

---
 docs/content/diagnostics/metrics.md                   | 2 +-
 src/MySqlConnector.DependencyInjection/docs/README.md | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/docs/content/diagnostics/metrics.md b/docs/content/diagnostics/metrics.md
index b916991ed..f62f7f569 100644
--- a/docs/content/diagnostics/metrics.md
+++ b/docs/content/diagnostics/metrics.md
@@ -73,7 +73,7 @@ builder.Services.AddMySqlDataSource(builder.Configuration.GetConnectionString("D
 
 ### Keyed Services
 
-Use the `AddKeyedMySqlDataSource` method to register a `MySqlDataSource` as a [keyed service](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#keyed-di-services).
+Use the `AddKeyedMySqlDataSource` method to register a `MySqlDataSource` as a [keyed service](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8/runtime#keyed-di-services).
 If the service key is a string, it will automatically be used as the `MySqlDataSource` name.
 
 ```csharp
diff --git a/src/MySqlConnector.DependencyInjection/docs/README.md b/src/MySqlConnector.DependencyInjection/docs/README.md
index a19a3f96b..ea0e5ed2c 100644
--- a/src/MySqlConnector.DependencyInjection/docs/README.md
+++ b/src/MySqlConnector.DependencyInjection/docs/README.md
@@ -51,7 +51,7 @@ builder.Services.AddMySqlDataSource("Server=server;User ID=test;Password=test;Da
 
 ## Keyed Services
 
-Use the `AddKeyedMySqlDataSource` method to register a `MySqlDataSource` as a [keyed service](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8#keyed-di-services).
+Use the `AddKeyedMySqlDataSource` method to register a `MySqlDataSource` as a [keyed service](https://learn.microsoft.com/en-us/dotnet/core/whats-new/dotnet-8/runtime#keyed-di-services).
 This is useful if you have multiple connection strings or need to connect to multiple databases.
 If the service key is a string, it will automatically be used as the `MySqlDataSource` name;
 to customize this, call the `AddKeyedMySqlDataSource(object?, string, Action<MySqlDataSourceBuilder>)` overload and call `MySqlDataSourceBuilder.UseName`.

From 158dce28d4bc4a6e2ef8e45d3316c8001648ea01 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 23 Dec 2024 11:00:09 -0800
Subject: [PATCH 353/393] Prefer ordinal comparison.

---
 src/MySqlConnector/Utilities/Utility.cs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 69f23a5a9..8cc9759b7 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -351,7 +351,7 @@ public static bool TryParseRedirectionHeader(string redirectUrl, string initialU
 			var uri = new Uri(redirectUrl);
 			host = uri.Host;
 			if (string.IsNullOrEmpty(host)) return false;
-			if (host.StartsWith('[') && host.EndsWith("]", StringComparison.InvariantCulture)) host = host.Substring(1, host.Length - 2);
+			if (host.StartsWith('[') && host.EndsWith("]", StringComparison.Ordinal)) host = host.Substring(1, host.Length - 2);
 
 			port = uri.Port;
 			user = Uri.UnescapeDataString(uri.UserInfo.Split(':')[0]);
@@ -361,7 +361,7 @@ public static bool TryParseRedirectionHeader(string redirectUrl, string initialU
 				var q = uri.Query.Substring(1);
 				foreach (var token in q.Split('&'))
 				{
-					if (token.StartsWith("user=", StringComparison.InvariantCulture))
+					if (token.StartsWith("user=", StringComparison.Ordinal))
 					{
 						user = Uri.UnescapeDataString(token.Substring(5));
 					}

From f2d7379f2a4b5f3b26e537d01da56165c798f2f9 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 12 Jan 2025 07:12:10 -0800
Subject: [PATCH 354/393] Add two Connector/NET bugs.

---
 docs/content/tutorials/migrating-from-connector-net.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 65782f642..6d82e772d 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -257,6 +257,8 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector.
 * [#115937](https://bugs.mysql.com/bug.php?id=115937): Transaction isn't released after I/O timeout
 * [#116146](https://bugs.mysql.com/bug.php?id=116146): `MySqlConnection.OpenAsync` throws exception when `UseCompression = true`
 * [#116257](https://bugs.mysql.com/bug.php?id=116257): `NullReferenceException` thrown when using prepared statement under OpenTelemetry
+* [#116382](https://bugs.mysql.com/bug.php?id=116382): `ExecuteNonQueryAsync` hangs with DDL if `UseCompression=True`
+* [#117020](https://bugs.mysql.com/bug.php?id=117020): `MinPoolSize` configuration is not honored
 
 ### Fixed First by MySqlConnector
 

From 8e17bb122a4510a273051986699c3121176875d2 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 22 Jan 2025 10:19:44 -0800
Subject: [PATCH 355/393] Update to MySQL 9.2.

---
 .github/workflows/ci.yml                      |   2 +-
 Directory.Packages.props                      |   4 +-
 azure-pipelines.yml                           |   4 +-
 docs/content/home.md                          |   2 +-
 .../tutorials/migrating-from-connector-net.md |   2 +-
 .../packages.lock.json                        |   6 +-
 src/MySqlConnector/packages.lock.json         |  12 +-
 .../IntegrationTests/IntegrationTests.csproj  |   1 +
 tests/IntegrationTests/app.config             |   2 +-
 tests/IntegrationTests/packages.lock.json     | 134 ++++++++++++++----
 .../packages.lock.json                        |  24 ++--
 tests/README.md                               |   2 +-
 12 files changed, 135 insertions(+), 60 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 6329153ca..07726b0d0 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,7 +35,7 @@ jobs:
         tfm: [ 'net8.0', 'net9.0' ]
     services:
       mysql:
-        image: mysql:9.1
+        image: mysql:9.2
         env:
           MYSQL_DATABASE: testdb
           MYSQL_ROOT_PASSWORD: pass
diff --git a/Directory.Packages.props b/Directory.Packages.props
index e46e0df6a..673b04c49 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -16,12 +16,12 @@
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
-    <PackageVersion Include="MySql.Data" Version="9.1.0" />
+    <PackageVersion Include="MySql.Data" Version="9.2.0" />
     <PackageVersion Include="NLog" Version="4.7.15" />
     <PackageVersion Include="Serilog" Version="2.12.0" />
     <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
-    <PackageVersion Include="System.Text.Json" Version="8.0.5" />
+    <PackageVersion Include="System.Text.Json" Version="9.0.1" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
     <PackageVersion Include="xunit" Version="2.9.2" />
     <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index c53d407b4..c8518d9b0 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -176,8 +176,8 @@ jobs:
         image: 'mysql:8.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
-      'MySQL 9.1':
-        image: 'mysql:9.1'
+      'MySQL 9.2':
+        image: 'mysql:9.2'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
       'MariaDB 10.6':
diff --git a/docs/content/home.md b/docs/content/home.md
index 4cf363195..5c4c5c3da 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -65,7 +65,7 @@ Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://m
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
 MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**) |
-MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.1**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
+MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.2**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
 ProxySQL | 2.x | Some [compatibility issues](https://github.com/search?q=repo%3Amysql-net%2FMySqlConnector+proxysql&type=issues)
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 6d82e772d..92d64f4ba 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -41,7 +41,7 @@ In a .NET Framework application, make the following `app.config` change to regis
     <!-- add name="MySQL Data Provider"
       invariant="MySql.Data.MySqlClient"
       description=".Net Framework Data Provider for MySQL"
-      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.1.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.2.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
     <add name="MySqlConnector"
diff --git a/src/MySqlConnector.DependencyInjection/packages.lock.json b/src/MySqlConnector.DependencyInjection/packages.lock.json
index a4d61d7a7..765f6deab 100644
--- a/src/MySqlConnector.DependencyInjection/packages.lock.json
+++ b/src/MySqlConnector.DependencyInjection/packages.lock.json
@@ -148,9 +148,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.11, )",
-        "resolved": "8.0.11",
-        "contentHash": "zk5lnZrYJgtuJG8L4v17Ej8rZ3PUcR2iweNV08BaO5LbYHIi2wNaVNcJoLxvqgQdnjLlKnCCfVGLDr6QHeAarQ=="
+        "requested": "[8.0.12, )",
+        "resolved": "8.0.12",
+        "contentHash": "FV4HnQ3JI15PHnJ5PGTbz+rYvrih42oLi/7UMIshNwCwUZhTq13UzrggtXk4ygrcMcN+4jsS6hhshx2p/Zd0ig=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
diff --git a/src/MySqlConnector/packages.lock.json b/src/MySqlConnector/packages.lock.json
index 529ea2ee7..d47bd21c3 100644
--- a/src/MySqlConnector/packages.lock.json
+++ b/src/MySqlConnector/packages.lock.json
@@ -690,9 +690,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.11, )",
-        "resolved": "8.0.11",
-        "contentHash": "zk5lnZrYJgtuJG8L4v17Ej8rZ3PUcR2iweNV08BaO5LbYHIi2wNaVNcJoLxvqgQdnjLlKnCCfVGLDr6QHeAarQ=="
+        "requested": "[8.0.12, )",
+        "resolved": "8.0.12",
+        "contentHash": "FV4HnQ3JI15PHnJ5PGTbz+rYvrih42oLi/7UMIshNwCwUZhTq13UzrggtXk4ygrcMcN+4jsS6hhshx2p/Zd0ig=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -753,9 +753,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[9.0.0, )",
-        "resolved": "9.0.0",
-        "contentHash": "zAwp213evC3UkimtVXRb+Dlgc/40QG145nmZDtp2LO9zJJMfrp+i/87BnXN7tRXEA4liyzdFkjqG1HE8/RPb4A=="
+        "requested": "[9.0.1, )",
+        "resolved": "9.0.1",
+        "contentHash": "BKFAkdhUUJivAoKuSWcT23WH9E+AWQMJootzt9X+lnesOD2a2yNIe1ZQFtxB8H3ayskLBD5gYyIbj5GIfw7rZg=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index b68d6a05a..dcb0ecf9d 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -17,6 +17,7 @@
     <PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
     <AssemblyOriginatorKeyFile>..\..\MySqlConnector.snk</AssemblyOriginatorKeyFile>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
+    <SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
     <PreserveCompilationContext>true</PreserveCompilationContext>
     <AssemblyName>IntegrationTests</AssemblyName>
     <ServerGarbageCollection>true</ServerGarbageCollection>
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index 00cf3c1b7..27c32de31 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -4,7 +4,7 @@
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
       <remove invariant="MySql.Data.MySqlClient" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.1.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.2.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>
diff --git a/tests/IntegrationTests/packages.lock.json b/tests/IntegrationTests/packages.lock.json
index cacaafd6a..8f49f3060 100644
--- a/tests/IntegrationTests/packages.lock.json
+++ b/tests/IntegrationTests/packages.lock.json
@@ -67,15 +67,16 @@
       },
       "System.Text.Json": {
         "type": "Direct",
-        "requested": "[8.0.5, )",
-        "resolved": "8.0.5",
-        "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==",
+        "requested": "[9.0.1, )",
+        "resolved": "9.0.1",
+        "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==",
         "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "Microsoft.Bcl.AsyncInterfaces": "9.0.1",
           "System.Buffers": "4.5.1",
+          "System.IO.Pipelines": "9.0.1",
           "System.Memory": "4.5.5",
           "System.Runtime.CompilerServices.Unsafe": "6.0.0",
-          "System.Text.Encodings.Web": "8.0.0",
+          "System.Text.Encodings.Web": "9.0.1",
           "System.Threading.Tasks.Extensions": "4.5.4",
           "System.ValueTuple": "4.5.0"
         }
@@ -102,8 +103,8 @@
       },
       "Microsoft.Bcl.AsyncInterfaces": {
         "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "resolved": "9.0.1",
+        "contentHash": "IVkmUqf+KzbuXKrxi2tyQlg11RArYk26t2eU5cHekff+7Ao09vH8vt8idC0BJSMnpiRV2OK66zM2EwJU6Tm5Cw==",
         "dependencies": {
           "System.Threading.Tasks.Extensions": "4.5.4"
         }
@@ -203,6 +204,16 @@
         "resolved": "1.5.0",
         "contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ=="
       },
+      "System.IO.Pipelines": {
+        "type": "Transitive",
+        "resolved": "9.0.1",
+        "contentHash": "uXf5o8eV/gtzDQY4lGROLFMWQvcViKcF8o4Q6KpIOjloAQXrnscQSu6gTxYJMHuNJnh7szIF9AzkaEq+zDLoEg==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Memory": "4.5.5",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
       "System.Memory": {
         "type": "Transitive",
         "resolved": "4.5.5",
@@ -233,8 +244,8 @@
       },
       "System.Text.Encodings.Web": {
         "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
+        "resolved": "9.0.1",
+        "contentHash": "XkspqduP2t1e1x2vBUAD/xZ5ZDvmywuUwsmB93MvyQLospJfqtX0GsR/kU0vUL2h4kmvf777z3txV2W4NrQ9Qg==",
         "dependencies": {
           "System.Buffers": "4.5.1",
           "System.Memory": "4.5.5",
@@ -408,15 +419,16 @@
       },
       "System.Text.Json": {
         "type": "Direct",
-        "requested": "[8.0.5, )",
-        "resolved": "8.0.5",
-        "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==",
+        "requested": "[9.0.1, )",
+        "resolved": "9.0.1",
+        "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==",
         "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
+          "Microsoft.Bcl.AsyncInterfaces": "9.0.1",
           "System.Buffers": "4.5.1",
+          "System.IO.Pipelines": "9.0.1",
           "System.Memory": "4.5.5",
           "System.Runtime.CompilerServices.Unsafe": "6.0.0",
-          "System.Text.Encodings.Web": "8.0.0",
+          "System.Text.Encodings.Web": "9.0.1",
           "System.Threading.Tasks.Extensions": "4.5.4",
           "System.ValueTuple": "4.5.0"
         }
@@ -443,8 +455,8 @@
       },
       "Microsoft.Bcl.AsyncInterfaces": {
         "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
+        "resolved": "9.0.1",
+        "contentHash": "IVkmUqf+KzbuXKrxi2tyQlg11RArYk26t2eU5cHekff+7Ao09vH8vt8idC0BJSMnpiRV2OK66zM2EwJU6Tm5Cw==",
         "dependencies": {
           "System.Threading.Tasks.Extensions": "4.5.4"
         }
@@ -544,6 +556,16 @@
         "resolved": "1.5.0",
         "contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ=="
       },
+      "System.IO.Pipelines": {
+        "type": "Transitive",
+        "resolved": "9.0.1",
+        "contentHash": "uXf5o8eV/gtzDQY4lGROLFMWQvcViKcF8o4Q6KpIOjloAQXrnscQSu6gTxYJMHuNJnh7szIF9AzkaEq+zDLoEg==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Memory": "4.5.5",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
       "System.Memory": {
         "type": "Transitive",
         "resolved": "4.5.5",
@@ -574,8 +596,8 @@
       },
       "System.Text.Encodings.Web": {
         "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
+        "resolved": "9.0.1",
+        "contentHash": "XkspqduP2t1e1x2vBUAD/xZ5ZDvmywuUwsmB93MvyQLospJfqtX0GsR/kU0vUL2h4kmvf777z3txV2W4NrQ9Qg==",
         "dependencies": {
           "System.Buffers": "4.5.1",
           "System.Memory": "4.5.5",
@@ -750,12 +772,17 @@
       },
       "System.Text.Json": {
         "type": "Direct",
-        "requested": "[8.0.5, )",
-        "resolved": "8.0.5",
-        "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg==",
+        "requested": "[9.0.1, )",
+        "resolved": "9.0.1",
+        "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==",
         "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "9.0.1",
+          "System.Buffers": "4.5.1",
+          "System.IO.Pipelines": "9.0.1",
+          "System.Memory": "4.5.5",
           "System.Runtime.CompilerServices.Unsafe": "6.0.0",
-          "System.Text.Encodings.Web": "8.0.0"
+          "System.Text.Encodings.Web": "9.0.1",
+          "System.Threading.Tasks.Extensions": "4.5.4"
         }
       },
       "xunit": {
@@ -775,6 +802,11 @@
         "resolved": "2.8.2",
         "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
       },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "9.0.1",
+        "contentHash": "IVkmUqf+KzbuXKrxi2tyQlg11RArYk26t2eU5cHekff+7Ao09vH8vt8idC0BJSMnpiRV2OK66zM2EwJU6Tm5Cw=="
+      },
       "Microsoft.Build.Tasks.Git": {
         "type": "Transitive",
         "resolved": "8.0.0",
@@ -872,6 +904,26 @@
         "resolved": "13.0.1",
         "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
       },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.IO.Pipelines": {
+        "type": "Transitive",
+        "resolved": "9.0.1",
+        "contentHash": "uXf5o8eV/gtzDQY4lGROLFMWQvcViKcF8o4Q6KpIOjloAQXrnscQSu6gTxYJMHuNJnh7szIF9AzkaEq+zDLoEg==",
+        "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Memory": "4.5.5",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.5",
+        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
+      },
       "System.Reflection.Metadata": {
         "type": "Transitive",
         "resolved": "1.6.0",
@@ -884,9 +936,11 @@
       },
       "System.Text.Encodings.Web": {
         "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "yev/k9GHAEGx2Rg3/tU6MQh4HGBXJs70y7j1LaM1i/ER9po+6nnQ6RRqTJn1E7Xu0fbIFK80Nh5EoODxrbxwBQ==",
+        "resolved": "9.0.1",
+        "contentHash": "XkspqduP2t1e1x2vBUAD/xZ5ZDvmywuUwsmB93MvyQLospJfqtX0GsR/kU0vUL2h4kmvf777z3txV2W4NrQ9Qg==",
         "dependencies": {
+          "System.Buffers": "4.5.1",
+          "System.Memory": "4.5.5",
           "System.Runtime.CompilerServices.Unsafe": "6.0.0"
         }
       },
@@ -967,6 +1021,12 @@
         "dependencies": {
           "System.Runtime.CompilerServices.Unsafe": "6.0.0"
         }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "CentralTransitive",
+        "requested": "[4.5.4, )",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg=="
       }
     },
     "net8.0": {
@@ -1035,9 +1095,13 @@
       },
       "System.Text.Json": {
         "type": "Direct",
-        "requested": "[8.0.5, )",
-        "resolved": "8.0.5",
-        "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg=="
+        "requested": "[9.0.1, )",
+        "resolved": "9.0.1",
+        "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==",
+        "dependencies": {
+          "System.IO.Pipelines": "9.0.1",
+          "System.Text.Encodings.Web": "9.0.1"
+        }
       },
       "xunit": {
         "type": "Direct",
@@ -1150,11 +1214,21 @@
         "resolved": "13.0.1",
         "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
       },
+      "System.IO.Pipelines": {
+        "type": "Transitive",
+        "resolved": "9.0.1",
+        "contentHash": "uXf5o8eV/gtzDQY4lGROLFMWQvcViKcF8o4Q6KpIOjloAQXrnscQSu6gTxYJMHuNJnh7szIF9AzkaEq+zDLoEg=="
+      },
       "System.Reflection.Metadata": {
         "type": "Transitive",
         "resolved": "1.6.0",
         "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
       },
+      "System.Text.Encodings.Web": {
+        "type": "Transitive",
+        "resolved": "9.0.1",
+        "contentHash": "XkspqduP2t1e1x2vBUAD/xZ5ZDvmywuUwsmB93MvyQLospJfqtX0GsR/kU0vUL2h4kmvf777z3txV2W4NrQ9Qg=="
+      },
       "xunit.abstractions": {
         "type": "Transitive",
         "resolved": "2.0.3",
@@ -1289,9 +1363,9 @@
       },
       "System.Text.Json": {
         "type": "Direct",
-        "requested": "[8.0.5, )",
-        "resolved": "8.0.5",
-        "contentHash": "0f1B50Ss7rqxXiaBJyzUu9bWFOO2/zSlifZ/UNMdiIpDYe4cY4LQQicP4nirK1OS31I43rn062UIJ1Q9bpmHpg=="
+        "requested": "[9.0.1, )",
+        "resolved": "9.0.1",
+        "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA=="
       },
       "xunit": {
         "type": "Direct",
diff --git a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
index a77830d51..1a774936f 100644
--- a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
+++ b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
@@ -4,15 +4,15 @@
     "net8.0": {
       "Microsoft.DotNet.ILCompiler": {
         "type": "Direct",
-        "requested": "[8.0.11, )",
-        "resolved": "8.0.11",
-        "contentHash": "9SWRKJzFrE07m77G/9jSR1lqdlPmKCsQX1UflZEFBGYzmOZ53sjLEEcAiWtnWY0QJ750h2ahciOPO8oVjztQ0g=="
+        "requested": "[8.0.12, )",
+        "resolved": "8.0.12",
+        "contentHash": "zhXnz2574mBc/ocGoG+qB0BgyoK9bH7f7Te1fNQJGbpwLLwZO5KKaTBmfM8N8THb3a2vQVFonbBzQ//d15TmQQ=="
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.11, )",
-        "resolved": "8.0.11",
-        "contentHash": "zk5lnZrYJgtuJG8L4v17Ej8rZ3PUcR2iweNV08BaO5LbYHIi2wNaVNcJoLxvqgQdnjLlKnCCfVGLDr6QHeAarQ=="
+        "requested": "[8.0.12, )",
+        "resolved": "8.0.12",
+        "contentHash": "FV4HnQ3JI15PHnJ5PGTbz+rYvrih42oLi/7UMIshNwCwUZhTq13UzrggtXk4ygrcMcN+4jsS6hhshx2p/Zd0ig=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -72,15 +72,15 @@
     "net9.0": {
       "Microsoft.DotNet.ILCompiler": {
         "type": "Direct",
-        "requested": "[9.0.0, )",
-        "resolved": "9.0.0",
-        "contentHash": "bbnlV2PbUmCQ8Ndpx0kJaicLyV28IU+4IzyctQLL57+DxrHurYr2qsJrC8+yD44Q0DyPfv2oM168c1Tk6Bxbmg=="
+        "requested": "[9.0.1, )",
+        "resolved": "9.0.1",
+        "contentHash": "YSn2LXCcOfV6VntSFASNmg1W8ldLPbI74YFx+uke2lYFuahhHfeQIgKS3W52R9phB6YLWT4MvLUPDQqUelplJg=="
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[9.0.0, )",
-        "resolved": "9.0.0",
-        "contentHash": "zAwp213evC3UkimtVXRb+Dlgc/40QG145nmZDtp2LO9zJJMfrp+i/87BnXN7tRXEA4liyzdFkjqG1HE8/RPb4A=="
+        "requested": "[9.0.1, )",
+        "resolved": "9.0.1",
+        "contentHash": "BKFAkdhUUJivAoKuSWcT23WH9E+AWQMJootzt9X+lnesOD2a2yNIe1ZQFtxB8H3ayskLBD5gYyIbj5GIfw7rZg=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
diff --git a/tests/README.md b/tests/README.md
index a81ebc761..31b9686b2 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,7 @@ It also verifies that MySqlConnector and MySQL Connector/NET (MySql.Data) have s
 
 The tests require a MySQL server. The simplest way to run one is with [Docker](https://www.docker.com/community-edition):
 
-    docker run -d --rm --pull always --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.1 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
+    docker run -d --rm --pull always --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.2 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
     docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes'; CREATE USER 'caching-sha2-user'@'%' IDENTIFIED WITH caching_sha2_password BY 'Cach!ng-Sh@2-Pa55'; GRANT ALL PRIVILEGES ON *.* TO 'caching-sha2-user'@'%';"
 
 Copy the file `IntegrationTests/config.json.example` to `IntegrationTests/config.json`, then edit

From 246735c3c4ef5a0036d4850cbc947943d87c4681 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 22 Jan 2025 11:58:06 -0800
Subject: [PATCH 356/393] Update test packages.

---
 Directory.Packages.props                      |   6 +-
 tests/Conformance.Tests/packages.lock.json    |  62 ++--
 tests/IntegrationTests/packages.lock.json     | 286 +++++++++---------
 .../packages.lock.json                        |  62 ++--
 tests/MySqlConnector.Tests/packages.lock.json | 112 +++----
 .../packages.lock.json                        |   6 +-
 6 files changed, 267 insertions(+), 267 deletions(-)

diff --git a/Directory.Packages.props b/Directory.Packages.props
index 673b04c49..aca765692 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -15,7 +15,7 @@
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
-    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
+    <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
     <PackageVersion Include="MySql.Data" Version="9.2.0" />
     <PackageVersion Include="NLog" Version="4.7.15" />
     <PackageVersion Include="Serilog" Version="2.12.0" />
@@ -23,9 +23,9 @@
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
     <PackageVersion Include="System.Text.Json" Version="9.0.1" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
-    <PackageVersion Include="xunit" Version="2.9.2" />
+    <PackageVersion Include="xunit" Version="2.9.3" />
     <PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2" />
-    <PackageVersion Include="YamlDotNet" Version="16.1.3" />
+    <PackageVersion Include="YamlDotNet" Version="16.3.0" />
   </ItemGroup>
 
   <ItemGroup>
diff --git a/tests/Conformance.Tests/packages.lock.json b/tests/Conformance.Tests/packages.lock.json
index c74127043..c03d88f95 100644
--- a/tests/Conformance.Tests/packages.lock.json
+++ b/tests/Conformance.Tests/packages.lock.json
@@ -16,12 +16,12 @@
       },
       "Microsoft.NET.Test.Sdk": {
         "type": "Direct",
-        "requested": "[17.11.1, )",
-        "resolved": "17.11.1",
-        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "requested": "[17.12.0, )",
+        "resolved": "17.12.0",
+        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
         "dependencies": {
-          "Microsoft.CodeCoverage": "17.11.1",
-          "Microsoft.TestPlatform.TestHost": "17.11.1"
+          "Microsoft.CodeCoverage": "17.12.0",
+          "Microsoft.TestPlatform.TestHost": "17.12.0"
         }
       },
       "Microsoft.SourceLink.GitHub": {
@@ -42,13 +42,13 @@
       },
       "xunit": {
         "type": "Direct",
-        "requested": "[2.9.2, )",
-        "resolved": "2.9.2",
-        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "requested": "[2.9.3, )",
+        "resolved": "2.9.3",
+        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
         "dependencies": {
-          "xunit.analyzers": "1.16.0",
-          "xunit.assert": "2.9.2",
-          "xunit.core": "[2.9.2]"
+          "xunit.analyzers": "1.18.0",
+          "xunit.assert": "2.9.3",
+          "xunit.core": "[2.9.3]"
         }
       },
       "xunit.runner.visualstudio": {
@@ -64,8 +64,8 @@
       },
       "Microsoft.CodeCoverage": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+        "resolved": "17.12.0",
+        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
       "Microsoft.NETFramework.ReferenceAssemblies": {
         "type": "Transitive",
@@ -87,18 +87,18 @@
       },
       "Microsoft.TestPlatform.ObjectModel": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "resolved": "17.12.0",
+        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
         "dependencies": {
           "System.Reflection.Metadata": "1.6.0"
         }
       },
       "Microsoft.TestPlatform.TestHost": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "resolved": "17.12.0",
+        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
         "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
           "Newtonsoft.Json": "13.0.1"
         }
       },
@@ -124,37 +124,37 @@
       },
       "xunit.analyzers": {
         "type": "Transitive",
-        "resolved": "1.16.0",
-        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+        "resolved": "1.18.0",
+        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
       },
       "xunit.assert": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+        "resolved": "2.9.3",
+        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
       },
       "xunit.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "resolved": "2.9.3",
+        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]",
-          "xunit.extensibility.execution": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]",
+          "xunit.extensibility.execution": "[2.9.3]"
         }
       },
       "xunit.extensibility.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "resolved": "2.9.3",
+        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
         "dependencies": {
           "xunit.abstractions": "2.0.3"
         }
       },
       "xunit.extensibility.execution": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "resolved": "2.9.3",
+        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]"
         }
       },
       "Xunit.SkippableFact": {
diff --git a/tests/IntegrationTests/packages.lock.json b/tests/IntegrationTests/packages.lock.json
index 8f49f3060..221b2675f 100644
--- a/tests/IntegrationTests/packages.lock.json
+++ b/tests/IntegrationTests/packages.lock.json
@@ -42,11 +42,11 @@
       },
       "Microsoft.NET.Test.Sdk": {
         "type": "Direct",
-        "requested": "[17.11.1, )",
-        "resolved": "17.11.1",
-        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "requested": "[17.12.0, )",
+        "resolved": "17.12.0",
+        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
         "dependencies": {
-          "Microsoft.CodeCoverage": "17.11.1"
+          "Microsoft.CodeCoverage": "17.12.0"
         }
       },
       "Microsoft.SourceLink.GitHub": {
@@ -83,13 +83,13 @@
       },
       "xunit": {
         "type": "Direct",
-        "requested": "[2.9.2, )",
-        "resolved": "2.9.2",
-        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "requested": "[2.9.3, )",
+        "resolved": "2.9.3",
+        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
         "dependencies": {
-          "xunit.analyzers": "1.16.0",
-          "xunit.assert": "2.9.2",
-          "xunit.core": "[2.9.2]"
+          "xunit.analyzers": "1.18.0",
+          "xunit.assert": "2.9.3",
+          "xunit.core": "[2.9.3]"
         }
       },
       "xunit.runner.visualstudio": {
@@ -116,8 +116,8 @@
       },
       "Microsoft.CodeCoverage": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+        "resolved": "17.12.0",
+        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
       "Microsoft.Extensions.Configuration": {
         "type": "Transitive",
@@ -264,37 +264,37 @@
       },
       "xunit.analyzers": {
         "type": "Transitive",
-        "resolved": "1.16.0",
-        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+        "resolved": "1.18.0",
+        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
       },
       "xunit.assert": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+        "resolved": "2.9.3",
+        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
       },
       "xunit.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "resolved": "2.9.3",
+        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]",
-          "xunit.extensibility.execution": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]",
+          "xunit.extensibility.execution": "[2.9.3]"
         }
       },
       "xunit.extensibility.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "resolved": "2.9.3",
+        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
         "dependencies": {
           "xunit.abstractions": "2.0.3"
         }
       },
       "xunit.extensibility.execution": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "resolved": "2.9.3",
+        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]"
         }
       },
       "mysqlconnector": {
@@ -394,11 +394,11 @@
       },
       "Microsoft.NET.Test.Sdk": {
         "type": "Direct",
-        "requested": "[17.11.1, )",
-        "resolved": "17.11.1",
-        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "requested": "[17.12.0, )",
+        "resolved": "17.12.0",
+        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
         "dependencies": {
-          "Microsoft.CodeCoverage": "17.11.1"
+          "Microsoft.CodeCoverage": "17.12.0"
         }
       },
       "Microsoft.SourceLink.GitHub": {
@@ -435,13 +435,13 @@
       },
       "xunit": {
         "type": "Direct",
-        "requested": "[2.9.2, )",
-        "resolved": "2.9.2",
-        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "requested": "[2.9.3, )",
+        "resolved": "2.9.3",
+        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
         "dependencies": {
-          "xunit.analyzers": "1.16.0",
-          "xunit.assert": "2.9.2",
-          "xunit.core": "[2.9.2]"
+          "xunit.analyzers": "1.18.0",
+          "xunit.assert": "2.9.3",
+          "xunit.core": "[2.9.3]"
         }
       },
       "xunit.runner.visualstudio": {
@@ -468,8 +468,8 @@
       },
       "Microsoft.CodeCoverage": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+        "resolved": "17.12.0",
+        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
       "Microsoft.Extensions.Configuration": {
         "type": "Transitive",
@@ -616,37 +616,37 @@
       },
       "xunit.analyzers": {
         "type": "Transitive",
-        "resolved": "1.16.0",
-        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+        "resolved": "1.18.0",
+        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
       },
       "xunit.assert": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+        "resolved": "2.9.3",
+        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
       },
       "xunit.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "resolved": "2.9.3",
+        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]",
-          "xunit.extensibility.execution": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]",
+          "xunit.extensibility.execution": "[2.9.3]"
         }
       },
       "xunit.extensibility.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "resolved": "2.9.3",
+        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
         "dependencies": {
           "xunit.abstractions": "2.0.3"
         }
       },
       "xunit.extensibility.execution": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "resolved": "2.9.3",
+        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]"
         }
       },
       "mysqlconnector": {
@@ -746,12 +746,12 @@
       },
       "Microsoft.NET.Test.Sdk": {
         "type": "Direct",
-        "requested": "[17.11.1, )",
-        "resolved": "17.11.1",
-        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "requested": "[17.12.0, )",
+        "resolved": "17.12.0",
+        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
         "dependencies": {
-          "Microsoft.CodeCoverage": "17.11.1",
-          "Microsoft.TestPlatform.TestHost": "17.11.1"
+          "Microsoft.CodeCoverage": "17.12.0",
+          "Microsoft.TestPlatform.TestHost": "17.12.0"
         }
       },
       "Microsoft.SourceLink.GitHub": {
@@ -787,13 +787,13 @@
       },
       "xunit": {
         "type": "Direct",
-        "requested": "[2.9.2, )",
-        "resolved": "2.9.2",
-        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "requested": "[2.9.3, )",
+        "resolved": "2.9.3",
+        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
         "dependencies": {
-          "xunit.analyzers": "1.16.0",
-          "xunit.assert": "2.9.2",
-          "xunit.core": "[2.9.2]"
+          "xunit.analyzers": "1.18.0",
+          "xunit.assert": "2.9.3",
+          "xunit.core": "[2.9.3]"
         }
       },
       "xunit.runner.visualstudio": {
@@ -814,8 +814,8 @@
       },
       "Microsoft.CodeCoverage": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+        "resolved": "17.12.0",
+        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
       "Microsoft.Extensions.Configuration": {
         "type": "Transitive",
@@ -884,18 +884,18 @@
       },
       "Microsoft.TestPlatform.ObjectModel": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "resolved": "17.12.0",
+        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
         "dependencies": {
           "System.Reflection.Metadata": "1.6.0"
         }
       },
       "Microsoft.TestPlatform.TestHost": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "resolved": "17.12.0",
+        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
         "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
           "Newtonsoft.Json": "13.0.1"
         }
       },
@@ -951,37 +951,37 @@
       },
       "xunit.analyzers": {
         "type": "Transitive",
-        "resolved": "1.16.0",
-        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+        "resolved": "1.18.0",
+        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
       },
       "xunit.assert": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+        "resolved": "2.9.3",
+        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
       },
       "xunit.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "resolved": "2.9.3",
+        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]",
-          "xunit.extensibility.execution": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]",
+          "xunit.extensibility.execution": "[2.9.3]"
         }
       },
       "xunit.extensibility.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "resolved": "2.9.3",
+        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
         "dependencies": {
           "xunit.abstractions": "2.0.3"
         }
       },
       "xunit.extensibility.execution": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "resolved": "2.9.3",
+        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]"
         }
       },
       "mysqlconnector": {
@@ -1069,12 +1069,12 @@
       },
       "Microsoft.NET.Test.Sdk": {
         "type": "Direct",
-        "requested": "[17.11.1, )",
-        "resolved": "17.11.1",
-        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "requested": "[17.12.0, )",
+        "resolved": "17.12.0",
+        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
         "dependencies": {
-          "Microsoft.CodeCoverage": "17.11.1",
-          "Microsoft.TestPlatform.TestHost": "17.11.1"
+          "Microsoft.CodeCoverage": "17.12.0",
+          "Microsoft.TestPlatform.TestHost": "17.12.0"
         }
       },
       "Microsoft.SourceLink.GitHub": {
@@ -1105,13 +1105,13 @@
       },
       "xunit": {
         "type": "Direct",
-        "requested": "[2.9.2, )",
-        "resolved": "2.9.2",
-        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "requested": "[2.9.3, )",
+        "resolved": "2.9.3",
+        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
         "dependencies": {
-          "xunit.analyzers": "1.16.0",
-          "xunit.assert": "2.9.2",
-          "xunit.core": "[2.9.2]"
+          "xunit.analyzers": "1.18.0",
+          "xunit.assert": "2.9.3",
+          "xunit.core": "[2.9.3]"
         }
       },
       "xunit.runner.visualstudio": {
@@ -1127,8 +1127,8 @@
       },
       "Microsoft.CodeCoverage": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+        "resolved": "17.12.0",
+        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
       "Microsoft.Extensions.Configuration": {
         "type": "Transitive",
@@ -1194,18 +1194,18 @@
       },
       "Microsoft.TestPlatform.ObjectModel": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "resolved": "17.12.0",
+        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
         "dependencies": {
           "System.Reflection.Metadata": "1.6.0"
         }
       },
       "Microsoft.TestPlatform.TestHost": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "resolved": "17.12.0",
+        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
         "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
           "Newtonsoft.Json": "13.0.1"
         }
       },
@@ -1236,37 +1236,37 @@
       },
       "xunit.analyzers": {
         "type": "Transitive",
-        "resolved": "1.16.0",
-        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+        "resolved": "1.18.0",
+        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
       },
       "xunit.assert": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+        "resolved": "2.9.3",
+        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
       },
       "xunit.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "resolved": "2.9.3",
+        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]",
-          "xunit.extensibility.execution": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]",
+          "xunit.extensibility.execution": "[2.9.3]"
         }
       },
       "xunit.extensibility.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "resolved": "2.9.3",
+        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
         "dependencies": {
           "xunit.abstractions": "2.0.3"
         }
       },
       "xunit.extensibility.execution": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "resolved": "2.9.3",
+        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]"
         }
       },
       "mysqlconnector": {
@@ -1337,12 +1337,12 @@
       },
       "Microsoft.NET.Test.Sdk": {
         "type": "Direct",
-        "requested": "[17.11.1, )",
-        "resolved": "17.11.1",
-        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "requested": "[17.12.0, )",
+        "resolved": "17.12.0",
+        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
         "dependencies": {
-          "Microsoft.CodeCoverage": "17.11.1",
-          "Microsoft.TestPlatform.TestHost": "17.11.1"
+          "Microsoft.CodeCoverage": "17.12.0",
+          "Microsoft.TestPlatform.TestHost": "17.12.0"
         }
       },
       "Microsoft.SourceLink.GitHub": {
@@ -1369,13 +1369,13 @@
       },
       "xunit": {
         "type": "Direct",
-        "requested": "[2.9.2, )",
-        "resolved": "2.9.2",
-        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "requested": "[2.9.3, )",
+        "resolved": "2.9.3",
+        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
         "dependencies": {
-          "xunit.analyzers": "1.16.0",
-          "xunit.assert": "2.9.2",
-          "xunit.core": "[2.9.2]"
+          "xunit.analyzers": "1.18.0",
+          "xunit.assert": "2.9.3",
+          "xunit.core": "[2.9.3]"
         }
       },
       "xunit.runner.visualstudio": {
@@ -1391,8 +1391,8 @@
       },
       "Microsoft.CodeCoverage": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+        "resolved": "17.12.0",
+        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
       "Microsoft.Extensions.Configuration": {
         "type": "Transitive",
@@ -1458,18 +1458,18 @@
       },
       "Microsoft.TestPlatform.ObjectModel": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "resolved": "17.12.0",
+        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
         "dependencies": {
           "System.Reflection.Metadata": "1.6.0"
         }
       },
       "Microsoft.TestPlatform.TestHost": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "resolved": "17.12.0",
+        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
         "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
           "Newtonsoft.Json": "13.0.1"
         }
       },
@@ -1490,37 +1490,37 @@
       },
       "xunit.analyzers": {
         "type": "Transitive",
-        "resolved": "1.16.0",
-        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+        "resolved": "1.18.0",
+        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
       },
       "xunit.assert": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+        "resolved": "2.9.3",
+        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
       },
       "xunit.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "resolved": "2.9.3",
+        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]",
-          "xunit.extensibility.execution": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]",
+          "xunit.extensibility.execution": "[2.9.3]"
         }
       },
       "xunit.extensibility.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "resolved": "2.9.3",
+        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
         "dependencies": {
           "xunit.abstractions": "2.0.3"
         }
       },
       "xunit.extensibility.execution": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "resolved": "2.9.3",
+        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]"
         }
       },
       "mysqlconnector": {
diff --git a/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json b/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
index 3cf8566e4..05f0d9982 100644
--- a/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
+++ b/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
@@ -13,12 +13,12 @@
       },
       "Microsoft.NET.Test.Sdk": {
         "type": "Direct",
-        "requested": "[17.11.1, )",
-        "resolved": "17.11.1",
-        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "requested": "[17.12.0, )",
+        "resolved": "17.12.0",
+        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
         "dependencies": {
-          "Microsoft.CodeCoverage": "17.11.1",
-          "Microsoft.TestPlatform.TestHost": "17.11.1"
+          "Microsoft.CodeCoverage": "17.12.0",
+          "Microsoft.TestPlatform.TestHost": "17.12.0"
         }
       },
       "Microsoft.SourceLink.GitHub": {
@@ -39,13 +39,13 @@
       },
       "xunit": {
         "type": "Direct",
-        "requested": "[2.9.2, )",
-        "resolved": "2.9.2",
-        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "requested": "[2.9.3, )",
+        "resolved": "2.9.3",
+        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
         "dependencies": {
-          "xunit.analyzers": "1.16.0",
-          "xunit.assert": "2.9.2",
-          "xunit.core": "[2.9.2]"
+          "xunit.analyzers": "1.18.0",
+          "xunit.assert": "2.9.3",
+          "xunit.core": "[2.9.3]"
         }
       },
       "xunit.runner.visualstudio": {
@@ -61,8 +61,8 @@
       },
       "Microsoft.CodeCoverage": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+        "resolved": "17.12.0",
+        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
@@ -71,18 +71,18 @@
       },
       "Microsoft.TestPlatform.ObjectModel": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "resolved": "17.12.0",
+        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
         "dependencies": {
           "System.Reflection.Metadata": "1.6.0"
         }
       },
       "Microsoft.TestPlatform.TestHost": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "resolved": "17.12.0",
+        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
         "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
           "Newtonsoft.Json": "13.0.1"
         }
       },
@@ -103,37 +103,37 @@
       },
       "xunit.analyzers": {
         "type": "Transitive",
-        "resolved": "1.16.0",
-        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+        "resolved": "1.18.0",
+        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
       },
       "xunit.assert": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+        "resolved": "2.9.3",
+        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
       },
       "xunit.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "resolved": "2.9.3",
+        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]",
-          "xunit.extensibility.execution": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]",
+          "xunit.extensibility.execution": "[2.9.3]"
         }
       },
       "xunit.extensibility.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "resolved": "2.9.3",
+        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
         "dependencies": {
           "xunit.abstractions": "2.0.3"
         }
       },
       "xunit.extensibility.execution": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "resolved": "2.9.3",
+        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]"
         }
       },
       "mysqlconnector": {
diff --git a/tests/MySqlConnector.Tests/packages.lock.json b/tests/MySqlConnector.Tests/packages.lock.json
index f9ff016c5..a75c501bc 100644
--- a/tests/MySqlConnector.Tests/packages.lock.json
+++ b/tests/MySqlConnector.Tests/packages.lock.json
@@ -4,11 +4,11 @@
     ".NETFramework,Version=v4.8.1": {
       "Microsoft.NET.Test.Sdk": {
         "type": "Direct",
-        "requested": "[17.11.1, )",
-        "resolved": "17.11.1",
-        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "requested": "[17.12.0, )",
+        "resolved": "17.12.0",
+        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
         "dependencies": {
-          "Microsoft.CodeCoverage": "17.11.1"
+          "Microsoft.CodeCoverage": "17.12.0"
         }
       },
       "Microsoft.SourceLink.GitHub": {
@@ -29,13 +29,13 @@
       },
       "xunit": {
         "type": "Direct",
-        "requested": "[2.9.2, )",
-        "resolved": "2.9.2",
-        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "requested": "[2.9.3, )",
+        "resolved": "2.9.3",
+        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
         "dependencies": {
-          "xunit.analyzers": "1.16.0",
-          "xunit.assert": "2.9.2",
-          "xunit.core": "[2.9.2]"
+          "xunit.analyzers": "1.18.0",
+          "xunit.assert": "2.9.3",
+          "xunit.core": "[2.9.3]"
         }
       },
       "xunit.runner.visualstudio": {
@@ -62,8 +62,8 @@
       },
       "Microsoft.CodeCoverage": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+        "resolved": "17.12.0",
+        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
@@ -123,37 +123,37 @@
       },
       "xunit.analyzers": {
         "type": "Transitive",
-        "resolved": "1.16.0",
-        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+        "resolved": "1.18.0",
+        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
       },
       "xunit.assert": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+        "resolved": "2.9.3",
+        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
       },
       "xunit.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "resolved": "2.9.3",
+        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]",
-          "xunit.extensibility.execution": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]",
+          "xunit.extensibility.execution": "[2.9.3]"
         }
       },
       "xunit.extensibility.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "resolved": "2.9.3",
+        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
         "dependencies": {
           "xunit.abstractions": "2.0.3"
         }
       },
       "xunit.extensibility.execution": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "resolved": "2.9.3",
+        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]"
         }
       },
       "mysqlconnector": {
@@ -209,12 +209,12 @@
     "net9.0": {
       "Microsoft.NET.Test.Sdk": {
         "type": "Direct",
-        "requested": "[17.11.1, )",
-        "resolved": "17.11.1",
-        "contentHash": "U3Ty4BaGoEu+T2bwSko9tWqWUOU16WzSFkq6U8zve75oRBMSLTBdMAZrVNNz1Tq12aCdDom9fcOcM9QZaFHqFg==",
+        "requested": "[17.12.0, )",
+        "resolved": "17.12.0",
+        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
         "dependencies": {
-          "Microsoft.CodeCoverage": "17.11.1",
-          "Microsoft.TestPlatform.TestHost": "17.11.1"
+          "Microsoft.CodeCoverage": "17.12.0",
+          "Microsoft.TestPlatform.TestHost": "17.12.0"
         }
       },
       "Microsoft.SourceLink.GitHub": {
@@ -235,13 +235,13 @@
       },
       "xunit": {
         "type": "Direct",
-        "requested": "[2.9.2, )",
-        "resolved": "2.9.2",
-        "contentHash": "7LhFS2N9Z6Xgg8aE5lY95cneYivRMfRI8v+4PATa4S64D5Z/Plkg0qa8dTRHSiGRgVZ/CL2gEfJDE5AUhOX+2Q==",
+        "requested": "[2.9.3, )",
+        "resolved": "2.9.3",
+        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
         "dependencies": {
-          "xunit.analyzers": "1.16.0",
-          "xunit.assert": "2.9.2",
-          "xunit.core": "[2.9.2]"
+          "xunit.analyzers": "1.18.0",
+          "xunit.assert": "2.9.3",
+          "xunit.core": "[2.9.3]"
         }
       },
       "xunit.runner.visualstudio": {
@@ -257,8 +257,8 @@
       },
       "Microsoft.CodeCoverage": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "nPJqrcA5iX+Y0kqoT3a+pD/8lrW/V7ayqnEJQsTonSoPz59J8bmoQhcSN4G8+UJ64Hkuf0zuxnfuj2lkHOq4cA=="
+        "resolved": "17.12.0",
+        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
@@ -267,18 +267,18 @@
       },
       "Microsoft.TestPlatform.ObjectModel": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "E2jZqAU6JeWEVsyOEOrSW1o1bpHLgb25ypvKNB/moBXPVsFYBPd/Jwi7OrYahG50J83LfHzezYI+GaEkpAotiA==",
+        "resolved": "17.12.0",
+        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
         "dependencies": {
           "System.Reflection.Metadata": "1.6.0"
         }
       },
       "Microsoft.TestPlatform.TestHost": {
         "type": "Transitive",
-        "resolved": "17.11.1",
-        "contentHash": "DnG+GOqJXO/CkoqlJWeDFTgPhqD/V6VqUIL3vINizCWZ3X+HshCtbbyDdSHQQEjrc2Sl/K3yaxX6s+5LFEdYuw==",
+        "resolved": "17.12.0",
+        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
         "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.11.1",
+          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
           "Newtonsoft.Json": "13.0.1"
         }
       },
@@ -299,37 +299,37 @@
       },
       "xunit.analyzers": {
         "type": "Transitive",
-        "resolved": "1.16.0",
-        "contentHash": "hptYM7vGr46GUIgZt21YHO4rfuBAQS2eINbFo16CV/Dqq+24Tp+P5gDCACu1AbFfW4Sp/WRfDPSK8fmUUb8s0Q=="
+        "resolved": "1.18.0",
+        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
       },
       "xunit.assert": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "QkNBAQG4pa66cholm28AxijBjrmki98/vsEh4Sx5iplzotvPgpiotcxqJQMRC8d7RV7nIT8ozh97957hDnZwsQ=="
+        "resolved": "2.9.3",
+        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
       },
       "xunit.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "O6RrNSdmZ0xgEn5kT927PNwog5vxTtKrWMihhhrT0Sg9jQ7iBDciYOwzBgP2krBEk5/GBXI18R1lKvmnxGcb4w==",
+        "resolved": "2.9.3",
+        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]",
-          "xunit.extensibility.execution": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]",
+          "xunit.extensibility.execution": "[2.9.3]"
         }
       },
       "xunit.extensibility.core": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "Ol+KlBJz1x8BrdnhN2DeOuLrr1I/cTwtHCggL9BvYqFuVd/TUSzxNT5O0NxCIXth30bsKxgMfdqLTcORtM52yQ==",
+        "resolved": "2.9.3",
+        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
         "dependencies": {
           "xunit.abstractions": "2.0.3"
         }
       },
       "xunit.extensibility.execution": {
         "type": "Transitive",
-        "resolved": "2.9.2",
-        "contentHash": "rKMpq4GsIUIJibXuZoZ8lYp5EpROlnYaRpwu9Zr0sRZXE7JqJfEEbCsUriZqB+ByXCLFBJyjkTRULMdC+U566g==",
+        "resolved": "2.9.3",
+        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
         "dependencies": {
-          "xunit.extensibility.core": "[2.9.2]"
+          "xunit.extensibility.core": "[2.9.3]"
         }
       },
       "mysqlconnector": {
diff --git a/tools/SchemaCollectionGenerator/packages.lock.json b/tools/SchemaCollectionGenerator/packages.lock.json
index 4180ad51e..87121a3f9 100644
--- a/tools/SchemaCollectionGenerator/packages.lock.json
+++ b/tools/SchemaCollectionGenerator/packages.lock.json
@@ -20,9 +20,9 @@
       },
       "YamlDotNet": {
         "type": "Direct",
-        "requested": "[16.1.3, )",
-        "resolved": "16.1.3",
-        "contentHash": "gtHGiDvU9VTtWte8f0thIM38cL1oowOjStKpeAEKKfA+Rc4AvekJzqFDZiiPcc4kw00ZiwR4OTJS56L16q98DQ=="
+        "requested": "[16.3.0, )",
+        "resolved": "16.3.0",
+        "contentHash": "SgMOdxbz8X65z8hraIs6hOEdnkH6hESTAIUa7viEngHOYaH+6q5XJmwr1+yb9vJpNQ19hCQY69xbFsLtXpobQA=="
       },
       "Microsoft.Build.Tasks.Git": {
         "type": "Transitive",

From 4c2009f88e1fe4b19912556f51538319d807e1b7 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 22 Jan 2025 13:04:03 -0800
Subject: [PATCH 357/393] Make StyleCop a GlobalPackageReference.

Add style checking to tests; fix errors.
---
 Directory.Packages.props                      |   2 +-
 src/Directory.Build.props                     |   7 -
 .../ConnectionStringBuilderTests.cs           |   1 -
 tests/Conformance.Tests/DataReaderTests.cs    |   4 +-
 .../GetValueConversionTests.cs                |   1 -
 tests/Conformance.Tests/packages.lock.json    |  14 ++
 tests/Directory.Build.props                   |   8 +
 tests/IntegrationTests/BatchTests.cs          |   3 +-
 tests/IntegrationTests/BulkLoaderAsync.cs     |   8 +-
 tests/IntegrationTests/BulkLoaderSync.cs      |  18 +-
 tests/IntegrationTests/CancelTests.cs         |  12 +-
 tests/IntegrationTests/CharacterSetTests.cs   |   2 +-
 tests/IntegrationTests/ClientFactoryTests.cs  |   1 -
 tests/IntegrationTests/CommandBuilderTests.cs |   8 +-
 tests/IntegrationTests/CommandTests.cs        |   6 +-
 tests/IntegrationTests/CommandTimeoutTests.cs |   4 +-
 tests/IntegrationTests/ConnectAsync.cs        |   9 +-
 tests/IntegrationTests/ConnectSync.cs         |  10 +-
 tests/IntegrationTests/DataAdapterTests.cs    |   3 +-
 tests/IntegrationTests/DataTypes.cs           |  37 ++--
 tests/IntegrationTests/DatabaseFixture.cs     |   4 +-
 tests/IntegrationTests/InsertTests.cs         |  34 ++--
 .../IntegrationTests/IntegrationTests.csproj  |   2 +-
 tests/IntegrationTests/LoadDataInfileAsync.cs |   6 +-
 tests/IntegrationTests/LoadDataInfileSync.cs  |   6 +-
 .../IntegrationTests/MySqlDataSourceTests.cs  |   2 +-
 tests/IntegrationTests/ParameterCollection.cs |   4 +-
 .../IntegrationTests/PreparedCommandTests.cs  |   1 -
 tests/IntegrationTests/QueryTests.cs          |   9 +-
 tests/IntegrationTests/RedirectionTests.cs    | 176 +++++++++---------
 tests/IntegrationTests/SchemaProviderTests.cs |   6 +-
 tests/IntegrationTests/SslTests.cs            |   4 +-
 .../IntegrationTests/StoredProcedureTests.cs  |  14 +-
 tests/IntegrationTests/Transaction.cs         |   4 +-
 .../IntegrationTests/TransactionScopeTests.cs |  15 +-
 tests/IntegrationTests/UpdateTests.cs         |   2 +-
 tests/IntegrationTests/packages.lock.json     |  70 +++++++
 .../DependencyInjectionTests.cs               |   2 +-
 .../packages.lock.json                        |  14 ++
 .../packages.lock.json                        |  28 +++
 .../MySqlConnector.Tests/CancellationTests.cs |  21 ++-
 tests/MySqlConnector.Tests/ConnectionTests.cs |   4 +-
 tests/MySqlConnector.Tests/DummyEnum.cs       |   2 +-
 tests/MySqlConnector.Tests/FakeMySqlServer.cs |  12 +-
 .../FakeMySqlServerConnection.cs              |  60 +++---
 .../Metrics/IConnectionCreator.cs             |   5 +-
 .../Metrics/MetricsTestsBase.cs               |   3 +-
 .../MySqlConnectionStringBuilderTests.cs      |   4 +-
 .../MySqlConnector.Tests.csproj               |   1 +
 .../MySqlDateTimeTests.cs                     |  18 +-
 ...ySqlParameterCollectionNameToIndexTests.cs |   2 +-
 .../MySqlParameterCollectionTests.cs          |   2 +-
 .../MySqlParameterTests.cs                    |   1 -
 tests/MySqlConnector.Tests/TypeMapperTests.cs |  14 +-
 tests/MySqlConnector.Tests/UtilityTests.cs    |  20 +-
 tests/MySqlConnector.Tests/packages.lock.json |  28 +++
 .../packages.lock.json                        |  14 ++
 57 files changed, 482 insertions(+), 290 deletions(-)
 create mode 100644 tests/Directory.Build.props

diff --git a/Directory.Packages.props b/Directory.Packages.props
index aca765692..8c36d5fa8 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -19,7 +19,6 @@
     <PackageVersion Include="MySql.Data" Version="9.2.0" />
     <PackageVersion Include="NLog" Version="4.7.15" />
     <PackageVersion Include="Serilog" Version="2.12.0" />
-    <PackageVersion Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
     <PackageVersion Include="System.Text.Json" Version="9.0.1" />
     <PackageVersion Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
@@ -31,6 +30,7 @@
   <ItemGroup>
     <GlobalPackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
     <GlobalPackageReference Include="MinVer" Version="6.0.0" />
+    <GlobalPackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556" />
   </ItemGroup>
 
 </Project>
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 0968ec93d..34b8405fd 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -27,13 +27,6 @@
     <MinVerDefaultPreReleaseIdentifiers>beta.0</MinVerDefaultPreReleaseIdentifiers>
   </PropertyGroup>
 
-  <ItemGroup>
-    <PackageReference Include="StyleCop.Analyzers">
-      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
-      <PrivateAssets>all</PrivateAssets>
-    </PackageReference>
-  </ItemGroup>
-
   <ItemGroup>
     <None Include="..\..\docs\static\img\logo.png" Pack="true" PackagePath="\" />
   </ItemGroup>
diff --git a/tests/Conformance.Tests/ConnectionStringBuilderTests.cs b/tests/Conformance.Tests/ConnectionStringBuilderTests.cs
index 51b026177..5aaf6ef2a 100644
--- a/tests/Conformance.Tests/ConnectionStringBuilderTests.cs
+++ b/tests/Conformance.Tests/ConnectionStringBuilderTests.cs
@@ -9,4 +9,3 @@ public ConnectionStringBuilderTests(DbFactoryFixture fixture)
 	{
 	}
 }
-
diff --git a/tests/Conformance.Tests/DataReaderTests.cs b/tests/Conformance.Tests/DataReaderTests.cs
index 41bcbdad3..4618d5f35 100644
--- a/tests/Conformance.Tests/DataReaderTests.cs
+++ b/tests/Conformance.Tests/DataReaderTests.cs
@@ -11,5 +11,7 @@ public DataReaderTests(SelectValueFixture fixture)
 	}
 
 	[Fact(Skip = "Deliberately throws InvalidCastException")]
-	public override void GetTextReader_returns_empty_for_null_String() { }
+	public override void GetTextReader_returns_empty_for_null_String()
+	{
+	}
 }
diff --git a/tests/Conformance.Tests/GetValueConversionTests.cs b/tests/Conformance.Tests/GetValueConversionTests.cs
index c2fbbbedd..6cbe2b90f 100644
--- a/tests/Conformance.Tests/GetValueConversionTests.cs
+++ b/tests/Conformance.Tests/GetValueConversionTests.cs
@@ -270,7 +270,6 @@ public GetValueConversionTests(SelectValueFixture fixture)
 	public override void GetByte_throws_for_zero_Decimal_with_GetFieldValue() => TestGetValue(DbType.Decimal, ValueKind.Zero, x => x.GetFieldValue<byte>(0), (byte) 0);
 	public override Task GetByte_throws_for_zero_Decimal_with_GetFieldValueAsync() => TestGetValueAsync(DbType.Decimal, ValueKind.Zero, x => x.GetFieldValueAsync<byte>(0), (byte) 0);
 
-
 	// the minimum date permitted by MySQL is 1000-01-01; override the minimum value for DateTime tests
 	public override void GetDateTime_for_minimum_Date() => TestGetValue(DbType.Date, ValueKind.Minimum, x => x.GetDateTime(0), new DateTime(1000, 1, 1));
 	public override void GetDateTime_for_minimum_Date_with_GetFieldValue() => TestGetValue(DbType.Date, ValueKind.Minimum, x => x.GetFieldValue<DateTime>(0), new DateTime(1000, 1, 1));
diff --git a/tests/Conformance.Tests/packages.lock.json b/tests/Conformance.Tests/packages.lock.json
index c03d88f95..0bd8e590e 100644
--- a/tests/Conformance.Tests/packages.lock.json
+++ b/tests/Conformance.Tests/packages.lock.json
@@ -40,6 +40,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "xunit": {
         "type": "Direct",
         "requested": "[2.9.3, )",
@@ -107,6 +116,11 @@
         "resolved": "13.0.1",
         "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "System.Reflection.Metadata": {
         "type": "Transitive",
         "resolved": "1.6.0",
diff --git a/tests/Directory.Build.props b/tests/Directory.Build.props
new file mode 100644
index 000000000..78b9e666c
--- /dev/null
+++ b/tests/Directory.Build.props
@@ -0,0 +1,8 @@
+<Project>
+  <Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />
+
+  <PropertyGroup>
+    <NoWarn>$(NoWarn);SA0001</NoWarn>
+  </PropertyGroup>
+
+</Project>
diff --git a/tests/IntegrationTests/BatchTests.cs b/tests/IntegrationTests/BatchTests.cs
index dc0b5a947..3a62700ee 100644
--- a/tests/IntegrationTests/BatchTests.cs
+++ b/tests/IntegrationTests/BatchTests.cs
@@ -278,7 +278,6 @@ public void SingleRow(bool prepare)
 			Assert.Equal(3, reader.GetInt32(0));
 			Assert.False(reader.Read());
 		}
-
 	}
 
 	[Fact]
@@ -344,7 +343,7 @@ public void PrepareNeedsCommandsWithText()
 	private static string GetIgnoreCommandTransactionConnectionString() =>
 		new MySqlConnectionStringBuilder(AppConfig.ConnectionString)
 		{
-			IgnoreCommandTransaction = true
+			IgnoreCommandTransaction = true,
 		}.ConnectionString;
 }
 #endif
diff --git a/tests/IntegrationTests/BulkLoaderAsync.cs b/tests/IntegrationTests/BulkLoaderAsync.cs
index b697a0b4b..ddd11c4a4 100644
--- a/tests/IntegrationTests/BulkLoaderAsync.cs
+++ b/tests/IntegrationTests/BulkLoaderAsync.cs
@@ -160,7 +160,7 @@ public async Task BulkLoadLocalCsvFileNotFound()
 		await connection.OpenAsync();
 		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
 		{
-			Timeout = 3, //Set a short timeout for this test because the file not found exception takes a long time otherwise, the timeout does not change the result
+			Timeout = 3, // Set a short timeout for this test because the file not found exception takes a long time otherwise, the timeout does not change the result
 			FileName = AppConfig.MySqlBulkLoaderLocalCsvFile + "-junk",
 			TableName = m_testTable,
 			CharacterSet = "UTF8",
@@ -197,7 +197,7 @@ public async Task BulkLoadLocalCsvFileNotFound()
 		}
 		catch (Exception exception)
 		{
-			//We know that the exception is not a MySqlException, just use the assertion to fail the test
+			// We know that the exception is not a MySqlException, just use the assertion to fail the test
 			Assert.IsType<MySqlException>(exception);
 		}
 	}
@@ -640,6 +640,6 @@ public async Task BulkCopyNullDataReader()
 	private static string GetConnectionString() => BulkLoaderSync.GetConnectionString();
 	private static string GetLocalConnectionString() => BulkLoaderSync.GetLocalConnectionString();
 
-	readonly string m_testTable;
-	readonly byte[] m_memoryStreamBytes;
+	private readonly string m_testTable;
+	private readonly byte[] m_memoryStreamBytes;
 }
diff --git a/tests/IntegrationTests/BulkLoaderSync.cs b/tests/IntegrationTests/BulkLoaderSync.cs
index 3226f2223..ddcb849cc 100644
--- a/tests/IntegrationTests/BulkLoaderSync.cs
+++ b/tests/IntegrationTests/BulkLoaderSync.cs
@@ -179,7 +179,7 @@ public void BulkLoadLocalCsvFileNotFound()
 		connection.Open();
 		MySqlBulkLoader bl = new MySqlBulkLoader(connection)
 		{
-			Timeout = 3, //Set a short timeout for this test because the file not found exception takes a long time otherwise, the timeout does not change the result
+			Timeout = 3, // Set a short timeout for this test because the file not found exception takes a long time otherwise, the timeout does not change the result
 			FileName = AppConfig.MySqlBulkLoaderLocalCsvFile + "-junk",
 			TableName = m_testTable,
 			CharacterSet = "UTF8",
@@ -216,7 +216,7 @@ public void BulkLoadLocalCsvFileNotFound()
 		}
 		catch (Exception exception)
 		{
-			//We know that the exception is not a MySqlException, just use the assertion to fail the test
+			// We know that the exception is not a MySqlException, just use the assertion to fail the test
 			Assert.IsType<MySqlException>(exception);
 		}
 	}
@@ -1048,7 +1048,7 @@ public void BulkCopyColumnMappings()
 				new object[] { 1, 100, "a", "A", new byte[] { 0x33, 0x30 } },
 				new object[] { 2, 200, "bb", "BB", new byte[] { 0x33, 0x31 } },
 				new object[] { 3, 300, "ccc", "CCC", new byte[] { 0x33, 0x32 } },
-			}
+			},
 		};
 
 		var result = bulkCopy.WriteToServer(dataTable);
@@ -1102,7 +1102,7 @@ public void BulkCopyColumnMappingsInvalidSourceOrdinal()
 				new object[] { 1 },
 				new object[] { 2 },
 				new object[] { 3 },
-			}
+			},
 		};
 
 		Assert.Throws<InvalidOperationException>(() => bulkCopy.WriteToServer(dataTable));
@@ -1139,7 +1139,7 @@ public void BulkCopyColumnMappingsInvalidDestinationColumn()
 				new object[] { 1 },
 				new object[] { 2 },
 				new object[] { 3 },
-			}
+			},
 		};
 
 		Assert.Throws<InvalidOperationException>(() => bulkCopy.WriteToServer(dataTable));
@@ -1156,7 +1156,7 @@ public void BulkCopyDoesNotInsertAllRows()
 
 		var bcp = new MySqlBulkCopy(connection)
 		{
-			DestinationTableName = "bulk_copy_duplicate_pk"
+			DestinationTableName = "bulk_copy_duplicate_pk",
 		};
 
 		var dataTable = new DataTable()
@@ -1171,7 +1171,7 @@ public void BulkCopyDoesNotInsertAllRows()
 				new object[] { 1, "a" },
 				new object[] { 1, "b" },
 				new object[] { 3, "c" },
-			}
+			},
 		};
 
 		var ex = Assert.Throws<MySqlException>(() => bcp.WriteToServer(dataTable));
@@ -1295,6 +1295,6 @@ internal static string GetLocalConnectionString()
 		return csb.ConnectionString;
 	}
 
-	readonly string m_testTable;
-	readonly byte[] m_memoryStreamBytes;
+	private readonly string m_testTable;
+	private readonly byte[] m_memoryStreamBytes;
 }
diff --git a/tests/IntegrationTests/CancelTests.cs b/tests/IntegrationTests/CancelTests.cs
index 28f9d4e61..74cc640de 100644
--- a/tests/IntegrationTests/CancelTests.cs
+++ b/tests/IntegrationTests/CancelTests.cs
@@ -717,6 +717,7 @@ public async Task CancelSlowQueryBatchWithTokenAfterNextResult()
 
 		Assert.False(await reader.NextResultAsync());
 	}
+#endif
 #endif
 
 	private static CancellationToken GetCanceledToken()
@@ -726,15 +727,14 @@ private static CancellationToken GetCanceledToken()
 		return cts.Token;
 	}
 
-	static readonly CancellationToken s_canceledToken = GetCanceledToken();
-#endif
-
 	// returns billions of rows
-	const string c_hugeQuery = @"select * from integers a join integers b join integers c join integers d join integers e join integers f join integers g join integers h;";
+	private const string c_hugeQuery = @"select * from integers a join integers b join integers c join integers d join integers e join integers f join integers g join integers h;";
 
 	// takes a long time to return any rows
-	const string c_slowQuery = @"select * from integers a join integers b join integers c join integers d join integers e join integers f join integers g join integers h
+	private const string c_slowQuery = @"select * from integers a join integers b join integers c join integers d join integers e join integers f join integers g join integers h
 where sqrt(a.value) + sqrt(b.value) + sqrt(c.value) + sqrt(d.value) + sqrt(e.value) + sqrt(f.value) + sqrt(g.value) + sqrt(h.value) = 20;";
 
-	readonly DatabaseFixture m_database;
+	private static readonly CancellationToken s_canceledToken = GetCanceledToken();
+
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/CharacterSetTests.cs b/tests/IntegrationTests/CharacterSetTests.cs
index 152ca3449..15bb88856 100644
--- a/tests/IntegrationTests/CharacterSetTests.cs
+++ b/tests/IntegrationTests/CharacterSetTests.cs
@@ -84,5 +84,5 @@ public void CollationConnection(bool reopenConnection)
 		Assert.Equal(expected, collation);
 	}
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/ClientFactoryTests.cs b/tests/IntegrationTests/ClientFactoryTests.cs
index f74017a73..01ba718c0 100644
--- a/tests/IntegrationTests/ClientFactoryTests.cs
+++ b/tests/IntegrationTests/ClientFactoryTests.cs
@@ -24,7 +24,6 @@ public void CreateConnectionStringBuilder()
 		Assert.IsType<MySqlConnectionStringBuilder>(MySqlConnectorFactory.Instance.CreateConnectionStringBuilder());
 	}
 
-
 	[Fact]
 	public void CreateParameter()
 	{
diff --git a/tests/IntegrationTests/CommandBuilderTests.cs b/tests/IntegrationTests/CommandBuilderTests.cs
index 73b109876..584c8fe30 100644
--- a/tests/IntegrationTests/CommandBuilderTests.cs
+++ b/tests/IntegrationTests/CommandBuilderTests.cs
@@ -122,11 +122,11 @@ value varchar(100)
 	[Theory]
 	[InlineData("test", "`test`")]
 	[InlineData("te`st", "`te``st`")]
-	[InlineData("`test`", "```test```"
 #if MYSQL_DATA
-		, Skip = "Doesn't quote leading quotes"
+	[InlineData("`test`", "```test```", Skip = "Doesn't quote leading quotes")]
+#else
+	[InlineData("`test`", "```test```")]
 #endif
-	)]
 	public void QuoteIdentifier(string input, string expected)
 	{
 		var cb = new MySqlCommandBuilder();
@@ -144,5 +144,5 @@ public void UnquoteIdentifier(string input, string expected)
 		Assert.Equal(expected, cb.UnquoteIdentifier(input));
 	}
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/CommandTests.cs b/tests/IntegrationTests/CommandTests.cs
index c3f0556d3..2543cc367 100644
--- a/tests/IntegrationTests/CommandTests.cs
+++ b/tests/IntegrationTests/CommandTests.cs
@@ -267,7 +267,7 @@ public void IgnoreCommandTransactionIgnoresNull()
 	{
 		using var connection = new MySqlConnection(GetIgnoreCommandTransactionConnectionString());
 		connection.Open();
-		using var _ = connection.BeginTransaction();
+		using var ignoredTransaction = connection.BeginTransaction();
 		using var command = connection.CreateCommand();
 		command.CommandText = "SELECT 1;";
 		TestUtilities.AssertIsOne(command.ExecuteScalar());
@@ -467,10 +467,10 @@ private static string GetIgnoreCommandTransactionConnectionString()
 #else
 		return new MySqlConnectionStringBuilder(AppConfig.ConnectionString)
 		{
-			IgnoreCommandTransaction = true
+			IgnoreCommandTransaction = true,
 		}.ConnectionString;
 #endif
 	}
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/CommandTimeoutTests.cs b/tests/IntegrationTests/CommandTimeoutTests.cs
index d3e9f1224..d49246e4e 100644
--- a/tests/IntegrationTests/CommandTimeoutTests.cs
+++ b/tests/IntegrationTests/CommandTimeoutTests.cs
@@ -278,6 +278,6 @@ public async Task TransactionCommandTimeoutWithSleepAsync()
 		Assert.Equal(connectionState, m_connection.State);
 	}
 
-	readonly DatabaseFixture m_database;
-	readonly MySqlConnection m_connection;
+	private readonly DatabaseFixture m_database;
+	private readonly MySqlConnection m_connection;
 }
diff --git a/tests/IntegrationTests/ConnectAsync.cs b/tests/IntegrationTests/ConnectAsync.cs
index 27e8d428d..ce31fdf55 100644
--- a/tests/IntegrationTests/ConnectAsync.cs
+++ b/tests/IntegrationTests/ConnectAsync.cs
@@ -191,7 +191,11 @@ public async Task UsePasswordProviderPasswordTakesPrecedence()
 		await MySqlConnection.ClearPoolAsync(connection);
 
 		var wasCalled = false;
-		connection.ProvidePasswordCallback = _ => { wasCalled = true; return password; };
+		connection.ProvidePasswordCallback = _ =>
+		{
+			wasCalled = true;
+			return password;
+		};
 
 		await connection.OpenAsync();
 		Assert.False(wasCalled);
@@ -473,6 +477,7 @@ public async Task GoodServerSPN()
 	{
 		var csb = AppConfig.CreateGSSAPIConnectionStringBuilder();
 		string serverSPN;
+
 		// Use server's variable gssapi_principal_name as SPN
 		using (var connection = new MySqlConnection(csb.ConnectionString))
 		{
@@ -542,5 +547,5 @@ public async Task DisposeAsyncRaisesDisposed()
 		Assert.Equal(1, disposedCount);
 	}
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/ConnectSync.cs b/tests/IntegrationTests/ConnectSync.cs
index 502548416..3137d9c0c 100644
--- a/tests/IntegrationTests/ConnectSync.cs
+++ b/tests/IntegrationTests/ConnectSync.cs
@@ -98,7 +98,7 @@ public void NonExistentPipe()
 			PipeName = "nonexistingpipe",
 			ConnectionProtocol = MySqlConnectionProtocol.NamedPipe,
 			Server = ".",
-			ConnectionTimeout = 1
+			ConnectionTimeout = 1,
 		};
 
 		var sw = Stopwatch.StartNew();
@@ -265,7 +265,11 @@ public void UsePasswordProviderPasswordTakesPrecedence()
 		MySqlConnection.ClearPool(connection);
 
 		var wasCalled = false;
-		connection.ProvidePasswordCallback = _ => { wasCalled = true; return password; };
+		connection.ProvidePasswordCallback = _ =>
+		{
+			wasCalled = true;
+			return password;
+		};
 
 		connection.Open();
 		Assert.False(wasCalled);
@@ -598,5 +602,5 @@ public void DisposeRaisesDisposed()
 		Assert.Equal(1, disposedCount);
 	}
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/DataAdapterTests.cs b/tests/IntegrationTests/DataAdapterTests.cs
index 6f60955ba..7cee96197 100644
--- a/tests/IntegrationTests/DataAdapterTests.cs
+++ b/tests/IntegrationTests/DataAdapterTests.cs
@@ -205,7 +205,6 @@ public void BatchUpdate()
 		Assert.Equal(new[] { "two", "three", "four" }, m_connection.Query<string>("SELECT text_value FROM data_adapter ORDER BY id"));
 	}
 
-
 	[Fact]
 	public void BatchInsert()
 	{
@@ -341,5 +340,5 @@ public void ConvertBatchToCommandParameters()
 	}
 #endif
 
-	readonly MySqlConnection m_connection;
+	private readonly MySqlConnection m_connection;
 }
diff --git a/tests/IntegrationTests/DataTypes.cs b/tests/IntegrationTests/DataTypes.cs
index 033751097..b03650cbd 100644
--- a/tests/IntegrationTests/DataTypes.cs
+++ b/tests/IntegrationTests/DataTypes.cs
@@ -8,17 +8,17 @@
 // However, DbDataReader.GetString etc. are documented as throwing InvalidCastException: https://msdn.microsoft.com/en-us/library/system.data.common.dbdatareader.getstring.aspx
 // Additionally, that is what DbDataReader.GetFieldValue<T> throws. For consistency, we prefer InvalidCastException.
 #if MYSQL_DATA
-using GetValueWhenNullException = System.Data.SqlTypes.SqlNullValueException;
-using GetGuidWhenNullException = MySql.Data.MySqlClient.MySqlException;
 using GetBytesWhenNullException = System.NullReferenceException;
 using GetGeometryWhenNullException = System.Exception;
+using GetGuidWhenNullException = MySql.Data.MySqlClient.MySqlException;
 using GetStreamWhenNullException = System.ArgumentNullException;
+using GetValueWhenNullException = System.Data.SqlTypes.SqlNullValueException;
 #else
-using GetValueWhenNullException = System.InvalidCastException;
-using GetGuidWhenNullException = System.InvalidCastException;
 using GetBytesWhenNullException = System.InvalidCastException;
 using GetGeometryWhenNullException = System.InvalidCastException;
+using GetGuidWhenNullException = System.InvalidCastException;
 using GetStreamWhenNullException = System.InvalidCastException;
+using GetValueWhenNullException = System.InvalidCastException;
 #endif
 
 namespace IntegrationTests;
@@ -221,7 +221,7 @@ public void QueryTinyIntSbyte(string column, string dataTypeName, object[] expec
 		DoQuery("bools", column, dataTypeName, expected, reader => reader.GetSByte(0), mySqlDataCoercedNullValue: default(sbyte), connection: connection);
 	}
 
-	[Theory()]
+	[Theory]
 	[InlineData("TinyInt1U", "TINYINT", new object[] { null, (byte) 0, (byte) 1, (byte) 0, (byte) 1, (byte) 255, (byte) 123 })]
 	public void QueryTinyInt1Unsigned(string column, string dataTypeName, object[] expected)
 	{
@@ -313,8 +313,8 @@ public void QueryDouble(string column, string dataTypeName, object[] expected)
 	[Theory]
 	[InlineData("SmallDecimal", new object[] { null, "0", "-999.99", "-0.01", "999.99", "0.01" })]
 	[InlineData("MediumDecimal", new object[] { null, "0", "-999999999999.99999999", "-0.00000001", "999999999999.99999999", "0.00000001" })]
-	// value exceeds the range of a decimal and cannot be deserialized
-	// [InlineData("BigDecimal", new object[] { null, "0", "-99999999999999999999.999999999999999999999999999999", "-0.000000000000000000000000000001", "99999999999999999999.999999999999999999999999999999", "0.000000000000000000000000000001" })]
+	//// value exceeds the range of a decimal and cannot be deserialized
+	//// [InlineData("BigDecimal", new object[] { null, "0", "-99999999999999999999.999999999999999999999999999999", "-0.000000000000000000000000000001", "99999999999999999999.999999999999999999999999999999", "0.000000000000000000000000000001" })]
 	public void QueryDecimal(string column, object[] expected)
 	{
 		for (int i = 0; i < expected.Length; i++)
@@ -340,7 +340,7 @@ public void QueryString(string column, string[] expected)
 		}, getFieldValueType: typeof(TextReader));
 #endif
 	}
-	const string c_251ByteString = "This string has exactly 251 characters in it. The encoded length is stored as 0xFC 0xFB 0x00. 0xFB (i.e., 251) is the sentinel byte indicating \"this field is null\". Incorrectly interpreting the (decoded) length as the sentinel byte would corrupt data.";
+	private const string c_251ByteString = "This string has exactly 251 characters in it. The encoded length is stored as 0xFC 0xFB 0x00. 0xFB (i.e., 251) is the sentinel byte indicating \"this field is null\". Incorrectly interpreting the (decoded) length as the sentinel byte would corrupt data.";
 
 	[Theory]
 	[InlineData("guid", "CHAR(36)", new object[] { null, "00000000-0000-0000-0000-000000000000", "00000000-0000-0000-c000-000000000046", "fd24a0e8-c3f2-4821-a456-35da2dc4bb8f", "6A0E0A40-6228-11D3-A996-0050041896C8" })]
@@ -448,7 +448,7 @@ public async Task QueryWithGuidParameter(bool oldGuids)
 			Assert.Equal(oldGuids ? 0L : 1L, (await connection.QueryAsync<long>(@"select count(*) from datatypes_strings where guid = @guid", new { guid = new Guid("fd24a0e8-c3f2-4821-a456-35da2dc4bb8f") }).ConfigureAwait(false)).SingleOrDefault());
 			Assert.Equal(oldGuids ? 0L : 1L, (await connection.QueryAsync<long>(@"select count(*) from datatypes_strings where guidbin = @guid", new { guid = new Guid("fd24a0e8-c3f2-4821-a456-35da2dc4bb8f") }).ConfigureAwait(false)).SingleOrDefault());
 		}
-		catch (MySqlException ex) when (oldGuids && ex.Number is 1300 or 3854) // InvalidCharacterString, CannotConvertString
+		catch (MySqlException ex) when (oldGuids && ex.Number is 1300 or 3854) //// InvalidCharacterString, CannotConvertString
 		{
 			// new error in MySQL 8.0.24, MariaDB 10.5
 		}
@@ -562,14 +562,14 @@ insert into guid_format(c36, c32, b16, tsb16, leb16, t, b) values(
 				new() { Value = guidAsLittleEndianBinary16 },
 				new() { Value = guidAsChar36 },
 				new() { Value = isBinary16 ? guidAsBinary16 : isTimeSwapBinary16 ? guidAsTimeSwapBinary16 : guidAsLittleEndianBinary16 },
-				new() { Value = isChar36 ?  guid : guidAsChar36 },
-				new() { Value = isChar32 ?  guid : guidAsChar32 },
-				new() { Value = isBinary16 ?  guid : guidAsBinary16 },
-				new() { Value = isTimeSwapBinary16 ?  guid : guidAsTimeSwapBinary16 },
-				new() { Value = isLittleEndianBinary16 ?  guid : guidAsLittleEndianBinary16 },
+				new() { Value = isChar36 ? guid : guidAsChar36 },
+				new() { Value = isChar32 ? guid : guidAsChar32 },
+				new() { Value = isBinary16 ? guid : guidAsBinary16 },
+				new() { Value = isTimeSwapBinary16 ? guid : guidAsTimeSwapBinary16 },
+				new() { Value = isLittleEndianBinary16 ? guid : guidAsLittleEndianBinary16 },
 				new() { Value = guidAsChar32 },
 				new() { Value = isBinary16 ? guidAsBinary16 : isTimeSwapBinary16 ? guidAsTimeSwapBinary16 : guidAsLittleEndianBinary16 },
-			}
+			},
 		};
 		cmd.ExecuteNonQuery();
 		cmd.CommandText = "select c36, c32, b16, tsb16, leb16, t, b from guid_format;";
@@ -736,7 +736,7 @@ insert into date_time_kind(d, dt0, dt1, dt2, dt3, dt4, dt5, dt6) values(?, ?, ?,
 				new() { Value = dateTimeIn },
 				new() { Value = dateTimeIn },
 				new() { Value = dateTimeIn },
-			}
+			},
 		};
 		if (success)
 		{
@@ -1069,6 +1069,7 @@ public void QueryGeometry(string columnName, string dataTypeName, byte[] expecte
 			expected,
 		};
 
+#pragma warning disable SA1111 // Closing parenthesis should be on line of last parameter
 		DoQuery("geometry", columnName, dataTypeName, geometryData.ToArray(),
 #if !MYSQL_DATA
 			GetBytes
@@ -1091,6 +1092,7 @@ public void QueryGeometry(string columnName, string dataTypeName, byte[] expecte
 			assertEqual: (x, y) => Assert.Equal(((MySqlGeometry) x)?.Value.ToArray(), ((MySqlGeometry) y)?.Value.ToArray())
 #endif
 			);
+#pragma warning restore SA1111 // Closing parenthesis should be on line of last parameter
 	}
 
 	private static object CreateGeometry(byte[] data)
@@ -1513,7 +1515,7 @@ public void StoredProcedureOutParameter(string column, string table, MySqlDbType
 	[InlineData("DateTime", "datatypes_times", "DATETIME(6)")]
 	[InlineData("Timestamp", "datatypes_times", "TIMESTAMP(6)")]
 	[InlineData("Time", "datatypes_times", "TIME(6)")]
-	// [InlineData("Year", "datatypes_times", "YEAR")]
+	//// [InlineData("Year", "datatypes_times", "YEAR")]
 	[InlineData("value", "datatypes_json_core", "JSON")]
 	[InlineData("Geometry", "datatypes_geometry", "GEOMETRY")]
 	public void BulkCopyDataReader(string column, string table, string dataTypeName)
@@ -1848,4 +1850,3 @@ private static int[] SplitAndParse(object obj)
 
 	private MySqlConnectionStringBuilder CreateConnectionStringBuilder() => new(AppConfig.ConnectionString);
 }
-
diff --git a/tests/IntegrationTests/DatabaseFixture.cs b/tests/IntegrationTests/DatabaseFixture.cs
index 29424330b..857e460af 100644
--- a/tests/IntegrationTests/DatabaseFixture.cs
+++ b/tests/IntegrationTests/DatabaseFixture.cs
@@ -53,6 +53,6 @@ protected virtual void Dispose(bool disposing)
 		}
 	}
 
-	static readonly object s_lock = new();
-	static bool s_isInitialized;
+	private static readonly object s_lock = new();
+	private static bool s_isInitialized;
 }
diff --git a/tests/IntegrationTests/InsertTests.cs b/tests/IntegrationTests/InsertTests.cs
index 8669836db..0557a19e6 100644
--- a/tests/IntegrationTests/InsertTests.cs
+++ b/tests/IntegrationTests/InsertTests.cs
@@ -295,7 +295,7 @@ public void InsertDateTimeOffset()
 	{
 		m_database.Connection.Execute(@"drop table if exists insert_datetimeoffset;
 create table insert_datetimeoffset(rowid integer not null primary key auto_increment, datetimeoffset1 datetime null);");
-		var value = new DateTimeOffsetValues { datetimeoffset1 = new DateTimeOffset(2017, 1, 2, 3, 4, 5, TimeSpan.FromMinutes(678)) };
+		var value = new DateTimeOffsetValues { DateTimeOffset1 = new DateTimeOffset(2017, 1, 2, 3, 4, 5, TimeSpan.FromMinutes(678)) };
 
 		m_database.Connection.Open();
 		try
@@ -306,7 +306,7 @@ public void InsertDateTimeOffset()
 			{
 				ParameterName = "@datetimeoffset1",
 				DbType = DbType.DateTimeOffset,
-				Value = value.datetimeoffset1
+				Value = value.DateTimeOffset1,
 			});
 			Assert.Equal(1, cmd.ExecuteNonQuery());
 		}
@@ -319,7 +319,7 @@ public void InsertDateTimeOffset()
 
 		DateTime.SpecifyKind(datetime, DateTimeKind.Utc);
 
-		Assert.Equal(value.datetimeoffset1.Value.UtcDateTime, datetime);
+		Assert.Equal(value.DateTimeOffset1.Value.UtcDateTime, datetime);
 	}
 
 	[SkippableFact(MySqlData = "https://bugs.mysql.com/bug.php?id=91199")]
@@ -327,7 +327,7 @@ public void InsertMySqlDateTime()
 	{
 		m_database.Connection.Execute(@"drop table if exists insert_mysqldatetime;
 create table insert_mysqldatetime(rowid integer not null primary key auto_increment, ts timestamp(6) null);");
-		var value = new DateTimeOffsetValues { datetimeoffset1 = new DateTimeOffset(2017, 1, 2, 3, 4, 5, TimeSpan.FromMinutes(678)) };
+		var value = new DateTimeOffsetValues { DateTimeOffset1 = new DateTimeOffset(2017, 1, 2, 3, 4, 5, TimeSpan.FromMinutes(678)) };
 
 		m_database.Connection.Open();
 		try
@@ -643,40 +643,39 @@ public async Task EnumParametersAreParsedCorrectly()
 		{
 			m_database.Connection.Close();
 		}
-
 	}
 
-	enum Enum16 : short
+	private enum Enum16 : short
 	{
 		Off,
 		On,
 	}
 
-	enum Enum32 : int
+	private enum Enum32 : int
 	{
 		Off,
 		On,
 	}
 
-	enum Enum64 : long
+	private enum Enum64 : long
 	{
 		Off,
 		On,
 	}
 
-	class DateTimeOffsetValues
+	private class DateTimeOffsetValues
 	{
-		public DateTimeOffset? datetimeoffset1 { get; set; }
+		public DateTimeOffset? DateTimeOffset1 { get; set; }
 	}
 
-	class ColorEnumValues
+	private class ColorEnumValues
 	{
 		public string Varchar { get; set; }
 		public string String { get; set; }
 		public int Int { get; set; }
 	}
 
-	class EnumValues
+	private class EnumValues
 	{
 		public Enum16? Enum16 { get; set; }
 		public Enum32? Enum32 { get; set; }
@@ -697,17 +696,17 @@ color enum('red', 'orange', 'yellow', 'green', 'blue', 'indigo', 'violet') not n
 		Assert.Equal(new[] { "blue" }, m_database.Connection.Query<string>(@"select color from insert_mysql_enums"));
 	}
 
-	enum MySqlSize
+	private enum MySqlSize
 	{
 		None,
 		XSmall,
 		Small,
 		Medium,
 		Large,
-		XLarge
+		XLarge,
 	}
 
-	enum MySqlColor
+	private enum MySqlColor
 	{
 		None,
 		Red,
@@ -716,7 +715,7 @@ enum MySqlColor
 		Green,
 		Blue,
 		Indigo,
-		Violet
+		Violet,
 	}
 
 	[Fact]
@@ -757,7 +756,6 @@ public static IEnumerable<object[]> GetChars() =>
 		new[] { '\0', 'a', '\'', '\"', '\\', 'A', '\b', '\n', '\r', '\t', '\x1A' }
 			.SelectMany(x => new[] { false, true }.Select(y => new object[] { x, y }));
 
-
 #if !MYSQL_DATA
 	[Theory]
 	[MemberData(nameof(GetBlobs))]
@@ -797,5 +795,5 @@ public static IEnumerable<object[]> GetBlobs()
 	}
 #endif
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/IntegrationTests.csproj b/tests/IntegrationTests/IntegrationTests.csproj
index dcb0ecf9d..bb388f662 100644
--- a/tests/IntegrationTests/IntegrationTests.csproj
+++ b/tests/IntegrationTests/IntegrationTests.csproj
@@ -25,7 +25,7 @@
     <ThreadPoolMinThreads>64</ThreadPoolMinThreads>
     <LangVersion>11.0</LangVersion>
     <ImplicitUsings>enable</ImplicitUsings>
-    <NoWarn>$(NoWarn);xUnit1030</NoWarn>
+    <NoWarn>$(NoWarn);SA0001;SA1021;SA1133;xUnit1030</NoWarn>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/tests/IntegrationTests/LoadDataInfileAsync.cs b/tests/IntegrationTests/LoadDataInfileAsync.cs
index 5d88d032b..e14f761bd 100644
--- a/tests/IntegrationTests/LoadDataInfileAsync.cs
+++ b/tests/IntegrationTests/LoadDataInfileAsync.cs
@@ -66,7 +66,7 @@ public async Task ThrowsNotSupportedExceptionForNotTrustedHostAndNotStream()
 		await Assert.ThrowsAsync<MySqlException>(async () => await command.ExecuteNonQueryAsync());
 	}
 
-	readonly DatabaseFixture m_database;
-	readonly string m_testTable;
-	readonly string m_loadDataInfileCommand;
+	private readonly DatabaseFixture m_database;
+	private readonly string m_testTable;
+	private readonly string m_loadDataInfileCommand;
 }
diff --git a/tests/IntegrationTests/LoadDataInfileSync.cs b/tests/IntegrationTests/LoadDataInfileSync.cs
index e59e7b1d1..2d75556db 100644
--- a/tests/IntegrationTests/LoadDataInfileSync.cs
+++ b/tests/IntegrationTests/LoadDataInfileSync.cs
@@ -63,7 +63,7 @@ public void ThrowsNotSupportedExceptionForNotTrustedHostAndNotStream()
 		m_database.Connection.Close();
 	}
 
-	readonly DatabaseFixture m_database;
-	readonly string m_testTable;
-	readonly string m_loadDataInfileCommand;
+	private readonly DatabaseFixture m_database;
+	private readonly string m_testTable;
+	private readonly string m_loadDataInfileCommand;
 }
diff --git a/tests/IntegrationTests/MySqlDataSourceTests.cs b/tests/IntegrationTests/MySqlDataSourceTests.cs
index c753fe97c..0d8f9a72f 100644
--- a/tests/IntegrationTests/MySqlDataSourceTests.cs
+++ b/tests/IntegrationTests/MySqlDataSourceTests.cs
@@ -3,7 +3,7 @@ namespace IntegrationTests;
 
 public class MySqlDataSourceTests : IClassFixture<DatabaseFixture>
 {
-	public MySqlDataSourceTests(DatabaseFixture _)
+	public MySqlDataSourceTests(DatabaseFixture ignoredFixture)
 	{
 	}
 
diff --git a/tests/IntegrationTests/ParameterCollection.cs b/tests/IntegrationTests/ParameterCollection.cs
index 0e6398e12..c19206465 100644
--- a/tests/IntegrationTests/ParameterCollection.cs
+++ b/tests/IntegrationTests/ParameterCollection.cs
@@ -299,6 +299,6 @@ public void SetTwoParametersToSameNAme()
 #endif
 	}
 
-	readonly MySqlCommand m_command;
-	readonly MySqlParameterCollection m_parameterCollection;
+	private readonly MySqlCommand m_command;
+	private readonly MySqlParameterCollection m_parameterCollection;
 }
diff --git a/tests/IntegrationTests/PreparedCommandTests.cs b/tests/IntegrationTests/PreparedCommandTests.cs
index d087072ad..e61ac87f0 100644
--- a/tests/IntegrationTests/PreparedCommandTests.cs
+++ b/tests/IntegrationTests/PreparedCommandTests.cs
@@ -489,7 +489,6 @@ private enum TwoByteEnum : short
 		Value = 111,
 	}
 
-
 	private static MySqlConnection CreateConnection()
 	{
 		var connection = new MySqlConnection(AppConfig.ConnectionString);
diff --git a/tests/IntegrationTests/QueryTests.cs b/tests/IntegrationTests/QueryTests.cs
index 8e40848e2..a6077f5fe 100644
--- a/tests/IntegrationTests/QueryTests.cs
+++ b/tests/IntegrationTests/QueryTests.cs
@@ -1407,6 +1407,7 @@ END AS value
 		Assert.True(await reader.NextResultAsync());
 
 		Assert.True(await reader.ReadAsync());
+
 		// MySQL returns ulong, MariaDB returns decimal; GetBoolean will coerce both
 		Assert.True(reader.GetBoolean(0));
 		Assert.False(await reader.ReadAsync());
@@ -1687,13 +1688,13 @@ public void GetBytesByName()
 	}
 #endif
 
-	class BoolTest
+	private class BoolTest
 	{
 		public int Id { get; set; }
 		public bool? IsBold { get; set; }
 	}
 
-	class UseReaderWithoutDisposingThreadData
+	private class UseReaderWithoutDisposingThreadData
 	{
 		public UseReaderWithoutDisposingThreadData(List<Exception> exceptions, MySqlConnectionStringBuilder csb)
 		{
@@ -1706,10 +1707,10 @@ public UseReaderWithoutDisposingThreadData(List<Exception> exceptions, MySqlConn
 		public MySqlConnectionStringBuilder ConnectionStringBuilder { get; }
 	}
 
-	enum TestLongEnum : long
+	private enum TestLongEnum : long
 	{
 		Value = long.MaxValue,
 	}
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/RedirectionTests.cs b/tests/IntegrationTests/RedirectionTests.cs
index f5f374eb9..7d1155782 100644
--- a/tests/IntegrationTests/RedirectionTests.cs
+++ b/tests/IntegrationTests/RedirectionTests.cs
@@ -79,68 +79,65 @@ public void RedirectionTest()
 				Assert.Equal(proxy.ListenPort, db.SessionEndPoint!.Port);
 				db.Close();
 			}
-
-		} finally{
-			m_database.Connection.Execute(
-				$"set @@global.redirect_url=\"\"");
+		}
+		finally
+		{
+			m_database.Connection.Execute($"set @@global.redirect_url=\"\"");
 		}
 		MySqlConnection.ClearAllPools();
+
 		// ensure that when required, throwing error if no redirection
 		csb.ServerRedirectionMode = MySqlServerRedirectionMode.Required;
 		using (var db = new MySqlConnection(csb.ConnectionString))
 		{
-			try
-			{
-				db.Open();
-				Assert.Fail("must have thrown error");
-			}
-			catch (MySqlException ex)
-			{
-				Assert.Equal((int) MySqlErrorCode.UnableToConnectToHost, ex.Number);
-			}
+			var exception = Assert.Throws<MySqlException>(db.Open);
+			Assert.Equal(MySqlErrorCode.UnableToConnectToHost, exception.ErrorCode);
 		}
 
 		StopProxy();
 	}
 
-    protected void StartProxy()
-    {
-	    var csb = AppConfig.CreateConnectionStringBuilder();
-	    proxy = new ServerConfiguration( csb.Server, (int)csb.Port );
-	    Thread serverThread = new Thread( ServerThread );
-	    serverThread.Start( proxy );
-    }
-
-    protected void StopProxy()
-    {
-	    proxy.RunServer = false;
-	    proxy.ServerSocket.Close();
-    }
-
-    private class ServerConfiguration {
-
-        public IPAddress RemoteAddress;
-        public int    RemotePort;
-        public int    ListenPort;
-        public Socket ServerSocket;
-        public ServerConfiguration(String remoteAddress, int remotePort) {
+	protected void StartProxy()
+	{
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		proxy = new ServerConfiguration( csb.Server, (int)csb.Port );
+		Thread serverThread = new Thread( ServerThread );
+		serverThread.Start( proxy );
+	}
+
+	protected void StopProxy()
+	{
+		proxy.RunServer = false;
+		proxy.ServerSocket.Close();
+	}
+
+	private class ServerConfiguration
+	{
+		public IPAddress RemoteAddress { get; set; }
+		public int RemotePort { get; set; }
+		public int ListenPort { get; set; }
+		public Socket ServerSocket { get; set; }
+		public ServerConfiguration(string remoteAddress, int remotePort)
+		{
 			var ipHostEntry = Dns.GetHostEntry(remoteAddress);
 			RemoteAddress = ipHostEntry.AddressList[0];
-            RemotePort    = remotePort;
-            ListenPort    = 0;
-        }
-        public bool RunServer = true;
-    }
-
-    private static void ServerThread(Object configObj) {
-        ServerConfiguration config = (ServerConfiguration)configObj;
-        Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
-
-        serverSocket.Bind( new IPEndPoint( IPAddress.Any, 0 ) );
-        serverSocket.Listen(1);
-        config.ListenPort = ((IPEndPoint) serverSocket.LocalEndPoint).Port;
-        config.ServerSocket = serverSocket;
-        while( config.RunServer ) {
+			RemotePort = remotePort;
+			ListenPort = 0;
+		}
+		public bool RunServer { get; set; } = true;
+	}
+
+	private static void ServerThread(object configObj)
+	{
+		ServerConfiguration config = (ServerConfiguration)configObj;
+		Socket serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
+
+		serverSocket.Bind( new IPEndPoint( IPAddress.Any, 0 ) );
+		serverSocket.Listen(1);
+		config.ListenPort = ((IPEndPoint) serverSocket.LocalEndPoint).Port;
+		config.ServerSocket = serverSocket;
+		while (config.RunServer)
+		{
 			try
 			{
 				Socket client = serverSocket.Accept();
@@ -151,43 +148,50 @@ private static void ServerThread(Object configObj) {
 			{
 				return;
 			}
-        }
-    }
-
-    private class ClientContext {
-        public ServerConfiguration Config;
-        public Socket              Client;
-    }
-
-    private static void ClientThread(Object contextObj) {
-        ClientContext context = (ClientContext)contextObj;
-        Socket              client = context.Client;
-        ServerConfiguration config = context.Config;
-        IPEndPoint remoteEndPoint = new IPEndPoint( config.RemoteAddress, config.RemotePort );
-        Socket remote = new Socket( remoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
-        remote.Connect( remoteEndPoint );
-        Byte[] buffer = new Byte[4096];
-        for(;;) {
-	        if (!config.RunServer)
-	        {
-		        remote.Close();
-		        client.Close();
-		        return;
-	        }
-            if( client.Available > 0 ) {
-                var count = client.Receive( buffer );
-                if( count == 0 ) return;
-                remote.Send( buffer, count, SocketFlags.None );
-            }
-            if( remote.Available > 0 ) {
-                var count = remote.Receive( buffer );
-                if( count == 0 ) return;
-                client.Send( buffer, count, SocketFlags.None );
-            }
-        }
-    }
-
-	readonly DatabaseFixture m_database;
+		}
+	}
+
+	private class ClientContext
+	{
+		public ServerConfiguration Config { get; set; }
+		public Socket Client { get; set; }
+	}
+
+	private static void ClientThread(object contextObj)
+	{
+		ClientContext context = (ClientContext) contextObj;
+		Socket client = context.Client;
+		ServerConfiguration config = context.Config;
+		IPEndPoint remoteEndPoint = new IPEndPoint(config.RemoteAddress, config.RemotePort);
+		Socket remote = new Socket(remoteEndPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
+		remote.Connect(remoteEndPoint);
+		var buffer = new byte[4096];
+		while (true)
+		{
+			if (!config.RunServer)
+			{
+				remote.Close();
+				client.Close();
+				return;
+			}
+			if (client.Available > 0)
+			{
+				var count = client.Receive(buffer);
+				if (count == 0)
+					return;
+				remote.Send(buffer, count, SocketFlags.None);
+			}
+			if (remote.Available > 0)
+			{
+				var count = remote.Receive(buffer);
+				if (count == 0)
+					return;
+				client.Send(buffer, count, SocketFlags.None);
+			}
+		}
+	}
+
+	private readonly DatabaseFixture m_database;
 	private ServerConfiguration proxy;
 }
 #endif
diff --git a/tests/IntegrationTests/SchemaProviderTests.cs b/tests/IntegrationTests/SchemaProviderTests.cs
index 259d28e1f..d4c589ab7 100644
--- a/tests/IntegrationTests/SchemaProviderTests.cs
+++ b/tests/IntegrationTests/SchemaProviderTests.cs
@@ -121,10 +121,10 @@ public void MetaDataCollectionsRestriction() =>
 	[InlineData("datatypes", "DataTypes")]
 	[InlineData("Indexes")]
 	[InlineData("IndexColumns")]
-	// only in 8.0 - [InlineData("KeyWords")]
+	//// only in 8.0 - [InlineData("KeyWords")]
 	[InlineData("MetaDataCollections")]
 	[InlineData("Procedures")]
-	// only in 8.0 - [InlineData("ResourceGroups")]
+	//// only in 8.0 - [InlineData("ResourceGroups")]
 	[InlineData("Tables")]
 	[InlineData("Triggers")]
 	[InlineData("Views")]
@@ -258,5 +258,5 @@ public void IndexColumnsWithColumnName()
 		Assert.Equal(expected, actual);
 	}
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/SslTests.cs b/tests/IntegrationTests/SslTests.cs
index e33d33917..b27742e02 100644
--- a/tests/IntegrationTests/SslTests.cs
+++ b/tests/IntegrationTests/SslTests.cs
@@ -147,7 +147,7 @@ public async Task ConnectSslClientCertificateFromCertificateStore(string certFil
 			Assert.True(connection.SslIsMutuallyAuthenticated);
 #endif
 #endif
-            cmd.CommandText = "SHOW SESSION STATUS LIKE 'Ssl_version'";
+			cmd.CommandText = "SHOW SESSION STATUS LIKE 'Ssl_version'";
 			var sslVersion = (string) await cmd.ExecuteScalarAsync();
 			Assert.False(string.IsNullOrWhiteSpace(sslVersion));
 		}
@@ -306,5 +306,5 @@ public async Task ForceTls11()
 	}
 #endif
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/StoredProcedureTests.cs b/tests/IntegrationTests/StoredProcedureTests.cs
index 68b7f4b1f..cd0e79ede 100644
--- a/tests/IntegrationTests/StoredProcedureTests.cs
+++ b/tests/IntegrationTests/StoredProcedureTests.cs
@@ -532,7 +532,7 @@ public async Task InOut(bool prepare)
 			ParameterName = "high",
 			DbType = DbType.Int32,
 			Direction = ParameterDirection.InputOutput,
-			Value = 1
+			Value = 1,
 		};
 		while ((int) parameter.Value < 8)
 		{
@@ -676,11 +676,11 @@ OUT param2 VARCHAR(100))
 #endif
 	[InlineData("char(30)", 30)]
 	[InlineData("varchar(50)", 50)]
-	// These return nonzero sizes for some versions of MySQL Server 8.0
-	// [InlineData("bit", 0)]
-	// [InlineData("tinyint", 0)]
-	// [InlineData("bigint", 0)]
-	// [InlineData("bigint unsigned", 0)]
+	//// These return nonzero sizes for some versions of MySQL Server 8.0
+	//// [InlineData("bit", 0)]
+	//// [InlineData("tinyint", 0)]
+	//// [InlineData("bigint", 0)]
+	//// [InlineData("bigint unsigned", 0)]
 	public void DeriveParametersParameterSize(string parameterType, int expectedSize)
 	{
 		var csb = AppConfig.CreateConnectionStringBuilder();
@@ -895,5 +895,5 @@ private static MySqlConnection CreateOpenConnection()
 		return connection;
 	}
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/Transaction.cs b/tests/IntegrationTests/Transaction.cs
index ac33ab5e0..4fb94d316 100644
--- a/tests/IntegrationTests/Transaction.cs
+++ b/tests/IntegrationTests/Transaction.cs
@@ -449,6 +449,6 @@ public void TransactionHoldsLocks()
 		task2.GetAwaiter().GetResult();
 	}
 
-	readonly TransactionFixture m_database;
-	readonly MySqlConnection m_connection;
+	private readonly TransactionFixture m_database;
+	private readonly MySqlConnection m_connection;
 }
diff --git a/tests/IntegrationTests/TransactionScopeTests.cs b/tests/IntegrationTests/TransactionScopeTests.cs
index 5a808f193..1104a7015 100644
--- a/tests/IntegrationTests/TransactionScopeTests.cs
+++ b/tests/IntegrationTests/TransactionScopeTests.cs
@@ -9,7 +9,7 @@ public TransactionScopeTests(DatabaseFixture database)
 		m_database = database;
 	}
 
-	public static IEnumerable<object[]> ConnectionStrings = new[]
+	public static IEnumerable<object[]> ConnectionStrings { get; } = new[]
 	{
 #if MYSQL_DATA
 		new object[] { "" },
@@ -274,7 +274,7 @@ public void UsingSequentialConnectionsInOneTransactionDoesNotDeadlock(string con
 		var transactionOptions = new TransactionOptions
 		{
 			IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted,
-			Timeout = TransactionManager.MaximumTimeout
+			Timeout = TransactionManager.MaximumTimeout,
 		};
 		using (var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions, TransactionScopeAsyncFlowOption.Enabled))
 		{
@@ -316,7 +316,7 @@ public void UsingSequentialConnectionsInOneTransactionDoesNotDeadlockWithoutComp
 		var transactionOptions = new TransactionOptions
 		{
 			IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted,
-			Timeout = TransactionManager.MaximumTimeout
+			Timeout = TransactionManager.MaximumTimeout,
 		};
 		using (new TransactionScope(TransactionScopeOption.Required, transactionOptions, TransactionScopeAsyncFlowOption.Enabled))
 		{
@@ -454,7 +454,7 @@ public void ReusingConnectionInOneTransactionDoesNotDeadlock(string connectionSt
 		var transactionOptions = new TransactionOptions
 		{
 			IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted,
-			Timeout = TransactionManager.MaximumTimeout
+			Timeout = TransactionManager.MaximumTimeout,
 		};
 		using (var scope = new TransactionScope(TransactionScopeOption.Required, transactionOptions, TransactionScopeAsyncFlowOption.Enabled))
 		{
@@ -494,7 +494,7 @@ public void ReusingConnectionInOneTransactionDoesNotDeadlockWithoutComplete(stri
 		var transactionOptions = new TransactionOptions
 		{
 			IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted,
-			Timeout = TransactionManager.MaximumTimeout
+			Timeout = TransactionManager.MaximumTimeout,
 		};
 		using (new TransactionScope(TransactionScopeOption.Required, transactionOptions, TransactionScopeAsyncFlowOption.Enabled))
 		{
@@ -847,6 +847,7 @@ public void CannotMixNonXaAndXaTransactions()
 	public void Bug1348()
 	{
 		var xid = string.Empty;
+
 		// TransactionAbortedException、MySqlException
 		Assert.ThrowsAny<Exception>(() =>
 		{
@@ -858,6 +859,7 @@ public void Bug1348()
 
 				using var conn2 = new MySqlConnection(AppConfig.ConnectionString);
 				conn2.Open();
+
 				// Rolling back the second branch transaction early so that it has an exception in the preparation phase
 				var command2 = conn2.CreateCommand();
 				command2.CommandText = $"XA END '{xid}','2';XA ROLLBACK '{xid}','2'";
@@ -920,6 +922,5 @@ public void ConnectionOpenedCallbackAutoEnlistInTransaction()
 	}
 #endif
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
-
diff --git a/tests/IntegrationTests/UpdateTests.cs b/tests/IntegrationTests/UpdateTests.cs
index 046d6c6b8..9aa336cf4 100644
--- a/tests/IntegrationTests/UpdateTests.cs
+++ b/tests/IntegrationTests/UpdateTests.cs
@@ -161,5 +161,5 @@ public void UpdateFieldCount()
 		}
 	}
 
-	readonly DatabaseFixture m_database;
+	private readonly DatabaseFixture m_database;
 }
diff --git a/tests/IntegrationTests/packages.lock.json b/tests/IntegrationTests/packages.lock.json
index 221b2675f..4e6439560 100644
--- a/tests/IntegrationTests/packages.lock.json
+++ b/tests/IntegrationTests/packages.lock.json
@@ -65,6 +65,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "System.Text.Json": {
         "type": "Direct",
         "requested": "[9.0.1, )",
@@ -194,6 +203,11 @@
           "System.Reflection.Metadata": "1.6.0"
         }
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "System.Buffers": {
         "type": "Transitive",
         "resolved": "4.5.1",
@@ -417,6 +431,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "System.Text.Json": {
         "type": "Direct",
         "requested": "[9.0.1, )",
@@ -546,6 +569,11 @@
           "System.Reflection.Metadata": "1.6.0"
         }
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "System.Buffers": {
         "type": "Transitive",
         "resolved": "4.5.1",
@@ -770,6 +798,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "System.Text.Json": {
         "type": "Direct",
         "requested": "[9.0.1, )",
@@ -904,6 +941,11 @@
         "resolved": "13.0.1",
         "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "System.Buffers": {
         "type": "Transitive",
         "resolved": "4.5.1",
@@ -1093,6 +1135,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "System.Text.Json": {
         "type": "Direct",
         "requested": "[9.0.1, )",
@@ -1214,6 +1265,11 @@
         "resolved": "13.0.1",
         "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "System.IO.Pipelines": {
         "type": "Transitive",
         "resolved": "9.0.1",
@@ -1361,6 +1417,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "System.Text.Json": {
         "type": "Direct",
         "requested": "[9.0.1, )",
@@ -1478,6 +1543,11 @@
         "resolved": "13.0.1",
         "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "System.Reflection.Metadata": {
         "type": "Transitive",
         "resolved": "1.6.0",
diff --git a/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs b/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs
index 7779bba61..7942994d3 100644
--- a/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs
+++ b/tests/MySqlConnector.DependencyInjection.Tests/DependencyInjectionTests.cs
@@ -215,5 +215,5 @@ public async Task KeyedDbDataSourceIsRegistered()
 		Assert.Equal(c_connectionString, connection.ConnectionString);
 	}
 
-	const string c_connectionString = "Server=localhost;User ID=root;Password=pass";
+	private const string c_connectionString = "Server=localhost;User ID=root;Password=pass";
 }
diff --git a/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json b/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
index 05f0d9982..0d8fc35b6 100644
--- a/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
+++ b/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
@@ -37,6 +37,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "xunit": {
         "type": "Direct",
         "requested": "[2.9.3, )",
@@ -91,6 +100,11 @@
         "resolved": "13.0.1",
         "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "System.Reflection.Metadata": {
         "type": "Transitive",
         "resolved": "1.6.0",
diff --git a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
index 1a774936f..4f5603cf7 100644
--- a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
+++ b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
@@ -30,6 +30,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "Microsoft.Build.Tasks.Git": {
         "type": "Transitive",
         "resolved": "8.0.0",
@@ -40,6 +49,11 @@
         "resolved": "8.0.0",
         "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "mysqlconnector": {
         "type": "Project",
         "dependencies": {
@@ -98,6 +112,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "Microsoft.Build.Tasks.Git": {
         "type": "Transitive",
         "resolved": "8.0.0",
@@ -108,6 +131,11 @@
         "resolved": "8.0.0",
         "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "mysqlconnector": {
         "type": "Project",
         "dependencies": {
diff --git a/tests/MySqlConnector.Tests/CancellationTests.cs b/tests/MySqlConnector.Tests/CancellationTests.cs
index e195fc619..e70967e4a 100644
--- a/tests/MySqlConnector.Tests/CancellationTests.cs
+++ b/tests/MySqlConnector.Tests/CancellationTests.cs
@@ -18,7 +18,7 @@ public CancellationTests()
 
 	public void Dispose() => m_server.Stop();
 
-	// NOTE: Multiple nested classes in order to force tests to run in parallel against each other
+	//// NOTE: Multiple nested classes in order to force tests to run in parallel against each other
 
 	public class CancelWithCommandTimeout : CancellationTests
 	{
@@ -425,8 +425,19 @@ public static IEnumerable<object[]> GetAsyncMethodSteps()
 
 	private static int ExecuteScalar(MySqlCommand command) => (int) command.ExecuteScalar();
 	private static async Task<int> ExecuteScalarAsync(MySqlCommand command, CancellationToken token) => (int) await command.ExecuteScalarAsync(token);
-	private static int ExecuteNonQuery(MySqlCommand command) { command.ExecuteNonQuery(); return 0; }
-	private static async Task<int> ExecuteNonQueryAsync(MySqlCommand command, CancellationToken token) { await command.ExecuteNonQueryAsync(token); return 0; }
+
+	private static int ExecuteNonQuery(MySqlCommand command)
+	{
+		command.ExecuteNonQuery();
+		return 0;
+	}
+
+	private static async Task<int> ExecuteNonQueryAsync(MySqlCommand command, CancellationToken token)
+	{
+		await command.ExecuteNonQueryAsync(token);
+		return 0;
+	}
+
 	private static int ExecuteReader(MySqlCommand command)
 	{
 		using var reader = command.ExecuteReader();
@@ -450,6 +461,6 @@ private static async Task<int> ExecuteReaderAsync(MySqlCommand command, Cancella
 		return value.Value;
 	}
 
-	readonly FakeMySqlServer m_server;
-	readonly MySqlConnectionStringBuilder m_csb;
+	private readonly FakeMySqlServer m_server;
+	private readonly MySqlConnectionStringBuilder m_csb;
 }
diff --git a/tests/MySqlConnector.Tests/ConnectionTests.cs b/tests/MySqlConnector.Tests/ConnectionTests.cs
index 454c00242..abe4cf99c 100644
--- a/tests/MySqlConnector.Tests/ConnectionTests.cs
+++ b/tests/MySqlConnector.Tests/ConnectionTests.cs
@@ -285,6 +285,6 @@ private static async Task WaitForConditionAsync<T>(T expected, Func<T> getValue)
 		Assert.Equal(expected, getValue());
 	}
 
-	readonly FakeMySqlServer m_server;
-	readonly MySqlConnectionStringBuilder m_csb;
+	private readonly FakeMySqlServer m_server;
+	private readonly MySqlConnectionStringBuilder m_csb;
 }
diff --git a/tests/MySqlConnector.Tests/DummyEnum.cs b/tests/MySqlConnector.Tests/DummyEnum.cs
index 627e5e0c1..e0e61ff1b 100644
--- a/tests/MySqlConnector.Tests/DummyEnum.cs
+++ b/tests/MySqlConnector.Tests/DummyEnum.cs
@@ -3,7 +3,7 @@ namespace MySqlConnector.Tests;
 internal enum DummyEnum
 {
 	FirstValue,
-	SecondValue
+	SecondValue,
 }
 
 internal enum DummyByteEnum : byte
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServer.cs b/tests/MySqlConnector.Tests/FakeMySqlServer.cs
index 035e48b51..a5e61d82a 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServer.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServer.cs
@@ -81,10 +81,10 @@ private async Task AcceptConnectionsAsync()
 		}
 	}
 
-	readonly object m_lock;
-	readonly TcpListener m_tcpListener;
-	readonly List<FakeMySqlServerConnection> m_connections;
-	readonly List<Task> m_tasks;
-	CancellationTokenSource m_cts;
-	int m_activeConnections;
+	private readonly object m_lock;
+	private readonly TcpListener m_tcpListener;
+	private readonly List<FakeMySqlServerConnection> m_connections;
+	private readonly List<Task> m_tasks;
+	private CancellationTokenSource m_cts;
+	private int m_activeConnections;
 }
diff --git a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
index f07e421c0..ef117f28e 100644
--- a/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
+++ b/tests/MySqlConnector.Tests/FakeMySqlServerConnection.cs
@@ -107,20 +107,20 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 								var negativeOne = new byte[] { 2, 0x2D, 0x31 };
 								var packets = new[]
 								{
-								new byte[] { 0xFF, 0x25, 0x05, 0x23, 0x37, 0x30, 0x31, 0x30, 0x30 }.Concat(Encoding.ASCII.GetBytes("Query execution was interrupted")).ToArray(), // error
-								[1], // one column
-								[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0], // column definition
-								[0xFE, 0, 0, 2, 0], // EOF
-								data,
-								negativeOne,
-								negativeOne,
-								[0xFE, 0, 0, 10, 0], // EOF, more results exist
-								[1], // one column
-								[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0], // column definition
-								[0xFE, 0, 0, 2, 0], // EOF
-								negativeOne,
-								[0xFE, 0, 0, 2, 0], // EOF
-							};
+									new byte[] { 0xFF, 0x25, 0x05, 0x23, 0x37, 0x30, 0x31, 0x30, 0x30 }.Concat(Encoding.ASCII.GetBytes("Query execution was interrupted")).ToArray(), // error
+									[1], // one column
+									[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0], // column definition
+									[0xFE, 0, 0, 2, 0], // EOF
+									data,
+									negativeOne,
+									negativeOne,
+									[0xFE, 0, 0, 10, 0], // EOF, more results exist
+									[1], // one column
+									[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x5F, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x81, 0, 0, 0, 0], // column definition
+									[0xFE, 0, 0, 2, 0], // EOF
+									negativeOne,
+									[0xFE, 0, 0, 2, 0], // EOF
+								};
 
 								if (bufferOutput)
 								{
@@ -167,15 +167,15 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 							{
 								var packets = new[]
 								{
-								new byte[] { 2 }, // two columns
-								[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x46, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 4, 0x01, 0, 0x1F, 0, 0], // column definition (float)
-								[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x44, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 5, 0x01, 0, 0x1F, 0, 0], // column definition (double)
-								[0xFE, 0, 0, 2, 0], // EOF
-								[3, 0x6e, 0x61, 0x6e, 3, 0x6e, 0x61, 0x6e], // nan
-								[3, 0x69, 0x6e, 0x66, 3, 0x69, 0x6e, 0x66], // inf
-								[4, 0x2d, 0x69, 0x6e, 0x66, 4, 0x2d, 0x69, 0x6e, 0x66], // -inf
-								[0xFE, 0, 0, 2, 0], // EOF
-							};
+									new byte[] { 2 }, // two columns
+									[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x46, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 4, 0x01, 0, 0x1F, 0, 0], // column definition (float)
+									[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x44, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 5, 0x01, 0, 0x1F, 0, 0], // column definition (double)
+									[0xFE, 0, 0, 2, 0], // EOF
+									[3, 0x6e, 0x61, 0x6e, 3, 0x6e, 0x61, 0x6e], // nan
+									[3, 0x69, 0x6e, 0x66, 3, 0x69, 0x6e, 0x66], // inf
+									[4, 0x2d, 0x69, 0x6e, 0x66, 4, 0x2d, 0x69, 0x6e, 0x66], // -inf
+									[0xFE, 0, 0, 2, 0], // EOF
+								};
 								for (var packetIndex = 0; packetIndex < packets.Length; packetIndex++)
 									await SendAsync(stream, packetIndex + 1, x => x.Write(packets[packetIndex]));
 							}
@@ -183,11 +183,11 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
 							{
 								var packets = new[]
 								{
-								new byte[] { 1 }, // one column
-								[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x41, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x01, 0, 0x1F, 0, 0], // column definition (int)
-								[0xFE, 0, 0, 2, 0], // EOF
-								[1, 0x31], // 1
-							};
+									new byte[] { 1 }, // one column
+									[3, 0x64, 0x65, 0x66, 0, 0, 0, 1, 0x41, 0, 0x0c, 0x3f, 0, 1, 0, 0, 0, 3, 0x01, 0, 0x1F, 0, 0], // column definition (int)
+									[0xFE, 0, 0, 2, 0], // EOF
+									[1, 0x31], // 1
+								};
 								for (var packetIndex = 0; packetIndex < packets.Length; packetIndex++)
 									await SendAsync(stream, packetIndex + 1, x => x.Write(packets[packetIndex]));
 								stream.Close();
@@ -308,6 +308,6 @@ private static void WriteError(BinaryWriter writer, string message = "An unknown
 		writer.WriteRaw(message);
 	}
 
-	readonly FakeMySqlServer m_server;
-	readonly int m_connectionId;
+	private readonly FakeMySqlServer m_server;
+	private readonly int m_connectionId;
 }
diff --git a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
index 8bc870e27..4f8b51a63 100644
--- a/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
+++ b/tests/MySqlConnector.Tests/Metrics/IConnectionCreator.cs
@@ -45,7 +45,10 @@ public MySqlConnection OpenConnection()
 	}
 
 	public string PoolName { get; }
-	public void Dispose() { }
+
+	public void Dispose()
+	{
+	}
 
 	private readonly string m_connectionString;
 }
diff --git a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
index 2a3f977f2..51b912a1e 100644
--- a/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
+++ b/tests/MySqlConnector.Tests/Metrics/MetricsTestsBase.cs
@@ -20,7 +20,7 @@ public MetricsTestsBase()
 			{
 				if (instrument.Meter.Name == "MySqlConnector")
 					listener.EnableMeasurementEvents(instrument);
-			}
+			},
 		};
 		m_meterListener.SetMeasurementEventCallback<int>(OnMeasurementRecorded);
 		m_meterListener.SetMeasurementEventCallback<double>(OnMeasurementRecorded);
@@ -124,7 +124,6 @@ private void OnMeasurementRecorded(Instrument instrument, double measurement, Re
 		return (poolName, state);
 	}
 
-
 	private readonly Dictionary<string, int> m_measurements;
 	private readonly Dictionary<string, List<double>> m_timeMeasurements;
 	private readonly MeterListener m_meterListener;
diff --git a/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs b/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
index b602b431f..0601f6e3a 100644
--- a/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlConnectionStringBuilderTests.cs
@@ -158,7 +158,7 @@ public void ParseConnectionString()
 				"ssl mode=verifyca;" +
 				"tls version=Tls12, TLS v1.3;" +
 				"Uid=username;" +
-				"useaffectedrows=true"
+				"useaffectedrows=true",
 		};
 		Assert.True(csb.AllowLoadLocalInfile);
 		Assert.True(csb.AllowPublicKeyRetrieval);
@@ -167,7 +167,7 @@ public void ParseConnectionString()
 		Assert.False(csb.AutoEnlist);
 #if !MYSQL_DATA
 		Assert.Equal(-1, csb.CancellationTimeout);
-		// Connector/NET treats "CertificateFile" (client certificate) and "SslCa" (server CA) as aliases
+		//// Connector/NET treats "CertificateFile" (client certificate) and "SslCa" (server CA) as aliases
 		Assert.Equal("file.pfx", csb.CertificateFile);
 #endif
 		Assert.Equal("Pass2345", csb.CertificatePassword);
diff --git a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
index 8de95ae49..b375b14ae 100644
--- a/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
+++ b/tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj
@@ -16,6 +16,7 @@
     <AssemblyOriginatorKeyFile>..\..\MySqlConnector.snk</AssemblyOriginatorKeyFile>
     <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
     <LangVersion>12.0</LangVersion>
+    <NoWarn>$(NoWarn);SA1021</NoWarn>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(GITHUB_ACTIONS)' == 'true' OR '$(APPVEYOR)' == 'True' OR '$(TF_BUILD)' == 'True' ">
diff --git a/tests/MySqlConnector.Tests/MySqlDateTimeTests.cs b/tests/MySqlConnector.Tests/MySqlDateTimeTests.cs
index 837dd0592..3cb53ead7 100644
--- a/tests/MySqlConnector.Tests/MySqlDateTimeTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlDateTimeTests.cs
@@ -7,7 +7,7 @@ public class MySqlDateTimeTests
 	[Fact]
 	public void NewMySqlDateTimeIsNotValidDateTime()
 	{
-		var msdt = new MySqlDateTime();
+		var msdt = default(MySqlDateTime);
 		Assert.False(msdt.IsValidDateTime);
 	}
 
@@ -52,7 +52,7 @@ public void GetDateTime()
 	[Fact]
 	public void GetDateTimeForInvalidDate()
 	{
-		var msdt = new MySqlDateTime();
+		var msdt = default(MySqlDateTime);
 		Assert.False(msdt.IsValidDateTime);
 		Assert.Throws<MySqlConversionException>(() => msdt.GetDateTime());
 	}
@@ -60,7 +60,7 @@ public void GetDateTimeForInvalidDate()
 	[Fact]
 	public void SetMicrosecond()
 	{
-		var msdt = new MySqlDateTime();
+		var msdt = default(MySqlDateTime);
 		Assert.Equal(0, msdt.Microsecond);
 		msdt.Microsecond = 123456;
 		Assert.Equal(123, msdt.Millisecond);
@@ -93,7 +93,7 @@ public void ChangeTypeToDateTime()
 	[Fact]
 	public void NotConvertibleToDateTime()
 	{
-		IConvertible convertible = new MySqlDateTime();
+		IConvertible convertible = default(MySqlDateTime);
 #if !MYSQL_DATA
 		Assert.Throws<InvalidCastException>(() => convertible.ToDateTime(CultureInfo.InvariantCulture));
 #else
@@ -104,7 +104,7 @@ public void NotConvertibleToDateTime()
 	[Fact]
 	public void NotConvertToDateTime()
 	{
-		object obj = new MySqlDateTime();
+		object obj = default(MySqlDateTime);
 #if !MYSQL_DATA
 		Assert.Throws<InvalidCastException>(() => Convert.ToDateTime(obj));
 #else
@@ -115,7 +115,7 @@ public void NotConvertToDateTime()
 	[Fact]
 	public void NotChangeTypeToDateTime()
 	{
-		object obj = new MySqlDateTime();
+		object obj = default(MySqlDateTime);
 #if !MYSQL_DATA
 		Assert.Throws<InvalidCastException>(() => Convert.ChangeType(obj, TypeCode.DateTime));
 #else
@@ -134,7 +134,7 @@ public void ValidDateTimeConvertibleToString()
 	[Fact]
 	public void InvalidDateTimeConvertibleToString()
 	{
-		IConvertible convertible = new MySqlDateTime();
+		IConvertible convertible = default(MySqlDateTime);
 		Assert.Equal("0000-00-00", convertible.ToString(CultureInfo.InvariantCulture));
 	}
 #endif
@@ -255,6 +255,6 @@ public void Equal()
 	}
 #endif
 
-	static readonly MySqlDateTime s_mySqlDateTime = new(2018, 6, 9, 12, 34, 56, 123456);
-	static readonly DateTime s_dateTime = new DateTime(2018, 6, 9, 12, 34, 56, 123).AddTicks(4560);
+	private static readonly MySqlDateTime s_mySqlDateTime = new(2018, 6, 9, 12, 34, 56, 123456);
+	private static readonly DateTime s_dateTime = new DateTime(2018, 6, 9, 12, 34, 56, 123).AddTicks(4560);
 }
diff --git a/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs b/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs
index 603cda983..fb289e30c 100644
--- a/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterCollectionNameToIndexTests.cs
@@ -124,5 +124,5 @@ public void ChangeParameter()
 		Assert.Equal(1, m_collection.NormalizedIndexOf("D"));
 	}
 
-	readonly MySqlParameterCollection m_collection;
+	private readonly MySqlParameterCollection m_collection;
 }
diff --git a/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs b/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs
index 708de4aa3..23e816e66 100644
--- a/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterCollectionTests.cs
@@ -19,5 +19,5 @@ public MySqlParameterCollectionTests()
 	[Fact]
 	public void RemoveAtEnd() => Assert.Throws<ArgumentOutOfRangeException>(() => m_collection.RemoveAt(0));
 
-	readonly MySqlParameterCollection m_collection;
+	private readonly MySqlParameterCollection m_collection;
 }
diff --git a/tests/MySqlConnector.Tests/MySqlParameterTests.cs b/tests/MySqlConnector.Tests/MySqlParameterTests.cs
index 3d2309e6b..710c699cf 100644
--- a/tests/MySqlConnector.Tests/MySqlParameterTests.cs
+++ b/tests/MySqlConnector.Tests/MySqlParameterTests.cs
@@ -319,7 +319,6 @@ public void SetValueToByteArrayInfersType()
 		Assert.Equal(MySqlDbType.Blob, parameter.MySqlDbType);
 	}
 
-
 	[Fact]
 	public void SetValueDoesNotInferType()
 	{
diff --git a/tests/MySqlConnector.Tests/TypeMapperTests.cs b/tests/MySqlConnector.Tests/TypeMapperTests.cs
index 6aa1ad6b9..677e75e1d 100644
--- a/tests/MySqlConnector.Tests/TypeMapperTests.cs
+++ b/tests/MySqlConnector.Tests/TypeMapperTests.cs
@@ -36,13 +36,13 @@ public void DbTypeMappingTest(Type clrType, DbType dbType)
 	[InlineData((byte) 1, DbType.Int16, (short) 1)]
 	[InlineData((short) 1, DbType.UInt16, (ushort) 1)]
 	[InlineData((ushort) 1, DbType.Int32, 1)]
-	[InlineData(1, DbType.UInt32, (uint) 1)]
-	[InlineData((uint) 1, DbType.Int64, (long) 1)]
-	[InlineData((long) 1, DbType.UInt64, (ulong) 1)]
-	[InlineData((ulong) 1, DbType.String, "1")]
-	[InlineData((ulong) 1, DbType.StringFixedLength, "1")]
-	[InlineData((ulong) 1, DbType.AnsiString, "1")]
-	[InlineData((ulong) 1, DbType.AnsiStringFixedLength, "1")]
+	[InlineData(1, DbType.UInt32, 1U)]
+	[InlineData(1U, DbType.Int64, 1L)]
+	[InlineData(1L, DbType.UInt64, 1UL)]
+	[InlineData(1UL, DbType.String, "1")]
+	[InlineData(1UL, DbType.StringFixedLength, "1")]
+	[InlineData(1UL, DbType.AnsiString, "1")]
+	[InlineData(1UL, DbType.AnsiStringFixedLength, "1")]
 	public void ConversionTest(object original, DbType dbType, object expected)
 	{
 		Assert.Equal(expected, TypeMapper.Instance.GetDbTypeMapping(dbType).DoConversion(original));
diff --git a/tests/MySqlConnector.Tests/UtilityTests.cs b/tests/MySqlConnector.Tests/UtilityTests.cs
index 8040c2284..773a4f46d 100644
--- a/tests/MySqlConnector.Tests/UtilityTests.cs
+++ b/tests/MySqlConnector.Tests/UtilityTests.cs
@@ -127,7 +127,7 @@ public void DecodePrivateKey(string pre, string post)
 		Assert.Equal(s_iq, parameters.InverseQ);
 	}
 
-	const string c_publicKey = @"-----BEGIN PUBLIC KEY-----
+	private const string c_publicKey = @"-----BEGIN PUBLIC KEY-----
 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwmlDX62hQAQNvSJZ/HAO
 UjCbAiEPQquEyPpxjqDxyx1fVxL93U1au50xGk4sad4OH+GSZCChqj3kvwJhXc52
 iHdBzjQbucGYlC1wLNMc1F+H89vMjEq1ZexsRDWQSrgL1I6i9Mn5NFgS563yPBpO
@@ -137,7 +137,7 @@ public void DecodePrivateKey(string pre, string post)
 LwIDAQAB
 -----END PUBLIC KEY-----";
 
-	const string c_privateKey = @"-----BEGIN RSA PRIVATE KEY-----
+	private const string c_privateKey = @"-----BEGIN RSA PRIVATE KEY-----
 MIIEpAIBAAKCAQEAwmlDX62hQAQNvSJZ/HAOUjCbAiEPQquEyPpxjqDxyx1fVxL9
 3U1au50xGk4sad4OH+GSZCChqj3kvwJhXc52iHdBzjQbucGYlC1wLNMc1F+H89vM
 jEq1ZexsRDWQSrgL1I6i9Mn5NFgS563yPBpOyfYyGWCrL5w7yI+we3MCwy2q8JIO
@@ -165,12 +165,12 @@ public void DecodePrivateKey(string pre, string post)
 joMn7CFmwV9EYjPTxwkByKs5a8AbNYJDSfNCa3KoHTFjOvVMPec90Q==
 -----END RSA PRIVATE KEY-----";
 
-	static readonly byte[] s_modulus = [0xC2, 0x69, 0x43, 0x5F, 0xAD, 0xA1, 0x40, 0x04, 0x0D, 0xBD, 0x22, 0x59, 0xFC, 0x70, 0x0E, 0x52, 0x30, 0x9B, 0x02, 0x21, 0x0F, 0x42, 0xAB, 0x84, 0xC8, 0xFA, 0x71, 0x8E, 0xA0, 0xF1, 0xCB, 0x1D, 0x5F, 0x57, 0x12, 0xFD, 0xDD, 0x4D, 0x5A, 0xBB, 0x9D, 0x31, 0x1A, 0x4E, 0x2C, 0x69, 0xDE, 0x0E, 0x1F, 0xE1, 0x92, 0x64, 0x20, 0xA1, 0xAA, 0x3D, 0xE4, 0xBF, 0x02, 0x61, 0x5D, 0xCE, 0x76, 0x88, 0x77, 0x41, 0xCE, 0x34, 0x1B, 0xB9, 0xC1, 0x98, 0x94, 0x2D, 0x70, 0x2C, 0xD3, 0x1C, 0xD4, 0x5F, 0x87, 0xF3, 0xDB, 0xCC, 0x8C, 0x4A, 0xB5, 0x65, 0xEC, 0x6C, 0x44, 0x35, 0x90, 0x4A, 0xB8, 0x0B, 0xD4, 0x8E, 0xA2, 0xF4, 0xC9, 0xF9, 0x34, 0x58, 0x12, 0xE7, 0xAD, 0xF2, 0x3C, 0x1A, 0x4E, 0xC9, 0xF6, 0x32, 0x19, 0x60, 0xAB, 0x2F, 0x9C, 0x3B, 0xC8, 0x8F, 0xB0, 0x7B, 0x73, 0x02, 0xC3, 0x2D, 0xAA, 0xF0, 0x92, 0x0E, 0xC9, 0x97, 0xA0, 0xCE, 0x1E, 0xFA, 0x5B, 0xE7, 0xF7, 0x17, 0xDA, 0x45, 0x76, 0x77, 0xE3, 0x40, 0xEF, 0xAF, 0x8C, 0xFC, 0x8D, 0xBA, 0x99, 0xD3, 0x10, 0x3B, 0x58, 0x6C, 0x68, 0xC5, 0x61, 0xAA, 0x98, 0x21, 0x33, 0x67, 0x71, 0x9F, 0x1C, 0x2F, 0x52, 0xD8, 0x57, 0x0B, 0x94, 0x5D, 0x6D, 0x0F, 0x5B, 0xC6, 0xDF, 0x94, 0x47, 0x45, 0x77, 0xF0, 0x6B, 0xF1, 0xFC, 0xC2, 0x80, 0xAE, 0x08, 0x55, 0xD4, 0xA6, 0xE8, 0xE5, 0x4F, 0xCD, 0x26, 0x5A, 0xB5, 0xAC, 0x38, 0xDA, 0x37, 0x17, 0xDA, 0xD3, 0x53, 0xF1, 0xFD, 0xCF, 0xDC, 0x29, 0x40, 0xBC, 0xBD, 0x55, 0xDE, 0x3E, 0x04, 0x74, 0x21, 0xF2, 0x60, 0xDA, 0xCA, 0x26, 0xAC, 0x19, 0xEE, 0x74, 0xED, 0x8E, 0xA0, 0x41, 0x3E, 0xA3, 0x66, 0x36, 0x27, 0x11, 0xDD, 0xE3, 0x3C, 0xB2, 0xE1, 0xA3, 0x31, 0x6C, 0xA6, 0x2F];
-	static readonly byte[] s_exponent = [0x01, 0x00, 0x01];
-	static readonly byte[] s_d = [0x11, 0xB3, 0x61, 0xD6, 0x01, 0x4A, 0x10, 0x39, 0x0E, 0x43, 0x2D, 0x30, 0x82, 0x42, 0x75, 0x9B, 0x58, 0x1F, 0x94, 0xE7, 0x0B, 0xAB, 0xA7, 0x50, 0x34, 0xB8, 0x50, 0xC4, 0x8A, 0xB4, 0xD9, 0x28, 0x78, 0x78, 0xC6, 0x1A, 0xE5, 0x1E, 0x58, 0xE7, 0x82, 0x1D, 0x69, 0x66, 0xBA, 0xB6, 0x7C, 0xE8, 0x4E, 0x50, 0xCC, 0x72, 0x5F, 0x62, 0x38, 0xCC, 0xDF, 0xD9, 0xE0, 0x4E, 0x9B, 0x2A, 0xE4, 0x31, 0xF2, 0xA1, 0xC4, 0x33, 0x8C, 0x5E, 0xB2, 0x58, 0xC1, 0x0D, 0x0E, 0x0C, 0x96, 0xC0, 0x26, 0x39, 0xF8, 0x77, 0xE4, 0x88, 0x50, 0x31, 0xB1, 0x28, 0x71, 0x89, 0x3E, 0x00, 0x9E, 0x7D, 0x9F, 0x1A, 0x3C, 0xA5, 0x2C, 0x03, 0x1B, 0xAC, 0x9B, 0xEE, 0x74, 0xF2, 0x31, 0x2D, 0x3F, 0xF9, 0xA9, 0x38, 0xCC, 0x38, 0xAD, 0x84, 0xD6, 0xB1, 0x44, 0x09, 0x51, 0x6E, 0x7B, 0xC4, 0x11, 0xA9, 0xAB, 0xB9, 0x80, 0xD4, 0xA8, 0x87, 0xEF, 0x2C, 0x76, 0x51, 0x64, 0x3E, 0xE5, 0x37, 0x7F, 0xF1, 0x26, 0xD7, 0xC0, 0x80, 0xEB, 0x96, 0xA9, 0x1A, 0x4D, 0xB8, 0xAF, 0xC9, 0xE5, 0xA2, 0x43, 0x7A, 0x6C, 0xEB, 0x9F, 0xF3, 0x8A, 0x72, 0xDD, 0xE7, 0xD2, 0xEB, 0x60, 0xD6, 0x01, 0xDD, 0x25, 0x40, 0x8B, 0x71, 0xBA, 0x0D, 0x49, 0x55, 0x3A, 0x1D, 0x9B, 0x60, 0x71, 0x2C, 0x37, 0xAF, 0xFB, 0xD9, 0x3C, 0xEF, 0x77, 0xF1, 0x43, 0xE9, 0x7F, 0xE9, 0x2C, 0xDC, 0x76, 0xDC, 0x34, 0x48, 0xFF, 0x4F, 0xB4, 0xBE, 0x93, 0x82, 0xC3, 0x14, 0x42, 0xC6, 0xE4, 0x01, 0xF5, 0xB0, 0xDE, 0xED, 0x64, 0x82, 0xB9, 0x1C, 0x17, 0x0A, 0x28, 0x89, 0x66, 0xC2, 0x45, 0xA3, 0x4E, 0xBC, 0x61, 0xB4, 0xD7, 0xCC, 0x47, 0xDC, 0x86, 0x8E, 0x1F, 0xEE, 0x8E, 0x0D, 0x0F, 0x51, 0xB1, 0xAC, 0x62, 0xEF, 0xDC, 0x6D, 0x90, 0x81];
-	static readonly byte[] s_p = [0xFA, 0x63, 0x13, 0x19, 0xF3, 0x13, 0x7A, 0xC0, 0x70, 0x67, 0xAD, 0xE8, 0x05, 0x26, 0x70, 0x68, 0x9F, 0x30, 0xFB, 0x06, 0x9E, 0x45, 0xFF, 0x38, 0xF2, 0x56, 0xA3, 0x35, 0x4F, 0xAE, 0x21, 0x20, 0x94, 0x44, 0x80, 0x42, 0x61, 0x39, 0xC3, 0xEB, 0xA9, 0x8F, 0xD2, 0x61, 0x88, 0xE9, 0x6A, 0x62, 0x42, 0xF0, 0x18, 0x55, 0xDE, 0x50, 0x73, 0x1E, 0xFA, 0x70, 0xE5, 0x25, 0x73, 0x28, 0x6F, 0x29, 0x40, 0xE0, 0x09, 0x1A, 0xB6, 0x11, 0xF4, 0xB1, 0xEC, 0x89, 0x25, 0x66, 0x7D, 0x41, 0xC7, 0x53, 0xFA, 0x3A, 0x58, 0xF0, 0xB2, 0x27, 0x73, 0xE8, 0x63, 0x49, 0x06, 0x15, 0xF1, 0x4C, 0x35, 0x9D, 0x33, 0xE5, 0x28, 0x3E, 0xBF, 0x2F, 0x9F, 0xE4, 0x33, 0xE9, 0xCE, 0x6F, 0x91, 0x18, 0xAD, 0x95, 0xD4, 0xF5, 0x12, 0xD6, 0x92, 0xBD, 0xDA, 0xDF, 0x64, 0xA8, 0xB4, 0xEC, 0x7E, 0xD8, 0x1A, 0x41];
-	static readonly byte[] s_q = [0xC6, 0xC4, 0xF4, 0x19, 0x1A, 0x74, 0xFB, 0xA1, 0x07, 0x3D, 0x8A, 0x80, 0x9A, 0x6F, 0xC5, 0x60, 0xCB, 0x68, 0x32, 0x14, 0xCA, 0x9D, 0x21, 0x59, 0x7C, 0xA5, 0xAC, 0x0B, 0x68, 0x0E, 0x8C, 0xE2, 0x82, 0x77, 0x6C, 0xBC, 0xC2, 0xA6, 0xB3, 0xF2, 0x8B, 0xE4, 0xC8, 0xDF, 0xF9, 0x7B, 0x92, 0x82, 0xE6, 0x20, 0x84, 0xCB, 0xE0, 0x46, 0x44, 0x0A, 0xB4, 0xD7, 0x48, 0x43, 0x29, 0x78, 0xE7, 0xCA, 0xE4, 0x8F, 0xEC, 0x52, 0x2A, 0x6F, 0xDA, 0x58, 0x8D, 0xAA, 0xEB, 0xC0, 0x5E, 0x4F, 0xC1, 0x70, 0x9A, 0x97, 0xCE, 0xE4, 0xFF, 0x70, 0xEC, 0xA1, 0x2A, 0xED, 0xD8, 0x7A, 0xA1, 0xA5, 0x1C, 0x79, 0x2A, 0x8E, 0xBC, 0x74, 0xA3, 0x45, 0x20, 0x27, 0x14, 0xE1, 0xB9, 0x16, 0x80, 0x26, 0x81, 0xC2, 0x17, 0x26, 0xBB, 0x83, 0x04, 0x88, 0xFB, 0x0B, 0xB2, 0x06, 0x49, 0x88, 0x9A, 0xC0, 0x44, 0x6F];
-	static readonly byte[] s_dp = [0xA9, 0x1C, 0xD1, 0x81, 0xED, 0x53, 0x72, 0xCD, 0x17, 0x1E, 0x6F, 0xAF, 0x0E, 0x0B, 0x70, 0x50, 0xB9, 0x74, 0x73, 0x97, 0x7C, 0xBE, 0xFA, 0x2A, 0x94, 0x43, 0x3F, 0xE8, 0x79, 0xF0, 0xBA, 0x1F, 0x2F, 0x05, 0x35, 0x3B, 0xA0, 0x3F, 0x4B, 0xBC, 0x97, 0xC1, 0xB3, 0xEE, 0x7C, 0x72, 0x6A, 0x90, 0x03, 0x75, 0xF6, 0x79, 0xC8, 0xB5, 0xBD, 0x8B, 0x66, 0xCB, 0x69, 0x79, 0x69, 0xD6, 0x1D, 0x7E, 0x57, 0x7A, 0xF9, 0xAB, 0x38, 0xDD, 0xD7, 0xCF, 0x74, 0x5B, 0x0D, 0x0F, 0xB3, 0xFA, 0x01, 0x8F, 0x0E, 0xAA, 0xE9, 0xF7, 0x67, 0xDC, 0x8D, 0xC4, 0x9F, 0x8A, 0x6A, 0xB9, 0xD7, 0x48, 0x09, 0xCE, 0x2B, 0x86, 0xD3, 0x2F, 0xCE, 0x21, 0x74, 0x74, 0xC5, 0x3B, 0x5D, 0x5A, 0xAD, 0x3D, 0x65, 0xC0, 0x95, 0x1D, 0x11, 0xA4, 0x2B, 0x68, 0x5F, 0xB4, 0x12, 0x60, 0x80, 0x12, 0x5B, 0x79, 0xCA, 0x01];
-	static readonly byte[] s_dq = [0x99, 0x7F, 0xF2, 0x0E, 0x0C, 0xDB, 0x78, 0x93, 0x63, 0x97, 0x08, 0x05, 0xC6, 0xBE, 0x38, 0x4C, 0x46, 0xE9, 0x21, 0x7B, 0xE2, 0xF8, 0xB3, 0x8F, 0x7A, 0xEE, 0x2A, 0x4D, 0xE8, 0xBF, 0x0B, 0xD7, 0xC4, 0xEF, 0x5B, 0x3E, 0xEE, 0x87, 0x74, 0x82, 0x03, 0xBC, 0xDB, 0xCF, 0xF3, 0xC7, 0x95, 0x5E, 0x97, 0x3F, 0x57, 0xAE, 0x66, 0x75, 0x7D, 0x08, 0x53, 0x9D, 0xC9, 0x85, 0x6C, 0x5A, 0x9D, 0x8E, 0x97, 0x31, 0xFA, 0x4B, 0x7D, 0xD0, 0x41, 0x6B, 0x8F, 0x84, 0x06, 0x69, 0xD9, 0xAB, 0x77, 0xF4, 0x70, 0xBE, 0x79, 0x9D, 0x69, 0x13, 0x18, 0xFA, 0x61, 0xBF, 0xAA, 0x25, 0x11, 0xFE, 0x03, 0x5E, 0x23, 0x1A, 0x75, 0xB9, 0x11, 0x79, 0x49, 0x9A, 0x44, 0xCB, 0x08, 0x77, 0xB2, 0xE1, 0x89, 0xE5, 0x8C, 0xA1, 0x71, 0xBF, 0x7D, 0x5A, 0x17, 0x9D, 0x71, 0x8E, 0xDF, 0x6C, 0xAA, 0x2D, 0x7E, 0xEB];
-	static readonly byte[] s_iq = [0x2F, 0x91, 0x84, 0x58, 0xAB, 0x47, 0xAE, 0xE6, 0x92, 0xBC, 0xE4, 0x13, 0xBA, 0x9C, 0xDD, 0x66, 0xFF, 0x91, 0xA8, 0x94, 0x41, 0xBF, 0xD6, 0x54, 0x1B, 0x6D, 0x92, 0xC0, 0x67, 0xC6, 0x08, 0x23, 0xA4, 0x6D, 0x24, 0xA1, 0xC1, 0xBD, 0xCB, 0xC5, 0xAF, 0x4E, 0x96, 0x39, 0x46, 0x4A, 0x44, 0xD7, 0x10, 0x8F, 0x35, 0x76, 0xF5, 0xCB, 0x86, 0xE2, 0x9A, 0xAD, 0x32, 0xCD, 0x2C, 0xC0, 0xB8, 0x4A, 0x31, 0xB0, 0xAC, 0xF8, 0xFA, 0xAB, 0xEB, 0x0E, 0x8A, 0x7D, 0x92, 0x0D, 0xB5, 0x5C, 0x8B, 0x36, 0xD6, 0xC3, 0x56, 0x8F, 0x6B, 0xC0, 0x54, 0x5C, 0x8E, 0x83, 0x27, 0xEC, 0x21, 0x66, 0xC1, 0x5F, 0x44, 0x62, 0x33, 0xD3, 0xC7, 0x09, 0x01, 0xC8, 0xAB, 0x39, 0x6B, 0xC0, 0x1B, 0x35, 0x82, 0x43, 0x49, 0xF3, 0x42, 0x6B, 0x72, 0xA8, 0x1D, 0x31, 0x63, 0x3A, 0xF5, 0x4C, 0x3D, 0xE7, 0x3D, 0xD1];
+	private static readonly byte[] s_modulus = [0xC2, 0x69, 0x43, 0x5F, 0xAD, 0xA1, 0x40, 0x04, 0x0D, 0xBD, 0x22, 0x59, 0xFC, 0x70, 0x0E, 0x52, 0x30, 0x9B, 0x02, 0x21, 0x0F, 0x42, 0xAB, 0x84, 0xC8, 0xFA, 0x71, 0x8E, 0xA0, 0xF1, 0xCB, 0x1D, 0x5F, 0x57, 0x12, 0xFD, 0xDD, 0x4D, 0x5A, 0xBB, 0x9D, 0x31, 0x1A, 0x4E, 0x2C, 0x69, 0xDE, 0x0E, 0x1F, 0xE1, 0x92, 0x64, 0x20, 0xA1, 0xAA, 0x3D, 0xE4, 0xBF, 0x02, 0x61, 0x5D, 0xCE, 0x76, 0x88, 0x77, 0x41, 0xCE, 0x34, 0x1B, 0xB9, 0xC1, 0x98, 0x94, 0x2D, 0x70, 0x2C, 0xD3, 0x1C, 0xD4, 0x5F, 0x87, 0xF3, 0xDB, 0xCC, 0x8C, 0x4A, 0xB5, 0x65, 0xEC, 0x6C, 0x44, 0x35, 0x90, 0x4A, 0xB8, 0x0B, 0xD4, 0x8E, 0xA2, 0xF4, 0xC9, 0xF9, 0x34, 0x58, 0x12, 0xE7, 0xAD, 0xF2, 0x3C, 0x1A, 0x4E, 0xC9, 0xF6, 0x32, 0x19, 0x60, 0xAB, 0x2F, 0x9C, 0x3B, 0xC8, 0x8F, 0xB0, 0x7B, 0x73, 0x02, 0xC3, 0x2D, 0xAA, 0xF0, 0x92, 0x0E, 0xC9, 0x97, 0xA0, 0xCE, 0x1E, 0xFA, 0x5B, 0xE7, 0xF7, 0x17, 0xDA, 0x45, 0x76, 0x77, 0xE3, 0x40, 0xEF, 0xAF, 0x8C, 0xFC, 0x8D, 0xBA, 0x99, 0xD3, 0x10, 0x3B, 0x58, 0x6C, 0x68, 0xC5, 0x61, 0xAA, 0x98, 0x21, 0x33, 0x67, 0x71, 0x9F, 0x1C, 0x2F, 0x52, 0xD8, 0x57, 0x0B, 0x94, 0x5D, 0x6D, 0x0F, 0x5B, 0xC6, 0xDF, 0x94, 0x47, 0x45, 0x77, 0xF0, 0x6B, 0xF1, 0xFC, 0xC2, 0x80, 0xAE, 0x08, 0x55, 0xD4, 0xA6, 0xE8, 0xE5, 0x4F, 0xCD, 0x26, 0x5A, 0xB5, 0xAC, 0x38, 0xDA, 0x37, 0x17, 0xDA, 0xD3, 0x53, 0xF1, 0xFD, 0xCF, 0xDC, 0x29, 0x40, 0xBC, 0xBD, 0x55, 0xDE, 0x3E, 0x04, 0x74, 0x21, 0xF2, 0x60, 0xDA, 0xCA, 0x26, 0xAC, 0x19, 0xEE, 0x74, 0xED, 0x8E, 0xA0, 0x41, 0x3E, 0xA3, 0x66, 0x36, 0x27, 0x11, 0xDD, 0xE3, 0x3C, 0xB2, 0xE1, 0xA3, 0x31, 0x6C, 0xA6, 0x2F];
+	private static readonly byte[] s_exponent = [0x01, 0x00, 0x01];
+	private static readonly byte[] s_d = [0x11, 0xB3, 0x61, 0xD6, 0x01, 0x4A, 0x10, 0x39, 0x0E, 0x43, 0x2D, 0x30, 0x82, 0x42, 0x75, 0x9B, 0x58, 0x1F, 0x94, 0xE7, 0x0B, 0xAB, 0xA7, 0x50, 0x34, 0xB8, 0x50, 0xC4, 0x8A, 0xB4, 0xD9, 0x28, 0x78, 0x78, 0xC6, 0x1A, 0xE5, 0x1E, 0x58, 0xE7, 0x82, 0x1D, 0x69, 0x66, 0xBA, 0xB6, 0x7C, 0xE8, 0x4E, 0x50, 0xCC, 0x72, 0x5F, 0x62, 0x38, 0xCC, 0xDF, 0xD9, 0xE0, 0x4E, 0x9B, 0x2A, 0xE4, 0x31, 0xF2, 0xA1, 0xC4, 0x33, 0x8C, 0x5E, 0xB2, 0x58, 0xC1, 0x0D, 0x0E, 0x0C, 0x96, 0xC0, 0x26, 0x39, 0xF8, 0x77, 0xE4, 0x88, 0x50, 0x31, 0xB1, 0x28, 0x71, 0x89, 0x3E, 0x00, 0x9E, 0x7D, 0x9F, 0x1A, 0x3C, 0xA5, 0x2C, 0x03, 0x1B, 0xAC, 0x9B, 0xEE, 0x74, 0xF2, 0x31, 0x2D, 0x3F, 0xF9, 0xA9, 0x38, 0xCC, 0x38, 0xAD, 0x84, 0xD6, 0xB1, 0x44, 0x09, 0x51, 0x6E, 0x7B, 0xC4, 0x11, 0xA9, 0xAB, 0xB9, 0x80, 0xD4, 0xA8, 0x87, 0xEF, 0x2C, 0x76, 0x51, 0x64, 0x3E, 0xE5, 0x37, 0x7F, 0xF1, 0x26, 0xD7, 0xC0, 0x80, 0xEB, 0x96, 0xA9, 0x1A, 0x4D, 0xB8, 0xAF, 0xC9, 0xE5, 0xA2, 0x43, 0x7A, 0x6C, 0xEB, 0x9F, 0xF3, 0x8A, 0x72, 0xDD, 0xE7, 0xD2, 0xEB, 0x60, 0xD6, 0x01, 0xDD, 0x25, 0x40, 0x8B, 0x71, 0xBA, 0x0D, 0x49, 0x55, 0x3A, 0x1D, 0x9B, 0x60, 0x71, 0x2C, 0x37, 0xAF, 0xFB, 0xD9, 0x3C, 0xEF, 0x77, 0xF1, 0x43, 0xE9, 0x7F, 0xE9, 0x2C, 0xDC, 0x76, 0xDC, 0x34, 0x48, 0xFF, 0x4F, 0xB4, 0xBE, 0x93, 0x82, 0xC3, 0x14, 0x42, 0xC6, 0xE4, 0x01, 0xF5, 0xB0, 0xDE, 0xED, 0x64, 0x82, 0xB9, 0x1C, 0x17, 0x0A, 0x28, 0x89, 0x66, 0xC2, 0x45, 0xA3, 0x4E, 0xBC, 0x61, 0xB4, 0xD7, 0xCC, 0x47, 0xDC, 0x86, 0x8E, 0x1F, 0xEE, 0x8E, 0x0D, 0x0F, 0x51, 0xB1, 0xAC, 0x62, 0xEF, 0xDC, 0x6D, 0x90, 0x81];
+	private static readonly byte[] s_p = [0xFA, 0x63, 0x13, 0x19, 0xF3, 0x13, 0x7A, 0xC0, 0x70, 0x67, 0xAD, 0xE8, 0x05, 0x26, 0x70, 0x68, 0x9F, 0x30, 0xFB, 0x06, 0x9E, 0x45, 0xFF, 0x38, 0xF2, 0x56, 0xA3, 0x35, 0x4F, 0xAE, 0x21, 0x20, 0x94, 0x44, 0x80, 0x42, 0x61, 0x39, 0xC3, 0xEB, 0xA9, 0x8F, 0xD2, 0x61, 0x88, 0xE9, 0x6A, 0x62, 0x42, 0xF0, 0x18, 0x55, 0xDE, 0x50, 0x73, 0x1E, 0xFA, 0x70, 0xE5, 0x25, 0x73, 0x28, 0x6F, 0x29, 0x40, 0xE0, 0x09, 0x1A, 0xB6, 0x11, 0xF4, 0xB1, 0xEC, 0x89, 0x25, 0x66, 0x7D, 0x41, 0xC7, 0x53, 0xFA, 0x3A, 0x58, 0xF0, 0xB2, 0x27, 0x73, 0xE8, 0x63, 0x49, 0x06, 0x15, 0xF1, 0x4C, 0x35, 0x9D, 0x33, 0xE5, 0x28, 0x3E, 0xBF, 0x2F, 0x9F, 0xE4, 0x33, 0xE9, 0xCE, 0x6F, 0x91, 0x18, 0xAD, 0x95, 0xD4, 0xF5, 0x12, 0xD6, 0x92, 0xBD, 0xDA, 0xDF, 0x64, 0xA8, 0xB4, 0xEC, 0x7E, 0xD8, 0x1A, 0x41];
+	private static readonly byte[] s_q = [0xC6, 0xC4, 0xF4, 0x19, 0x1A, 0x74, 0xFB, 0xA1, 0x07, 0x3D, 0x8A, 0x80, 0x9A, 0x6F, 0xC5, 0x60, 0xCB, 0x68, 0x32, 0x14, 0xCA, 0x9D, 0x21, 0x59, 0x7C, 0xA5, 0xAC, 0x0B, 0x68, 0x0E, 0x8C, 0xE2, 0x82, 0x77, 0x6C, 0xBC, 0xC2, 0xA6, 0xB3, 0xF2, 0x8B, 0xE4, 0xC8, 0xDF, 0xF9, 0x7B, 0x92, 0x82, 0xE6, 0x20, 0x84, 0xCB, 0xE0, 0x46, 0x44, 0x0A, 0xB4, 0xD7, 0x48, 0x43, 0x29, 0x78, 0xE7, 0xCA, 0xE4, 0x8F, 0xEC, 0x52, 0x2A, 0x6F, 0xDA, 0x58, 0x8D, 0xAA, 0xEB, 0xC0, 0x5E, 0x4F, 0xC1, 0x70, 0x9A, 0x97, 0xCE, 0xE4, 0xFF, 0x70, 0xEC, 0xA1, 0x2A, 0xED, 0xD8, 0x7A, 0xA1, 0xA5, 0x1C, 0x79, 0x2A, 0x8E, 0xBC, 0x74, 0xA3, 0x45, 0x20, 0x27, 0x14, 0xE1, 0xB9, 0x16, 0x80, 0x26, 0x81, 0xC2, 0x17, 0x26, 0xBB, 0x83, 0x04, 0x88, 0xFB, 0x0B, 0xB2, 0x06, 0x49, 0x88, 0x9A, 0xC0, 0x44, 0x6F];
+	private static readonly byte[] s_dp = [0xA9, 0x1C, 0xD1, 0x81, 0xED, 0x53, 0x72, 0xCD, 0x17, 0x1E, 0x6F, 0xAF, 0x0E, 0x0B, 0x70, 0x50, 0xB9, 0x74, 0x73, 0x97, 0x7C, 0xBE, 0xFA, 0x2A, 0x94, 0x43, 0x3F, 0xE8, 0x79, 0xF0, 0xBA, 0x1F, 0x2F, 0x05, 0x35, 0x3B, 0xA0, 0x3F, 0x4B, 0xBC, 0x97, 0xC1, 0xB3, 0xEE, 0x7C, 0x72, 0x6A, 0x90, 0x03, 0x75, 0xF6, 0x79, 0xC8, 0xB5, 0xBD, 0x8B, 0x66, 0xCB, 0x69, 0x79, 0x69, 0xD6, 0x1D, 0x7E, 0x57, 0x7A, 0xF9, 0xAB, 0x38, 0xDD, 0xD7, 0xCF, 0x74, 0x5B, 0x0D, 0x0F, 0xB3, 0xFA, 0x01, 0x8F, 0x0E, 0xAA, 0xE9, 0xF7, 0x67, 0xDC, 0x8D, 0xC4, 0x9F, 0x8A, 0x6A, 0xB9, 0xD7, 0x48, 0x09, 0xCE, 0x2B, 0x86, 0xD3, 0x2F, 0xCE, 0x21, 0x74, 0x74, 0xC5, 0x3B, 0x5D, 0x5A, 0xAD, 0x3D, 0x65, 0xC0, 0x95, 0x1D, 0x11, 0xA4, 0x2B, 0x68, 0x5F, 0xB4, 0x12, 0x60, 0x80, 0x12, 0x5B, 0x79, 0xCA, 0x01];
+	private static readonly byte[] s_dq = [0x99, 0x7F, 0xF2, 0x0E, 0x0C, 0xDB, 0x78, 0x93, 0x63, 0x97, 0x08, 0x05, 0xC6, 0xBE, 0x38, 0x4C, 0x46, 0xE9, 0x21, 0x7B, 0xE2, 0xF8, 0xB3, 0x8F, 0x7A, 0xEE, 0x2A, 0x4D, 0xE8, 0xBF, 0x0B, 0xD7, 0xC4, 0xEF, 0x5B, 0x3E, 0xEE, 0x87, 0x74, 0x82, 0x03, 0xBC, 0xDB, 0xCF, 0xF3, 0xC7, 0x95, 0x5E, 0x97, 0x3F, 0x57, 0xAE, 0x66, 0x75, 0x7D, 0x08, 0x53, 0x9D, 0xC9, 0x85, 0x6C, 0x5A, 0x9D, 0x8E, 0x97, 0x31, 0xFA, 0x4B, 0x7D, 0xD0, 0x41, 0x6B, 0x8F, 0x84, 0x06, 0x69, 0xD9, 0xAB, 0x77, 0xF4, 0x70, 0xBE, 0x79, 0x9D, 0x69, 0x13, 0x18, 0xFA, 0x61, 0xBF, 0xAA, 0x25, 0x11, 0xFE, 0x03, 0x5E, 0x23, 0x1A, 0x75, 0xB9, 0x11, 0x79, 0x49, 0x9A, 0x44, 0xCB, 0x08, 0x77, 0xB2, 0xE1, 0x89, 0xE5, 0x8C, 0xA1, 0x71, 0xBF, 0x7D, 0x5A, 0x17, 0x9D, 0x71, 0x8E, 0xDF, 0x6C, 0xAA, 0x2D, 0x7E, 0xEB];
+	private static readonly byte[] s_iq = [0x2F, 0x91, 0x84, 0x58, 0xAB, 0x47, 0xAE, 0xE6, 0x92, 0xBC, 0xE4, 0x13, 0xBA, 0x9C, 0xDD, 0x66, 0xFF, 0x91, 0xA8, 0x94, 0x41, 0xBF, 0xD6, 0x54, 0x1B, 0x6D, 0x92, 0xC0, 0x67, 0xC6, 0x08, 0x23, 0xA4, 0x6D, 0x24, 0xA1, 0xC1, 0xBD, 0xCB, 0xC5, 0xAF, 0x4E, 0x96, 0x39, 0x46, 0x4A, 0x44, 0xD7, 0x10, 0x8F, 0x35, 0x76, 0xF5, 0xCB, 0x86, 0xE2, 0x9A, 0xAD, 0x32, 0xCD, 0x2C, 0xC0, 0xB8, 0x4A, 0x31, 0xB0, 0xAC, 0xF8, 0xFA, 0xAB, 0xEB, 0x0E, 0x8A, 0x7D, 0x92, 0x0D, 0xB5, 0x5C, 0x8B, 0x36, 0xD6, 0xC3, 0x56, 0x8F, 0x6B, 0xC0, 0x54, 0x5C, 0x8E, 0x83, 0x27, 0xEC, 0x21, 0x66, 0xC1, 0x5F, 0x44, 0x62, 0x33, 0xD3, 0xC7, 0x09, 0x01, 0xC8, 0xAB, 0x39, 0x6B, 0xC0, 0x1B, 0x35, 0x82, 0x43, 0x49, 0xF3, 0x42, 0x6B, 0x72, 0xA8, 0x1D, 0x31, 0x63, 0x3A, 0xF5, 0x4C, 0x3D, 0xE7, 0x3D, 0xD1];
 }
diff --git a/tests/MySqlConnector.Tests/packages.lock.json b/tests/MySqlConnector.Tests/packages.lock.json
index a75c501bc..ee75ae00b 100644
--- a/tests/MySqlConnector.Tests/packages.lock.json
+++ b/tests/MySqlConnector.Tests/packages.lock.json
@@ -27,6 +27,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "xunit": {
         "type": "Direct",
         "requested": "[2.9.3, )",
@@ -78,6 +87,11 @@
           "System.Reflection.Metadata": "1.6.0"
         }
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "System.Buffers": {
         "type": "Transitive",
         "resolved": "4.5.1",
@@ -233,6 +247,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "xunit": {
         "type": "Direct",
         "requested": "[2.9.3, )",
@@ -287,6 +310,11 @@
         "resolved": "13.0.1",
         "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
       },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
+      },
       "System.Reflection.Metadata": {
         "type": "Transitive",
         "resolved": "1.6.0",
diff --git a/tools/SchemaCollectionGenerator/packages.lock.json b/tools/SchemaCollectionGenerator/packages.lock.json
index 87121a3f9..c76e60b00 100644
--- a/tools/SchemaCollectionGenerator/packages.lock.json
+++ b/tools/SchemaCollectionGenerator/packages.lock.json
@@ -18,6 +18,15 @@
         "resolved": "6.0.0",
         "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
       },
+      "StyleCop.Analyzers": {
+        "type": "Direct",
+        "requested": "[1.2.0-beta.556, )",
+        "resolved": "1.2.0-beta.556",
+        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
+        "dependencies": {
+          "StyleCop.Analyzers.Unstable": "1.2.0.556"
+        }
+      },
       "YamlDotNet": {
         "type": "Direct",
         "requested": "[16.3.0, )",
@@ -33,6 +42,11 @@
         "type": "Transitive",
         "resolved": "8.0.0",
         "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
+      },
+      "StyleCop.Analyzers.Unstable": {
+        "type": "Transitive",
+        "resolved": "1.2.0.556",
+        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
       }
     }
   }

From 9c242cc245975fd50ac1e9ef17bff9ef9963aeff Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 25 Jan 2025 19:55:55 -0800
Subject: [PATCH 358/393] Allow clear password over loopback connection. Fixes
 #1534

Assume that a localhost TCP connection isn't subject to a MITM attack and so it's OK to send a cleartext password. This allows the use of the Google Cloud SQL Auth Proxy, which runs on a local TCP port.
---
 src/MySqlConnector/Core/ServerSession.cs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index fdead8006..a2ae01f4c 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -860,7 +860,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			case "mysql_clear_password":
-				if (!m_isSecureConnection)
+				if (!m_isSecureConnection && !m_isLoopbackConnection)
 				{
 					Log.NeedsSecureConnection(m_logger, Id, switchRequest.Name);
 					throw new MySqlException(MySqlErrorCode.UnableToConnectToHost, $"Authentication method '{switchRequest.Name}' requires a secure connection.");
@@ -1302,6 +1302,7 @@ private async Task<bool> OpenTcpSocketAsync(ConnectionSettings cs, ILoadBalancer
 					m_socket.NoDelay = true;
 					m_stream = m_tcpClient.GetStream();
 					m_socket.SetKeepAlive(cs.Keepalive);
+					m_isLoopbackConnection = IPAddress.IsLoopback(ipAddress);
 				}
 				catch (ObjectDisposedException) when (cancellationToken.IsCancellationRequested)
 				{
@@ -2169,6 +2170,7 @@ protected override void OnStatementBegin(int index)
 	private IPayloadHandler? m_payloadHandler;
 	private CompressionMethod m_compressionMethod;
 	private bool m_isSecureConnection;
+	private bool m_isLoopbackConnection;
 	private bool m_supportsConnectionAttributes;
 	private bool m_supportsPipelining;
 	private CharacterSet m_characterSet;

From a64e5c4f7e2b798a6e2a23a57192178311fc0172 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 25 Jan 2025 20:54:33 -0800
Subject: [PATCH 359/393] Allow public key retrieval over loopback connection.
 Fixes #1542

---
 src/MySqlConnector/Core/ServerSession.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index a2ae01f4c..afcb1a123 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -996,7 +996,7 @@ private async Task<string> GetRsaPublicKeyAsync(string switchRequestName, Connec
 			}
 		}
 
-		if (cs.AllowPublicKeyRetrieval)
+		if (cs.AllowPublicKeyRetrieval || m_isLoopbackConnection)
 		{
 			// request the RSA public key
 			var payloadContent = switchRequestName == "caching_sha2_password" ? (byte) 0x02 : (byte) 0x01;

From 11937af65b98afe9fdefa983975fe219d829fa65 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 26 Jan 2025 17:49:58 -0800
Subject: [PATCH 360/393] Run tests on MariaDB 11.6.

This adds support for the 'parsec' plugin.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .ci/docker-run.sh                                   |  4 ++--
 azure-pipelines.yml                                 |  4 ++++
 docs/content/home.md                                |  2 +-
 src/MySqlConnector/Protocol/CharacterSet.cs         |  2 ++
 .../Protocol/Serialization/ProtocolUtility.cs       |  2 ++
 tests/IntegrationTests/CharacterSetTests.cs         |  2 +-
 tests/IntegrationTests/TestUtilities.cs             | 13 ++++++++++---
 7 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index 045f43753..7a791e21d 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -26,9 +26,9 @@ MYSQL_EXTRA=
 MYSQL=mysql
 
 if [[ "$IMAGE" == mariadb* ]]; then
-  MYSQL_EXTRA='--in-predicate-conversion-threshold=100000'
+  MYSQL_EXTRA='--in-predicate-conversion-threshold=100000 --plugin-maturity=beta'
 fi
-if [ "$IMAGE" == "mariadb:11.4" ]; then
+if [ "$IMAGE" == "mariadb:11.4" ] || [ "$IMAGE" == "mariadb:11.6" ]; then
   MYSQL='mariadb'
 fi
 
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index c8518d9b0..fa4d3d285 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -192,6 +192,10 @@ jobs:
         image: 'mariadb:11.4'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin,Redirection'
+      'MariaDB 11.6':
+        image: 'mariadb:11.6'
+        connectionStringExtra: ''
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,UuidToBin'
   steps:
   - template: '.ci/integration-tests-steps.yml'
     parameters:
diff --git a/docs/content/home.md b/docs/content/home.md
index 5c4c5c3da..0db29f09c 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -64,7 +64,7 @@ Server  | Versions | Notes
 Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://mysqlconnector.net/troubleshooting/aurora-freeze/)
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
-MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**) |
+MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**, **11.6**) |
 MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.2**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
diff --git a/src/MySqlConnector/Protocol/CharacterSet.cs b/src/MySqlConnector/Protocol/CharacterSet.cs
index b2833f5b7..4bdbf4fb2 100644
--- a/src/MySqlConnector/Protocol/CharacterSet.cs
+++ b/src/MySqlConnector/Protocol/CharacterSet.cs
@@ -296,9 +296,11 @@ internal enum CharacterSet : ushort
 	Utf8Mb3CroatianCaseInsensitiveMariaDb = 576,
 	Utf8Mb3MyanmarCaseInsensitive = 577,
 	Utf8Mb3ThaiUnicode520Weight2 = 578,
+	Utf8Mb3General1400AccentSensitiveCaseInsensitive = 579,
 	Utf8Mb4CroatianCaseInsensitiveMariaDb = 608,
 	Utf8Mb4MyanmarCaseInsensitive = 609,
 	Utf8Mb4ThaiUnicode520Weight2 = 610,
+	Utf8Mb4General1400AccentSensitiveCaseInsensitive = 611,
 	Ucs2CroatianCaseInsensitiveMariaDb = 640,
 	Ucs2MyanmarCaseInsensitive = 641,
 	Ucs2ThaiUnicode520Weight2 = 642,
diff --git a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
index 259f3f963..53625a351 100644
--- a/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/ProtocolUtility.cs
@@ -225,6 +225,7 @@ public static int GetBytesPerCharacter(CharacterSet characterSet)
 			case CharacterSet.Utf8Mb3CroatianCaseInsensitiveMariaDb:
 			case CharacterSet.Utf8Mb3MyanmarCaseInsensitive:
 			case CharacterSet.Utf8Mb3ThaiUnicode520Weight2:
+			case CharacterSet.Utf8Mb3General1400AccentSensitiveCaseInsensitive:
 			case CharacterSet.UjisJapaneseNoPadCaseInsensitive:
 			case CharacterSet.Utf8Mb3GeneralNoPadCaseInsensitive:
 			case CharacterSet.Utf8Mb3NoPadBinary:
@@ -384,6 +385,7 @@ public static int GetBytesPerCharacter(CharacterSet characterSet)
 			case CharacterSet.Utf8Mb4CroatianCaseInsensitiveMariaDb:
 			case CharacterSet.Utf8Mb4MyanmarCaseInsensitive:
 			case CharacterSet.Utf8Mb4ThaiUnicode520Weight2:
+			case CharacterSet.Utf8Mb4General1400AccentSensitiveCaseInsensitive:
 			case CharacterSet.Utf16CroatianCaseInsensitiveMariaDb:
 			case CharacterSet.Utf16MyanmarCaseInsensitive:
 			case CharacterSet.Utf16ThaiUnicode520Weight2:
diff --git a/tests/IntegrationTests/CharacterSetTests.cs b/tests/IntegrationTests/CharacterSetTests.cs
index 15bb88856..2dcb685a8 100644
--- a/tests/IntegrationTests/CharacterSetTests.cs
+++ b/tests/IntegrationTests/CharacterSetTests.cs
@@ -79,7 +79,7 @@ public void CollationConnection(bool reopenConnection)
 
 		var collation = connection.Query<string>(@"select @@collation_connection;").Single();
 		var expected = connection.ServerVersion.Substring(0, 2) is "8." or "9." ? "utf8mb4_0900_ai_ci" :
-			connection.ServerVersion.StartsWith("11.4.", StringComparison.Ordinal) ? "utf8mb4_uca1400_ai_ci" :
+			connection.ServerVersion.StartsWith("11.4.", StringComparison.Ordinal) || connection.ServerVersion.StartsWith("11.6.", StringComparison.Ordinal) ? "utf8mb4_uca1400_ai_ci" :
 			"utf8mb4_general_ci";
 		Assert.Equal(expected, collation);
 	}
diff --git a/tests/IntegrationTests/TestUtilities.cs b/tests/IntegrationTests/TestUtilities.cs
index 737c9a9a4..dfdc4ff39 100644
--- a/tests/IntegrationTests/TestUtilities.cs
+++ b/tests/IntegrationTests/TestUtilities.cs
@@ -79,11 +79,18 @@ private static async Task AssertExecuteScalarReturnsOneOrThrowsExceptionAsync(My
 		}
 		else
 		{
-			var ex = await Assert.ThrowsAsync<MySqlException>(async () => await command.ExecuteScalarAsync(token));
+			var ex = await Assert.ThrowsAnyAsync<Exception>(async () => await command.ExecuteScalarAsync(token));
+			MySqlException exception = ex as MySqlException;
+			while (exception is null && ex is not null)
+			{
+				ex = ex.InnerException;
+				exception = ex as MySqlException;
+			}
+			Assert.NotNull(exception);
 #if MYSQL_DATA
-			Assert.Equal((int) expectedCode, ex.Number);
+			Assert.Equal((int) expectedCode, exception.Number);
 #else
-			Assert.Equal(expectedCode, ex.ErrorCode);
+			Assert.Equal(expectedCode, exception.ErrorCode);
 #endif
 		}
 	}

From 3c77a6e0f03de438b7c2247ef9c086ecc23ebc24 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 27 Jan 2025 13:01:02 -0800
Subject: [PATCH 361/393] Use built-in type names.

---
 tests/IntegrationTests/TestUtilities.cs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tests/IntegrationTests/TestUtilities.cs b/tests/IntegrationTests/TestUtilities.cs
index 737c9a9a4..c7210f8bd 100644
--- a/tests/IntegrationTests/TestUtilities.cs
+++ b/tests/IntegrationTests/TestUtilities.cs
@@ -18,7 +18,7 @@ public static void AssertEqual(byte[] expected, byte[] actual)
 	}
 
 	/// <summary>
-	/// Verifies that <paramref name="value"/> is an integer (<see cref="Int32"/> or <see cref="Int64"/>) with the value <code>1</code>.
+	/// Verifies that <paramref name="value"/> is an integer (<see cref="int"/> or <see cref="long"/>) with the value <code>1</code>.
 	/// </summary>
 	public static void AssertIsOne(object value)
 	{

From 3da6f66b9c69ccd1135f3cc913466abfebf79cfb Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 27 Jan 2025 13:12:07 -0800
Subject: [PATCH 362/393] Set up project for AI assistance.

---
 .github/copilot-instructions.md | 25 +++++++++++++++++++++++++
 .gitignore                      |  1 +
 2 files changed, 26 insertions(+)
 create mode 100644 .github/copilot-instructions.md

diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md
new file mode 100644
index 000000000..a79c209a5
--- /dev/null
+++ b/.github/copilot-instructions.md
@@ -0,0 +1,25 @@
+# AI Instructions
+
+## Project Context
+
+MySqlConnector is a high-performance ADO.NET Data Provider for MySQL.
+It implements the MySQL protocol and handles authentication, connection pooling, and data access.
+
+## Key Conventions
+
+### Code Style
+- follow Microsoft's C# coding conventions with the exception that field names are prefixed with `m_` and are specified at the end of the class definition
+- use C# nullable reference types and implicit usings
+- XML documentation required for public APIs
+- use tabs for indentation in *.cs files; use two spaces for indentation in XML and JSON files, including *.csproj files
+- follow the rules from `.editorconfig`
+- implement async methods using the IOBehavior pattern: sync and async methods delegate to a common implementation that takes an IOBehavior parameter and handles both I/O types
+
+### Performance
+- minimize allocations in hot paths
+- use Span-based APIs wherever possible
+
+### Testing
+- Test files should be located in corresponding test directories. Place unit tests in tests/MySqlConnector.Tests. Place integration tests in tests/IntegrationTests.
+- Test names should be descriptive and follow the pattern `CamelCaseShortDescription`.
+- Use xUnit for tests.
diff --git a/.gitignore b/.gitignore
index e72e818f0..c40f33ac0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,6 +11,7 @@ project.lock.json
 _ReSharper.*
 TestResult.xml
 Thumbs.db
+.aider*
 *.csproj.user
 *.DotSettings.user
 .idea

From fc59a06556fd73f61fe52b0a1793bf73755e2e7b Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 30 Jan 2025 04:39:55 -0800
Subject: [PATCH 363/393] Don't log error when canceled. Fixes #1544

---
 src/MySqlConnector/Core/ServerSession.cs | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index afcb1a123..0d9ffe429 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1731,7 +1731,8 @@ await sslStream.AuthenticateAsClientAsync(clientAuthenticationOptions.TargetHost
 		}
 		catch (Exception ex)
 		{
-			Log.CouldNotInitializeTlsConnection(m_logger, ex, Id);
+			if (ex is not OperationCanceledException)
+				Log.CouldNotInitializeTlsConnection(m_logger, ex, Id);
 			sslStream.Dispose();
 			ShutdownSocket();
 			HostName = "";

From bbd66bba6becfc40ab981404cede067ad1a291ca Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 1 Feb 2025 15:05:55 -0800
Subject: [PATCH 364/393] Add options to configure tracing. Fixes #1524

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/ResultSet.cs          |  2 +-
 src/MySqlConnector/MySqlConnection.cs         |  2 +
 .../MySqlConnectorTracingOptions.cs           | 11 +++++
 .../MySqlConnectorTracingOptionsBuilder.cs    | 22 ++++++++++
 src/MySqlConnector/MySqlDataSource.cs         |  6 ++-
 src/MySqlConnector/MySqlDataSourceBuilder.cs  | 25 +++++++++++
 tests/IntegrationTests/ActivityTests.cs       | 42 +++++++++++++++++++
 7 files changed, 108 insertions(+), 2 deletions(-)
 create mode 100644 src/MySqlConnector/MySqlConnectorTracingOptions.cs
 create mode 100644 src/MySqlConnector/MySqlConnectorTracingOptionsBuilder.cs

diff --git a/src/MySqlConnector/Core/ResultSet.cs b/src/MySqlConnector/Core/ResultSet.cs
index 2b93879ca..dd535c66b 100644
--- a/src/MySqlConnector/Core/ResultSet.cs
+++ b/src/MySqlConnector/Core/ResultSet.cs
@@ -166,7 +166,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
 						ContainsCommandParameters = true;
 					WarningCount = 0;
 					State = ResultSetState.ReadResultSetHeader;
-					if (DataReader.Activity is { IsAllDataRequested: true })
+					if (DataReader.Activity is { IsAllDataRequested: true } && (Command?.Connection!.MySqlDataSource?.TracingOptions.EnableResultSetHeaderEvent ?? MySqlConnectorTracingOptions.Default.EnableResultSetHeaderEvent))
 						DataReader.Activity.AddEvent(new ActivityEvent("read-result-set-header"));
 					break;
 				}
diff --git a/src/MySqlConnector/MySqlConnection.cs b/src/MySqlConnector/MySqlConnection.cs
index 27219bb47..62f70bdac 100644
--- a/src/MySqlConnector/MySqlConnection.cs
+++ b/src/MySqlConnector/MySqlConnection.cs
@@ -1138,6 +1138,8 @@ private async ValueTask<ServerSession> CreateSessionAsync(ConnectionPool? pool,
 
 	internal IPEndPoint? SessionEndPoint => m_session!.IPEndPoint;
 
+	internal MySqlDataSource? MySqlDataSource => m_dataSource;
+
 	internal void SetState(ConnectionState newState)
 	{
 		if (m_connectionState != newState)
diff --git a/src/MySqlConnector/MySqlConnectorTracingOptions.cs b/src/MySqlConnector/MySqlConnectorTracingOptions.cs
new file mode 100644
index 000000000..8bc0d71f5
--- /dev/null
+++ b/src/MySqlConnector/MySqlConnectorTracingOptions.cs
@@ -0,0 +1,11 @@
+namespace MySqlConnector;
+
+internal sealed class MySqlConnectorTracingOptions
+{
+	public bool EnableResultSetHeaderEvent { get; set; }
+
+	public static MySqlConnectorTracingOptions Default { get; } = new()
+	{
+		EnableResultSetHeaderEvent = true,
+	};
+}
diff --git a/src/MySqlConnector/MySqlConnectorTracingOptionsBuilder.cs b/src/MySqlConnector/MySqlConnectorTracingOptionsBuilder.cs
new file mode 100644
index 000000000..8b2ce81a4
--- /dev/null
+++ b/src/MySqlConnector/MySqlConnectorTracingOptionsBuilder.cs
@@ -0,0 +1,22 @@
+namespace MySqlConnector;
+
+public sealed class MySqlConnectorTracingOptionsBuilder
+{
+	/// <summary>
+	/// Gets or sets a value indicating whether to enable the "time-to-first-read" event.
+	/// Default is true to preserve existing behavior.
+	/// </summary>
+	public MySqlConnectorTracingOptionsBuilder EnableResultSetHeaderEvent(bool enable = true)
+	{
+		m_enableResultSetHeaderEvent = enable;
+		return this;
+	}
+
+	internal MySqlConnectorTracingOptions Build() =>
+		new()
+		{
+			EnableResultSetHeaderEvent = m_enableResultSetHeaderEvent,
+		};
+
+	private bool m_enableResultSetHeaderEvent = MySqlConnectorTracingOptions.Default.EnableResultSetHeaderEvent;
+}
diff --git a/src/MySqlConnector/MySqlDataSource.cs b/src/MySqlConnector/MySqlDataSource.cs
index 56e9718af..5b5b38436 100644
--- a/src/MySqlConnector/MySqlDataSource.cs
+++ b/src/MySqlConnector/MySqlDataSource.cs
@@ -19,12 +19,13 @@ public sealed class MySqlDataSource : DbDataSource
 	/// <param name="connectionString">The connection string for the MySQL Server. This parameter is required.</param>
 	/// <exception cref="ArgumentNullException">Thrown if <paramref name="connectionString"/> is <c>null</c>.</exception>
 	public MySqlDataSource(string connectionString)
-		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, default, default, default, default)
+		: this(connectionString ?? throw new ArgumentNullException(nameof(connectionString)), MySqlConnectorLoggingConfiguration.NullConfiguration, null, null, null, null, null, default, default, default, default)
 	{
 	}
 
 	internal MySqlDataSource(string connectionString,
 		MySqlConnectorLoggingConfiguration loggingConfiguration,
+		MySqlConnectorTracingOptions? tracingOptions,
 		string? name,
 		Func<X509CertificateCollection, ValueTask>? clientCertificatesCallback,
 		RemoteCertificateValidationCallback? remoteCertificateValidationCallback,
@@ -36,6 +37,7 @@ internal MySqlDataSource(string connectionString,
 	{
 		m_connectionString = connectionString;
 		LoggingConfiguration = loggingConfiguration;
+		TracingOptions = tracingOptions ?? MySqlConnectorTracingOptions.Default;
 		Name = name;
 		m_clientCertificatesCallback = clientCertificatesCallback;
 		m_remoteCertificateValidationCallback = remoteCertificateValidationCallback;
@@ -202,6 +204,8 @@ private async Task RefreshPassword()
 
 	internal MySqlConnectorLoggingConfiguration LoggingConfiguration { get; }
 
+	internal MySqlConnectorTracingOptions TracingOptions { get; }
+
 	internal string? Name { get; }
 
 	private string ProvidePasswordFromField(MySqlProvidePasswordContext context) => m_password!;
diff --git a/src/MySqlConnector/MySqlDataSourceBuilder.cs b/src/MySqlConnector/MySqlDataSourceBuilder.cs
index 4bceec180..5ea16dce0 100644
--- a/src/MySqlConnector/MySqlDataSourceBuilder.cs
+++ b/src/MySqlConnector/MySqlDataSourceBuilder.cs
@@ -21,6 +21,23 @@ public MySqlDataSourceBuilder(string? connectionString = null)
 		ConnectionStringBuilder = new(connectionString ?? "");
 	}
 
+	/// <summary>
+	/// Configures OpenTelemetry tracing options.
+	/// </summary>
+	/// <returns>This builder, so that method calls can be chained.</returns>
+	public MySqlDataSourceBuilder ConfigureTracing(Action<MySqlConnectorTracingOptionsBuilder> configureAction)
+	{
+#if NET6_0_OR_GREATER
+		ArgumentNullException.ThrowIfNull(configureAction);
+#else
+		if (configureAction is null)
+			throw new ArgumentNullException(nameof(configureAction));
+#endif
+		m_tracingOptionsBuilderCallbacks ??= [];
+		m_tracingOptionsBuilderCallbacks.Add(configureAction);
+		return this;
+	}
+
 	/// <summary>
 	/// Sets the <see cref="ILoggerFactory"/> that will be used for logging.
 	/// </summary>
@@ -107,8 +124,15 @@ public MySqlDataSourceBuilder UseConnectionOpenedCallback(MySqlConnectionOpenedC
 	public MySqlDataSource Build()
 	{
 		var loggingConfiguration = m_loggerFactory is null ? MySqlConnectorLoggingConfiguration.NullConfiguration : new(m_loggerFactory);
+
+		var tracingOptionsBuilder = new MySqlConnectorTracingOptionsBuilder();
+		foreach (var callback in m_tracingOptionsBuilderCallbacks ?? (IEnumerable<Action<MySqlConnectorTracingOptionsBuilder>>) [])
+			callback.Invoke(tracingOptionsBuilder);
+		var tracingOptions = tracingOptionsBuilder.Build();
+
 		return new(ConnectionStringBuilder.ConnectionString,
 			loggingConfiguration,
+			tracingOptions,
 			m_name,
 			m_clientCertificatesCallback,
 			m_remoteCertificateValidationCallback,
@@ -135,4 +159,5 @@ public MySqlDataSource Build()
 	private TimeSpan m_periodicPasswordProviderSuccessRefreshInterval;
 	private TimeSpan m_periodicPasswordProviderFailureRefreshInterval;
 	private MySqlConnectionOpenedCallback? m_connectionOpenedCallback;
+	private List<Action<MySqlConnectorTracingOptionsBuilder>>? m_tracingOptionsBuilderCallbacks;
 }
diff --git a/tests/IntegrationTests/ActivityTests.cs b/tests/IntegrationTests/ActivityTests.cs
index 60ada3b6f..e486494bf 100644
--- a/tests/IntegrationTests/ActivityTests.cs
+++ b/tests/IntegrationTests/ActivityTests.cs
@@ -140,6 +140,48 @@ public void SelectTags()
 		AssertTag(activity.Tags, "db.statement", "SELECT 1;");
 	}
 
+	[Theory]
+	[InlineData(true)]
+	[InlineData(false)]
+	public void ReadResultSetHeaderEvent(bool enableEvent)
+	{
+		var dataSourceBuilder = new MySqlDataSourceBuilder(AppConfig.ConnectionString)
+			.ConfigureTracing(o => o.EnableResultSetHeaderEvent(enableEvent));
+		using var dataSource = dataSourceBuilder.Build();
+		using var connection = dataSource.OpenConnection();
+
+		using var parentActivity = new Activity(nameof(ReadResultSetHeaderEvent));
+		parentActivity.Start();
+
+		Activity activity = null;
+		using var listener = new ActivityListener
+		{
+			ShouldListenTo = x => x.Name == "MySqlConnector",
+			Sample = (ref ActivityCreationOptions<ActivityContext> options) =>
+				options.TraceId == parentActivity.TraceId ? ActivitySamplingResult.AllData : ActivitySamplingResult.None,
+			ActivityStopped = x => activity = x,
+		};
+		ActivitySource.AddActivityListener(listener);
+
+		using (var command = new MySqlCommand("SELECT 1;", connection))
+		{
+			command.ExecuteScalar();
+		}
+
+		Assert.NotNull(activity);
+		Assert.Equal(ActivityKind.Client, activity.Kind);
+		Assert.Equal("Execute", activity.OperationName);
+		if (enableEvent)
+		{
+			var activityEvent = Assert.Single(activity.Events);
+			Assert.Equal("read-result-set-header", activityEvent.Name);
+		}
+		else
+		{
+			Assert.Empty(activity.Events);
+		}
+	}
+
 	private void AssertTags(IEnumerable<KeyValuePair<string, string>> tags, MySqlConnectionStringBuilder csb)
 	{
 		AssertTag(tags, "db.system", "mysql");

From e5a5ed84cbfd90b49c71e55b2afcd591251703c5 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 2 Feb 2025 08:04:51 -0800
Subject: [PATCH 365/393] Update package lock files.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../packages.lock.json                        | 14 ++++++++++
 .../packages.lock.json                        | 14 ++++++++++
 .../packages.lock.json                        | 14 ++++++++++
 .../packages.lock.json                        | 14 ++++++++++
 src/MySqlConnector/packages.lock.json         | 28 +++++++++++++++++++
 tests/IntegrationTests/packages.lock.json     | 28 +++++++++++++++++++
 tests/MySqlConnector.Tests/packages.lock.json | 14 ++++++++++
 7 files changed, 126 insertions(+)

diff --git a/src/MySqlConnector.Authentication.Ed25519/packages.lock.json b/src/MySqlConnector.Authentication.Ed25519/packages.lock.json
index c4c1e88fd..7c8f4f591 100644
--- a/src/MySqlConnector.Authentication.Ed25519/packages.lock.json
+++ b/src/MySqlConnector.Authentication.Ed25519/packages.lock.json
@@ -2,6 +2,15 @@
   "version": 2,
   "dependencies": {
     ".NETFramework,Version=v4.6.2": {
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -40,6 +49,11 @@
         "resolved": "8.0.0",
         "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
diff --git a/src/MySqlConnector.Logging.NLog/packages.lock.json b/src/MySqlConnector.Logging.NLog/packages.lock.json
index 1c8d67f69..5eb10212c 100644
--- a/src/MySqlConnector.Logging.NLog/packages.lock.json
+++ b/src/MySqlConnector.Logging.NLog/packages.lock.json
@@ -2,6 +2,15 @@
   "version": 2,
   "dependencies": {
     ".NETFramework,Version=v4.6.2": {
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -46,6 +55,11 @@
         "resolved": "8.0.0",
         "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
diff --git a/src/MySqlConnector.Logging.Serilog/packages.lock.json b/src/MySqlConnector.Logging.Serilog/packages.lock.json
index 4083d5c77..8d6f759f4 100644
--- a/src/MySqlConnector.Logging.Serilog/packages.lock.json
+++ b/src/MySqlConnector.Logging.Serilog/packages.lock.json
@@ -2,6 +2,15 @@
   "version": 2,
   "dependencies": {
     ".NETFramework,Version=v4.6.2": {
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -46,6 +55,11 @@
         "resolved": "8.0.0",
         "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
diff --git a/src/MySqlConnector.Logging.log4net/packages.lock.json b/src/MySqlConnector.Logging.log4net/packages.lock.json
index 05fb71c2d..9e0d38ab3 100644
--- a/src/MySqlConnector.Logging.log4net/packages.lock.json
+++ b/src/MySqlConnector.Logging.log4net/packages.lock.json
@@ -8,6 +8,15 @@
         "resolved": "2.0.17",
         "contentHash": "qnnDf/ubJzwm2i1xH7nRMjEDoD+ctse7nZDqb+p7L1PvZc6ykpMoEesWr1/9hFqlsbII2v9e8yyQHJhoDQh7ZA=="
       },
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -46,6 +55,11 @@
         "resolved": "8.0.0",
         "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
diff --git a/src/MySqlConnector/packages.lock.json b/src/MySqlConnector/packages.lock.json
index d47bd21c3..a41858cfe 100644
--- a/src/MySqlConnector/packages.lock.json
+++ b/src/MySqlConnector/packages.lock.json
@@ -23,6 +23,15 @@
           "System.Memory": "4.5.5"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -80,6 +89,11 @@
         "resolved": "8.0.0",
         "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
@@ -150,6 +164,15 @@
           "System.Memory": "4.5.5"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net471": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -207,6 +230,11 @@
         "resolved": "8.0.0",
         "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net471": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "Kf3vusy+mwHzn+0osmJAMrZNG00/UkwywjwfFKLUWSYEXiIMD6SEMGP9bG3sH1B54IiydxAneLubS5/Ajjn/Tw=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
diff --git a/tests/IntegrationTests/packages.lock.json b/tests/IntegrationTests/packages.lock.json
index 4e6439560..ccd66edad 100644
--- a/tests/IntegrationTests/packages.lock.json
+++ b/tests/IntegrationTests/packages.lock.json
@@ -49,6 +49,15 @@
           "Microsoft.CodeCoverage": "17.12.0"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -190,6 +199,11 @@
           "System.Runtime.CompilerServices.Unsafe": "6.0.0"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
@@ -415,6 +429,15 @@
           "Microsoft.CodeCoverage": "17.12.0"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -556,6 +579,11 @@
           "System.Runtime.CompilerServices.Unsafe": "6.0.0"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net481": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
diff --git a/tests/MySqlConnector.Tests/packages.lock.json b/tests/MySqlConnector.Tests/packages.lock.json
index ee75ae00b..5cea0042c 100644
--- a/tests/MySqlConnector.Tests/packages.lock.json
+++ b/tests/MySqlConnector.Tests/packages.lock.json
@@ -11,6 +11,15 @@
           "Microsoft.CodeCoverage": "17.12.0"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -74,6 +83,11 @@
         "resolved": "17.12.0",
         "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net481": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",

From 15396fa29f11cf86c5a76b2c1def2de97e2568eb Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 2 Feb 2025 12:16:48 -0800
Subject: [PATCH 366/393] Add security policy.

---
 SECURITY.md | 27 +++++++++++++++++++++++++++
 1 file changed, 27 insertions(+)
 create mode 100644 SECURITY.md

diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 000000000..ccc859d04
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,27 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported          |
+| ------- | ------------------ |
+| 2.4.0   | :white_check_mark: |
+| 2.3.7   | :white_check_mark: |
+| 2.2.7   | :x:                |
+| 2.1.13  | :x:                |
+| 2.0.0   | :x:                |
+| < 2.0   | :x:                |
+
+
+## Reporting a Vulnerability
+
+You can [report a vulnerability through GitHub](https://github.com/mysql-net/MySqlConnector/security).
+
+Your report will be acknowledged within 5 days and you will receive a more detailed response outlining the next steps in handling your submission.
+
+Please include the information listed below to help better understand the nature and scope of the possible issue:
+
+  * Type of issue (e.g. SQL injection, data leakage, etc.)
+  * Full paths of the files related to the issue
+  * Step by step instructions to reproduce the issue
+  * Proof of concept or exploit code (if possible)
+  * Impact of the issue

From a9709c4285d0d44456316bbc6a972c1664f38e33 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 8 Feb 2025 13:30:44 -0800
Subject: [PATCH 367/393] Use mysql.proc with MariaDB. Fixes #1547

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 src/MySqlConnector/Core/CachedProcedure.cs | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index 441a7982b..acafaa43a 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -13,9 +13,8 @@ internal sealed class CachedProcedure
 	public static async Task<CachedProcedure?> FillAsync(IOBehavior ioBehavior, MySqlConnection connection, string schema, string component, ILogger logger, CancellationToken cancellationToken)
 	{
 		// try to use mysql.proc first, as it is much faster
-		if (!connection.Session.ServerVersion.IsMariaDb &&
-			connection.Session.ServerVersion.Version < ServerVersions.RemovesMySqlProcTable &&
-			!connection.Session.ProcAccessDenied)
+		if (!connection.Session.ProcAccessDenied &&
+			(connection.Session.ServerVersion.IsMariaDb || connection.Session.ServerVersion.Version < ServerVersions.RemovesMySqlProcTable))
 		{
 			try
 			{

From 66269a4d0b8376f4a181e0758cc3d41a19214a12 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 15 Feb 2025 14:08:25 -0800
Subject: [PATCH 368/393] Allow SDK roll-forward.

---
 global.json                                   |  3 ++-
 .../packages.lock.json                        |  6 ++---
 src/MySqlConnector/packages.lock.json         | 26 ++++++++++++++-----
 .../packages.lock.json                        | 24 ++++++++---------
 4 files changed, 37 insertions(+), 22 deletions(-)

diff --git a/global.json b/global.json
index 653245229..cdbb589ed 100644
--- a/global.json
+++ b/global.json
@@ -1,5 +1,6 @@
 {
   "sdk": {
-    "version": "9.0.100"
+    "version": "9.0.100",
+    "rollForward": "latestFeature"
   }
 }
diff --git a/src/MySqlConnector.DependencyInjection/packages.lock.json b/src/MySqlConnector.DependencyInjection/packages.lock.json
index 765f6deab..73ee05a17 100644
--- a/src/MySqlConnector.DependencyInjection/packages.lock.json
+++ b/src/MySqlConnector.DependencyInjection/packages.lock.json
@@ -148,9 +148,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.12, )",
-        "resolved": "8.0.12",
-        "contentHash": "FV4HnQ3JI15PHnJ5PGTbz+rYvrih42oLi/7UMIshNwCwUZhTq13UzrggtXk4ygrcMcN+4jsS6hhshx2p/Zd0ig=="
+        "requested": "[8.0.13, )",
+        "resolved": "8.0.13",
+        "contentHash": "R19ZTaRiQAK+xo9ZwaHbF/1vb1wwR1Wn5+sqp9v8+CDjbdS8R6qftKdw0VSXWKm7VAMi7P+NCU4zxDzhEWcAwQ=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
diff --git a/src/MySqlConnector/packages.lock.json b/src/MySqlConnector/packages.lock.json
index a41858cfe..3fc5d7c53 100644
--- a/src/MySqlConnector/packages.lock.json
+++ b/src/MySqlConnector/packages.lock.json
@@ -300,6 +300,15 @@
           "System.Memory": "4.5.5"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -357,6 +366,11 @@
         "resolved": "8.0.0",
         "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net48": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
@@ -718,9 +732,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.12, )",
-        "resolved": "8.0.12",
-        "contentHash": "FV4HnQ3JI15PHnJ5PGTbz+rYvrih42oLi/7UMIshNwCwUZhTq13UzrggtXk4ygrcMcN+4jsS6hhshx2p/Zd0ig=="
+        "requested": "[8.0.13, )",
+        "resolved": "8.0.13",
+        "contentHash": "R19ZTaRiQAK+xo9ZwaHbF/1vb1wwR1Wn5+sqp9v8+CDjbdS8R6qftKdw0VSXWKm7VAMi7P+NCU4zxDzhEWcAwQ=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -781,9 +795,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[9.0.1, )",
-        "resolved": "9.0.1",
-        "contentHash": "BKFAkdhUUJivAoKuSWcT23WH9E+AWQMJootzt9X+lnesOD2a2yNIe1ZQFtxB8H3ayskLBD5gYyIbj5GIfw7rZg=="
+        "requested": "[9.0.2, )",
+        "resolved": "9.0.2",
+        "contentHash": "+KFnCLVPicEq99ko0tq+ycTvNLXHw0tImmTZjPloB/DOFLPT56KLfk5aS7wbgXRPzYhXTTBYLGaABea5mke77w=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
diff --git a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
index 4f5603cf7..b75c400e8 100644
--- a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
+++ b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
@@ -4,15 +4,15 @@
     "net8.0": {
       "Microsoft.DotNet.ILCompiler": {
         "type": "Direct",
-        "requested": "[8.0.12, )",
-        "resolved": "8.0.12",
-        "contentHash": "zhXnz2574mBc/ocGoG+qB0BgyoK9bH7f7Te1fNQJGbpwLLwZO5KKaTBmfM8N8THb3a2vQVFonbBzQ//d15TmQQ=="
+        "requested": "[8.0.13, )",
+        "resolved": "8.0.13",
+        "contentHash": "CCIhseY9KUJDIYKt7qD1IRLQA6Hr/8Dky31KS6UrM2sFyaFUb2JLagT0Uy2BiSf1i1Qy3nPjRb0zc1JFogOi9w=="
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.12, )",
-        "resolved": "8.0.12",
-        "contentHash": "FV4HnQ3JI15PHnJ5PGTbz+rYvrih42oLi/7UMIshNwCwUZhTq13UzrggtXk4ygrcMcN+4jsS6hhshx2p/Zd0ig=="
+        "requested": "[8.0.13, )",
+        "resolved": "8.0.13",
+        "contentHash": "R19ZTaRiQAK+xo9ZwaHbF/1vb1wwR1Wn5+sqp9v8+CDjbdS8R6qftKdw0VSXWKm7VAMi7P+NCU4zxDzhEWcAwQ=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -86,15 +86,15 @@
     "net9.0": {
       "Microsoft.DotNet.ILCompiler": {
         "type": "Direct",
-        "requested": "[9.0.1, )",
-        "resolved": "9.0.1",
-        "contentHash": "YSn2LXCcOfV6VntSFASNmg1W8ldLPbI74YFx+uke2lYFuahhHfeQIgKS3W52R9phB6YLWT4MvLUPDQqUelplJg=="
+        "requested": "[9.0.2, )",
+        "resolved": "9.0.2",
+        "contentHash": "eyIUUqqPy7brzmoV04zabOUm0puEAAjZd3O3vuzan9iTTh0oIPwts8fLxZFoEZCFi63ACFtHbNBs5Y7myB3ZDQ=="
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[9.0.1, )",
-        "resolved": "9.0.1",
-        "contentHash": "BKFAkdhUUJivAoKuSWcT23WH9E+AWQMJootzt9X+lnesOD2a2yNIe1ZQFtxB8H3ayskLBD5gYyIbj5GIfw7rZg=="
+        "requested": "[9.0.2, )",
+        "resolved": "9.0.2",
+        "contentHash": "+KFnCLVPicEq99ko0tq+ycTvNLXHw0tImmTZjPloB/DOFLPT56KLfk5aS7wbgXRPzYhXTTBYLGaABea5mke77w=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",

From 2f977d87375c1924c33680f731c0689a6b519cce Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 17 Feb 2025 10:03:18 -0800
Subject: [PATCH 369/393] Add links to Faithlife.Data.

---
 README.md                              | 1 +
 docs/content/overview/use-with-orms.md | 1 +
 2 files changed, 2 insertions(+)

diff --git a/README.md b/README.md
index 15d8953d8..a3da17a0e 100644
--- a/README.md
+++ b/README.md
@@ -44,6 +44,7 @@ from Oracle.
 This library is compatible with popular .NET ORMs including:
 
 * [Dapper](https://dapperlib.github.io/Dapper/) ([GitHub](https://github.com/DapperLib/Dapper), [NuGet](https://www.nuget.org/packages/Dapper))
+* [Faithlife.Data](https://faithlife.github.io/FaithlifeData/) ([GitHub](https://github.com/Faithlife/FaithlifeData), [NuGet](https://www.nuget.org/packages/Faithlife.Data))
 * [FreeSql](https://freesql.net/) ([GitHub](https://github.com/dotnetcore/FreeSql), [NuGet](https://www.nuget.org/packages/FreeSql.Provider.MySqlConnector/))
 * [LINQ to DB](https://linq2db.github.io/) ([GitHub](https://github.com/linq2db/linq2db), [NuGet](https://www.nuget.org/packages/linq2db.MySqlConnector)) including ClickHouse support
 * [NHibernate](https://nhibernate.info/) ([GitHub](https://github.com/nhibernate/NHibernate.MySqlConnector), [NuGet](https://www.nuget.org/packages/NHibernate.Driver.MySqlConnector))
diff --git a/docs/content/overview/use-with-orms.md b/docs/content/overview/use-with-orms.md
index 4285767e6..840389b18 100644
--- a/docs/content/overview/use-with-orms.md
+++ b/docs/content/overview/use-with-orms.md
@@ -16,6 +16,7 @@ This library is compatible with popular .NET ORMs including:
 
 * [Dapper](https://dapperlib.github.io/Dapper/) ([GitHub](https://github.com/DapperLib/Dapper), [NuGet](https://www.nuget.org/packages/Dapper))
   * See [Tutorial: Connect to MySQL with Dapper](/tutorials/dapper/).
+* [Faithlife.Data](https://faithlife.github.io/FaithlifeData/) ([GitHub](https://github.com/Faithlife/FaithlifeData), [NuGet](https://www.nuget.org/packages/Faithlife.Data))
 * [FreeSql](https://freesql.net/) ([GitHub](https://github.com/dotnetcore/FreeSql), [NuGet](https://www.nuget.org/packages/FreeSql.Provider.MySqlConnector/))
 * [LINQ to DB](https://linq2db.github.io) ([GitHub](https://github.com/linq2db/linq2db), [NuGet](https://www.nuget.org/packages/linq2db.MySqlConnector))
 * [NHibernate](https://nhibernate.info/) ([GitHub](https://github.com/nhibernate/NHibernate.MySqlConnector), [NuGet](https://www.nuget.org/packages/NHibernate.Driver.MySqlConnector))

From a78c66800d21e15341b24b6d87c00f1b0cf5382d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 26 Feb 2025 11:01:47 -0800
Subject: [PATCH 370/393] Update lock files.

---
 src/MySqlConnector/packages.lock.json         | 14 --------------
 tests/IntegrationTests/packages.lock.json     | 14 --------------
 tests/MySqlConnector.Tests/packages.lock.json | 14 --------------
 3 files changed, 42 deletions(-)

diff --git a/src/MySqlConnector/packages.lock.json b/src/MySqlConnector/packages.lock.json
index 3fc5d7c53..3b22aa928 100644
--- a/src/MySqlConnector/packages.lock.json
+++ b/src/MySqlConnector/packages.lock.json
@@ -300,15 +300,6 @@
           "System.Memory": "4.5.5"
         }
       },
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net48": "1.0.3"
-        }
-      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -366,11 +357,6 @@
         "resolved": "8.0.0",
         "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
       },
-      "Microsoft.NETFramework.ReferenceAssemblies.net48": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "zMk4D+9zyiEWByyQ7oPImPN/Jhpj166Ky0Nlla4eXlNL8hI/BtSJsgR8Inldd4NNpIAH3oh8yym0W2DrhXdSLQ=="
-      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
diff --git a/tests/IntegrationTests/packages.lock.json b/tests/IntegrationTests/packages.lock.json
index ccd66edad..544dbe088 100644
--- a/tests/IntegrationTests/packages.lock.json
+++ b/tests/IntegrationTests/packages.lock.json
@@ -429,15 +429,6 @@
           "Microsoft.CodeCoverage": "17.12.0"
         }
       },
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3"
-        }
-      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -579,11 +570,6 @@
           "System.Runtime.CompilerServices.Unsafe": "6.0.0"
         }
       },
-      "Microsoft.NETFramework.ReferenceAssemblies.net481": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ=="
-      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
diff --git a/tests/MySqlConnector.Tests/packages.lock.json b/tests/MySqlConnector.Tests/packages.lock.json
index 5cea0042c..ee75ae00b 100644
--- a/tests/MySqlConnector.Tests/packages.lock.json
+++ b/tests/MySqlConnector.Tests/packages.lock.json
@@ -11,15 +11,6 @@
           "Microsoft.CodeCoverage": "17.12.0"
         }
       },
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3"
-        }
-      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -83,11 +74,6 @@
         "resolved": "17.12.0",
         "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
-      "Microsoft.NETFramework.ReferenceAssemblies.net481": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ=="
-      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",

From 4ba9742d8238863b669bc36aedca32970293749f Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Wed, 26 Feb 2025 11:22:34 -0800
Subject: [PATCH 371/393] Use PemEncoding in .NET 5 and later.

This prepares the way to use the new methods that will be added in .NET 10.
---
 src/MySqlConnector/Utilities/Utility.cs    | 10 ++++++++++
 tests/MySqlConnector.Tests/UtilityTests.cs | 12 ++++++------
 2 files changed, 16 insertions(+), 6 deletions(-)

diff --git a/src/MySqlConnector/Utilities/Utility.cs b/src/MySqlConnector/Utilities/Utility.cs
index 8cc9759b7..a62931e5a 100644
--- a/src/MySqlConnector/Utilities/Utility.cs
+++ b/src/MySqlConnector/Utilities/Utility.cs
@@ -97,6 +97,11 @@ public static void LoadRsaParameters(string key, RSA rsa)
 	public static RSAParameters GetRsaParameters(string key)
 #endif
 	{
+#if NET5_0_OR_GREATER
+		if (!PemEncoding.TryFind(key, out var pemFields))
+			throw new FormatException(string.Concat("Unrecognized PEM data: ", key.AsSpan(0, Math.Min(key.Length, 80))));
+		var isPrivate = key.AsSpan()[pemFields.Label].SequenceEqual("RSA PRIVATE KEY");
+#else
 		const string beginRsaPrivateKey = "-----BEGIN RSA PRIVATE KEY-----";
 		const string endRsaPrivateKey = "-----END RSA PRIVATE KEY-----";
 		const string beginPublicKey = "-----BEGIN PUBLIC KEY-----";
@@ -135,9 +140,14 @@ public static RSAParameters GetRsaParameters(string key)
 #else
 			throw new FormatException($"Missing expected '{pemFooter}' PEM footer: " + key[Math.Max(key.Length - 80, 0)..]);
 #endif
+#endif
 
 #if NETCOREAPP2_1_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+#if NET5_0_OR_GREATER
+		var keyChars = key.AsSpan()[pemFields.Base64Data];
+#else
 		var keyChars = key.AsSpan()[keyStartIndex..keyEndIndex];
+#endif
 		var bufferLength = keyChars.Length / 4 * 3;
 		byte[]? buffer = null;
 		Span<byte> bufferBytes = bufferLength > 1024 ?
diff --git a/tests/MySqlConnector.Tests/UtilityTests.cs b/tests/MySqlConnector.Tests/UtilityTests.cs
index 773a4f46d..9b4fab4af 100644
--- a/tests/MySqlConnector.Tests/UtilityTests.cs
+++ b/tests/MySqlConnector.Tests/UtilityTests.cs
@@ -87,9 +87,9 @@ public void ParseTimeSpanFails(string input)
 
 	[Theory]
 	[InlineData("", "")]
-	[InlineData("pre", "")]
-	[InlineData("", "post")]
-	[InlineData("pre", "post")]
+	[InlineData("pre\n", "")]
+	[InlineData("", "\npost")]
+	[InlineData("pre\n", "\npost")]
 	public void DecodePublicKey(string pre, string post)
 	{
 #if NET5_0_OR_GREATER
@@ -105,9 +105,9 @@ public void DecodePublicKey(string pre, string post)
 
 	[Theory]
 	[InlineData("", "")]
-	[InlineData("pre", "")]
-	[InlineData("", "post")]
-	[InlineData("pre", "post")]
+	[InlineData("pre\n", "")]
+	[InlineData("", "\npost")]
+	[InlineData("pre\n", "\npost")]
 	public void DecodePrivateKey(string pre, string post)
 	{
 #if NET5_0_OR_GREATER

From 5c0df946b6a51ead9a28dc5c1728c1cb5ab93161 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 27 Feb 2025 11:53:01 -0800
Subject: [PATCH 372/393] Use new solution file format.

---
 MySqlConnector.sln                 | 101 -------
 MySqlConnector.slnx                |  17 ++
 global.json                        |   2 +-
 tests/Benchmark/Benchmark.csproj   |   6 +-
 tests/Benchmark/Benchmark.sln      |  28 --
 tests/Benchmark/Benchmark.slnx     |   4 +
 tests/Benchmark/packages.lock.json | 459 +++++++++++++++++++++++++++++
 7 files changed, 484 insertions(+), 133 deletions(-)
 delete mode 100644 MySqlConnector.sln
 create mode 100644 MySqlConnector.slnx
 delete mode 100644 tests/Benchmark/Benchmark.sln
 create mode 100644 tests/Benchmark/Benchmark.slnx
 create mode 100644 tests/Benchmark/packages.lock.json

diff --git a/MySqlConnector.sln b/MySqlConnector.sln
deleted file mode 100644
index 85c7e52ff..000000000
--- a/MySqlConnector.sln
+++ /dev/null
@@ -1,101 +0,0 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 17
-VisualStudioVersion = 17.3.32611.2
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector", "src\MySqlConnector\MySqlConnector.csproj", "{F82378AF-274E-4FBA-8E45-27126D607B85}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.Tests", "tests\MySqlConnector.Tests\MySqlConnector.Tests.csproj", "{5E4FF768-D007-4D66-8220-AE789CC614C2}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IntegrationTests", "tests\IntegrationTests\IntegrationTests.csproj", "{407C2AC9-0CCA-4D6B-8698-362976D97730}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Conformance.Tests", "tests\Conformance.Tests\Conformance.Tests.csproj", "{CC0DA702-43E8-471E-9320-F36685C540A1}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.Logging.log4net", "src\MySqlConnector.Logging.log4net\MySqlConnector.Logging.log4net.csproj", "{A15647B8-FA3F-4536-BF4E-4F93F2FBFC75}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.Logging.Microsoft.Extensions.Logging", "src\MySqlConnector.Logging.Microsoft.Extensions.Logging\MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj", "{6A3F1732-F874-463E-9BB8-21690E7B8ED0}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.Logging.Serilog", "src\MySqlConnector.Logging.Serilog\MySqlConnector.Logging.Serilog.csproj", "{38806B85-6526-4A81-9905-45D3411B0AE2}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.Logging.NLog", "src\MySqlConnector.Logging.NLog\MySqlConnector.Logging.NLog.csproj", "{92015BEE-563A-4595-9243-0510D2B8767F}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.Authentication.Ed25519", "src\MySqlConnector.Authentication.Ed25519\MySqlConnector.Authentication.Ed25519.csproj", "{5DB4FA2E-910B-47CE-B467-F6852104D567}"
-EndProject
-Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{D0EDA522-FB29-4C44-B4EC-0D3AF72C016D}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SchemaCollectionGenerator", "tools\SchemaCollectionGenerator\SchemaCollectionGenerator.csproj", "{2C8FA08E-755D-454D-B98E-10A122673ABE}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.DependencyInjection", "src\MySqlConnector.DependencyInjection\MySqlConnector.DependencyInjection.csproj", "{D48B3619-7FE1-420C-A96C-B231B7EA73EA}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector.DependencyInjection.Tests", "tests\MySqlConnector.DependencyInjection.Tests\MySqlConnector.DependencyInjection.Tests.csproj", "{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySqlConnector.NativeAot.Tests", "tests\MySqlConnector.NativeAot.Tests\MySqlConnector.NativeAot.Tests.csproj", "{F3A1E4DF-A5CC-4447-A0FC-C6971218EEDE}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{F82378AF-274E-4FBA-8E45-27126D607B85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F82378AF-274E-4FBA-8E45-27126D607B85}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F82378AF-274E-4FBA-8E45-27126D607B85}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F82378AF-274E-4FBA-8E45-27126D607B85}.Release|Any CPU.Build.0 = Release|Any CPU
-		{5E4FF768-D007-4D66-8220-AE789CC614C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{5E4FF768-D007-4D66-8220-AE789CC614C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{5E4FF768-D007-4D66-8220-AE789CC614C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{5E4FF768-D007-4D66-8220-AE789CC614C2}.Release|Any CPU.Build.0 = Release|Any CPU
-		{407C2AC9-0CCA-4D6B-8698-362976D97730}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{407C2AC9-0CCA-4D6B-8698-362976D97730}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{407C2AC9-0CCA-4D6B-8698-362976D97730}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{407C2AC9-0CCA-4D6B-8698-362976D97730}.Release|Any CPU.Build.0 = Release|Any CPU
-		{CC0DA702-43E8-471E-9320-F36685C540A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{CC0DA702-43E8-471E-9320-F36685C540A1}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{CC0DA702-43E8-471E-9320-F36685C540A1}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{CC0DA702-43E8-471E-9320-F36685C540A1}.Release|Any CPU.Build.0 = Release|Any CPU
-		{A15647B8-FA3F-4536-BF4E-4F93F2FBFC75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{A15647B8-FA3F-4536-BF4E-4F93F2FBFC75}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{A15647B8-FA3F-4536-BF4E-4F93F2FBFC75}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{A15647B8-FA3F-4536-BF4E-4F93F2FBFC75}.Release|Any CPU.Build.0 = Release|Any CPU
-		{6A3F1732-F874-463E-9BB8-21690E7B8ED0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{6A3F1732-F874-463E-9BB8-21690E7B8ED0}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{6A3F1732-F874-463E-9BB8-21690E7B8ED0}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{6A3F1732-F874-463E-9BB8-21690E7B8ED0}.Release|Any CPU.Build.0 = Release|Any CPU
-		{38806B85-6526-4A81-9905-45D3411B0AE2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{38806B85-6526-4A81-9905-45D3411B0AE2}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{38806B85-6526-4A81-9905-45D3411B0AE2}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{38806B85-6526-4A81-9905-45D3411B0AE2}.Release|Any CPU.Build.0 = Release|Any CPU
-		{92015BEE-563A-4595-9243-0510D2B8767F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{92015BEE-563A-4595-9243-0510D2B8767F}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{92015BEE-563A-4595-9243-0510D2B8767F}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{92015BEE-563A-4595-9243-0510D2B8767F}.Release|Any CPU.Build.0 = Release|Any CPU
-		{5DB4FA2E-910B-47CE-B467-F6852104D567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{5DB4FA2E-910B-47CE-B467-F6852104D567}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{5DB4FA2E-910B-47CE-B467-F6852104D567}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{5DB4FA2E-910B-47CE-B467-F6852104D567}.Release|Any CPU.Build.0 = Release|Any CPU
-		{2C8FA08E-755D-454D-B98E-10A122673ABE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{2C8FA08E-755D-454D-B98E-10A122673ABE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{2C8FA08E-755D-454D-B98E-10A122673ABE}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{2C8FA08E-755D-454D-B98E-10A122673ABE}.Release|Any CPU.Build.0 = Release|Any CPU
-		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{D48B3619-7FE1-420C-A96C-B231B7EA73EA}.Release|Any CPU.Build.0 = Release|Any CPU
-		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{E41AD8B7-2F67-444F-A8DC-51C3C8B1FD16}.Release|Any CPU.Build.0 = Release|Any CPU
-		{F3A1E4DF-A5CC-4447-A0FC-C6971218EEDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{F3A1E4DF-A5CC-4447-A0FC-C6971218EEDE}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{F3A1E4DF-A5CC-4447-A0FC-C6971218EEDE}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{F3A1E4DF-A5CC-4447-A0FC-C6971218EEDE}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-	GlobalSection(NestedProjects) = preSolution
-		{2C8FA08E-755D-454D-B98E-10A122673ABE} = {D0EDA522-FB29-4C44-B4EC-0D3AF72C016D}
-	EndGlobalSection
-	GlobalSection(ExtensibilityGlobals) = postSolution
-		SolutionGuid = {4784EA55-5DE0-404F-8BEC-E14C202F01FA}
-	EndGlobalSection
-EndGlobal
diff --git a/MySqlConnector.slnx b/MySqlConnector.slnx
new file mode 100644
index 000000000..1c13b6d95
--- /dev/null
+++ b/MySqlConnector.slnx
@@ -0,0 +1,17 @@
+<Solution>
+  <Folder Name="/tools/">
+    <Project Path="tools/SchemaCollectionGenerator/SchemaCollectionGenerator.csproj" />
+  </Folder>
+  <Project Path="src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj" />
+  <Project Path="src/MySqlConnector.DependencyInjection/MySqlConnector.DependencyInjection.csproj" />
+  <Project Path="src/MySqlConnector.Logging.log4net/MySqlConnector.Logging.log4net.csproj" />
+  <Project Path="src/MySqlConnector.Logging.Microsoft.Extensions.Logging/MySqlConnector.Logging.Microsoft.Extensions.Logging.csproj" />
+  <Project Path="src/MySqlConnector.Logging.NLog/MySqlConnector.Logging.NLog.csproj" />
+  <Project Path="src/MySqlConnector.Logging.Serilog/MySqlConnector.Logging.Serilog.csproj" />
+  <Project Path="src/MySqlConnector/MySqlConnector.csproj" />
+  <Project Path="tests/Conformance.Tests/Conformance.Tests.csproj" />
+  <Project Path="tests/IntegrationTests/IntegrationTests.csproj" />
+  <Project Path="tests/MySqlConnector.DependencyInjection.Tests/MySqlConnector.DependencyInjection.Tests.csproj" />
+  <Project Path="tests/MySqlConnector.NativeAot.Tests/MySqlConnector.NativeAot.Tests.csproj" />
+  <Project Path="tests/MySqlConnector.Tests/MySqlConnector.Tests.csproj" />
+</Solution>
diff --git a/global.json b/global.json
index cdbb589ed..a02bac5a2 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
 {
   "sdk": {
-    "version": "9.0.100",
+    "version": "9.0.200",
     "rollForward": "latestFeature"
   }
 }
diff --git a/tests/Benchmark/Benchmark.csproj b/tests/Benchmark/Benchmark.csproj
index 96ff1f704..3edca13de 100644
--- a/tests/Benchmark/Benchmark.csproj
+++ b/tests/Benchmark/Benchmark.csproj
@@ -6,9 +6,9 @@
     <ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="BenchmarkDotNet" Version="0.13.10" />
-    <PackageReference Include="MySqlConnector" Version="2.3.1" />
-    <PackageReference Include="MySql.Data" Version="8.2.0" />
+    <PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
+    <PackageReference Include="MySqlConnector" Version="2.4.0" />
+    <PackageReference Include="MySql.Data" Version="9.2.0" />
   </ItemGroup>
 
 <!--  <ItemGroup>-->
diff --git a/tests/Benchmark/Benchmark.sln b/tests/Benchmark/Benchmark.sln
deleted file mode 100644
index 882832779..000000000
--- a/tests/Benchmark/Benchmark.sln
+++ /dev/null
@@ -1,28 +0,0 @@
-﻿
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 15
-VisualStudioVersion = 15.0.26403.7
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Benchmark", "Benchmark.csproj", "{102C06D1-ADB7-4661-9EC5-F6460BFEDB2A}"
-EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MySqlConnector", "..\..\src\MySqlConnector\MySqlConnector.csproj", "{6B4FEA15-4504-4BF7-B157-CEF9B60CFE13}"
-EndProject
-Global
-	GlobalSection(SolutionConfigurationPlatforms) = preSolution
-		Debug|Any CPU = Debug|Any CPU
-		Release|Any CPU = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(ProjectConfigurationPlatforms) = postSolution
-		{102C06D1-ADB7-4661-9EC5-F6460BFEDB2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{102C06D1-ADB7-4661-9EC5-F6460BFEDB2A}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{102C06D1-ADB7-4661-9EC5-F6460BFEDB2A}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{102C06D1-ADB7-4661-9EC5-F6460BFEDB2A}.Release|Any CPU.Build.0 = Release|Any CPU
-		{6B4FEA15-4504-4BF7-B157-CEF9B60CFE13}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{6B4FEA15-4504-4BF7-B157-CEF9B60CFE13}.Debug|Any CPU.Build.0 = Debug|Any CPU
-		{6B4FEA15-4504-4BF7-B157-CEF9B60CFE13}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{6B4FEA15-4504-4BF7-B157-CEF9B60CFE13}.Release|Any CPU.Build.0 = Release|Any CPU
-	EndGlobalSection
-	GlobalSection(SolutionProperties) = preSolution
-		HideSolutionNode = FALSE
-	EndGlobalSection
-EndGlobal
diff --git a/tests/Benchmark/Benchmark.slnx b/tests/Benchmark/Benchmark.slnx
new file mode 100644
index 000000000..e8c8f4395
--- /dev/null
+++ b/tests/Benchmark/Benchmark.slnx
@@ -0,0 +1,4 @@
+<Solution>
+  <Project Path="../../src/MySqlConnector/MySqlConnector.csproj" />
+  <Project Path="Benchmark.csproj" />
+</Solution>
diff --git a/tests/Benchmark/packages.lock.json b/tests/Benchmark/packages.lock.json
new file mode 100644
index 000000000..2fcbdebbd
--- /dev/null
+++ b/tests/Benchmark/packages.lock.json
@@ -0,0 +1,459 @@
+{
+  "version": 1,
+  "dependencies": {
+    "net8.0": {
+      "BenchmarkDotNet": {
+        "type": "Direct",
+        "requested": "[0.14.0, )",
+        "resolved": "0.14.0",
+        "contentHash": "eIPSDKi3oni734M1rt/XJAwGQQOIf9gLjRRKKJ0HuVy3vYd7gnmAIX1bTjzI9ZbAY/nPddgqqgM/TeBYitMCIg==",
+        "dependencies": {
+          "BenchmarkDotNet.Annotations": "0.14.0",
+          "CommandLineParser": "2.9.1",
+          "Gee.External.Capstone": "2.3.0",
+          "Iced": "1.17.0",
+          "Microsoft.CodeAnalysis.CSharp": "4.1.0",
+          "Microsoft.Diagnostics.Runtime": "2.2.332302",
+          "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.8",
+          "Microsoft.DotNet.PlatformAbstractions": "3.1.6",
+          "Perfolizer": "[0.3.17]",
+          "System.Management": "5.0.0"
+        }
+      },
+      "MySql.Data": {
+        "type": "Direct",
+        "requested": "[9.2.0, )",
+        "resolved": "9.2.0",
+        "contentHash": "S/O4qGf5+fG64d+IHXpZ+wC08m0HsqIwb2xtEY41jdjXYdV2uEtdEy+Jo2ObQamGj0/GD7X6LUCBbh4eb3fKew==",
+        "dependencies": {
+          "BouncyCastle.Cryptography": "2.3.1",
+          "Google.Protobuf": "3.26.1",
+          "K4os.Compression.LZ4.Streams": "1.3.8",
+          "System.Buffers": "4.5.1",
+          "System.Configuration.ConfigurationManager": "8.0.0",
+          "System.Diagnostics.DiagnosticSource": "8.0.1",
+          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
+          "System.Runtime.Loader": "4.3.0",
+          "System.Security.Permissions": "8.0.0",
+          "System.Text.Encoding.CodePages": "8.0.0",
+          "System.Text.Json": "9.0.0",
+          "System.Threading.Tasks.Extensions": "4.5.4",
+          "ZstdSharp.Port": "0.8.0"
+        }
+      },
+      "MySqlConnector": {
+        "type": "Direct",
+        "requested": "[2.4.0, )",
+        "resolved": "2.4.0",
+        "contentHash": "78M+gVOjbdZEDIyXQqcA7EYlCGS3tpbUELHvn6638A2w0pkPI625ixnzsa5staAd3N9/xFmPJtkKDYwsXpFi/w==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
+          "Microsoft.Extensions.Logging.Abstractions": "8.0.2"
+        }
+      },
+      "BenchmarkDotNet.Annotations": {
+        "type": "Transitive",
+        "resolved": "0.14.0",
+        "contentHash": "CUDCg6bgHrDzhjnA+IOBl5gAo8Y5hZ2YSs7MBXrYMlMKpBZqrD5ez0537uDveOkcf+YWAoK+S4sMcuWPbIz8bw=="
+      },
+      "BouncyCastle.Cryptography": {
+        "type": "Transitive",
+        "resolved": "2.3.1",
+        "contentHash": "buwoISwecYke3CmgG1AQSg+sNZjJeIb93vTAtJiHZX35hP/teYMxsfg0NDXGUKjGx6BKBTNKc77O2M3vKvlXZQ=="
+      },
+      "CommandLineParser": {
+        "type": "Transitive",
+        "resolved": "2.9.1",
+        "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA=="
+      },
+      "Gee.External.Capstone": {
+        "type": "Transitive",
+        "resolved": "2.3.0",
+        "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw=="
+      },
+      "Google.Protobuf": {
+        "type": "Transitive",
+        "resolved": "3.26.1",
+        "contentHash": "CHZX8zXqhF/fdUtd+AYzew8T2HFkAoe5c7lbGxZY/qryAlQXckDvM5BfOJjXlMS7kyICqQTMszj4w1bX5uBJ/w=="
+      },
+      "Iced": {
+        "type": "Transitive",
+        "resolved": "1.17.0",
+        "contentHash": "8x+HCVTl/HHTGpscH3vMBhV8sknN/muZFw9s3TsI8SA6+c43cOTCi2+jE4KsU8pNLbJ++iF2ZFcpcXHXtDglnw=="
+      },
+      "K4os.Compression.LZ4": {
+        "type": "Transitive",
+        "resolved": "1.3.8",
+        "contentHash": "LhwlPa7c1zs1OV2XadMtAWdImjLIsqFJPoRcIWAadSRn0Ri1DepK65UbWLPmt4riLqx2d40xjXRk0ogpqNtK7g=="
+      },
+      "K4os.Compression.LZ4.Streams": {
+        "type": "Transitive",
+        "resolved": "1.3.8",
+        "contentHash": "P15qr8dZAeo9GvYbUIPEYFQ0MEJ0i5iqr37wsYeRC3la2uCldOoeCa6to0CZ1taiwxIV+Mk8NGuZi+4iWivK9w==",
+        "dependencies": {
+          "K4os.Compression.LZ4": "1.3.8",
+          "K4os.Hash.xxHash": "1.0.8",
+          "System.IO.Pipelines": "6.0.3"
+        }
+      },
+      "K4os.Hash.xxHash": {
+        "type": "Transitive",
+        "resolved": "1.0.8",
+        "contentHash": "Wp2F7BamQ2Q/7Hk834nV9vRQapgcr8kgv9Jvfm8J3D0IhDqZMMl+a2yxUq5ltJitvXvQfB8W6K4F4fCbw/P6YQ=="
+      },
+      "Microsoft.Bcl.AsyncInterfaces": {
+        "type": "Transitive",
+        "resolved": "1.1.0",
+        "contentHash": "1Am6l4Vpn3/K32daEqZI+FFr96OlZkgwK2LcT3pZ2zWubR5zTPW3/FkO1Rat9kb7oQOa4rxgl9LJHc5tspCWfg=="
+      },
+      "Microsoft.CodeAnalysis.Analyzers": {
+        "type": "Transitive",
+        "resolved": "3.3.3",
+        "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ=="
+      },
+      "Microsoft.CodeAnalysis.Common": {
+        "type": "Transitive",
+        "resolved": "4.1.0",
+        "contentHash": "bNzTyxP3iD5FPFHfVDl15Y6/wSoI7e3MeV0lOaj9igbIKTjgrmuw6LoVJ06jUNFA7+KaDC/OIsStWl/FQJz6sQ==",
+        "dependencies": {
+          "Microsoft.CodeAnalysis.Analyzers": "3.3.3",
+          "System.Collections.Immutable": "5.0.0",
+          "System.Memory": "4.5.4",
+          "System.Reflection.Metadata": "5.0.0",
+          "System.Runtime.CompilerServices.Unsafe": "5.0.0",
+          "System.Text.Encoding.CodePages": "4.5.1",
+          "System.Threading.Tasks.Extensions": "4.5.4"
+        }
+      },
+      "Microsoft.CodeAnalysis.CSharp": {
+        "type": "Transitive",
+        "resolved": "4.1.0",
+        "contentHash": "sbu6kDGzo9bfQxuqWpeEE7I9P30bSuZEnpDz9/qz20OU6pm79Z63+/BsAzO2e/R/Q97kBrpj647wokZnEVr97w==",
+        "dependencies": {
+          "Microsoft.CodeAnalysis.Common": "[4.1.0]"
+        }
+      },
+      "Microsoft.Diagnostics.NETCore.Client": {
+        "type": "Transitive",
+        "resolved": "0.2.251802",
+        "contentHash": "bqnYl6AdSeboeN4v25hSukK6Odm6/54E3Y2B8rBvgqvAW0mF8fo7XNRVE2DMOG7Rk0fiuA079QIH28+V+W1Zdg==",
+        "dependencies": {
+          "Microsoft.Bcl.AsyncInterfaces": "1.1.0",
+          "Microsoft.Extensions.Logging": "2.1.1"
+        }
+      },
+      "Microsoft.Diagnostics.Runtime": {
+        "type": "Transitive",
+        "resolved": "2.2.332302",
+        "contentHash": "Hp84ivxSKIMTBzYSATxmUsm3YSXHWivcwiRRbsydGmqujMUK8BAueLN0ssAVEOkOBmh0vjUBhrq7YcroT7VCug==",
+        "dependencies": {
+          "Microsoft.Diagnostics.NETCore.Client": "0.2.251802",
+          "System.Collections.Immutable": "5.0.0",
+          "System.Runtime.CompilerServices.Unsafe": "5.0.0"
+        }
+      },
+      "Microsoft.Diagnostics.Tracing.TraceEvent": {
+        "type": "Transitive",
+        "resolved": "3.1.8",
+        "contentHash": "kl3UMrZKSeSEYZ8rt/GjLUQToREjgQABqfg6PzQBmSlYHTZOKE9ePEOS2xptROQ9SVvngg3QGX51TIT11iZ0wA==",
+        "dependencies": {
+          "Microsoft.Win32.Registry": "4.4.0",
+          "System.Runtime.CompilerServices.Unsafe": "5.0.0"
+        }
+      },
+      "Microsoft.DotNet.PlatformAbstractions": {
+        "type": "Transitive",
+        "resolved": "3.1.6",
+        "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg=="
+      },
+      "Microsoft.Extensions.Configuration": {
+        "type": "Transitive",
+        "resolved": "2.1.1",
+        "contentHash": "LjVKO6P2y52c5ZhTLX/w8zc5H4Y3J/LJsgqTBj49TtFq/hAtVNue/WA0F6/7GMY90xhD7K0MDZ4qpOeWXbLvzg==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Abstractions": "2.1.1"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Abstractions": {
+        "type": "Transitive",
+        "resolved": "2.1.1",
+        "contentHash": "VfuZJNa0WUshZ/+8BFZAhwFKiKuu/qOUCFntfdLpHj7vcRnsGHqd3G2Hse78DM+pgozczGM63lGPRLmy+uhUOA==",
+        "dependencies": {
+          "Microsoft.Extensions.Primitives": "2.1.1"
+        }
+      },
+      "Microsoft.Extensions.Configuration.Binder": {
+        "type": "Transitive",
+        "resolved": "2.1.1",
+        "contentHash": "fcLCTS03poWE4v9tSNBr3pWn0QwGgAn1vzqHXlXgvqZeOc7LvQNzaWcKRQZTdEc3+YhQKwMsOtm3VKSA2aWQ8w==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration": "2.1.1"
+        }
+      },
+      "Microsoft.Extensions.DependencyInjection.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.2",
+        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
+      },
+      "Microsoft.Extensions.Logging": {
+        "type": "Transitive",
+        "resolved": "2.1.1",
+        "contentHash": "hh+mkOAQDTp6XH80xJt3+wwYVzkbwYQl9XZRCz4Um0JjP/o7N9vHM3rZ6wwwtr+BBe/L6iBO2sz0px6OWBzqZQ==",
+        "dependencies": {
+          "Microsoft.Extensions.Configuration.Binder": "2.1.1",
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1",
+          "Microsoft.Extensions.Logging.Abstractions": "2.1.1",
+          "Microsoft.Extensions.Options": "2.1.1"
+        }
+      },
+      "Microsoft.Extensions.Logging.Abstractions": {
+        "type": "Transitive",
+        "resolved": "8.0.2",
+        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
+        }
+      },
+      "Microsoft.Extensions.Options": {
+        "type": "Transitive",
+        "resolved": "2.1.1",
+        "contentHash": "V7lXCU78lAbzaulCGFKojcCyG8RTJicEbiBkPJjFqiqXwndEBBIehdXRMWEVU3UtzQ1yDvphiWUL9th6/4gJ7w==",
+        "dependencies": {
+          "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1",
+          "Microsoft.Extensions.Primitives": "2.1.1"
+        }
+      },
+      "Microsoft.Extensions.Primitives": {
+        "type": "Transitive",
+        "resolved": "2.1.1",
+        "contentHash": "scJ1GZNIxMmjpENh0UZ8XCQ6vzr/LzeF9WvEA51Ix2OQGAs9WPgPu8ABVUdvpKPLuor/t05gm6menJK3PwqOXg==",
+        "dependencies": {
+          "System.Memory": "4.5.1",
+          "System.Runtime.CompilerServices.Unsafe": "4.5.1"
+        }
+      },
+      "Microsoft.NETCore.Platforms": {
+        "type": "Transitive",
+        "resolved": "5.0.0",
+        "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ=="
+      },
+      "Microsoft.NETCore.Targets": {
+        "type": "Transitive",
+        "resolved": "1.1.0",
+        "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
+      },
+      "Microsoft.Win32.Registry": {
+        "type": "Transitive",
+        "resolved": "5.0.0",
+        "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
+        "dependencies": {
+          "System.Security.AccessControl": "5.0.0",
+          "System.Security.Principal.Windows": "5.0.0"
+        }
+      },
+      "Perfolizer": {
+        "type": "Transitive",
+        "resolved": "0.3.17",
+        "contentHash": "FQgtCoF2HFwvzKWulAwBS5BGLlh8pgbrJtOp47jyBwh2CW16juVtacN1azOA2BqdrJXkXTNLNRMo7ZlHHiuAnA=="
+      },
+      "System.Buffers": {
+        "type": "Transitive",
+        "resolved": "4.5.1",
+        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
+      },
+      "System.CodeDom": {
+        "type": "Transitive",
+        "resolved": "5.0.0",
+        "contentHash": "JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ=="
+      },
+      "System.Collections.Immutable": {
+        "type": "Transitive",
+        "resolved": "5.0.0",
+        "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g=="
+      },
+      "System.Configuration.ConfigurationManager": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==",
+        "dependencies": {
+          "System.Diagnostics.EventLog": "8.0.0",
+          "System.Security.Cryptography.ProtectedData": "8.0.0"
+        }
+      },
+      "System.Diagnostics.DiagnosticSource": {
+        "type": "Transitive",
+        "resolved": "8.0.1",
+        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg=="
+      },
+      "System.Diagnostics.EventLog": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A=="
+      },
+      "System.IO": {
+        "type": "Transitive",
+        "resolved": "4.3.0",
+        "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0",
+          "Microsoft.NETCore.Targets": "1.1.0",
+          "System.Runtime": "4.3.0",
+          "System.Text.Encoding": "4.3.0",
+          "System.Threading.Tasks": "4.3.0"
+        }
+      },
+      "System.IO.Pipelines": {
+        "type": "Transitive",
+        "resolved": "9.0.0",
+        "contentHash": "eA3cinogwaNB4jdjQHOP3Z3EuyiDII7MT35jgtnsA4vkn0LUrrSHsU0nzHTzFzmaFYeKV7MYyMxOocFzsBHpTw=="
+      },
+      "System.Management": {
+        "type": "Transitive",
+        "resolved": "5.0.0",
+        "contentHash": "MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "5.0.0",
+          "Microsoft.Win32.Registry": "5.0.0",
+          "System.CodeDom": "5.0.0"
+        }
+      },
+      "System.Memory": {
+        "type": "Transitive",
+        "resolved": "4.5.4",
+        "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw=="
+      },
+      "System.Reflection": {
+        "type": "Transitive",
+        "resolved": "4.3.0",
+        "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0",
+          "Microsoft.NETCore.Targets": "1.1.0",
+          "System.IO": "4.3.0",
+          "System.Reflection.Primitives": "4.3.0",
+          "System.Runtime": "4.3.0"
+        }
+      },
+      "System.Reflection.Metadata": {
+        "type": "Transitive",
+        "resolved": "5.0.0",
+        "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ=="
+      },
+      "System.Reflection.Primitives": {
+        "type": "Transitive",
+        "resolved": "4.3.0",
+        "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0",
+          "Microsoft.NETCore.Targets": "1.1.0",
+          "System.Runtime": "4.3.0"
+        }
+      },
+      "System.Runtime": {
+        "type": "Transitive",
+        "resolved": "4.3.0",
+        "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0",
+          "Microsoft.NETCore.Targets": "1.1.0"
+        }
+      },
+      "System.Runtime.CompilerServices.Unsafe": {
+        "type": "Transitive",
+        "resolved": "6.0.0",
+        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
+      },
+      "System.Runtime.Loader": {
+        "type": "Transitive",
+        "resolved": "4.3.0",
+        "contentHash": "DHMaRn8D8YCK2GG2pw+UzNxn/OHVfaWx7OTLBD/hPegHZZgcZh3H6seWegrC4BYwsfuGrywIuT+MQs+rPqRLTQ==",
+        "dependencies": {
+          "System.IO": "4.3.0",
+          "System.Reflection": "4.3.0",
+          "System.Runtime": "4.3.0"
+        }
+      },
+      "System.Security.AccessControl": {
+        "type": "Transitive",
+        "resolved": "5.0.0",
+        "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "5.0.0",
+          "System.Security.Principal.Windows": "5.0.0"
+        }
+      },
+      "System.Security.Cryptography.ProtectedData": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg=="
+      },
+      "System.Security.Permissions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "v/BBylw7XevuAsHXoX9dDUUfmBIcUf7Lkz8K3ZXIKz3YRKpw8YftpSir4n4e/jDTKFoaK37AsC3xnk+GNFI1Ow==",
+        "dependencies": {
+          "System.Windows.Extensions": "8.0.0"
+        }
+      },
+      "System.Security.Principal.Windows": {
+        "type": "Transitive",
+        "resolved": "5.0.0",
+        "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
+      },
+      "System.Text.Encoding": {
+        "type": "Transitive",
+        "resolved": "4.3.0",
+        "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0",
+          "Microsoft.NETCore.Targets": "1.1.0",
+          "System.Runtime": "4.3.0"
+        }
+      },
+      "System.Text.Encoding.CodePages": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg=="
+      },
+      "System.Text.Encodings.Web": {
+        "type": "Transitive",
+        "resolved": "9.0.0",
+        "contentHash": "e2hMgAErLbKyUUwt18qSBf9T5Y+SFAL3ZedM8fLupkVj8Rj2PZ9oxQ37XX2LF8fTO1wNIxvKpihD7Of7D/NxZw=="
+      },
+      "System.Text.Json": {
+        "type": "Transitive",
+        "resolved": "9.0.0",
+        "contentHash": "js7+qAu/9mQvnhA4EfGMZNEzXtJCDxgkgj8ohuxq/Qxv+R56G+ljefhiJHOxTNiw54q8vmABCWUwkMulNdlZ4A==",
+        "dependencies": {
+          "System.IO.Pipelines": "9.0.0",
+          "System.Text.Encodings.Web": "9.0.0"
+        }
+      },
+      "System.Threading.Tasks": {
+        "type": "Transitive",
+        "resolved": "4.3.0",
+        "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
+        "dependencies": {
+          "Microsoft.NETCore.Platforms": "1.1.0",
+          "Microsoft.NETCore.Targets": "1.1.0",
+          "System.Runtime": "4.3.0"
+        }
+      },
+      "System.Threading.Tasks.Extensions": {
+        "type": "Transitive",
+        "resolved": "4.5.4",
+        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg=="
+      },
+      "System.Windows.Extensions": {
+        "type": "Transitive",
+        "resolved": "8.0.0",
+        "contentHash": "Obg3a90MkOw9mYKxrardLpY2u0axDMrSmy4JCdq2cYbelM2cUwmUir5Bomvd1yxmPL9h5LVHU1tuKBZpUjfASg=="
+      },
+      "ZstdSharp.Port": {
+        "type": "Transitive",
+        "resolved": "0.8.0",
+        "contentHash": "Z62eNBIu8E8YtbqlMy57tK3dV1+m2b9NhPeaYovB5exmLKvrGCqOhJTzrEUH5VyUWU6vwX3c1XHJGhW5HVs8dA=="
+      }
+    }
+  }
+}
\ No newline at end of file

From 97b776b1a4fe21737a105bc5c3b49f60b6371bc1 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 9 Mar 2025 13:42:43 -0700
Subject: [PATCH 373/393] Read GUIDs from out parameters. Fixes #1528 (#1546)

---
 .../ColumnReaders/ColumnReader.cs             |   4 +-
 src/MySqlConnector/Core/CachedParameter.cs    |   4 +-
 src/MySqlConnector/Core/CachedProcedure.cs    |  19 +--
 src/MySqlConnector/Core/ColumnTypeMetadata.cs |  20 +++-
 .../Core/SingleCommandPayloadCreator.cs       |  18 ++-
 src/MySqlConnector/Core/TypeMapper.cs         |  18 ++-
 .../IntegrationTests/StoredProcedureTests.cs  |  54 +++++++++
 tests/IntegrationTests/packages.lock.json     |  14 +++
 .../CachedProcedureTests.cs                   | 110 ++++++++++--------
 tests/MySqlConnector.Tests/packages.lock.json |  14 +++
 10 files changed, 206 insertions(+), 69 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/ColumnReader.cs b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
index 86ba9145f..ec6d1c85d 100644
--- a/src/MySqlConnector/ColumnReaders/ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
@@ -45,13 +45,13 @@ public static ColumnReader Create(bool isBinary, ColumnDefinitionPayload columnD
 				return BitColumnReader.Instance;
 
 			case ColumnType.String:
+			case ColumnType.VarString:
 				if (connection.GuidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
 					return GuidChar36ColumnReader.Instance;
 				if (connection.GuidFormat == MySqlGuidFormat.Char32 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
 					return GuidChar32ColumnReader.Instance;
-				goto case ColumnType.VarString;
+				goto case ColumnType.VarChar;
 
-			case ColumnType.VarString:
 			case ColumnType.VarChar:
 			case ColumnType.TinyBlob:
 			case ColumnType.Blob:
diff --git a/src/MySqlConnector/Core/CachedParameter.cs b/src/MySqlConnector/Core/CachedParameter.cs
index 3edaed0c5..74d1c2911 100644
--- a/src/MySqlConnector/Core/CachedParameter.cs
+++ b/src/MySqlConnector/Core/CachedParameter.cs
@@ -2,7 +2,7 @@ namespace MySqlConnector.Core;
 
 internal sealed class CachedParameter
 {
-	public CachedParameter(int ordinalPosition, string? mode, string name, string dataType, bool unsigned, int length)
+	public CachedParameter(int ordinalPosition, string? mode, string name, string dataType, bool unsigned, int length, MySqlGuidFormat guidFormat)
 	{
 		Position = ordinalPosition;
 		if (Position == 0)
@@ -14,7 +14,7 @@ public CachedParameter(int ordinalPosition, string? mode, string name, string da
 		else if (string.Equals(mode, "out", StringComparison.OrdinalIgnoreCase))
 			Direction = ParameterDirection.Output;
 		Name = name;
-		MySqlDbType = TypeMapper.Instance.GetMySqlDbType(dataType, unsigned, length);
+		MySqlDbType = TypeMapper.Instance.GetMySqlDbType(dataType, unsigned, length, guidFormat);
 		Length = length;
 	}
 
diff --git a/src/MySqlConnector/Core/CachedProcedure.cs b/src/MySqlConnector/Core/CachedProcedure.cs
index acafaa43a..d018c88a9 100644
--- a/src/MySqlConnector/Core/CachedProcedure.cs
+++ b/src/MySqlConnector/Core/CachedProcedure.cs
@@ -39,11 +39,11 @@ internal sealed class CachedProcedure
 					object o => Encoding.UTF8.GetString((byte[]) o),
 				};
 
-				var parsedParameters = ParseParameters(parametersSql);
+				var parsedParameters = ParseParameters(parametersSql, connection.GuidFormat);
 				if (returnsSql.Length != 0)
 				{
 					var returnDataType = ParseDataType(returnsSql, out var unsigned, out var length);
-					parsedParameters.Insert(0, CreateCachedParameter(0, null, "", returnDataType, unsigned, length, returnsSql));
+					parsedParameters.Insert(0, CreateCachedParameter(0, null, "", returnDataType, unsigned, length, connection.GuidFormat, returnsSql));
 				}
 
 				return new CachedProcedure(schema, component, parsedParameters);
@@ -91,7 +91,8 @@ FROM information_schema.parameters
 					!reader.IsDBNull(2) ? reader.GetString(2) : "",
 					dataType,
 					unsigned,
-					length
+					length,
+					connection.GuidFormat
 				));
 			}
 		}
@@ -132,6 +133,10 @@ internal MySqlParameterCollection AlignParamsWithDb(MySqlParameterCollection? pa
 			if (!alignParam.HasSetDbType)
 				alignParam.MySqlDbType = cachedParam.MySqlDbType;
 
+			// for a GUID column, pass along the length so the out parameter can be cast to the right size
+			if (alignParam.MySqlDbType == MySqlDbType.Guid && cachedParam.Direction is ParameterDirection.Output or ParameterDirection.InputOutput)
+				alignParam.Size = cachedParam.Length;
+
 			// cached parameters are ordered by ordinal position
 			alignedParams.Add(alignParam);
 		}
@@ -139,7 +144,7 @@ internal MySqlParameterCollection AlignParamsWithDb(MySqlParameterCollection? pa
 		return alignedParams;
 	}
 
-	internal static List<CachedParameter> ParseParameters(string parametersSql)
+	internal static List<CachedParameter> ParseParameters(string parametersSql, MySqlGuidFormat guidFormat)
 	{
 		// strip comments
 		parametersSql = s_cStyleComments.Replace(parametersSql, "");
@@ -184,7 +189,7 @@ internal static List<CachedParameter> ParseParameters(string parametersSql)
 			var name = parts.Groups[1].Success ? parts.Groups[1].Value.Replace("``", "`") : parts.Groups[2].Value;
 
 			var dataType = ParseDataType(parts.Groups[3].Value, out var unsigned, out var length);
-			cachedParameters.Add(CreateCachedParameter(i + 1, direction, name, dataType, unsigned, length, originalString));
+			cachedParameters.Add(CreateCachedParameter(i + 1, direction, name, dataType, unsigned, length, guidFormat, originalString));
 		}
 
 		return cachedParameters;
@@ -222,11 +227,11 @@ internal static string ParseDataType(string sql, out bool unsigned, out int leng
 		return type ?? list[0];
 	}
 
-	private static CachedParameter CreateCachedParameter(int ordinal, string? direction, string name, string dataType, bool unsigned, int length, string originalSql)
+	private static CachedParameter CreateCachedParameter(int ordinal, string? direction, string name, string dataType, bool unsigned, int length, MySqlGuidFormat guidFormat, string originalSql)
 	{
 		try
 		{
-			return new CachedParameter(ordinal, direction, name, dataType, unsigned, length);
+			return new CachedParameter(ordinal, direction, name, dataType, unsigned, length, guidFormat);
 		}
 		catch (NullReferenceException ex)
 		{
diff --git a/src/MySqlConnector/Core/ColumnTypeMetadata.cs b/src/MySqlConnector/Core/ColumnTypeMetadata.cs
index eadb27de4..aeed89694 100644
--- a/src/MySqlConnector/Core/ColumnTypeMetadata.cs
+++ b/src/MySqlConnector/Core/ColumnTypeMetadata.cs
@@ -1,8 +1,21 @@
+using System.Runtime.CompilerServices;
+
 namespace MySqlConnector.Core;
 
-internal sealed class ColumnTypeMetadata(string dataTypeName, DbTypeMapping dbTypeMapping, MySqlDbType mySqlDbType, bool isUnsigned = false, bool binary = false, int length = 0, string? simpleDataTypeName = null, string? createFormat = null, long columnSize = 0)
+internal sealed class ColumnTypeMetadata(string dataTypeName, DbTypeMapping dbTypeMapping, MySqlDbType mySqlDbType, bool isUnsigned = false, bool binary = false, int length = 0, string? simpleDataTypeName = null, string? createFormat = null, long columnSize = 0, MySqlGuidFormat guidFormat = MySqlGuidFormat.Default)
 {
-	public static string CreateLookupKey(string columnTypeName, bool isUnsigned, int length) => $"{columnTypeName}|{(isUnsigned ? "u" : "s")}|{length}";
+	public static string CreateLookupKey(string columnTypeName, bool isUnsigned, int length, MySqlGuidFormat guidFormat) =>
+		$"{columnTypeName}|{(isUnsigned ? "u" : "s")}|{length}|{GetGuidFormatLookupKey(guidFormat)}";
+
+	[MethodImpl(MethodImplOptions.AggressiveInlining)]
+	private static string GetGuidFormatLookupKey(MySqlGuidFormat guidFormat) =>
+		guidFormat switch
+		{
+			MySqlGuidFormat.Char36 => "c36",
+			MySqlGuidFormat.Char32 => "c32",
+			MySqlGuidFormat.Binary16 or MySqlGuidFormat.TimeSwapBinary16 or MySqlGuidFormat.LittleEndianBinary16 => "b16",
+			_ => "def",
+		};
 
 	public string DataTypeName { get; } = dataTypeName;
 	public string SimpleDataTypeName { get; } = simpleDataTypeName ?? dataTypeName;
@@ -13,6 +26,7 @@ internal sealed class ColumnTypeMetadata(string dataTypeName, DbTypeMapping dbTy
 	public long ColumnSize { get; } = columnSize;
 	public bool IsUnsigned { get; } = isUnsigned;
 	public int Length { get; } = length;
+	public MySqlGuidFormat GuidFormat { get; } = guidFormat;
 
-	public string CreateLookupKey() => CreateLookupKey(DataTypeName, IsUnsigned, Length);
+	public string CreateLookupKey() => CreateLookupKey(DataTypeName, IsUnsigned, Length, GuidFormat);
 }
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index e6c2c641d..49b81a7b0 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -214,8 +214,24 @@ private static bool WriteStoredProcedure(IMySqlCommand command, IDictionary<stri
 					break;
 				case ParameterDirection.Output:
 					outParameters.Add(param);
-					outParameterNames.Add(outName);
 					argParameterNames.Add(outName);
+
+					// special handling for GUIDs to ensure that the result set has a type and length that will be autodetected as a GUID
+					switch (param.MySqlDbType, param.Size)
+					{
+						case (MySqlDbType.Guid, 16):
+							outParameterNames.Add($"CAST({outName} AS BINARY(16))");
+							break;
+						case (MySqlDbType.Guid, 32):
+							outParameterNames.Add($"CAST({outName} AS CHAR(32))");
+							break;
+						case (MySqlDbType.Guid, 36):
+							outParameterNames.Add($"CAST({outName} AS CHAR(36))");
+							break;
+						default:
+							outParameterNames.Add(outName);
+							break;
+					}
 					break;
 				case ParameterDirection.ReturnValue:
 					returnParameter = param;
diff --git a/src/MySqlConnector/Core/TypeMapper.cs b/src/MySqlConnector/Core/TypeMapper.cs
index 6397f9182..cfb6e97f1 100644
--- a/src/MySqlConnector/Core/TypeMapper.cs
+++ b/src/MySqlConnector/Core/TypeMapper.cs
@@ -115,6 +115,9 @@ private TypeMapper()
 #endif
 		var typeGuid = AddDbTypeMapping(new(typeof(Guid), [DbType.Guid], convert: convertGuid));
 		AddColumnTypeMetadata(new("CHAR", typeGuid, MySqlDbType.Guid, length: 36, simpleDataTypeName: "CHAR(36)", createFormat: "CHAR(36)"));
+		AddColumnTypeMetadata(new("CHAR", typeGuid, MySqlDbType.Guid, length: 32, guidFormat: MySqlGuidFormat.Char32));
+		AddColumnTypeMetadata(new("CHAR", typeGuid, MySqlDbType.Guid, length: 36, guidFormat: MySqlGuidFormat.Char36));
+		AddColumnTypeMetadata(new("BINARY", typeGuid, MySqlDbType.Guid, binary: true, length: 16, guidFormat: MySqlGuidFormat.Binary16));
 
 		// null
 		var typeNull = AddDbTypeMapping(new(typeof(object), [DbType.Object]));
@@ -181,15 +184,20 @@ private void AddColumnTypeMetadata(ColumnTypeMetadata columnTypeMetadata)
 
 	public DbTypeMapping? GetDbTypeMapping(string columnTypeName, bool unsigned = false, int length = 0)
 	{
-		return GetColumnTypeMetadata(columnTypeName, unsigned, length)?.DbTypeMapping;
+		return GetColumnTypeMetadata(columnTypeName, unsigned, length, MySqlGuidFormat.Default)?.DbTypeMapping;
 	}
 
-	public MySqlDbType GetMySqlDbType(string typeName, bool unsigned, int length) => GetColumnTypeMetadata(typeName, unsigned, length)!.MySqlDbType;
+	public MySqlDbType GetMySqlDbType(string typeName, bool unsigned, int length, MySqlGuidFormat guidFormat) =>
+		GetColumnTypeMetadata(typeName, unsigned, length, guidFormat)!.MySqlDbType;
 
-	private ColumnTypeMetadata? GetColumnTypeMetadata(string columnTypeName, bool unsigned, int length)
+	private ColumnTypeMetadata? GetColumnTypeMetadata(string columnTypeName, bool unsigned, int length, MySqlGuidFormat guidFormat)
 	{
-		if (!m_columnTypeMetadataLookup.TryGetValue(ColumnTypeMetadata.CreateLookupKey(columnTypeName, unsigned, length), out var columnTypeMetadata) && length != 0)
-			m_columnTypeMetadataLookup.TryGetValue(ColumnTypeMetadata.CreateLookupKey(columnTypeName, unsigned, 0), out columnTypeMetadata);
+		if (m_columnTypeMetadataLookup.TryGetValue(ColumnTypeMetadata.CreateLookupKey(columnTypeName, unsigned, length, guidFormat), out var columnTypeMetadata))
+			return columnTypeMetadata;
+		if (guidFormat != MySqlGuidFormat.Default && m_columnTypeMetadataLookup.TryGetValue(ColumnTypeMetadata.CreateLookupKey(columnTypeName, unsigned, length, MySqlGuidFormat.Default), out columnTypeMetadata))
+			return columnTypeMetadata;
+		if (length != 0)
+			m_columnTypeMetadataLookup.TryGetValue(ColumnTypeMetadata.CreateLookupKey(columnTypeName, unsigned, 0, MySqlGuidFormat.Default), out columnTypeMetadata);
 		return columnTypeMetadata;
 	}
 
diff --git a/tests/IntegrationTests/StoredProcedureTests.cs b/tests/IntegrationTests/StoredProcedureTests.cs
index cd0e79ede..0a1ae418d 100644
--- a/tests/IntegrationTests/StoredProcedureTests.cs
+++ b/tests/IntegrationTests/StoredProcedureTests.cs
@@ -874,6 +874,60 @@ public void SprocNameSpecialCharacters(string sprocName)
 		}
 	}
 
+#if !MYSQL_DATA
+	[Theory]
+	[InlineData(MySqlGuidFormat.Binary16, "BINARY(16)", "X'BABD8384C908499C9D95C02ADA94A970'", false, false)]
+	[InlineData(MySqlGuidFormat.Binary16, "BINARY(16)", "X'BABD8384C908499C9D95C02ADA94A970'", false, true)]
+	[InlineData(MySqlGuidFormat.Binary16, "BINARY(16)", "X'BABD8384C908499C9D95C02ADA94A970'", true, false)]
+	[InlineData(MySqlGuidFormat.Binary16, "BINARY(16)", "X'BABD8384C908499C9D95C02ADA94A970'", true, true)]
+	[InlineData(MySqlGuidFormat.Char32, "CHAR(32)", "'BABD8384C908499C9D95C02ADA94A970'", false, false)]
+	[InlineData(MySqlGuidFormat.Char32, "CHAR(32)", "'BABD8384C908499C9D95C02ADA94A970'", false, true)]
+	[InlineData(MySqlGuidFormat.Char32, "CHAR(32)", "'BABD8384C908499C9D95C02ADA94A970'", true, false)]
+	[InlineData(MySqlGuidFormat.Char32, "CHAR(32)", "'BABD8384C908499C9D95C02ADA94A970'", true, true)]
+	[InlineData(MySqlGuidFormat.Char36, "CHAR(36)", "'BABD8384-C908-499C-9D95-C02ADA94A970'", false, false)]
+	[InlineData(MySqlGuidFormat.Char36, "CHAR(36)", "'BABD8384-C908-499C-9D95-C02ADA94A970'", false, true)]
+	[InlineData(MySqlGuidFormat.Char36, "CHAR(36)", "'BABD8384-C908-499C-9D95-C02ADA94A970'", true, false)]
+	[InlineData(MySqlGuidFormat.Char36, "CHAR(36)", "'BABD8384-C908-499C-9D95-C02ADA94A970'", true, true)]
+	public void StoredProcedureReturnsGuid(MySqlGuidFormat guidFormat, string columnDefinition, string columnValue, bool setMySqlDbType, bool prepare)
+	{
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		csb.GuidFormat = guidFormat;
+		csb.Pooling = false;
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		connection.Open();
+
+		using (var command = new MySqlCommand($"""
+			DROP TABLE IF EXISTS out_guid_table;
+			CREATE TABLE out_guid_table (id INT PRIMARY KEY AUTO_INCREMENT, guid {columnDefinition});
+			INSERT INTO out_guid_table (guid) VALUES ({columnValue});
+			DROP PROCEDURE IF EXISTS out_guid;
+			CREATE PROCEDURE out_guid
+			(
+				OUT out_name {columnDefinition}
+			)
+			BEGIN
+				SELECT guid INTO out_name FROM out_guid_table;
+			END;
+			""", connection))
+		{
+			command.ExecuteNonQuery();
+		}
+
+		using (var command = new MySqlCommand("out_guid", connection))
+		{
+			command.CommandType = CommandType.StoredProcedure;
+			var param = new MySqlParameter("out_name", null) { Direction = ParameterDirection.Output };
+			if (setMySqlDbType)
+				param.MySqlDbType = MySqlDbType.Guid;
+			command.Parameters.Add(param);
+			command.ExecuteNonQuery();
+			if (prepare)
+				command.Prepare();
+			Assert.Equal(new Guid("BABD8384C908499C9D95C02ADA94A970"), param.Value);
+		}
+	}
+#endif
+
 	private static string NormalizeSpaces(string input)
 	{
 		input = input.Replace('\r', ' ');
diff --git a/tests/IntegrationTests/packages.lock.json b/tests/IntegrationTests/packages.lock.json
index 544dbe088..ccd66edad 100644
--- a/tests/IntegrationTests/packages.lock.json
+++ b/tests/IntegrationTests/packages.lock.json
@@ -429,6 +429,15 @@
           "Microsoft.CodeCoverage": "17.12.0"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -570,6 +579,11 @@
           "System.Runtime.CompilerServices.Unsafe": "6.0.0"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net481": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",
diff --git a/tests/MySqlConnector.Tests/CachedProcedureTests.cs b/tests/MySqlConnector.Tests/CachedProcedureTests.cs
index fcae8eb20..78484d00d 100644
--- a/tests/MySqlConnector.Tests/CachedProcedureTests.cs
+++ b/tests/MySqlConnector.Tests/CachedProcedureTests.cs
@@ -4,9 +4,9 @@ public class CachedProcedureTests
 {
 	[Theory]
 	[MemberData(nameof(CreateParseableParameters))]
-	public void ParseParameters(string sql, object[] expected)
+	public void ParseParameters(string sql, MySqlGuidFormat guidFormat, object[] expected)
 	{
-		var actual = CachedProcedure.ParseParameters(sql);
+		var actual = CachedProcedure.ParseParameters(sql, guidFormat);
 		Assert.Equal(expected.Length, actual.Count);
 		for (int i = 0; i < expected.Length; i++)
 		{
@@ -25,106 +25,118 @@ public static IEnumerable<object[]> CreateParseableParameters()
 		{
 			new object[]
 			{
-				"", new object[0],
+				"", MySqlGuidFormat.Binary16, new object[0],
 			},
 			[
-				"/* no, parameters */", new object[0],
+				"/* no, parameters */", MySqlGuidFormat.Binary16, new object[0],
 			],
 			[
-				"IN test INT", new object[]
+				"IN test INT", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "test", "INT", false, 0),
+					new CachedParameter(1, "IN", "test", "INT", false, 0, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"IN test INT UNSIGNED", new object[]
+				"IN test INT UNSIGNED", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "test", "INT", true, 0),
+					new CachedParameter(1, "IN", "test", "INT", true, 0, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"-- IN ignored INT UNSIGNED,\r\nIN notignored INT", new object[]
+				"-- IN ignored INT UNSIGNED,\r\nIN notignored INT", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "notignored", "INT", false, 0),
+					new CachedParameter(1, "IN", "notignored", "INT", false, 0, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"IN param1 INT,\r\nIN param2 INT", new object[]
+				"IN param1 INT,\r\nIN param2 INT", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "param1", "INT", false, 0),
-					new CachedParameter(2, "IN", "param2", "INT", false, 0),
+					new CachedParameter(1, "IN", "param1", "INT", false, 0, MySqlGuidFormat.Binary16),
+					new CachedParameter(2, "IN", "param2", "INT", false, 0, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"IN /* ignored BIGINT,\r\nIN*/ param1 INT", new object[]
+				"IN /* ignored BIGINT,\r\nIN*/ param1 INT", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "param1", "INT", false, 0),
+					new CachedParameter(1, "IN", "param1", "INT", false, 0, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"IN param1 INT(11)", new object[]
+				"IN param1 INT(11)", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "param1", "INT", false, 11),
+					new CachedParameter(1, "IN", "param1", "INT", false, 11, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"param1 BIGINT(21) UNSIGNED ZEROFILL", new object[]
+				"param1 BIGINT(21) UNSIGNED ZEROFILL", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "param1", "BIGINT", true, 21),
+					new CachedParameter(1, "IN", "param1", "BIGINT", true, 21, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"param1 VARCHAR(63)", new object[]
+				"param1 VARCHAR(63)", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63),
+					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"param1 VARCHAR(63) CHARSET latin1", new object[]
+				"param1 VARCHAR(63) CHARSET latin1", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63),
+					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"param1 VARCHAR(63) COLLATE utf8bin", new object[]
+				"param1 VARCHAR(63) COLLATE utf8bin", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63),
+					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"param1 VARCHAR(63) CHARACTER SET latin1 COLLATE latin1_bin", new object[]
+				"param1 VARCHAR(63) CHARACTER SET latin1 COLLATE latin1_bin", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63),
+					new CachedParameter(1, "IN", "param1", "VARCHAR", false, 63, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"`par``am` INT", new object[]
+				"`par``am` INT", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "par`am", "INT", false, 0),
+					new CachedParameter(1, "IN", "par`am", "INT", false, 0, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"IN input enum ('One', 'Two', 'Three')", new object[]
+				"IN input enum ('One', 'Two', 'Three')", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "input", "ENUM", false, 0),
+					new CachedParameter(1, "IN", "input", "ENUM", false, 0, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"OUT param DECIMAL(10,5)", new object[]
+				"OUT param DECIMAL(10,5)", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "OUT", "param", "DECIMAL", false, 0),
+					new CachedParameter(1, "OUT", "param", "DECIMAL", false, 0, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"INOUT param LONGTEXT", new object[]
+				"INOUT param LONGTEXT", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "INOUT", "param", "LONGTEXT", false, 0),
+					new CachedParameter(1, "INOUT", "param", "LONGTEXT", false, 0, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
-				"ColSet set('set1','set2','set3')", new object[]
+				"OUT param1 BINARY(16)", MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "ColSet", "SET", false, 0),
+					new CachedParameter(1, "OUT", "param1", "BINARY", false, 16, MySqlGuidFormat.Binary16),
+				}
+			],
+			[
+				"OUT param1 CHAR(36)", MySqlGuidFormat.Char36, new object[]
+				{
+					new CachedParameter(1, "OUT", "param1", "CHAR", false, 36, MySqlGuidFormat.Char36),
+				}
+			],
+			[
+				"ColSet set('set1','set2','set3')", MySqlGuidFormat.Binary16, new object[]
+				{
+					new CachedParameter(1, "IN", "ColSet", "SET", false, 0, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
@@ -135,14 +147,14 @@ param3 DECIMAL(20,10),
 inout param4 VARCHAR(63) CHARSET latin1,
 param5 bigint(20) unsigned zerofill,
 out param6 bool",
-				new object[]
+				MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "param1", "DATETIME", false, 6),
-					new CachedParameter(2, "OUT", "param2", "INT", false, 0),
-					new CachedParameter(3, "IN", "param3", "DECIMAL", false, 0),
-					new CachedParameter(4, "INOUT", "param4", "VARCHAR", false, 63),
-					new CachedParameter(5, "IN", "param5", "BIGINT", true, 20),
-					new CachedParameter(6, "OUT", "param6", "TINYINT", false, 1),
+					new CachedParameter(1, "IN", "param1", "DATETIME", false, 6, MySqlGuidFormat.Binary16),
+					new CachedParameter(2, "OUT", "param2", "INT", false, 0, MySqlGuidFormat.Binary16),
+					new CachedParameter(3, "IN", "param3", "DECIMAL", false, 0, MySqlGuidFormat.Binary16),
+					new CachedParameter(4, "INOUT", "param4", "VARCHAR", false, 63, MySqlGuidFormat.Binary16),
+					new CachedParameter(5, "IN", "param5", "BIGINT", true, 20, MySqlGuidFormat.Binary16),
+					new CachedParameter(6, "OUT", "param6", "TINYINT", false, 1, MySqlGuidFormat.Binary16),
 				}
 			],
 			[
@@ -153,12 +165,12 @@ param3 real(20,10),
 -- ignored INT
 param4 INTEGER(3)
 ",
-				new object[]
+				MySqlGuidFormat.Binary16, new object[]
 				{
-					new CachedParameter(1, "IN", "param1", "TINYINT", false, 1),
-					new CachedParameter(2, "IN", "param2", "VARCHAR", false, 0),
-					new CachedParameter(3, "IN", "param3", "DOUBLE", false, 20),
-					new CachedParameter(4, "IN", "param4", "INT", false, 3),
+					new CachedParameter(1, "IN", "param1", "TINYINT", false, 1, MySqlGuidFormat.Binary16),
+					new CachedParameter(2, "IN", "param2", "VARCHAR", false, 0, MySqlGuidFormat.Binary16),
+					new CachedParameter(3, "IN", "param3", "DOUBLE", false, 20, MySqlGuidFormat.Binary16),
+					new CachedParameter(4, "IN", "param4", "INT", false, 3, MySqlGuidFormat.Binary16),
 				}
 			],
 		};
diff --git a/tests/MySqlConnector.Tests/packages.lock.json b/tests/MySqlConnector.Tests/packages.lock.json
index ee75ae00b..5cea0042c 100644
--- a/tests/MySqlConnector.Tests/packages.lock.json
+++ b/tests/MySqlConnector.Tests/packages.lock.json
@@ -11,6 +11,15 @@
           "Microsoft.CodeCoverage": "17.12.0"
         }
       },
+      "Microsoft.NETFramework.ReferenceAssemblies": {
+        "type": "Direct",
+        "requested": "[1.0.3, )",
+        "resolved": "1.0.3",
+        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
+        "dependencies": {
+          "Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3"
+        }
+      },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
         "requested": "[8.0.0, )",
@@ -74,6 +83,11 @@
         "resolved": "17.12.0",
         "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
       },
+      "Microsoft.NETFramework.ReferenceAssemblies.net481": {
+        "type": "Transitive",
+        "resolved": "1.0.3",
+        "contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ=="
+      },
       "Microsoft.SourceLink.Common": {
         "type": "Transitive",
         "resolved": "8.0.0",

From b4bca5c47107206428c391f0efdd508735b886e1 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 9 Mar 2025 14:03:13 -0700
Subject: [PATCH 374/393] Add type documentation.

---
 src/MySqlConnector/MySqlConnectorTracingOptionsBuilder.cs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/MySqlConnector/MySqlConnectorTracingOptionsBuilder.cs b/src/MySqlConnector/MySqlConnectorTracingOptionsBuilder.cs
index 8b2ce81a4..dfc0432b0 100644
--- a/src/MySqlConnector/MySqlConnectorTracingOptionsBuilder.cs
+++ b/src/MySqlConnector/MySqlConnectorTracingOptionsBuilder.cs
@@ -1,5 +1,8 @@
 namespace MySqlConnector;
 
+/// <summary>
+/// <see cref="MySqlConnectorTracingOptionsBuilder"/> provides an API for configuring OpenTelemetry tracing options.
+/// </summary>
 public sealed class MySqlConnectorTracingOptionsBuilder
 {
 	/// <summary>

From bc1031d50e27887da48758a12d910d8bc7ddc10d Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 20 Mar 2025 20:43:01 -0700
Subject: [PATCH 375/393] Update package lock files.

---
 .../packages.lock.json                        |  6 ++---
 src/MySqlConnector/packages.lock.json         | 12 +++++-----
 .../packages.lock.json                        | 24 +++++++++----------
 3 files changed, 21 insertions(+), 21 deletions(-)

diff --git a/src/MySqlConnector.DependencyInjection/packages.lock.json b/src/MySqlConnector.DependencyInjection/packages.lock.json
index 73ee05a17..075bf2fd6 100644
--- a/src/MySqlConnector.DependencyInjection/packages.lock.json
+++ b/src/MySqlConnector.DependencyInjection/packages.lock.json
@@ -148,9 +148,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.13, )",
-        "resolved": "8.0.13",
-        "contentHash": "R19ZTaRiQAK+xo9ZwaHbF/1vb1wwR1Wn5+sqp9v8+CDjbdS8R6qftKdw0VSXWKm7VAMi7P+NCU4zxDzhEWcAwQ=="
+        "requested": "[8.0.14, )",
+        "resolved": "8.0.14",
+        "contentHash": "4U2fd7PexNKrK5ZqfqIcXZj9/lRRjFsLgA/pxuFQTuGQuLYP/+7yACz/j7EmWbEj/fspOf4mafi/vHIy/rKDzQ=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
diff --git a/src/MySqlConnector/packages.lock.json b/src/MySqlConnector/packages.lock.json
index 3b22aa928..a9524594f 100644
--- a/src/MySqlConnector/packages.lock.json
+++ b/src/MySqlConnector/packages.lock.json
@@ -718,9 +718,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.13, )",
-        "resolved": "8.0.13",
-        "contentHash": "R19ZTaRiQAK+xo9ZwaHbF/1vb1wwR1Wn5+sqp9v8+CDjbdS8R6qftKdw0VSXWKm7VAMi7P+NCU4zxDzhEWcAwQ=="
+        "requested": "[8.0.14, )",
+        "resolved": "8.0.14",
+        "contentHash": "4U2fd7PexNKrK5ZqfqIcXZj9/lRRjFsLgA/pxuFQTuGQuLYP/+7yACz/j7EmWbEj/fspOf4mafi/vHIy/rKDzQ=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -781,9 +781,9 @@
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[9.0.2, )",
-        "resolved": "9.0.2",
-        "contentHash": "+KFnCLVPicEq99ko0tq+ycTvNLXHw0tImmTZjPloB/DOFLPT56KLfk5aS7wbgXRPzYhXTTBYLGaABea5mke77w=="
+        "requested": "[9.0.3, )",
+        "resolved": "9.0.3",
+        "contentHash": "1rqGTfubVg0qj2PsK6esyq3PIxtYJYrN3LsYUV9RrvH3anmt3fT3ozYdAZZH4U8JU/pt5pPIUk8NBSu26wtekA=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
diff --git a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
index b75c400e8..ef443644e 100644
--- a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
+++ b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
@@ -4,15 +4,15 @@
     "net8.0": {
       "Microsoft.DotNet.ILCompiler": {
         "type": "Direct",
-        "requested": "[8.0.13, )",
-        "resolved": "8.0.13",
-        "contentHash": "CCIhseY9KUJDIYKt7qD1IRLQA6Hr/8Dky31KS6UrM2sFyaFUb2JLagT0Uy2BiSf1i1Qy3nPjRb0zc1JFogOi9w=="
+        "requested": "[8.0.14, )",
+        "resolved": "8.0.14",
+        "contentHash": "lJvndCQZJxHBVoD8VxfSJ2MaURHENC63sbeAJNfedvXvscgjQSpfVdcTsv0iGeSu9X05G05TYMCWVbZGj4Z51Q=="
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[8.0.13, )",
-        "resolved": "8.0.13",
-        "contentHash": "R19ZTaRiQAK+xo9ZwaHbF/1vb1wwR1Wn5+sqp9v8+CDjbdS8R6qftKdw0VSXWKm7VAMi7P+NCU4zxDzhEWcAwQ=="
+        "requested": "[8.0.14, )",
+        "resolved": "8.0.14",
+        "contentHash": "4U2fd7PexNKrK5ZqfqIcXZj9/lRRjFsLgA/pxuFQTuGQuLYP/+7yACz/j7EmWbEj/fspOf4mafi/vHIy/rKDzQ=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",
@@ -86,15 +86,15 @@
     "net9.0": {
       "Microsoft.DotNet.ILCompiler": {
         "type": "Direct",
-        "requested": "[9.0.2, )",
-        "resolved": "9.0.2",
-        "contentHash": "eyIUUqqPy7brzmoV04zabOUm0puEAAjZd3O3vuzan9iTTh0oIPwts8fLxZFoEZCFi63ACFtHbNBs5Y7myB3ZDQ=="
+        "requested": "[9.0.3, )",
+        "resolved": "9.0.3",
+        "contentHash": "UqR8lcAIRC5zPk97txAJlsCvDhP9jxV7JSxEMkm3QI384aq/sU0zYiC8kms4ooNtixORgZxbFrDAsYCewR4X2Q=="
       },
       "Microsoft.NET.ILLink.Tasks": {
         "type": "Direct",
-        "requested": "[9.0.2, )",
-        "resolved": "9.0.2",
-        "contentHash": "+KFnCLVPicEq99ko0tq+ycTvNLXHw0tImmTZjPloB/DOFLPT56KLfk5aS7wbgXRPzYhXTTBYLGaABea5mke77w=="
+        "requested": "[9.0.3, )",
+        "resolved": "9.0.3",
+        "contentHash": "1rqGTfubVg0qj2PsK6esyq3PIxtYJYrN3LsYUV9RrvH3anmt3fT3ozYdAZZH4U8JU/pt5pPIUk8NBSu26wtekA=="
       },
       "Microsoft.SourceLink.GitHub": {
         "type": "Direct",

From 25be098e65f1d982a222f544de708f94109bee33 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 20 Mar 2025 20:43:14 -0700
Subject: [PATCH 376/393] Remove package lock files.

We weren't using RestoreLockedMode in CI, so these were pointless. And that feature doesn't work cross-platform (https://github.com/NuGet/Home/issues/9195) so enabling it breaks the builds anyway.
---
 Directory.Build.props                         |    1 -
 .../packages.lock.json                        |  281 ---
 .../packages.lock.json                        |  212 ---
 .../packages.lock.json                        |  143 --
 .../packages.lock.json                        |  293 ---
 .../packages.lock.json                        |  293 ---
 .../packages.lock.json                        |  334 ----
 src/MySqlConnector/packages.lock.json         |  836 ---------
 tests/Benchmark/packages.lock.json            |  459 -----
 tests/Conformance.Tests/packages.lock.json    |  206 --
 tests/IntegrationTests/packages.lock.json     | 1653 -----------------
 .../packages.lock.json                        |  183 --
 .../packages.lock.json                        |  169 --
 tests/MySqlConnector.Tests/packages.lock.json |  400 ----
 .../packages.lock.json                        |   53 -
 15 files changed, 5516 deletions(-)
 delete mode 100644 src/MySqlConnector.Authentication.Ed25519/packages.lock.json
 delete mode 100644 src/MySqlConnector.DependencyInjection/packages.lock.json
 delete mode 100644 src/MySqlConnector.Logging.Microsoft.Extensions.Logging/packages.lock.json
 delete mode 100644 src/MySqlConnector.Logging.NLog/packages.lock.json
 delete mode 100644 src/MySqlConnector.Logging.Serilog/packages.lock.json
 delete mode 100644 src/MySqlConnector.Logging.log4net/packages.lock.json
 delete mode 100644 src/MySqlConnector/packages.lock.json
 delete mode 100644 tests/Benchmark/packages.lock.json
 delete mode 100644 tests/Conformance.Tests/packages.lock.json
 delete mode 100644 tests/IntegrationTests/packages.lock.json
 delete mode 100644 tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
 delete mode 100644 tests/MySqlConnector.NativeAot.Tests/packages.lock.json
 delete mode 100644 tests/MySqlConnector.Tests/packages.lock.json
 delete mode 100644 tools/SchemaCollectionGenerator/packages.lock.json

diff --git a/Directory.Build.props b/Directory.Build.props
index ff778ea9a..10bca8b48 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -9,7 +9,6 @@
     <NuGetAudit>true</NuGetAudit>
     <NuGetAuditLevel>low</NuGetAuditLevel>
     <NuGetAuditMode>all</NuGetAuditMode>
-    <RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
   </PropertyGroup>
 
   <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
diff --git a/src/MySqlConnector.Authentication.Ed25519/packages.lock.json b/src/MySqlConnector.Authentication.Ed25519/packages.lock.json
deleted file mode 100644
index 7c8f4f591..000000000
--- a/src/MySqlConnector.Authentication.Ed25519/packages.lock.json
+++ /dev/null
@@ -1,281 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    ".NETFramework,Version=v4.6.2": {
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.5.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    },
-    ".NETStandard,Version=v2.0": {
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "NETStandard.Library": {
-        "type": "Direct",
-        "requested": "[2.0.3, )",
-        "resolved": "2.0.3",
-        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0"
-        }
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETCore.Platforms": {
-        "type": "Transitive",
-        "resolved": "1.1.0",
-        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.4.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.4.0",
-        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/MySqlConnector.DependencyInjection/packages.lock.json b/src/MySqlConnector.DependencyInjection/packages.lock.json
deleted file mode 100644
index 075bf2fd6..000000000
--- a/src/MySqlConnector.DependencyInjection/packages.lock.json
+++ /dev/null
@@ -1,212 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    ".NETStandard,Version=v2.0": {
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "NETStandard.Library": {
-        "type": "Direct",
-        "requested": "[2.0.3, )",
-        "resolved": "2.0.3",
-        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0"
-        }
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETCore.Platforms": {
-        "type": "Transitive",
-        "resolved": "1.1.0",
-        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.4.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.4.0",
-        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    },
-    "net8.0": {
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      },
-      "Microsoft.NET.ILLink.Tasks": {
-        "type": "Direct",
-        "requested": "[8.0.14, )",
-        "resolved": "8.0.14",
-        "contentHash": "4U2fd7PexNKrK5ZqfqIcXZj9/lRRjFsLgA/pxuFQTuGQuLYP/+7yACz/j7EmWbEj/fspOf4mafi/vHIy/rKDzQ=="
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/packages.lock.json b/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/packages.lock.json
deleted file mode 100644
index fbfb4a12b..000000000
--- a/src/MySqlConnector.Logging.Microsoft.Extensions.Logging/packages.lock.json
+++ /dev/null
@@ -1,143 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    ".NETStandard,Version=v2.0": {
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "NETStandard.Library": {
-        "type": "Direct",
-        "requested": "[2.0.3, )",
-        "resolved": "2.0.3",
-        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0"
-        }
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETCore.Platforms": {
-        "type": "Transitive",
-        "resolved": "1.1.0",
-        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.4.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.4.0",
-        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/MySqlConnector.Logging.NLog/packages.lock.json b/src/MySqlConnector.Logging.NLog/packages.lock.json
deleted file mode 100644
index 5eb10212c..000000000
--- a/src/MySqlConnector.Logging.NLog/packages.lock.json
+++ /dev/null
@@ -1,293 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    ".NETFramework,Version=v4.6.2": {
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "NLog": {
-        "type": "Direct",
-        "requested": "[4.7.15, )",
-        "resolved": "4.7.15",
-        "contentHash": "vXMmsK17hXnMsKd8BdCrJzvb8JMT0+cPgBGRfnqcXIb6y8b6RMIZexO4hMhQgifcTHgU7dbubvCR5A5wX6Ne+A=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.5.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    },
-    ".NETStandard,Version=v2.0": {
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "NETStandard.Library": {
-        "type": "Direct",
-        "requested": "[2.0.3, )",
-        "resolved": "2.0.3",
-        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0"
-        }
-      },
-      "NLog": {
-        "type": "Direct",
-        "requested": "[4.7.15, )",
-        "resolved": "4.7.15",
-        "contentHash": "vXMmsK17hXnMsKd8BdCrJzvb8JMT0+cPgBGRfnqcXIb6y8b6RMIZexO4hMhQgifcTHgU7dbubvCR5A5wX6Ne+A=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETCore.Platforms": {
-        "type": "Transitive",
-        "resolved": "1.1.0",
-        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.4.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.4.0",
-        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/MySqlConnector.Logging.Serilog/packages.lock.json b/src/MySqlConnector.Logging.Serilog/packages.lock.json
deleted file mode 100644
index 8d6f759f4..000000000
--- a/src/MySqlConnector.Logging.Serilog/packages.lock.json
+++ /dev/null
@@ -1,293 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    ".NETFramework,Version=v4.6.2": {
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "Serilog": {
-        "type": "Direct",
-        "requested": "[2.12.0, )",
-        "resolved": "2.12.0",
-        "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.5.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    },
-    ".NETStandard,Version=v2.0": {
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "NETStandard.Library": {
-        "type": "Direct",
-        "requested": "[2.0.3, )",
-        "resolved": "2.0.3",
-        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0"
-        }
-      },
-      "Serilog": {
-        "type": "Direct",
-        "requested": "[2.12.0, )",
-        "resolved": "2.12.0",
-        "contentHash": "xaiJLIdu6rYMKfQMYUZgTy8YK7SMZjB4Yk50C/u//Z4OsvxkUfSPJy4nknfvwAC34yr13q7kcyh4grbwhSxyZg=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETCore.Platforms": {
-        "type": "Transitive",
-        "resolved": "1.1.0",
-        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.4.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.4.0",
-        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/MySqlConnector.Logging.log4net/packages.lock.json b/src/MySqlConnector.Logging.log4net/packages.lock.json
deleted file mode 100644
index 9e0d38ab3..000000000
--- a/src/MySqlConnector.Logging.log4net/packages.lock.json
+++ /dev/null
@@ -1,334 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    ".NETFramework,Version=v4.6.2": {
-      "log4net": {
-        "type": "Direct",
-        "requested": "[2.0.17, )",
-        "resolved": "2.0.17",
-        "contentHash": "qnnDf/ubJzwm2i1xH7nRMjEDoD+ctse7nZDqb+p7L1PvZc6ykpMoEesWr1/9hFqlsbII2v9e8yyQHJhoDQh7ZA=="
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.5.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    },
-    ".NETStandard,Version=v2.0": {
-      "log4net": {
-        "type": "Direct",
-        "requested": "[2.0.17, )",
-        "resolved": "2.0.17",
-        "contentHash": "qnnDf/ubJzwm2i1xH7nRMjEDoD+ctse7nZDqb+p7L1PvZc6ykpMoEesWr1/9hFqlsbII2v9e8yyQHJhoDQh7ZA==",
-        "dependencies": {
-          "System.Configuration.ConfigurationManager": "4.5.0"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "NETStandard.Library": {
-        "type": "Direct",
-        "requested": "[2.0.3, )",
-        "resolved": "2.0.3",
-        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0"
-        }
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETCore.Platforms": {
-        "type": "Transitive",
-        "resolved": "1.1.0",
-        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Configuration.ConfigurationManager": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "UIFvaFfuKhLr9u5tWMxmVoDPkFeD+Qv8gUuap4aZgVGYSYMdERck4OhLN/2gulAc0nYTEigWXSJNNWshrmxnng==",
-        "dependencies": {
-          "System.Security.Cryptography.ProtectedData": "4.5.0",
-          "System.Security.Permissions": "4.5.0"
-        }
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.4.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.4.0",
-        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "System.Security.AccessControl": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "vW8Eoq0TMyz5vAG/6ce483x/CP83fgm4SJe5P8Tb1tZaobcvPrbMEL7rhH1DRdrYbbb6F0vq3OlzmK0Pkwks5A==",
-        "dependencies": {
-          "System.Security.Principal.Windows": "4.5.0"
-        }
-      },
-      "System.Security.Cryptography.ProtectedData": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "wLBKzFnDCxP12VL9ANydSYhk59fC4cvOr9ypYQLPnAj48NQIhqnjdD2yhP8yEKyBJEjERWS9DisKL7rX5eU25Q==",
-        "dependencies": {
-          "System.Memory": "4.5.0"
-        }
-      },
-      "System.Security.Permissions": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "9gdyuARhUR7H+p5CjyUB/zPk7/Xut3wUSP8NJQB6iZr8L3XUXTMdoLeVAg9N4rqF8oIpE7MpdqHdDHQ7XgJe0g==",
-        "dependencies": {
-          "System.Security.AccessControl": "4.5.0"
-        }
-      },
-      "System.Security.Principal.Windows": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "U77HfRXlZlOeIXd//Yoj6Jnk8AXlbeisf1oq1os+hxOGVnuG+lGSfGqTwTZBoORFF6j/0q7HXIl8cqwQ9aUGqQ=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/src/MySqlConnector/packages.lock.json b/src/MySqlConnector/packages.lock.json
deleted file mode 100644
index a9524594f..000000000
--- a/src/MySqlConnector/packages.lock.json
+++ /dev/null
@@ -1,836 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    ".NETFramework,Version=v4.6.2": {
-      "IndexRange": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "tqgj3LuQHDH0OfGZamyiW1i9fhpny5piqvI+8Y7IIaRly+Bhqv5Z7zSlcIwA/DlYPS/cBmVwb+J4VGEFFqmnfQ==",
-        "dependencies": {
-          "System.ValueTuple": "4.5.0"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "Direct",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.5.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "System.ValueTuple": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      }
-    },
-    ".NETFramework,Version=v4.7.1": {
-      "IndexRange": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "tqgj3LuQHDH0OfGZamyiW1i9fhpny5piqvI+8Y7IIaRly+Bhqv5Z7zSlcIwA/DlYPS/cBmVwb+J4VGEFFqmnfQ=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net471": "1.0.3"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "Direct",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies.net471": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "Kf3vusy+mwHzn+0osmJAMrZNG00/UkwywjwfFKLUWSYEXiIMD6SEMGP9bG3sH1B54IiydxAneLubS5/Ajjn/Tw=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.5.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      }
-    },
-    ".NETFramework,Version=v4.8": {
-      "IndexRange": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "tqgj3LuQHDH0OfGZamyiW1i9fhpny5piqvI+8Y7IIaRly+Bhqv5Z7zSlcIwA/DlYPS/cBmVwb+J4VGEFFqmnfQ=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "Direct",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.5.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      }
-    },
-    ".NETStandard,Version=v2.0": {
-      "IndexRange": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "tqgj3LuQHDH0OfGZamyiW1i9fhpny5piqvI+8Y7IIaRly+Bhqv5Z7zSlcIwA/DlYPS/cBmVwb+J4VGEFFqmnfQ=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "NETStandard.Library": {
-        "type": "Direct",
-        "requested": "[2.0.3, )",
-        "resolved": "2.0.3",
-        "contentHash": "st47PosZSHrjECdjeIzZQbzivYBJFv6P2nv4cj2ypdI204DO+vZ7l5raGMiX4eXMJ53RfOIg+/s4DHVZ54Nu2A==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0"
-        }
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "Direct",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.NETCore.Platforms": {
-        "type": "Transitive",
-        "resolved": "1.1.0",
-        "contentHash": "kz0PEW2lhqygehI/d6XsPCQzD7ff7gUJaVGPVETX611eadGsA3A877GdSlU0LRVMCTH/+P3o2iDTak+S08V2+A=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.4.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.4.0",
-        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      }
-    },
-    ".NETStandard,Version=v2.1": {
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.4.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.4.0",
-        "contentHash": "UiLzLW+Lw6HLed1Hcg+8jSRttrbuXv7DANVj0DkL9g6EnnzbL75EB7EWsw5uRbhxd/4YdG8li5XizGWepmG3PQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      }
-    },
-    "net6.0": {
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Diagnostics.DiagnosticSource": "8.0.1"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      }
-    },
-    "net8.0": {
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      },
-      "Microsoft.NET.ILLink.Tasks": {
-        "type": "Direct",
-        "requested": "[8.0.14, )",
-        "resolved": "8.0.14",
-        "contentHash": "4U2fd7PexNKrK5ZqfqIcXZj9/lRRjFsLgA/pxuFQTuGQuLYP/+7yACz/j7EmWbEj/fspOf4mafi/vHIy/rKDzQ=="
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      }
-    },
-    "net9.0": {
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      },
-      "Microsoft.NET.ILLink.Tasks": {
-        "type": "Direct",
-        "requested": "[9.0.3, )",
-        "resolved": "9.0.3",
-        "contentHash": "1rqGTfubVg0qj2PsK6esyq3PIxtYJYrN3LsYUV9RrvH3anmt3fT3ozYdAZZH4U8JU/pt5pPIUk8NBSu26wtekA=="
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tests/Benchmark/packages.lock.json b/tests/Benchmark/packages.lock.json
deleted file mode 100644
index 2fcbdebbd..000000000
--- a/tests/Benchmark/packages.lock.json
+++ /dev/null
@@ -1,459 +0,0 @@
-{
-  "version": 1,
-  "dependencies": {
-    "net8.0": {
-      "BenchmarkDotNet": {
-        "type": "Direct",
-        "requested": "[0.14.0, )",
-        "resolved": "0.14.0",
-        "contentHash": "eIPSDKi3oni734M1rt/XJAwGQQOIf9gLjRRKKJ0HuVy3vYd7gnmAIX1bTjzI9ZbAY/nPddgqqgM/TeBYitMCIg==",
-        "dependencies": {
-          "BenchmarkDotNet.Annotations": "0.14.0",
-          "CommandLineParser": "2.9.1",
-          "Gee.External.Capstone": "2.3.0",
-          "Iced": "1.17.0",
-          "Microsoft.CodeAnalysis.CSharp": "4.1.0",
-          "Microsoft.Diagnostics.Runtime": "2.2.332302",
-          "Microsoft.Diagnostics.Tracing.TraceEvent": "3.1.8",
-          "Microsoft.DotNet.PlatformAbstractions": "3.1.6",
-          "Perfolizer": "[0.3.17]",
-          "System.Management": "5.0.0"
-        }
-      },
-      "MySql.Data": {
-        "type": "Direct",
-        "requested": "[9.2.0, )",
-        "resolved": "9.2.0",
-        "contentHash": "S/O4qGf5+fG64d+IHXpZ+wC08m0HsqIwb2xtEY41jdjXYdV2uEtdEy+Jo2ObQamGj0/GD7X6LUCBbh4eb3fKew==",
-        "dependencies": {
-          "BouncyCastle.Cryptography": "2.3.1",
-          "Google.Protobuf": "3.26.1",
-          "K4os.Compression.LZ4.Streams": "1.3.8",
-          "System.Buffers": "4.5.1",
-          "System.Configuration.ConfigurationManager": "8.0.0",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
-          "System.Runtime.Loader": "4.3.0",
-          "System.Security.Permissions": "8.0.0",
-          "System.Text.Encoding.CodePages": "8.0.0",
-          "System.Text.Json": "9.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4",
-          "ZstdSharp.Port": "0.8.0"
-        }
-      },
-      "MySqlConnector": {
-        "type": "Direct",
-        "requested": "[2.4.0, )",
-        "resolved": "2.4.0",
-        "contentHash": "78M+gVOjbdZEDIyXQqcA7EYlCGS3tpbUELHvn6638A2w0pkPI625ixnzsa5staAd3N9/xFmPJtkKDYwsXpFi/w==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "Microsoft.Extensions.Logging.Abstractions": "8.0.2"
-        }
-      },
-      "BenchmarkDotNet.Annotations": {
-        "type": "Transitive",
-        "resolved": "0.14.0",
-        "contentHash": "CUDCg6bgHrDzhjnA+IOBl5gAo8Y5hZ2YSs7MBXrYMlMKpBZqrD5ez0537uDveOkcf+YWAoK+S4sMcuWPbIz8bw=="
-      },
-      "BouncyCastle.Cryptography": {
-        "type": "Transitive",
-        "resolved": "2.3.1",
-        "contentHash": "buwoISwecYke3CmgG1AQSg+sNZjJeIb93vTAtJiHZX35hP/teYMxsfg0NDXGUKjGx6BKBTNKc77O2M3vKvlXZQ=="
-      },
-      "CommandLineParser": {
-        "type": "Transitive",
-        "resolved": "2.9.1",
-        "contentHash": "OE0sl1/sQ37bjVsPKKtwQlWDgqaxWgtme3xZz7JssWUzg5JpMIyHgCTY9MVMxOg48fJ1AgGT3tgdH5m/kQ5xhA=="
-      },
-      "Gee.External.Capstone": {
-        "type": "Transitive",
-        "resolved": "2.3.0",
-        "contentHash": "2ap/rYmjtzCOT8hxrnEW/QeiOt+paD8iRrIcdKX0cxVwWLFa1e+JDBNeECakmccXrSFeBQuu5AV8SNkipFMMMw=="
-      },
-      "Google.Protobuf": {
-        "type": "Transitive",
-        "resolved": "3.26.1",
-        "contentHash": "CHZX8zXqhF/fdUtd+AYzew8T2HFkAoe5c7lbGxZY/qryAlQXckDvM5BfOJjXlMS7kyICqQTMszj4w1bX5uBJ/w=="
-      },
-      "Iced": {
-        "type": "Transitive",
-        "resolved": "1.17.0",
-        "contentHash": "8x+HCVTl/HHTGpscH3vMBhV8sknN/muZFw9s3TsI8SA6+c43cOTCi2+jE4KsU8pNLbJ++iF2ZFcpcXHXtDglnw=="
-      },
-      "K4os.Compression.LZ4": {
-        "type": "Transitive",
-        "resolved": "1.3.8",
-        "contentHash": "LhwlPa7c1zs1OV2XadMtAWdImjLIsqFJPoRcIWAadSRn0Ri1DepK65UbWLPmt4riLqx2d40xjXRk0ogpqNtK7g=="
-      },
-      "K4os.Compression.LZ4.Streams": {
-        "type": "Transitive",
-        "resolved": "1.3.8",
-        "contentHash": "P15qr8dZAeo9GvYbUIPEYFQ0MEJ0i5iqr37wsYeRC3la2uCldOoeCa6to0CZ1taiwxIV+Mk8NGuZi+4iWivK9w==",
-        "dependencies": {
-          "K4os.Compression.LZ4": "1.3.8",
-          "K4os.Hash.xxHash": "1.0.8",
-          "System.IO.Pipelines": "6.0.3"
-        }
-      },
-      "K4os.Hash.xxHash": {
-        "type": "Transitive",
-        "resolved": "1.0.8",
-        "contentHash": "Wp2F7BamQ2Q/7Hk834nV9vRQapgcr8kgv9Jvfm8J3D0IhDqZMMl+a2yxUq5ltJitvXvQfB8W6K4F4fCbw/P6YQ=="
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "1.1.0",
-        "contentHash": "1Am6l4Vpn3/K32daEqZI+FFr96OlZkgwK2LcT3pZ2zWubR5zTPW3/FkO1Rat9kb7oQOa4rxgl9LJHc5tspCWfg=="
-      },
-      "Microsoft.CodeAnalysis.Analyzers": {
-        "type": "Transitive",
-        "resolved": "3.3.3",
-        "contentHash": "j/rOZtLMVJjrfLRlAMckJLPW/1rze9MT1yfWqSIbUPGRu1m1P0fuo9PmqapwsmePfGB5PJrudQLvmUOAMF0DqQ=="
-      },
-      "Microsoft.CodeAnalysis.Common": {
-        "type": "Transitive",
-        "resolved": "4.1.0",
-        "contentHash": "bNzTyxP3iD5FPFHfVDl15Y6/wSoI7e3MeV0lOaj9igbIKTjgrmuw6LoVJ06jUNFA7+KaDC/OIsStWl/FQJz6sQ==",
-        "dependencies": {
-          "Microsoft.CodeAnalysis.Analyzers": "3.3.3",
-          "System.Collections.Immutable": "5.0.0",
-          "System.Memory": "4.5.4",
-          "System.Reflection.Metadata": "5.0.0",
-          "System.Runtime.CompilerServices.Unsafe": "5.0.0",
-          "System.Text.Encoding.CodePages": "4.5.1",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.CodeAnalysis.CSharp": {
-        "type": "Transitive",
-        "resolved": "4.1.0",
-        "contentHash": "sbu6kDGzo9bfQxuqWpeEE7I9P30bSuZEnpDz9/qz20OU6pm79Z63+/BsAzO2e/R/Q97kBrpj647wokZnEVr97w==",
-        "dependencies": {
-          "Microsoft.CodeAnalysis.Common": "[4.1.0]"
-        }
-      },
-      "Microsoft.Diagnostics.NETCore.Client": {
-        "type": "Transitive",
-        "resolved": "0.2.251802",
-        "contentHash": "bqnYl6AdSeboeN4v25hSukK6Odm6/54E3Y2B8rBvgqvAW0mF8fo7XNRVE2DMOG7Rk0fiuA079QIH28+V+W1Zdg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "1.1.0",
-          "Microsoft.Extensions.Logging": "2.1.1"
-        }
-      },
-      "Microsoft.Diagnostics.Runtime": {
-        "type": "Transitive",
-        "resolved": "2.2.332302",
-        "contentHash": "Hp84ivxSKIMTBzYSATxmUsm3YSXHWivcwiRRbsydGmqujMUK8BAueLN0ssAVEOkOBmh0vjUBhrq7YcroT7VCug==",
-        "dependencies": {
-          "Microsoft.Diagnostics.NETCore.Client": "0.2.251802",
-          "System.Collections.Immutable": "5.0.0",
-          "System.Runtime.CompilerServices.Unsafe": "5.0.0"
-        }
-      },
-      "Microsoft.Diagnostics.Tracing.TraceEvent": {
-        "type": "Transitive",
-        "resolved": "3.1.8",
-        "contentHash": "kl3UMrZKSeSEYZ8rt/GjLUQToREjgQABqfg6PzQBmSlYHTZOKE9ePEOS2xptROQ9SVvngg3QGX51TIT11iZ0wA==",
-        "dependencies": {
-          "Microsoft.Win32.Registry": "4.4.0",
-          "System.Runtime.CompilerServices.Unsafe": "5.0.0"
-        }
-      },
-      "Microsoft.DotNet.PlatformAbstractions": {
-        "type": "Transitive",
-        "resolved": "3.1.6",
-        "contentHash": "jek4XYaQ/PGUwDKKhwR8K47Uh1189PFzMeLqO83mXrXQVIpARZCcfuDedH50YDTepBkfijCZN5U/vZi++erxtg=="
-      },
-      "Microsoft.Extensions.Configuration": {
-        "type": "Transitive",
-        "resolved": "2.1.1",
-        "contentHash": "LjVKO6P2y52c5ZhTLX/w8zc5H4Y3J/LJsgqTBj49TtFq/hAtVNue/WA0F6/7GMY90xhD7K0MDZ4qpOeWXbLvzg==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "2.1.1"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Abstractions": {
-        "type": "Transitive",
-        "resolved": "2.1.1",
-        "contentHash": "VfuZJNa0WUshZ/+8BFZAhwFKiKuu/qOUCFntfdLpHj7vcRnsGHqd3G2Hse78DM+pgozczGM63lGPRLmy+uhUOA==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "2.1.1"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Binder": {
-        "type": "Transitive",
-        "resolved": "2.1.1",
-        "contentHash": "fcLCTS03poWE4v9tSNBr3pWn0QwGgAn1vzqHXlXgvqZeOc7LvQNzaWcKRQZTdEc3+YhQKwMsOtm3VKSA2aWQ8w==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "2.1.1"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      },
-      "Microsoft.Extensions.Logging": {
-        "type": "Transitive",
-        "resolved": "2.1.1",
-        "contentHash": "hh+mkOAQDTp6XH80xJt3+wwYVzkbwYQl9XZRCz4Um0JjP/o7N9vHM3rZ6wwwtr+BBe/L6iBO2sz0px6OWBzqZQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Binder": "2.1.1",
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1",
-          "Microsoft.Extensions.Logging.Abstractions": "2.1.1",
-          "Microsoft.Extensions.Options": "2.1.1"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      },
-      "Microsoft.Extensions.Options": {
-        "type": "Transitive",
-        "resolved": "2.1.1",
-        "contentHash": "V7lXCU78lAbzaulCGFKojcCyG8RTJicEbiBkPJjFqiqXwndEBBIehdXRMWEVU3UtzQ1yDvphiWUL9th6/4gJ7w==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1",
-          "Microsoft.Extensions.Primitives": "2.1.1"
-        }
-      },
-      "Microsoft.Extensions.Primitives": {
-        "type": "Transitive",
-        "resolved": "2.1.1",
-        "contentHash": "scJ1GZNIxMmjpENh0UZ8XCQ6vzr/LzeF9WvEA51Ix2OQGAs9WPgPu8ABVUdvpKPLuor/t05gm6menJK3PwqOXg==",
-        "dependencies": {
-          "System.Memory": "4.5.1",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.1"
-        }
-      },
-      "Microsoft.NETCore.Platforms": {
-        "type": "Transitive",
-        "resolved": "5.0.0",
-        "contentHash": "VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ=="
-      },
-      "Microsoft.NETCore.Targets": {
-        "type": "Transitive",
-        "resolved": "1.1.0",
-        "contentHash": "aOZA3BWfz9RXjpzt0sRJJMjAscAUm3Hoa4UWAfceV9UTYxgwZ1lZt5nO2myFf+/jetYQo4uTP7zS8sJY67BBxg=="
-      },
-      "Microsoft.Win32.Registry": {
-        "type": "Transitive",
-        "resolved": "5.0.0",
-        "contentHash": "dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
-        "dependencies": {
-          "System.Security.AccessControl": "5.0.0",
-          "System.Security.Principal.Windows": "5.0.0"
-        }
-      },
-      "Perfolizer": {
-        "type": "Transitive",
-        "resolved": "0.3.17",
-        "contentHash": "FQgtCoF2HFwvzKWulAwBS5BGLlh8pgbrJtOp47jyBwh2CW16juVtacN1azOA2BqdrJXkXTNLNRMo7ZlHHiuAnA=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.CodeDom": {
-        "type": "Transitive",
-        "resolved": "5.0.0",
-        "contentHash": "JPJArwA1kdj8qDAkY2XGjSWoYnqiM7q/3yRNkt6n28Mnn95MuEGkZXUbPBf7qc3IjwrGY5ttQon7yqHZyQJmOQ=="
-      },
-      "System.Collections.Immutable": {
-        "type": "Transitive",
-        "resolved": "5.0.0",
-        "contentHash": "FXkLXiK0sVVewcso0imKQoOxjoPAj42R8HtjjbSjVPAzwDfzoyoznWxgA3c38LDbN9SJux1xXoXYAhz98j7r2g=="
-      },
-      "System.Configuration.ConfigurationManager": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "JlYi9XVvIREURRUlGMr1F6vOFLk7YSY4p1vHo4kX3tQ0AGrjqlRWHDi66ImHhy6qwXBG3BJ6Y1QlYQ+Qz6Xgww==",
-        "dependencies": {
-          "System.Diagnostics.EventLog": "8.0.0",
-          "System.Security.Cryptography.ProtectedData": "8.0.0"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "Transitive",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg=="
-      },
-      "System.Diagnostics.EventLog": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "fdYxcRjQqTTacKId/2IECojlDSFvp7LP5N78+0z/xH7v/Tuw5ZAxu23Y6PTCRinqyu2ePx+Gn1098NC6jM6d+A=="
-      },
-      "System.IO": {
-        "type": "Transitive",
-        "resolved": "4.3.0",
-        "contentHash": "3qjaHvxQPDpSOYICjUoTsmoq5u6QJAFRUITgeT/4gqkF1bajbSmb1kwSxEA8AHlofqgcKJcM8udgieRNhaJ5Cg==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0",
-          "Microsoft.NETCore.Targets": "1.1.0",
-          "System.Runtime": "4.3.0",
-          "System.Text.Encoding": "4.3.0",
-          "System.Threading.Tasks": "4.3.0"
-        }
-      },
-      "System.IO.Pipelines": {
-        "type": "Transitive",
-        "resolved": "9.0.0",
-        "contentHash": "eA3cinogwaNB4jdjQHOP3Z3EuyiDII7MT35jgtnsA4vkn0LUrrSHsU0nzHTzFzmaFYeKV7MYyMxOocFzsBHpTw=="
-      },
-      "System.Management": {
-        "type": "Transitive",
-        "resolved": "5.0.0",
-        "contentHash": "MF1CHaRcC+MLFdnDthv4/bKWBZnlnSpkGqa87pKukQefgEdwtb9zFW6zs0GjPp73qtpYYg4q6PEKbzJbxCpKfw==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "5.0.0",
-          "Microsoft.Win32.Registry": "5.0.0",
-          "System.CodeDom": "5.0.0"
-        }
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.4",
-        "contentHash": "1MbJTHS1lZ4bS4FmsJjnuGJOu88ZzTT2rLvrhW7Ygic+pC0NWA+3hgAen0HRdsocuQXCkUTdFn9yHJJhsijDXw=="
-      },
-      "System.Reflection": {
-        "type": "Transitive",
-        "resolved": "4.3.0",
-        "contentHash": "KMiAFoW7MfJGa9nDFNcfu+FpEdiHpWgTcS2HdMpDvt9saK3y/G4GwprPyzqjFH9NTaGPQeWNHU+iDlDILj96aQ==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0",
-          "Microsoft.NETCore.Targets": "1.1.0",
-          "System.IO": "4.3.0",
-          "System.Reflection.Primitives": "4.3.0",
-          "System.Runtime": "4.3.0"
-        }
-      },
-      "System.Reflection.Metadata": {
-        "type": "Transitive",
-        "resolved": "5.0.0",
-        "contentHash": "5NecZgXktdGg34rh1OenY1rFNDCI8xSjFr+Z4OU4cU06AQHUdRnIIEeWENu3Wl4YowbzkymAIMvi3WyK9U53pQ=="
-      },
-      "System.Reflection.Primitives": {
-        "type": "Transitive",
-        "resolved": "4.3.0",
-        "contentHash": "5RXItQz5As4xN2/YUDxdpsEkMhvw3e6aNveFXUn4Hl/udNTCNhnKp8lT9fnc3MhvGKh1baak5CovpuQUXHAlIA==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0",
-          "Microsoft.NETCore.Targets": "1.1.0",
-          "System.Runtime": "4.3.0"
-        }
-      },
-      "System.Runtime": {
-        "type": "Transitive",
-        "resolved": "4.3.0",
-        "contentHash": "JufQi0vPQ0xGnAczR13AUFglDyVYt4Kqnz1AZaiKZ5+GICq0/1MH/mO/eAJHt/mHW1zjKBJd7kV26SrxddAhiw==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0",
-          "Microsoft.NETCore.Targets": "1.1.0"
-        }
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "System.Runtime.Loader": {
-        "type": "Transitive",
-        "resolved": "4.3.0",
-        "contentHash": "DHMaRn8D8YCK2GG2pw+UzNxn/OHVfaWx7OTLBD/hPegHZZgcZh3H6seWegrC4BYwsfuGrywIuT+MQs+rPqRLTQ==",
-        "dependencies": {
-          "System.IO": "4.3.0",
-          "System.Reflection": "4.3.0",
-          "System.Runtime": "4.3.0"
-        }
-      },
-      "System.Security.AccessControl": {
-        "type": "Transitive",
-        "resolved": "5.0.0",
-        "contentHash": "dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "5.0.0",
-          "System.Security.Principal.Windows": "5.0.0"
-        }
-      },
-      "System.Security.Cryptography.ProtectedData": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "+TUFINV2q2ifyXauQXRwy4CiBhqvDEDZeVJU7qfxya4aRYOKzVBpN+4acx25VcPB9ywUN6C0n8drWl110PhZEg=="
-      },
-      "System.Security.Permissions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "v/BBylw7XevuAsHXoX9dDUUfmBIcUf7Lkz8K3ZXIKz3YRKpw8YftpSir4n4e/jDTKFoaK37AsC3xnk+GNFI1Ow==",
-        "dependencies": {
-          "System.Windows.Extensions": "8.0.0"
-        }
-      },
-      "System.Security.Principal.Windows": {
-        "type": "Transitive",
-        "resolved": "5.0.0",
-        "contentHash": "t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA=="
-      },
-      "System.Text.Encoding": {
-        "type": "Transitive",
-        "resolved": "4.3.0",
-        "contentHash": "BiIg+KWaSDOITze6jGQynxg64naAPtqGHBwDrLaCtixsa5bKiR8dpPOHA7ge3C0JJQizJE+sfkz1wV+BAKAYZw==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0",
-          "Microsoft.NETCore.Targets": "1.1.0",
-          "System.Runtime": "4.3.0"
-        }
-      },
-      "System.Text.Encoding.CodePages": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "OZIsVplFGaVY90G2SbpgU7EnCoOO5pw1t4ic21dBF3/1omrJFpAGoNAVpPyMVOC90/hvgkGG3VFqR13YgZMQfg=="
-      },
-      "System.Text.Encodings.Web": {
-        "type": "Transitive",
-        "resolved": "9.0.0",
-        "contentHash": "e2hMgAErLbKyUUwt18qSBf9T5Y+SFAL3ZedM8fLupkVj8Rj2PZ9oxQ37XX2LF8fTO1wNIxvKpihD7Of7D/NxZw=="
-      },
-      "System.Text.Json": {
-        "type": "Transitive",
-        "resolved": "9.0.0",
-        "contentHash": "js7+qAu/9mQvnhA4EfGMZNEzXtJCDxgkgj8ohuxq/Qxv+R56G+ljefhiJHOxTNiw54q8vmABCWUwkMulNdlZ4A==",
-        "dependencies": {
-          "System.IO.Pipelines": "9.0.0",
-          "System.Text.Encodings.Web": "9.0.0"
-        }
-      },
-      "System.Threading.Tasks": {
-        "type": "Transitive",
-        "resolved": "4.3.0",
-        "contentHash": "LbSxKEdOUhVe8BezB/9uOGGppt+nZf6e1VFyw6v3DN6lqitm0OSn2uXMOdtP0M3W4iMcqcivm2J6UgqiwwnXiA==",
-        "dependencies": {
-          "Microsoft.NETCore.Platforms": "1.1.0",
-          "Microsoft.NETCore.Targets": "1.1.0",
-          "System.Runtime": "4.3.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "Transitive",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg=="
-      },
-      "System.Windows.Extensions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "Obg3a90MkOw9mYKxrardLpY2u0axDMrSmy4JCdq2cYbelM2cUwmUir5Bomvd1yxmPL9h5LVHU1tuKBZpUjfASg=="
-      },
-      "ZstdSharp.Port": {
-        "type": "Transitive",
-        "resolved": "0.8.0",
-        "contentHash": "Z62eNBIu8E8YtbqlMy57tK3dV1+m2b9NhPeaYovB5exmLKvrGCqOhJTzrEUH5VyUWU6vwX3c1XHJGhW5HVs8dA=="
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tests/Conformance.Tests/packages.lock.json b/tests/Conformance.Tests/packages.lock.json
deleted file mode 100644
index 0bd8e590e..000000000
--- a/tests/Conformance.Tests/packages.lock.json
+++ /dev/null
@@ -1,206 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    "net9.0": {
-      "AdoNet.Specification.Tests": {
-        "type": "Direct",
-        "requested": "[2.0.0-beta.2, )",
-        "resolved": "2.0.0-beta.2",
-        "contentHash": "W64bxMUv/RH3Bs65HJrw/N3opKDvxpnei5BqVtaGaNyeGaimYnngATFPpMCw0ljBpmMAx7b+mWvdEXl40hoNUA==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies": "1.0.3",
-          "Xunit.SkippableFact": "1.4.13",
-          "xunit": "2.5.3",
-          "xunit.extensibility.execution": "2.5.3"
-        }
-      },
-      "Microsoft.NET.Test.Sdk": {
-        "type": "Direct",
-        "requested": "[17.12.0, )",
-        "resolved": "17.12.0",
-        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
-        "dependencies": {
-          "Microsoft.CodeCoverage": "17.12.0",
-          "Microsoft.TestPlatform.TestHost": "17.12.0"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "xunit": {
-        "type": "Direct",
-        "requested": "[2.9.3, )",
-        "resolved": "2.9.3",
-        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
-        "dependencies": {
-          "xunit.analyzers": "1.18.0",
-          "xunit.assert": "2.9.3",
-          "xunit.core": "[2.9.3]"
-        }
-      },
-      "xunit.runner.visualstudio": {
-        "type": "Direct",
-        "requested": "[2.8.2, )",
-        "resolved": "2.8.2",
-        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.CodeCoverage": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net461": "1.0.3"
-        }
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies.net461": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "AmOJZwCqnOCNp6PPcf9joyogScWLtwy0M1WkqfEQ0M9nYwyDD7EX9ZjscKS5iYnyvteX7kzSKFCKt9I9dXA6mA=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "Microsoft.TestPlatform.ObjectModel": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
-        "dependencies": {
-          "System.Reflection.Metadata": "1.6.0"
-        }
-      },
-      "Microsoft.TestPlatform.TestHost": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
-        "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
-          "Newtonsoft.Json": "13.0.1"
-        }
-      },
-      "Newtonsoft.Json": {
-        "type": "Transitive",
-        "resolved": "13.0.1",
-        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Reflection.Metadata": {
-        "type": "Transitive",
-        "resolved": "1.6.0",
-        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
-      },
-      "Validation": {
-        "type": "Transitive",
-        "resolved": "2.4.18",
-        "contentHash": "NfvWJ1QeuZ1FQCkqgXTu1cOkRkbNCfxs4Tat+abXLwom6OXbULVhRGp34BTvVB4XPxj6VIAl7KfLfStXMt/Ehw=="
-      },
-      "xunit.abstractions": {
-        "type": "Transitive",
-        "resolved": "2.0.3",
-        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
-      },
-      "xunit.analyzers": {
-        "type": "Transitive",
-        "resolved": "1.18.0",
-        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
-      },
-      "xunit.assert": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
-      },
-      "xunit.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]",
-          "xunit.extensibility.execution": "[2.9.3]"
-        }
-      },
-      "xunit.extensibility.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
-        "dependencies": {
-          "xunit.abstractions": "2.0.3"
-        }
-      },
-      "xunit.extensibility.execution": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]"
-        }
-      },
-      "Xunit.SkippableFact": {
-        "type": "Transitive",
-        "resolved": "1.4.13",
-        "contentHash": "IyzZNvJEtXGlXrzxDiSbtH5Lyxf4iJdRQADuyjGdDf00LjXRLJwIoezQNFhFGKTMtvk8IIgaSHxW4mAV4O7b8A==",
-        "dependencies": {
-          "Validation": "2.4.18",
-          "xunit.extensibility.execution": "2.4.0"
-        }
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tests/IntegrationTests/packages.lock.json b/tests/IntegrationTests/packages.lock.json
deleted file mode 100644
index ccd66edad..000000000
--- a/tests/IntegrationTests/packages.lock.json
+++ /dev/null
@@ -1,1653 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    ".NETFramework,Version=v4.6.2": {
-      "Dapper.StrongName": {
-        "type": "Direct",
-        "requested": "[2.1.35, )",
-        "resolved": "2.1.35",
-        "contentHash": "DkQpZMhT6Tk/ZrN1cksQ7lQx3BMFL6Eq2oq5VLlZxqnDBJhOUo6qSwFrI5E0xssdDLxt91mPHlxLqPH4tylLNw=="
-      },
-      "Microsoft.Extensions.Configuration.Binder": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.EnvironmentVariables": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Json": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1",
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "System.Text.Json": "8.0.5"
-        }
-      },
-      "Microsoft.NET.Test.Sdk": {
-        "type": "Direct",
-        "requested": "[17.12.0, )",
-        "resolved": "17.12.0",
-        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
-        "dependencies": {
-          "Microsoft.CodeCoverage": "17.12.0"
-        }
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net462": "1.0.3"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Text.Json": {
-        "type": "Direct",
-        "requested": "[9.0.1, )",
-        "resolved": "9.0.1",
-        "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "9.0.1",
-          "System.Buffers": "4.5.1",
-          "System.IO.Pipelines": "9.0.1",
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
-          "System.Text.Encodings.Web": "9.0.1",
-          "System.Threading.Tasks.Extensions": "4.5.4",
-          "System.ValueTuple": "4.5.0"
-        }
-      },
-      "xunit": {
-        "type": "Direct",
-        "requested": "[2.9.3, )",
-        "resolved": "2.9.3",
-        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
-        "dependencies": {
-          "xunit.analyzers": "1.18.0",
-          "xunit.assert": "2.9.3",
-          "xunit.core": "[2.9.3]"
-        }
-      },
-      "xunit.runner.visualstudio": {
-        "type": "Direct",
-        "requested": "[2.8.2, )",
-        "resolved": "2.8.2",
-        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ==",
-        "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.10.0"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "IVkmUqf+KzbuXKrxi2tyQlg11RArYk26t2eU5cHekff+7Ao09vH8vt8idC0BJSMnpiRV2OK66zM2EwJU6Tm5Cw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.CodeCoverage": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
-      },
-      "Microsoft.Extensions.Configuration": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "8.0.0",
-          "System.ValueTuple": "4.5.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.FileExtensions": {
-        "type": "Transitive",
-        "resolved": "8.0.1",
-        "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileProviders.Physical": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileProviders.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileProviders.Physical": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==",
-        "dependencies": {
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileSystemGlobbing": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileSystemGlobbing": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
-      },
-      "Microsoft.Extensions.Primitives": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies.net462": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "IzAV30z22ESCeQfxP29oVf4qEo8fBGXLXSU6oacv/9Iqe6PzgHDKCaWfwMBak7bSJQM0F5boXWoZS+kChztRIQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "Microsoft.TestPlatform.ObjectModel": {
-        "type": "Transitive",
-        "resolved": "17.10.0",
-        "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==",
-        "dependencies": {
-          "System.Reflection.Metadata": "1.6.0"
-        }
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Collections.Immutable": {
-        "type": "Transitive",
-        "resolved": "1.5.0",
-        "contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ=="
-      },
-      "System.IO.Pipelines": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "uXf5o8eV/gtzDQY4lGROLFMWQvcViKcF8o4Q6KpIOjloAQXrnscQSu6gTxYJMHuNJnh7szIF9AzkaEq+zDLoEg==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Memory": "4.5.5",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.5.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
-      },
-      "System.Reflection.Metadata": {
-        "type": "Transitive",
-        "resolved": "1.6.0",
-        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==",
-        "dependencies": {
-          "System.Collections.Immutable": "1.5.0"
-        }
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "System.Text.Encodings.Web": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "XkspqduP2t1e1x2vBUAD/xZ5ZDvmywuUwsmB93MvyQLospJfqtX0GsR/kU0vUL2h4kmvf777z3txV2W4NrQ9Qg==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.ValueTuple": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
-      },
-      "xunit.abstractions": {
-        "type": "Transitive",
-        "resolved": "2.0.3",
-        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
-      },
-      "xunit.analyzers": {
-        "type": "Transitive",
-        "resolved": "1.18.0",
-        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
-      },
-      "xunit.assert": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
-      },
-      "xunit.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]",
-          "xunit.extensibility.execution": "[2.9.3]"
-        }
-      },
-      "xunit.extensibility.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
-        "dependencies": {
-          "xunit.abstractions": "2.0.3"
-        }
-      },
-      "xunit.extensibility.execution": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]"
-        }
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "mysqlconnector.authentication.ed25519": {
-        "type": "Project",
-        "dependencies": {
-          "MySqlConnector": "[1.0.0, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    },
-    ".NETFramework,Version=v4.8.1": {
-      "Dapper.StrongName": {
-        "type": "Direct",
-        "requested": "[2.1.35, )",
-        "resolved": "2.1.35",
-        "contentHash": "DkQpZMhT6Tk/ZrN1cksQ7lQx3BMFL6Eq2oq5VLlZxqnDBJhOUo6qSwFrI5E0xssdDLxt91mPHlxLqPH4tylLNw=="
-      },
-      "Microsoft.Extensions.Configuration.Binder": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.EnvironmentVariables": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Json": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1",
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "System.Text.Json": "8.0.5"
-        }
-      },
-      "Microsoft.NET.Test.Sdk": {
-        "type": "Direct",
-        "requested": "[17.12.0, )",
-        "resolved": "17.12.0",
-        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
-        "dependencies": {
-          "Microsoft.CodeCoverage": "17.12.0"
-        }
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Text.Json": {
-        "type": "Direct",
-        "requested": "[9.0.1, )",
-        "resolved": "9.0.1",
-        "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "9.0.1",
-          "System.Buffers": "4.5.1",
-          "System.IO.Pipelines": "9.0.1",
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
-          "System.Text.Encodings.Web": "9.0.1",
-          "System.Threading.Tasks.Extensions": "4.5.4",
-          "System.ValueTuple": "4.5.0"
-        }
-      },
-      "xunit": {
-        "type": "Direct",
-        "requested": "[2.9.3, )",
-        "resolved": "2.9.3",
-        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
-        "dependencies": {
-          "xunit.analyzers": "1.18.0",
-          "xunit.assert": "2.9.3",
-          "xunit.core": "[2.9.3]"
-        }
-      },
-      "xunit.runner.visualstudio": {
-        "type": "Direct",
-        "requested": "[2.8.2, )",
-        "resolved": "2.8.2",
-        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ==",
-        "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.10.0"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "IVkmUqf+KzbuXKrxi2tyQlg11RArYk26t2eU5cHekff+7Ao09vH8vt8idC0BJSMnpiRV2OK66zM2EwJU6Tm5Cw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.CodeCoverage": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
-      },
-      "Microsoft.Extensions.Configuration": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "8.0.0",
-          "System.ValueTuple": "4.5.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.FileExtensions": {
-        "type": "Transitive",
-        "resolved": "8.0.1",
-        "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileProviders.Physical": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileProviders.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileProviders.Physical": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==",
-        "dependencies": {
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileSystemGlobbing": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileSystemGlobbing": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
-      },
-      "Microsoft.Extensions.Primitives": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies.net481": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "Microsoft.TestPlatform.ObjectModel": {
-        "type": "Transitive",
-        "resolved": "17.10.0",
-        "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==",
-        "dependencies": {
-          "System.Reflection.Metadata": "1.6.0"
-        }
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Collections.Immutable": {
-        "type": "Transitive",
-        "resolved": "1.5.0",
-        "contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ=="
-      },
-      "System.IO.Pipelines": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "uXf5o8eV/gtzDQY4lGROLFMWQvcViKcF8o4Q6KpIOjloAQXrnscQSu6gTxYJMHuNJnh7szIF9AzkaEq+zDLoEg==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Memory": "4.5.5",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.5.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
-      },
-      "System.Reflection.Metadata": {
-        "type": "Transitive",
-        "resolved": "1.6.0",
-        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==",
-        "dependencies": {
-          "System.Collections.Immutable": "1.5.0"
-        }
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "System.Text.Encodings.Web": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "XkspqduP2t1e1x2vBUAD/xZ5ZDvmywuUwsmB93MvyQLospJfqtX0GsR/kU0vUL2h4kmvf777z3txV2W4NrQ9Qg==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.ValueTuple": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "okurQJO6NRE/apDIP23ajJ0hpiNmJ+f0BwOlB/cSqTLQlw5upkf+5+96+iG2Jw40G1fCVCyPz/FhIABUjMR+RQ=="
-      },
-      "xunit.abstractions": {
-        "type": "Transitive",
-        "resolved": "2.0.3",
-        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
-      },
-      "xunit.analyzers": {
-        "type": "Transitive",
-        "resolved": "1.18.0",
-        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
-      },
-      "xunit.assert": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
-      },
-      "xunit.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]",
-          "xunit.extensibility.execution": "[2.9.3]"
-        }
-      },
-      "xunit.extensibility.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
-        "dependencies": {
-          "xunit.abstractions": "2.0.3"
-        }
-      },
-      "xunit.extensibility.execution": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]"
-        }
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "mysqlconnector.authentication.ed25519": {
-        "type": "Project",
-        "dependencies": {
-          "MySqlConnector": "[1.0.0, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    },
-    "net6.0": {
-      "Dapper.StrongName": {
-        "type": "Direct",
-        "requested": "[2.1.35, )",
-        "resolved": "2.1.35",
-        "contentHash": "DkQpZMhT6Tk/ZrN1cksQ7lQx3BMFL6Eq2oq5VLlZxqnDBJhOUo6qSwFrI5E0xssdDLxt91mPHlxLqPH4tylLNw=="
-      },
-      "Microsoft.Extensions.Configuration.Binder": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.EnvironmentVariables": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Json": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1",
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "System.Text.Json": "8.0.5"
-        }
-      },
-      "Microsoft.NET.Test.Sdk": {
-        "type": "Direct",
-        "requested": "[17.12.0, )",
-        "resolved": "17.12.0",
-        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
-        "dependencies": {
-          "Microsoft.CodeCoverage": "17.12.0",
-          "Microsoft.TestPlatform.TestHost": "17.12.0"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Text.Json": {
-        "type": "Direct",
-        "requested": "[9.0.1, )",
-        "resolved": "9.0.1",
-        "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "9.0.1",
-          "System.Buffers": "4.5.1",
-          "System.IO.Pipelines": "9.0.1",
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0",
-          "System.Text.Encodings.Web": "9.0.1",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "xunit": {
-        "type": "Direct",
-        "requested": "[2.9.3, )",
-        "resolved": "2.9.3",
-        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
-        "dependencies": {
-          "xunit.analyzers": "1.18.0",
-          "xunit.assert": "2.9.3",
-          "xunit.core": "[2.9.3]"
-        }
-      },
-      "xunit.runner.visualstudio": {
-        "type": "Direct",
-        "requested": "[2.8.2, )",
-        "resolved": "2.8.2",
-        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "IVkmUqf+KzbuXKrxi2tyQlg11RArYk26t2eU5cHekff+7Ao09vH8vt8idC0BJSMnpiRV2OK66zM2EwJU6Tm5Cw=="
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.CodeCoverage": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
-      },
-      "Microsoft.Extensions.Configuration": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.FileExtensions": {
-        "type": "Transitive",
-        "resolved": "8.0.1",
-        "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileProviders.Physical": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileProviders.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileProviders.Physical": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==",
-        "dependencies": {
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileSystemGlobbing": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileSystemGlobbing": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
-      },
-      "Microsoft.Extensions.Primitives": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "Microsoft.TestPlatform.ObjectModel": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
-        "dependencies": {
-          "System.Reflection.Metadata": "1.6.0"
-        }
-      },
-      "Microsoft.TestPlatform.TestHost": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
-        "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
-          "Newtonsoft.Json": "13.0.1"
-        }
-      },
-      "Newtonsoft.Json": {
-        "type": "Transitive",
-        "resolved": "13.0.1",
-        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.IO.Pipelines": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "uXf5o8eV/gtzDQY4lGROLFMWQvcViKcF8o4Q6KpIOjloAQXrnscQSu6gTxYJMHuNJnh7szIF9AzkaEq+zDLoEg==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Memory": "4.5.5",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw=="
-      },
-      "System.Reflection.Metadata": {
-        "type": "Transitive",
-        "resolved": "1.6.0",
-        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "System.Text.Encodings.Web": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "XkspqduP2t1e1x2vBUAD/xZ5ZDvmywuUwsmB93MvyQLospJfqtX0GsR/kU0vUL2h4kmvf777z3txV2W4NrQ9Qg==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "xunit.abstractions": {
-        "type": "Transitive",
-        "resolved": "2.0.3",
-        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
-      },
-      "xunit.analyzers": {
-        "type": "Transitive",
-        "resolved": "1.18.0",
-        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
-      },
-      "xunit.assert": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
-      },
-      "xunit.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]",
-          "xunit.extensibility.execution": "[2.9.3]"
-        }
-      },
-      "xunit.extensibility.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
-        "dependencies": {
-          "xunit.abstractions": "2.0.3"
-        }
-      },
-      "xunit.extensibility.execution": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]"
-        }
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )"
-        }
-      },
-      "mysqlconnector.authentication.ed25519": {
-        "type": "Project",
-        "dependencies": {
-          "MySqlConnector": "[1.0.0, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Diagnostics.DiagnosticSource": "8.0.1"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg=="
-      }
-    },
-    "net8.0": {
-      "Dapper.StrongName": {
-        "type": "Direct",
-        "requested": "[2.1.35, )",
-        "resolved": "2.1.35",
-        "contentHash": "DkQpZMhT6Tk/ZrN1cksQ7lQx3BMFL6Eq2oq5VLlZxqnDBJhOUo6qSwFrI5E0xssdDLxt91mPHlxLqPH4tylLNw=="
-      },
-      "Microsoft.Extensions.Configuration.Binder": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.EnvironmentVariables": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Json": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1",
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.NET.Test.Sdk": {
-        "type": "Direct",
-        "requested": "[17.12.0, )",
-        "resolved": "17.12.0",
-        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
-        "dependencies": {
-          "Microsoft.CodeCoverage": "17.12.0",
-          "Microsoft.TestPlatform.TestHost": "17.12.0"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Text.Json": {
-        "type": "Direct",
-        "requested": "[9.0.1, )",
-        "resolved": "9.0.1",
-        "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA==",
-        "dependencies": {
-          "System.IO.Pipelines": "9.0.1",
-          "System.Text.Encodings.Web": "9.0.1"
-        }
-      },
-      "xunit": {
-        "type": "Direct",
-        "requested": "[2.9.3, )",
-        "resolved": "2.9.3",
-        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
-        "dependencies": {
-          "xunit.analyzers": "1.18.0",
-          "xunit.assert": "2.9.3",
-          "xunit.core": "[2.9.3]"
-        }
-      },
-      "xunit.runner.visualstudio": {
-        "type": "Direct",
-        "requested": "[2.8.2, )",
-        "resolved": "2.8.2",
-        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.CodeCoverage": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
-      },
-      "Microsoft.Extensions.Configuration": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.FileExtensions": {
-        "type": "Transitive",
-        "resolved": "8.0.1",
-        "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileProviders.Physical": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileProviders.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileProviders.Physical": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==",
-        "dependencies": {
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileSystemGlobbing": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileSystemGlobbing": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
-      },
-      "Microsoft.Extensions.Primitives": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "Microsoft.TestPlatform.ObjectModel": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
-        "dependencies": {
-          "System.Reflection.Metadata": "1.6.0"
-        }
-      },
-      "Microsoft.TestPlatform.TestHost": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
-        "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
-          "Newtonsoft.Json": "13.0.1"
-        }
-      },
-      "Newtonsoft.Json": {
-        "type": "Transitive",
-        "resolved": "13.0.1",
-        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.IO.Pipelines": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "uXf5o8eV/gtzDQY4lGROLFMWQvcViKcF8o4Q6KpIOjloAQXrnscQSu6gTxYJMHuNJnh7szIF9AzkaEq+zDLoEg=="
-      },
-      "System.Reflection.Metadata": {
-        "type": "Transitive",
-        "resolved": "1.6.0",
-        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
-      },
-      "System.Text.Encodings.Web": {
-        "type": "Transitive",
-        "resolved": "9.0.1",
-        "contentHash": "XkspqduP2t1e1x2vBUAD/xZ5ZDvmywuUwsmB93MvyQLospJfqtX0GsR/kU0vUL2h4kmvf777z3txV2W4NrQ9Qg=="
-      },
-      "xunit.abstractions": {
-        "type": "Transitive",
-        "resolved": "2.0.3",
-        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
-      },
-      "xunit.analyzers": {
-        "type": "Transitive",
-        "resolved": "1.18.0",
-        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
-      },
-      "xunit.assert": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
-      },
-      "xunit.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]",
-          "xunit.extensibility.execution": "[2.9.3]"
-        }
-      },
-      "xunit.extensibility.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
-        "dependencies": {
-          "xunit.abstractions": "2.0.3"
-        }
-      },
-      "xunit.extensibility.execution": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]"
-        }
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
-        }
-      },
-      "mysqlconnector.authentication.ed25519": {
-        "type": "Project",
-        "dependencies": {
-          "MySqlConnector": "[1.0.0, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      }
-    },
-    "net9.0": {
-      "Dapper.StrongName": {
-        "type": "Direct",
-        "requested": "[2.1.35, )",
-        "resolved": "2.1.35",
-        "contentHash": "DkQpZMhT6Tk/ZrN1cksQ7lQx3BMFL6Eq2oq5VLlZxqnDBJhOUo6qSwFrI5E0xssdDLxt91mPHlxLqPH4tylLNw=="
-      },
-      "Microsoft.Extensions.Configuration.Binder": {
-        "type": "Direct",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "7IQhGK+wjyGrNsPBjJcZwWAr+Wf6D4+TwOptUt77bWtgNkiV8tDEbhFS+dDamtQFZ2X7kWG9m71iZQRj2x3zgQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.EnvironmentVariables": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "plvZ0ZIpq+97gdPNNvhwvrEZ92kNml9hd1pe3idMA7svR0PztdzVLkoWLcRFgySYXUJc3kSM3Xw3mNFMo/bxRA==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Json": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "L89DLNuimOghjV3tLx0ArFDwVEJD6+uGB3BMCMX01kaLzXkaXHb2021xOMl2QOxUxbdePKUZsUY7n2UUkycjRg==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.Configuration.FileExtensions": "8.0.1",
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0"
-        }
-      },
-      "Microsoft.NET.Test.Sdk": {
-        "type": "Direct",
-        "requested": "[17.12.0, )",
-        "resolved": "17.12.0",
-        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
-        "dependencies": {
-          "Microsoft.CodeCoverage": "17.12.0",
-          "Microsoft.TestPlatform.TestHost": "17.12.0"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "System.Text.Json": {
-        "type": "Direct",
-        "requested": "[9.0.1, )",
-        "resolved": "9.0.1",
-        "contentHash": "eqWHDZqYPv1PvuvoIIx5pF74plL3iEOZOl/0kQP+Y0TEbtgNnM2W6k8h8EPYs+LTJZsXuWa92n5W5sHTWvE3VA=="
-      },
-      "xunit": {
-        "type": "Direct",
-        "requested": "[2.9.3, )",
-        "resolved": "2.9.3",
-        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
-        "dependencies": {
-          "xunit.analyzers": "1.18.0",
-          "xunit.assert": "2.9.3",
-          "xunit.core": "[2.9.3]"
-        }
-      },
-      "xunit.runner.visualstudio": {
-        "type": "Direct",
-        "requested": "[2.8.2, )",
-        "resolved": "2.8.2",
-        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.CodeCoverage": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
-      },
-      "Microsoft.Extensions.Configuration": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "0J/9YNXTMWSZP2p2+nvl8p71zpSwokZXZuJW+VjdErkegAnFdO1XlqtA62SJtgVYHdKu3uPxJHcMR/r35HwFBA==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3lE/iLSutpgX1CC0NOW70FJoGARRHbyKmG7dc0klnUZ9Dd9hS6N/POPWhKhMLCEuNN5nXEY5agmlFtH562vqhQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.Configuration.FileExtensions": {
-        "type": "Transitive",
-        "resolved": "8.0.1",
-        "contentHash": "EJzSNO9oaAXnTdtdNO6npPRsIIeZCBSNmdQ091VDO7fBiOtJAAeEq6dtrVXIi3ZyjC5XRSAtVvF8SzcneRHqKQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Configuration": "8.0.0",
-          "Microsoft.Extensions.Configuration.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileProviders.Physical": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileProviders.Abstractions": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "ZbaMlhJlpisjuWbvXr4LdAst/1XxH3vZ6A0BsgTphZ2L4PGuxRLz7Jr/S7mkAAnOn78Vu0fKhEgNF5JO3zfjqQ==",
-        "dependencies": {
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileProviders.Physical": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "UboiXxpPUpwulHvIAVE36Knq0VSHaAmfrFkegLyBZeaADuKezJ/AIXYAW8F5GBlGk/VaibN2k/Zn1ca8YAfVdA==",
-        "dependencies": {
-          "Microsoft.Extensions.FileProviders.Abstractions": "8.0.0",
-          "Microsoft.Extensions.FileSystemGlobbing": "8.0.0",
-          "Microsoft.Extensions.Primitives": "8.0.0"
-        }
-      },
-      "Microsoft.Extensions.FileSystemGlobbing": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
-      },
-      "Microsoft.Extensions.Primitives": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bXJEZrW9ny8vjMF1JV253WeLhpEVzFo1lyaZu1vQ4ZxWUlVvknZ/+ftFgVheLubb4eZPSwwxBeqS1JkCOjxd8g=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "Microsoft.TestPlatform.ObjectModel": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
-        "dependencies": {
-          "System.Reflection.Metadata": "1.6.0"
-        }
-      },
-      "Microsoft.TestPlatform.TestHost": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
-        "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
-          "Newtonsoft.Json": "13.0.1"
-        }
-      },
-      "Newtonsoft.Json": {
-        "type": "Transitive",
-        "resolved": "13.0.1",
-        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Reflection.Metadata": {
-        "type": "Transitive",
-        "resolved": "1.6.0",
-        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
-      },
-      "xunit.abstractions": {
-        "type": "Transitive",
-        "resolved": "2.0.3",
-        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
-      },
-      "xunit.analyzers": {
-        "type": "Transitive",
-        "resolved": "1.18.0",
-        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
-      },
-      "xunit.assert": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
-      },
-      "xunit.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]",
-          "xunit.extensibility.execution": "[2.9.3]"
-        }
-      },
-      "xunit.extensibility.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
-        "dependencies": {
-          "xunit.abstractions": "2.0.3"
-        }
-      },
-      "xunit.extensibility.execution": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]"
-        }
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
-        }
-      },
-      "mysqlconnector.authentication.ed25519": {
-        "type": "Project",
-        "dependencies": {
-          "MySqlConnector": "[1.0.0, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json b/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
deleted file mode 100644
index 0d8fc35b6..000000000
--- a/tests/MySqlConnector.DependencyInjection.Tests/packages.lock.json
+++ /dev/null
@@ -1,183 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    "net9.0": {
-      "Microsoft.Extensions.DependencyInjection": {
-        "type": "Direct",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "BmANAnR5Xd4Oqw7yQ75xOAYODybZQRzdeNucg7kS5wWKd2PNnMdYtJ2Vciy0QLylRmv42DGl5+AFL9izA6F1Rw==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      },
-      "Microsoft.NET.Test.Sdk": {
-        "type": "Direct",
-        "requested": "[17.12.0, )",
-        "resolved": "17.12.0",
-        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
-        "dependencies": {
-          "Microsoft.CodeCoverage": "17.12.0",
-          "Microsoft.TestPlatform.TestHost": "17.12.0"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "xunit": {
-        "type": "Direct",
-        "requested": "[2.9.3, )",
-        "resolved": "2.9.3",
-        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
-        "dependencies": {
-          "xunit.analyzers": "1.18.0",
-          "xunit.assert": "2.9.3",
-          "xunit.core": "[2.9.3]"
-        }
-      },
-      "xunit.runner.visualstudio": {
-        "type": "Direct",
-        "requested": "[2.8.2, )",
-        "resolved": "2.8.2",
-        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.CodeCoverage": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "Microsoft.TestPlatform.ObjectModel": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
-        "dependencies": {
-          "System.Reflection.Metadata": "1.6.0"
-        }
-      },
-      "Microsoft.TestPlatform.TestHost": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
-        "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
-          "Newtonsoft.Json": "13.0.1"
-        }
-      },
-      "Newtonsoft.Json": {
-        "type": "Transitive",
-        "resolved": "13.0.1",
-        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Reflection.Metadata": {
-        "type": "Transitive",
-        "resolved": "1.6.0",
-        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
-      },
-      "xunit.abstractions": {
-        "type": "Transitive",
-        "resolved": "2.0.3",
-        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
-      },
-      "xunit.analyzers": {
-        "type": "Transitive",
-        "resolved": "1.18.0",
-        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
-      },
-      "xunit.assert": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
-      },
-      "xunit.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]",
-          "xunit.extensibility.execution": "[2.9.3]"
-        }
-      },
-      "xunit.extensibility.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
-        "dependencies": {
-          "xunit.abstractions": "2.0.3"
-        }
-      },
-      "xunit.extensibility.execution": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]"
-        }
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
-        }
-      },
-      "mysqlconnector.dependencyinjection": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "[8.0.2, )",
-          "MySqlConnector": "[1.0.0, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json b/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
deleted file mode 100644
index ef443644e..000000000
--- a/tests/MySqlConnector.NativeAot.Tests/packages.lock.json
+++ /dev/null
@@ -1,169 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    "net8.0": {
-      "Microsoft.DotNet.ILCompiler": {
-        "type": "Direct",
-        "requested": "[8.0.14, )",
-        "resolved": "8.0.14",
-        "contentHash": "lJvndCQZJxHBVoD8VxfSJ2MaURHENC63sbeAJNfedvXvscgjQSpfVdcTsv0iGeSu9X05G05TYMCWVbZGj4Z51Q=="
-      },
-      "Microsoft.NET.ILLink.Tasks": {
-        "type": "Direct",
-        "requested": "[8.0.14, )",
-        "resolved": "8.0.14",
-        "contentHash": "4U2fd7PexNKrK5ZqfqIcXZj9/lRRjFsLgA/pxuFQTuGQuLYP/+7yACz/j7EmWbEj/fspOf4mafi/vHIy/rKDzQ=="
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
-        }
-      },
-      "mysqlconnector.dependencyinjection": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "[8.0.2, )",
-          "MySqlConnector": "[1.0.0, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      }
-    },
-    "net9.0": {
-      "Microsoft.DotNet.ILCompiler": {
-        "type": "Direct",
-        "requested": "[9.0.3, )",
-        "resolved": "9.0.3",
-        "contentHash": "UqR8lcAIRC5zPk97txAJlsCvDhP9jxV7JSxEMkm3QI384aq/sU0zYiC8kms4ooNtixORgZxbFrDAsYCewR4X2Q=="
-      },
-      "Microsoft.NET.ILLink.Tasks": {
-        "type": "Direct",
-        "requested": "[9.0.3, )",
-        "resolved": "9.0.3",
-        "contentHash": "1rqGTfubVg0qj2PsK6esyq3PIxtYJYrN3LsYUV9RrvH3anmt3fT3ozYdAZZH4U8JU/pt5pPIUk8NBSu26wtekA=="
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
-        }
-      },
-      "mysqlconnector.dependencyinjection": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "[8.0.2, )",
-          "MySqlConnector": "[1.0.0, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tests/MySqlConnector.Tests/packages.lock.json b/tests/MySqlConnector.Tests/packages.lock.json
deleted file mode 100644
index 5cea0042c..000000000
--- a/tests/MySqlConnector.Tests/packages.lock.json
+++ /dev/null
@@ -1,400 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    ".NETFramework,Version=v4.8.1": {
-      "Microsoft.NET.Test.Sdk": {
-        "type": "Direct",
-        "requested": "[17.12.0, )",
-        "resolved": "17.12.0",
-        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
-        "dependencies": {
-          "Microsoft.CodeCoverage": "17.12.0"
-        }
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies": {
-        "type": "Direct",
-        "requested": "[1.0.3, )",
-        "resolved": "1.0.3",
-        "contentHash": "vUc9Npcs14QsyOD01tnv/m8sQUnGTGOw1BCmKcv77LBJY7OxhJ+zJF7UD/sCL3lYNFuqmQEVlkfS4Quif6FyYg==",
-        "dependencies": {
-          "Microsoft.NETFramework.ReferenceAssemblies.net481": "1.0.3"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "xunit": {
-        "type": "Direct",
-        "requested": "[2.9.3, )",
-        "resolved": "2.9.3",
-        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
-        "dependencies": {
-          "xunit.analyzers": "1.18.0",
-          "xunit.assert": "2.9.3",
-          "xunit.core": "[2.9.3]"
-        }
-      },
-      "xunit.runner.visualstudio": {
-        "type": "Direct",
-        "requested": "[2.8.2, )",
-        "resolved": "2.8.2",
-        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ==",
-        "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.10.0"
-        }
-      },
-      "Microsoft.Bcl.AsyncInterfaces": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "3WA9q9yVqJp222P3x1wYIGDAkpjAku0TMUaaQV22g6L67AI0LdOIrVS7Ht2vJfLHGSPVuqN94vIr15qn+HEkHw==",
-        "dependencies": {
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.CodeCoverage": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
-      },
-      "Microsoft.NETFramework.ReferenceAssemblies.net481": {
-        "type": "Transitive",
-        "resolved": "1.0.3",
-        "contentHash": "Vv/20vgHS7VglVOVh8J3Iz/MA+VYKVRp9f7r2qiKBMuzviTOmocG70yq0Q8T5OTmCONkEAIJwETD1zhEfLkAXQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "Microsoft.TestPlatform.ObjectModel": {
-        "type": "Transitive",
-        "resolved": "17.10.0",
-        "contentHash": "KkwhjQevuDj0aBRoPLY6OLAhGqbPUEBuKLbaCs0kUVw29qiOYncdORd4mLVJbn9vGZ7/iFGQ/+AoJl0Tu5Umdg==",
-        "dependencies": {
-          "System.Reflection.Metadata": "1.6.0"
-        }
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Buffers": {
-        "type": "Transitive",
-        "resolved": "4.5.1",
-        "contentHash": "Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg=="
-      },
-      "System.Collections.Immutable": {
-        "type": "Transitive",
-        "resolved": "1.5.0",
-        "contentHash": "EXKiDFsChZW0RjrZ4FYHu9aW6+P4MCgEDCklsVseRfhoO0F+dXeMSsMRAlVXIo06kGJ/zv+2w1a2uc2+kxxSaQ=="
-      },
-      "System.Memory": {
-        "type": "Transitive",
-        "resolved": "4.5.5",
-        "contentHash": "XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
-        "dependencies": {
-          "System.Buffers": "4.5.1",
-          "System.Numerics.Vectors": "4.5.0",
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      },
-      "System.Numerics.Vectors": {
-        "type": "Transitive",
-        "resolved": "4.5.0",
-        "contentHash": "QQTlPTl06J/iiDbJCiepZ4H//BVraReU4O4EoRw1U02H5TLUIT7xn3GnDp9AXPSlJUDyFs4uWjWafNX6WrAojQ=="
-      },
-      "System.Reflection.Metadata": {
-        "type": "Transitive",
-        "resolved": "1.6.0",
-        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ==",
-        "dependencies": {
-          "System.Collections.Immutable": "1.5.0"
-        }
-      },
-      "System.Runtime.CompilerServices.Unsafe": {
-        "type": "Transitive",
-        "resolved": "6.0.0",
-        "contentHash": "/iUeP3tq1S0XdNNoMz5C9twLSrM/TH+qElHkXWaPvuNOt+99G75NrV0OS2EqHx5wMN7popYjpc8oTjC1y16DLg=="
-      },
-      "xunit.abstractions": {
-        "type": "Transitive",
-        "resolved": "2.0.3",
-        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
-      },
-      "xunit.analyzers": {
-        "type": "Transitive",
-        "resolved": "1.18.0",
-        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
-      },
-      "xunit.assert": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
-      },
-      "xunit.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]",
-          "xunit.extensibility.execution": "[2.9.3]"
-        }
-      },
-      "xunit.extensibility.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
-        "dependencies": {
-          "xunit.abstractions": "2.0.3"
-        }
-      },
-      "xunit.extensibility.execution": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]"
-        }
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )",
-          "System.Diagnostics.DiagnosticSource": "[8.0.1, )",
-          "System.Threading.Tasks.Extensions": "[4.5.4, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg==",
-        "dependencies": {
-          "Microsoft.Bcl.AsyncInterfaces": "8.0.0",
-          "System.Threading.Tasks.Extensions": "4.5.4"
-        }
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2",
-          "System.Buffers": "4.5.1",
-          "System.Diagnostics.DiagnosticSource": "8.0.1",
-          "System.Memory": "4.5.5"
-        }
-      },
-      "System.Diagnostics.DiagnosticSource": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.1, )",
-        "resolved": "8.0.1",
-        "contentHash": "vaoWjvkG1aenR2XdjaVivlCV9fADfgyhW5bZtXT23qaEea0lWiUljdQuze4E31vKM7ZWJaSUsbYIKE3rnzfZUg==",
-        "dependencies": {
-          "System.Memory": "4.5.5",
-          "System.Runtime.CompilerServices.Unsafe": "6.0.0"
-        }
-      },
-      "System.Threading.Tasks.Extensions": {
-        "type": "CentralTransitive",
-        "requested": "[4.5.4, )",
-        "resolved": "4.5.4",
-        "contentHash": "zteT+G8xuGu6mS+mzDzYXbzS7rd3K6Fjb9RiZlYlJPam2/hU7JCBZBVEcywNuR+oZ1ncTvc/cq0faRr3P01OVg==",
-        "dependencies": {
-          "System.Runtime.CompilerServices.Unsafe": "4.5.3"
-        }
-      }
-    },
-    "net9.0": {
-      "Microsoft.NET.Test.Sdk": {
-        "type": "Direct",
-        "requested": "[17.12.0, )",
-        "resolved": "17.12.0",
-        "contentHash": "kt/PKBZ91rFCWxVIJZSgVLk+YR+4KxTuHf799ho8WNiK5ZQpJNAEZCAWX86vcKrs+DiYjiibpYKdGZP6+/N17w==",
-        "dependencies": {
-          "Microsoft.CodeCoverage": "17.12.0",
-          "Microsoft.TestPlatform.TestHost": "17.12.0"
-        }
-      },
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "xunit": {
-        "type": "Direct",
-        "requested": "[2.9.3, )",
-        "resolved": "2.9.3",
-        "contentHash": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
-        "dependencies": {
-          "xunit.analyzers": "1.18.0",
-          "xunit.assert": "2.9.3",
-          "xunit.core": "[2.9.3]"
-        }
-      },
-      "xunit.runner.visualstudio": {
-        "type": "Direct",
-        "requested": "[2.8.2, )",
-        "resolved": "2.8.2",
-        "contentHash": "vm1tbfXhFmjFMUmS4M0J0ASXz3/U5XvXBa6DOQUL3fEz4Vt6YPhv+ESCarx6M6D+9kJkJYZKCNvJMas1+nVfmQ=="
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.CodeCoverage": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "4svMznBd5JM21JIG2xZKGNanAHNXplxf/kQDFfLHXQ3OnpJkayRK/TjacFjA+EYmoyuNXHo/sOETEfcYtAzIrA=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "Microsoft.TestPlatform.ObjectModel": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "TDqkTKLfQuAaPcEb3pDDWnh7b3SyZF+/W9OZvWFp6eJCIiiYFdSB6taE2I6tWrFw5ywhzOb6sreoGJTI6m3rSQ==",
-        "dependencies": {
-          "System.Reflection.Metadata": "1.6.0"
-        }
-      },
-      "Microsoft.TestPlatform.TestHost": {
-        "type": "Transitive",
-        "resolved": "17.12.0",
-        "contentHash": "MiPEJQNyADfwZ4pJNpQex+t9/jOClBGMiCiVVFuELCMSX2nmNfvUor3uFVxNNCg30uxDP8JDYfPnMXQzsfzYyg==",
-        "dependencies": {
-          "Microsoft.TestPlatform.ObjectModel": "17.12.0",
-          "Newtonsoft.Json": "13.0.1"
-        }
-      },
-      "Newtonsoft.Json": {
-        "type": "Transitive",
-        "resolved": "13.0.1",
-        "contentHash": "ppPFpBcvxdsfUonNcvITKqLl3bqxWbDCZIzDWHzjpdAHRFfZe0Dw9HmA0+za13IdyrgJwpkDTDA9fHaxOrt20A=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      },
-      "System.Reflection.Metadata": {
-        "type": "Transitive",
-        "resolved": "1.6.0",
-        "contentHash": "COC1aiAJjCoA5GBF+QKL2uLqEBew4JsCkQmoHKbN3TlOZKa2fKLz5CpiRQKDz0RsAOEGsVKqOD5bomsXq/4STQ=="
-      },
-      "xunit.abstractions": {
-        "type": "Transitive",
-        "resolved": "2.0.3",
-        "contentHash": "pot1I4YOxlWjIb5jmwvvQNbTrZ3lJQ+jUGkGjWE3hEFM0l5gOnBWS+H3qsex68s5cO52g+44vpGzhAt+42vwKg=="
-      },
-      "xunit.analyzers": {
-        "type": "Transitive",
-        "resolved": "1.18.0",
-        "contentHash": "OtFMHN8yqIcYP9wcVIgJrq01AfTxijjAqVDy/WeQVSyrDC1RzBWeQPztL49DN2syXRah8TYnfvk035s7L95EZQ=="
-      },
-      "xunit.assert": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "/Kq28fCE7MjOV42YLVRAJzRF0WmEqsmflm0cfpMjGtzQ2lR5mYVj1/i0Y8uDAOLczkL3/jArrwehfMD0YogMAA=="
-      },
-      "xunit.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "BiAEvqGvyme19wE0wTKdADH+NloYqikiU0mcnmiNyXaF9HyHmE6sr/3DC5vnBkgsWaE6yPyWszKSPSApWdRVeQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]",
-          "xunit.extensibility.execution": "[2.9.3]"
-        }
-      },
-      "xunit.extensibility.core": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "kf3si0YTn2a8J8eZNb+zFpwfoyvIrQ7ivNk5ZYA5yuYk1bEtMe4DxJ2CF/qsRgmEnDr7MnW1mxylBaHTZ4qErA==",
-        "dependencies": {
-          "xunit.abstractions": "2.0.3"
-        }
-      },
-      "xunit.extensibility.execution": {
-        "type": "Transitive",
-        "resolved": "2.9.3",
-        "contentHash": "yMb6vMESlSrE3Wfj7V6cjQ3S4TXdXpRqYeNEI3zsX31uTsGMJjEw6oD5F5u1cHnMptjhEECnmZSsPxB6ChZHDQ==",
-        "dependencies": {
-          "xunit.extensibility.core": "[2.9.3]"
-        }
-      },
-      "mysqlconnector": {
-        "type": "Project",
-        "dependencies": {
-          "Microsoft.Extensions.Logging.Abstractions": "[8.0.2, )"
-        }
-      },
-      "Microsoft.Extensions.DependencyInjection.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "3iE7UF7MQkCv1cxzCahz+Y/guQbTqieyxyaWKhrRO91itI9cOKO76OHeQDahqG4MmW5umr3CcCvGmK92lWNlbg=="
-      },
-      "Microsoft.Extensions.Logging.Abstractions": {
-        "type": "CentralTransitive",
-        "requested": "[8.0.2, )",
-        "resolved": "8.0.2",
-        "contentHash": "nroMDjS7hNBPtkZqVBbSiQaQjWRDxITI8Y7XnDs97rqG3EbzVTNLZQf7bIeUJcaHOV8bca47s1Uxq94+2oGdxA==",
-        "dependencies": {
-          "Microsoft.Extensions.DependencyInjection.Abstractions": "8.0.2"
-        }
-      }
-    }
-  }
-}
\ No newline at end of file
diff --git a/tools/SchemaCollectionGenerator/packages.lock.json b/tools/SchemaCollectionGenerator/packages.lock.json
deleted file mode 100644
index c76e60b00..000000000
--- a/tools/SchemaCollectionGenerator/packages.lock.json
+++ /dev/null
@@ -1,53 +0,0 @@
-{
-  "version": 2,
-  "dependencies": {
-    "net9.0": {
-      "Microsoft.SourceLink.GitHub": {
-        "type": "Direct",
-        "requested": "[8.0.0, )",
-        "resolved": "8.0.0",
-        "contentHash": "G5q7OqtwIyGTkeIOAc3u2ZuV/kicQaec5EaRnc0pIeSnh9LUjj+PYQrJYBURvDt7twGl2PKA7nSN0kz1Zw5bnQ==",
-        "dependencies": {
-          "Microsoft.Build.Tasks.Git": "8.0.0",
-          "Microsoft.SourceLink.Common": "8.0.0"
-        }
-      },
-      "MinVer": {
-        "type": "Direct",
-        "requested": "[6.0.0, )",
-        "resolved": "6.0.0",
-        "contentHash": "+/SsmiySsXJlvQLCGBqaZKNVt3s/Y/HbAdwtop7Km2CnuZbaScoqkWJEBQ5Cy9ebkn6kCYKrHsXgwrFdTgcb3g=="
-      },
-      "StyleCop.Analyzers": {
-        "type": "Direct",
-        "requested": "[1.2.0-beta.556, )",
-        "resolved": "1.2.0-beta.556",
-        "contentHash": "llRPgmA1fhC0I0QyFLEcjvtM2239QzKr/tcnbsjArLMJxJlu0AA5G7Fft0OI30pHF3MW63Gf4aSSsjc5m82J1Q==",
-        "dependencies": {
-          "StyleCop.Analyzers.Unstable": "1.2.0.556"
-        }
-      },
-      "YamlDotNet": {
-        "type": "Direct",
-        "requested": "[16.3.0, )",
-        "resolved": "16.3.0",
-        "contentHash": "SgMOdxbz8X65z8hraIs6hOEdnkH6hESTAIUa7viEngHOYaH+6q5XJmwr1+yb9vJpNQ19hCQY69xbFsLtXpobQA=="
-      },
-      "Microsoft.Build.Tasks.Git": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "bZKfSIKJRXLTuSzLudMFte/8CempWjVamNUR5eHJizsy+iuOuO/k2gnh7W0dHJmYY0tBf+gUErfluCv5mySAOQ=="
-      },
-      "Microsoft.SourceLink.Common": {
-        "type": "Transitive",
-        "resolved": "8.0.0",
-        "contentHash": "dk9JPxTCIevS75HyEQ0E4OVAFhB2N+V9ShCXf8Q6FkUQZDkgLI12y679Nym1YqsiSysuQskT7Z+6nUf3yab6Vw=="
-      },
-      "StyleCop.Analyzers.Unstable": {
-        "type": "Transitive",
-        "resolved": "1.2.0.556",
-        "contentHash": "zvn9Mqs/ox/83cpYPignI8hJEM2A93s2HkHs8HYMOAQW0PkampyoErAiIyKxgTLqbbad29HX/shv/6LGSjPJNQ=="
-      }
-    }
-  }
-}
\ No newline at end of file

From 20a73edc460d095a3c553c8b68bda0b37deb0605 Mon Sep 17 00:00:00 2001
From: Holger Boskugel <133562689+NETSphereSoft@users.noreply.github.com>
Date: Fri, 28 Mar 2025 01:10:52 +0100
Subject: [PATCH 377/393] Mark MySqlDbType with DbProviderSpecificTypeProperty.
 Fixes #1557

---
 src/MySqlConnector/MySqlParameter.cs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 6d493cba8..2c97527f7 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -1,4 +1,5 @@
 using System.Buffers.Text;
+using System.Data.Common;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
@@ -72,6 +73,7 @@ public override DbType DbType
 		}
 	}
 
+	[DbProviderSpecificTypeProperty(true)]
 	public MySqlDbType MySqlDbType
 	{
 		get => m_mySqlDbType;

From 937f74e4081a9d272ccde64a0798414f818c014c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 5 Apr 2025 09:54:50 -0700
Subject: [PATCH 378/393] Log in directly with caching_sha2_password. Fixes
 #1562

If the server allows 'caching_sha2_password' in the initial handshake packet, then use that authentication method directly, saving a roundtrip.
---
 src/MySqlConnector/Core/ServerSession.cs      | 25 ++++++++++++++++++-
 .../Payloads/HandshakeResponse41Payload.cs    |  9 ++++---
 2 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 0d9ffe429..67e5a438e 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -528,8 +528,11 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			if (m_supportsConnectionAttributes && cs.ConnectionAttributes is null)
 				cs.ConnectionAttributes = CreateConnectionAttributes(cs.ApplicationName);
 
+			// send a caching_sha2_password response if the server advertised support in the initial handshake
+			var useCachingSha2 = initialHandshake.AuthPluginName == "caching_sha2_password";
+
 			var password = GetPassword(cs, connection);
-			using (var handshakeResponsePayload = HandshakeResponse41Payload.Create(initialHandshake, cs, password, m_compressionMethod, connection.ZstandardPlugin?.CompressionLevel, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null))
+			using (var handshakeResponsePayload = HandshakeResponse41Payload.Create(initialHandshake, cs, password, useCachingSha2, m_compressionMethod, connection.ZstandardPlugin?.CompressionLevel, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null))
 				await SendReplyAsync(handshakeResponsePayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
@@ -539,6 +542,26 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			}
 
+			// check if caching_sha2_password response was sent
+			if (useCachingSha2 && payload.HeaderByte == CachingSha2ServerResponsePayload.Signature)
+			{
+				// process a successful response, or fall back to sending the password if the server doesn't have it
+				var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload.Span);
+				if (cachingSha2ServerResponsePayload.Succeeded)
+				{
+					payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+				}
+				else if (!m_isSecureConnection && password.Length != 0)
+				{
+					var publicKey = await GetRsaPublicKeyAsync(m_currentAuthenticationMethod, cs, ioBehavior, cancellationToken).ConfigureAwait(false);
+					payload = await SendEncryptedPasswordAsync(AuthPluginData, publicKey, password, ioBehavior, cancellationToken).ConfigureAwait(false);
+				}
+				else
+				{
+					payload = await SendClearPasswordAsync(password, ioBehavior, cancellationToken).ConfigureAwait(false);
+				}
+			}
+
 			var ok = OkPayload.Create(payload.Span, this);
 
 			if (m_sslPolicyErrors != SslPolicyErrors.None)
diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
index 609fcfd45..fdc5f2352 100644
--- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
@@ -1,5 +1,6 @@
 using MySqlConnector.Core;
 using MySqlConnector.Protocol.Serialization;
+using MySqlConnector.Utilities;
 
 namespace MySqlConnector.Protocol.Payloads;
 
@@ -55,12 +56,14 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 	public static PayloadData CreateWithSsl(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, CompressionMethod compressionMethod, CharacterSet characterSet) =>
 		CreateCapabilitiesPayload(serverCapabilities, cs, compressionMethod, characterSet, ProtocolCapabilities.Ssl).ToPayloadData();
 
-	public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSettings cs, string password, CompressionMethod compressionMethod, int? compressionLevel, CharacterSet characterSet, byte[]? connectionAttributes)
+	public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSettings cs, string password, bool useCachingSha2, CompressionMethod compressionMethod, int? compressionLevel, CharacterSet characterSet, byte[]? connectionAttributes)
 	{
 		// TODO: verify server capabilities
 		var writer = CreateCapabilitiesPayload(handshake.ProtocolCapabilities, cs, compressionMethod, characterSet);
 		writer.WriteNullTerminatedString(cs.UserID);
-		var authenticationResponse = AuthenticationUtility.CreateAuthenticationResponse(handshake.AuthPluginData, password);
+
+		var authenticationResponse = useCachingSha2 ? AuthenticationUtility.CreateScrambleResponse(Utility.TrimZeroByte(handshake.AuthPluginData.AsSpan()), password) :
+			AuthenticationUtility.CreateAuthenticationResponse(handshake.AuthPluginData, password);
 		writer.Write((byte) authenticationResponse.Length);
 		writer.Write(authenticationResponse);
 
@@ -68,7 +71,7 @@ public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSe
 			writer.WriteNullTerminatedString(cs.Database);
 
 		if ((handshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
-			writer.Write("mysql_native_password\0"u8);
+			writer.Write(useCachingSha2 ? "caching_sha2_password\0"u8 : "mysql_native_password\0"u8);
 
 		if (connectionAttributes is not null)
 			writer.Write(connectionAttributes);

From e9cb2a176e16b198416c45f71780c659f344ff46 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Mon, 7 Apr 2025 10:36:54 -0700
Subject: [PATCH 379/393] Bump
 advanced-security/component-detection-dependency-submission-action (#1564)

Bumps [advanced-security/component-detection-dependency-submission-action](https://github.com/advanced-security/component-detection-dependency-submission-action) from 0.0.4 to 0.0.5.
- [Release notes](https://github.com/advanced-security/component-detection-dependency-submission-action/releases)
- [Commits](https://github.com/advanced-security/component-detection-dependency-submission-action/compare/v0.0.4...v0.0.5)

---
updated-dependencies:
- dependency-name: advanced-security/component-detection-dependency-submission-action
  dependency-version: 0.0.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 07726b0d0..5e3406737 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,7 +25,7 @@ jobs:
       run: dotnet build --configuration Release --no-restore
 
     - name: Component detection
-      uses: advanced-security/component-detection-dependency-submission-action@v0.0.4
+      uses: advanced-security/component-detection-dependency-submission-action@v0.0.5
 
   native-aot:
     name: Native AOT

From b81c038bfaa64aa3e30bdd06bc3ea020adfd6103 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Thu, 17 Apr 2025 12:38:32 -0700
Subject: [PATCH 380/393] Don't treat VARCHAR columns as GUIDs. Fixes #1565
 (#1566)

* Only treat CHAR(n) columns as Guids.
* Add test to reproduce bug.

This reverts a change in #1546 that was introduced to fix #1528. However, none of the integration tests fail locally when reverting this change, so it's not clear why it was necessary.
---
 src/MySqlConnector/ColumnReaders/ColumnReader.cs |  4 ++--
 tests/IntegrationTests/DataTypes.cs              |  6 ++++++
 tests/IntegrationTests/DataTypesFixture.cs       | 16 +++++++++-------
 3 files changed, 17 insertions(+), 9 deletions(-)

diff --git a/src/MySqlConnector/ColumnReaders/ColumnReader.cs b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
index ec6d1c85d..86ba9145f 100644
--- a/src/MySqlConnector/ColumnReaders/ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
@@ -45,13 +45,13 @@ public static ColumnReader Create(bool isBinary, ColumnDefinitionPayload columnD
 				return BitColumnReader.Instance;
 
 			case ColumnType.String:
-			case ColumnType.VarString:
 				if (connection.GuidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
 					return GuidChar36ColumnReader.Instance;
 				if (connection.GuidFormat == MySqlGuidFormat.Char32 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 32)
 					return GuidChar32ColumnReader.Instance;
-				goto case ColumnType.VarChar;
+				goto case ColumnType.VarString;
 
+			case ColumnType.VarString:
 			case ColumnType.VarChar:
 			case ColumnType.TinyBlob:
 			case ColumnType.Blob:
diff --git a/tests/IntegrationTests/DataTypes.cs b/tests/IntegrationTests/DataTypes.cs
index b03650cbd..6d5b1e170 100644
--- a/tests/IntegrationTests/DataTypes.cs
+++ b/tests/IntegrationTests/DataTypes.cs
@@ -329,6 +329,8 @@ public void QueryDecimal(string column, object[] expected)
 	[InlineData("latin1", new[] { null, "", "ASCII", "Lãtïñ", c_251ByteString })]
 	[InlineData("latin1bin", new[] { null, "", "ASCII", "Lãtïñ", c_251ByteString })]
 	[InlineData("cp1251", new[] { null, "", "ASCII", "АБВГабвг", c_251ByteString })]
+	[InlineData("nonguid_utf8", new[] { null, "", "ASCII", "Ũńıċōđĕ", "This string has 36 characters in it." })]
+	[InlineData("nonguid_latin1", new[] { null, "", "ASCII", "Lãtïñ", "This string has 36 characters in it." })]
 	public void QueryString(string column, string[] expected)
 	{
 		DoQuery("strings", column, "VARCHAR", expected, reader => reader.GetString(0));
@@ -1328,6 +1330,8 @@ public void GetSchemaTableAfterNextResult()
 	[InlineData("cp1251", "datatypes_strings", MySqlDbType.VarChar, "VARCHAR", 300, typeof(string), "N", -1, 0)]
 	[InlineData("guid", "datatypes_strings", MySqlDbType.Guid, "CHAR(36)", 36, typeof(Guid), "N", -1, 0)]
 	[InlineData("guidbin", "datatypes_strings", MySqlDbType.Guid, "CHAR(36)", 36, typeof(Guid), "N", -1, 0)]
+	[InlineData("nonguid_utf8", "datatypes_strings", MySqlDbType.VarChar, "VARCHAR", 36, typeof(string), "N", -1, 0)]
+	[InlineData("nonguid_latin1", "datatypes_strings", MySqlDbType.VarChar, "VARCHAR", 36, typeof(string), "N", -1, 0)]
 	[InlineData("Date", "datatypes_times", MySqlDbType.Date, "DATE", 10, typeof(DateTime), "N", -1, 0)]
 	[InlineData("DateTime", "datatypes_times", MySqlDbType.DateTime, "DATETIME", 26, typeof(DateTime), "N", -1, 6)]
 	[InlineData("Timestamp", "datatypes_times", MySqlDbType.Timestamp, "TIMESTAMP", 26, typeof(DateTime), "N", -1, 6)]
@@ -1420,6 +1424,8 @@ public void GetColumnSchema(string column, string table, MySqlDbType mySqlDbType
 	[InlineData("BigDecimal", "datatypes_reals", MySqlDbType.NewDecimal, "DECIMAL(50,30)", typeof(decimal), 3, null)]
 	[InlineData("utf8", "datatypes_strings", MySqlDbType.VarChar, "VARCHAR(300)", typeof(string), 3, "ASCII")]
 	[InlineData("latin1", "datatypes_strings", MySqlDbType.VarChar, "VARCHAR(300)", typeof(string), 3, "ASCII")]
+	[InlineData("nonguid_utf8", "datatypes_strings", MySqlDbType.VarChar, "VARCHAR(36)", typeof(string), 3, "ASCII")]
+	[InlineData("nonguid_latin1", "datatypes_strings", MySqlDbType.VarChar, "VARCHAR(36)", typeof(string), 3, "ASCII")]
 	[InlineData("Date", "datatypes_times", MySqlDbType.Date, "DATE", typeof(DateTime), 2, null)]
 	[InlineData("DateTime", "datatypes_times", MySqlDbType.DateTime, "DATETIME", typeof(DateTime), 2, null)]
 	[InlineData("Timestamp", "datatypes_times", MySqlDbType.Timestamp, "TIMESTAMP", typeof(DateTime), 2, null)]
diff --git a/tests/IntegrationTests/DataTypesFixture.cs b/tests/IntegrationTests/DataTypesFixture.cs
index f9ad28a38..471e519a8 100644
--- a/tests/IntegrationTests/DataTypesFixture.cs
+++ b/tests/IntegrationTests/DataTypesFixture.cs
@@ -121,21 +121,23 @@ latin1 varchar(300) character set 'latin1' null,
   latin1bin varchar(300) character set latin1 collate latin1_bin null,
   cp1251 varchar(300) character set 'cp1251' null,
   guid char(36) null,
-  guidbin char(36) binary null
+  guidbin char(36) binary null,
+  nonguid_utf8 varchar(36) character set 'utf8mb4' null,
+  nonguid_latin1 varchar(36) character set 'latin1' null
 );
 
-insert into datatypes_strings(utf8, utf8bin, latin1, latin1bin, cp1251, guid, guidbin)
+insert into datatypes_strings(utf8, utf8bin, latin1, latin1bin, cp1251, guid, guidbin, nonguid_utf8, nonguid_latin1)
 values
-  (null, null, null, null, null, null, null),
-  ('', '', '', '', '', '00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000'),
-  ('ASCII', 'ASCII', 'ASCII', 'ASCII', 'ASCII', '00000000-0000-0000-c000-000000000046', '00000000-0000-0000-c000-000000000046'),
-  ('Ũńıċōđĕ', 'Ũńıċōđĕ', 'Lãtïñ', 'Lãtïñ', 'АБВГабвг', 'fd24a0e8-c3f2-4821-a456-35da2dc4bb8f', 'fd24a0e8-c3f2-4821-a456-35da2dc4bb8f'),
+  (null, null, null, null, null, null, null, null, null),
+  ('', '', '', '', '', '00000000-0000-0000-0000-000000000000', '00000000-0000-0000-0000-000000000000', '', ''),
+  ('ASCII', 'ASCII', 'ASCII', 'ASCII', 'ASCII', '00000000-0000-0000-c000-000000000046', '00000000-0000-0000-c000-000000000046', 'ASCII', 'ASCII'),
+  ('Ũńıċōđĕ', 'Ũńıċōđĕ', 'Lãtïñ', 'Lãtïñ', 'АБВГабвг', 'fd24a0e8-c3f2-4821-a456-35da2dc4bb8f', 'fd24a0e8-c3f2-4821-a456-35da2dc4bb8f', 'Ũńıċōđĕ', 'Lãtïñ'),
   ('This string has exactly 251 characters in it. The encoded length is stored as 0xFC 0xFB 0x00. 0xFB (i.e., 251) is the sentinel byte indicating ""this field is null"". Incorrectly interpreting the (decoded) length as the sentinel byte would corrupt data.',
    'This string has exactly 251 characters in it. The encoded length is stored as 0xFC 0xFB 0x00. 0xFB (i.e., 251) is the sentinel byte indicating ""this field is null"". Incorrectly interpreting the (decoded) length as the sentinel byte would corrupt data.',
    'This string has exactly 251 characters in it. The encoded length is stored as 0xFC 0xFB 0x00. 0xFB (i.e., 251) is the sentinel byte indicating ""this field is null"". Incorrectly interpreting the (decoded) length as the sentinel byte would corrupt data.',
    'This string has exactly 251 characters in it. The encoded length is stored as 0xFC 0xFB 0x00. 0xFB (i.e., 251) is the sentinel byte indicating ""this field is null"". Incorrectly interpreting the (decoded) length as the sentinel byte would corrupt data.',
    'This string has exactly 251 characters in it. The encoded length is stored as 0xFC 0xFB 0x00. 0xFB (i.e., 251) is the sentinel byte indicating ""this field is null"". Incorrectly interpreting the (decoded) length as the sentinel byte would corrupt data.',
-   '6a0e0a40-6228-11d3-a996-0050041896c8', '6a0e0a40-6228-11d3-a996-0050041896c8');
+   '6a0e0a40-6228-11d3-a996-0050041896c8', '6a0e0a40-6228-11d3-a996-0050041896c8', 'This string has 36 characters in it.', 'This string has 36 characters in it.');
 
 drop table if exists datatypes_blobs;
 create table datatypes_blobs(

From 805d65e05fe0bdb632ea0d819f498393ad01b5e5 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 19 Apr 2025 10:53:39 -0700
Subject: [PATCH 381/393] Update to MySQL 9.3.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .github/workflows/ci.yml                               | 2 +-
 Directory.Packages.props                               | 2 +-
 azure-pipelines.yml                                    | 4 ++--
 docs/content/home.md                                   | 2 +-
 docs/content/tutorials/migrating-from-connector-net.md | 2 +-
 tests/Benchmark/Benchmark.csproj                       | 2 +-
 tests/IntegrationTests/app.config                      | 2 +-
 tests/README.md                                        | 2 +-
 8 files changed, 9 insertions(+), 9 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 5e3406737..68dda7743 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,7 +35,7 @@ jobs:
         tfm: [ 'net8.0', 'net9.0' ]
     services:
       mysql:
-        image: mysql:9.2
+        image: mysql:9.3
         env:
           MYSQL_DATABASE: testdb
           MYSQL_ROOT_PASSWORD: pass
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 8c36d5fa8..25fe3b765 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -16,7 +16,7 @@
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
-    <PackageVersion Include="MySql.Data" Version="9.2.0" />
+    <PackageVersion Include="MySql.Data" Version="9.3.0" />
     <PackageVersion Include="NLog" Version="4.7.15" />
     <PackageVersion Include="Serilog" Version="2.12.0" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index fa4d3d285..2d692f5d9 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -176,8 +176,8 @@ jobs:
         image: 'mysql:8.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
-      'MySQL 9.2':
-        image: 'mysql:9.2'
+      'MySQL 9.3':
+        image: 'mysql:9.3'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
       'MariaDB 10.6':
diff --git a/docs/content/home.md b/docs/content/home.md
index 0db29f09c..10e2fa408 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -65,7 +65,7 @@ Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://m
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
 MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**, **11.6**) |
-MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.2**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
+MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.3**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
 ProxySQL | 2.x | Some [compatibility issues](https://github.com/search?q=repo%3Amysql-net%2FMySqlConnector+proxysql&type=issues)
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 92d64f4ba..8666d78ea 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -41,7 +41,7 @@ In a .NET Framework application, make the following `app.config` change to regis
     <!-- add name="MySQL Data Provider"
       invariant="MySql.Data.MySqlClient"
       description=".Net Framework Data Provider for MySQL"
-      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.2.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.3.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
     <add name="MySqlConnector"
diff --git a/tests/Benchmark/Benchmark.csproj b/tests/Benchmark/Benchmark.csproj
index 3edca13de..c872995fc 100644
--- a/tests/Benchmark/Benchmark.csproj
+++ b/tests/Benchmark/Benchmark.csproj
@@ -8,7 +8,7 @@
   <ItemGroup>
     <PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
     <PackageReference Include="MySqlConnector" Version="2.4.0" />
-    <PackageReference Include="MySql.Data" Version="9.2.0" />
+    <PackageReference Include="MySql.Data" Version="9.3.0" />
   </ItemGroup>
 
 <!--  <ItemGroup>-->
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index 27c32de31..0f88d3075 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -4,7 +4,7 @@
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
       <remove invariant="MySql.Data.MySqlClient" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.2.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.3.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>
diff --git a/tests/README.md b/tests/README.md
index 31b9686b2..4ea82a640 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,7 @@ It also verifies that MySqlConnector and MySQL Connector/NET (MySql.Data) have s
 
 The tests require a MySQL server. The simplest way to run one is with [Docker](https://www.docker.com/community-edition):
 
-    docker run -d --rm --pull always --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.2 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
+    docker run -d --rm --pull always --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.3 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
     docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes'; CREATE USER 'caching-sha2-user'@'%' IDENTIFIED WITH caching_sha2_password BY 'Cach!ng-Sh@2-Pa55'; GRANT ALL PRIVILEGES ON *.* TO 'caching-sha2-user'@'%';"
 
 Copy the file `IntegrationTests/config.json.example` to `IntegrationTests/config.json`, then edit

From 90847189d004dfe3c63d904f9e535e69b4d3747a Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 15 May 2025 14:56:34 -0700
Subject: [PATCH 382/393] Bump
 advanced-security/component-detection-dependency-submission-action. (#1571)

Bumps [advanced-security/component-detection-dependency-submission-action](https://github.com/advanced-security/component-detection-dependency-submission-action) from 0.0.5 to 0.0.6.
- [Release notes](https://github.com/advanced-security/component-detection-dependency-submission-action/releases)
- [Commits](https://github.com/advanced-security/component-detection-dependency-submission-action/compare/v0.0.5...v0.0.6)

---
updated-dependencies:
- dependency-name: advanced-security/component-detection-dependency-submission-action
  dependency-version: 0.0.6
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 68dda7743..727a20971 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,7 +25,7 @@ jobs:
       run: dotnet build --configuration Release --no-restore
 
     - name: Component detection
-      uses: advanced-security/component-detection-dependency-submission-action@v0.0.5
+      uses: advanced-security/component-detection-dependency-submission-action@v0.0.6
 
   native-aot:
     name: Native AOT

From 413e6997a281ec6f9273bb5710a35d558023f73c Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 17 May 2025 13:21:31 -0700
Subject: [PATCH 383/393] Update Azure Pipelines images.

Update Ubuntu to 24.04 and Windows Server to 2022, as per https://devblogs.microsoft.com/devops/upcoming-updates-for-azure-pipelines-agents-images/.
---
 azure-pipelines.yml | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 2d692f5d9..58e7b9801 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -6,21 +6,21 @@ jobs:
 - job: linux_build
   displayName: 'Linux Build'
   pool:
-    vmimage: 'ubuntu-20.04'
+    vmimage: 'ubuntu-24.04'
   steps:
   - template: '.ci/build-steps.yml'
 
 - job: windows_build
   displayName: 'Windows Build'
   pool:
-    vmimage: 'windows-latest'
+    vmimage: 'windows-2022'
   steps:
   - template: '.ci/build-steps.yml'
 
 - job: windows_mysql_data
   displayName: 'MySql.Data Tests'
   pool:
-    vmimage: 'windows-2019'
+    vmimage: 'windows-2022'
   steps:
   - template: '.ci/install-mysql-windows.yml'
   - task: CopyFiles@2
@@ -69,7 +69,7 @@ jobs:
   dependsOn: linux_build
   displayName: 'Linux Unit Tests'
   pool:
-    vmimage: 'ubuntu-20.04'
+    vmimage: 'ubuntu-24.04'
   steps:
   - template: '.ci/mysqlconnector-tests-steps.yml'
 
@@ -77,7 +77,7 @@ jobs:
   dependsOn: windows_build
   displayName: 'Windows Tests'
   pool:
-    vmimage: 'windows-2019'
+    vmimage: 'windows-2022'
   steps:
   - template: '.ci/install-mysql-windows.yml'
   - template: '.ci/mysqlconnector-tests-steps.yml'
@@ -89,7 +89,7 @@ jobs:
 - job: windows_integration_tests_1
   displayName: 'Windows Integration Tests (Part 1)'
   pool:
-    vmimage: 'windows-2019'
+    vmimage: 'windows-2022'
   steps:
   - template: '.ci/install-mysql-windows.yml'
   - task: UseDotNet@2
@@ -126,7 +126,7 @@ jobs:
 - job: windows_integration_tests_2
   displayName: 'Windows Integration Tests (Part 2)'
   pool:
-    vmimage: 'windows-2019'
+    vmimage: 'windows-2022'
   steps:
   - template: '.ci/install-mysql-windows.yml'
   - task: UseDotNet@2
@@ -165,7 +165,7 @@ jobs:
   dependsOn: linux_build
   displayName: 'Linux Tests'
   pool:
-    vmimage: 'ubuntu-20.04'
+    vmimage: 'ubuntu-24.04'
   strategy:
     matrix:
       'MySQL 8.0':

From 227f5b0e6d4c54d95c1f4730f2f6abfc8d94cf90 Mon Sep 17 00:00:00 2001
From: "Bradley Grainger (aider)" <bgrainger@gmail.com>
Date: Mon, 27 Jan 2025 06:49:40 -0800
Subject: [PATCH 384/393] Add Parsec authentication plugin implementation.

Create a new ServerFeature for parsec.

Drop netstandard2.0 support. PBKDF2-SHA512 support was only added in .NET Framework 4.7.2 and .NET Standard 2.1.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .ci/config/config.compression.json            |  2 +-
 .ci/config/config.json                        |  2 +-
 .ci/docker-run.sh                             |  6 ++
 .ci/server/init_parsec.sql                    |  3 +
 Directory.Packages.props                      |  1 +
 azure-pipelines.yml                           | 18 ++--
 .../Chaos.NaCl/CryptoBytes.cs                 | 37 +++++++
 .../Chaos.NaCl/Ed25519.cs                     | 63 ++++++++++++
 .../Internal/Ed25519Ref10/GroupElement.cs     | 99 ++++++++++---------
 .../Chaos.NaCl/Internal/Ed25519Ref10/base2.cs | 50 ++++++++++
 .../Internal/Ed25519Ref10/keypair.cs          | 33 +++++++
 .../Chaos.NaCl/Internal/Ed25519Ref10/sign.cs  | 50 ++++++++++
 .../Internal/Ed25519Ref10/sqrtm1.cs           |  9 ++
 .../CompatibilitySuppressions.xml             |  2 +-
 ...SqlConnector.Authentication.Ed25519.csproj | 18 +++-
 .../ParsecAuthenticationPlugin.cs             | 90 +++++++++++++++++
 .../docs/README.md                            | 10 +-
 src/MySqlConnector/Core/ServerSession.cs      | 15 +++
 tests/IntegrationTests/ConnectAsync.cs        | 19 +++-
 tests/IntegrationTests/ServerFeatures.cs      |  5 +
 20 files changed, 464 insertions(+), 68 deletions(-)
 create mode 100644 .ci/server/init_parsec.sql
 create mode 100644 src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/CryptoBytes.cs
 create mode 100644 src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Ed25519.cs
 create mode 100644 src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/base2.cs
 create mode 100644 src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs
 create mode 100644 src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sign.cs
 create mode 100644 src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sqrtm1.cs
 create mode 100644 src/MySqlConnector.Authentication.Ed25519/ParsecAuthenticationPlugin.cs

diff --git a/.ci/config/config.compression.json b/.ci/config/config.compression.json
index 09326f1f6..448eb1b2b 100644
--- a/.ci/config/config.compression.json
+++ b/.ci/config/config.compression.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
+      "UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
diff --git a/.ci/config/config.json b/.ci/config/config.json
index bc38f605a..e32c72260 100644
--- a/.ci/config/config.json
+++ b/.ci/config/config.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
+      "UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index 7a791e21d..1247f47c2 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -79,6 +79,12 @@ for i in `seq 1 120`; do
 		if [ $? -ne 0 ]; then exit $?; fi
 	fi
 
+	if [[ $OMIT_FEATURES != *"ParsecAuthentication"* ]]; then
+		echo "Installing auth_parsec component"
+		docker exec mysql bash -c "$MYSQL -uroot -ptest < /etc/mysql/conf.d/init_parsec.sql"
+		if [ $? -ne 0 ]; then exit $?; fi
+	fi
+
 	if [[ $OMIT_FEATURES != *"QueryAttributes"* ]]; then
 		echo "Installing query_attributes component"
 		docker exec mysql $MYSQL -uroot -ptest -e "INSTALL COMPONENT 'file://component_query_attributes';"
diff --git a/.ci/server/init_parsec.sql b/.ci/server/init_parsec.sql
new file mode 100644
index 000000000..85c8d6894
--- /dev/null
+++ b/.ci/server/init_parsec.sql
@@ -0,0 +1,3 @@
+INSTALL SONAME 'auth_parsec';
+CREATE USER 'parsec-user'@'%' IDENTIFIED via parsec using PASSWORD('P@rs3c-Pa55');
+GRANT ALL PRIVILEGES ON *.* TO 'parsec-user'@'%';
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 25fe3b765..9f75afc5a 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -9,6 +9,7 @@
     <PackageVersion Include="Dapper.StrongName" Version="2.1.35" />
     <PackageVersion Include="IndexRange" Version="1.0.3" />
     <PackageVersion Include="log4net" Version="2.0.17" />
+    <PackageVersion Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" Version="8.0.16" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.2" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="8.0.0" />
     <PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="8.0.1" />
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 58e7b9801..d7cba324e 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -51,7 +51,7 @@ jobs:
       arguments: 'tests\IntegrationTests\IntegrationTests.csproj -c MySqlData'
       testRunTitle: 'MySql.Data integration tests'
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,TlsFingerprintValidation,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,StreamingResults,TlsFingerprintValidation,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=root;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600'
       DATA__CERTIFICATESPATH: '$(Build.Repository.LocalPath)\.ci\server\certs\'
       DATA__MYSQLBULKLOADERLOCALCSVFILE: '$(Build.Repository.LocalPath)\tests\TestData\LoadData_UTF8_BOM_Unix.CSV'
@@ -120,7 +120,7 @@ jobs:
       arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net481/net9.0', 'No SSL') }}
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True'
 
 - job: windows_integration_tests_2
@@ -158,7 +158,7 @@ jobs:
       arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net8.0', 'No SSL') }}
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
 
 - job: linux_integration_tests
@@ -171,27 +171,27 @@ jobs:
       'MySQL 8.0':
         image: 'mysql:8.0'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
       'MySQL 8.4':
         image: 'mysql:8.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
       'MySQL 9.3':
         image: 'mysql:9.3'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
       'MariaDB 10.6':
         image: 'mariadb:10.6'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
       'MariaDB 10.11':
         image: 'mariadb:10.11'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
       'MariaDB 11.4':
         image: 'mariadb:11.4'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin,Redirection'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Sha256Password,Tls11,UuidToBin,Redirection'
       'MariaDB 11.6':
         image: 'mariadb:11.6'
         connectionStringExtra: ''
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/CryptoBytes.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/CryptoBytes.cs
new file mode 100644
index 000000000..c637b6f98
--- /dev/null
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/CryptoBytes.cs
@@ -0,0 +1,37 @@
+using System;
+using System.Runtime.CompilerServices;
+
+namespace Chaos.NaCl
+{
+    internal static class CryptoBytes
+    {
+        public static void Wipe(byte[] data)
+        {
+            if (data == null)
+                throw new ArgumentNullException("data");
+            InternalWipe(data, 0, data.Length);
+        }
+
+        // Secure wiping is hard
+        // * the GC can move around and copy memory
+        //   Perhaps this can be avoided by using unmanaged memory or by fixing the position of the array in memory
+        // * Swap files and error dumps can contain secret information
+        //   It seems possible to lock memory in RAM, no idea about error dumps
+        // * Compiler could optimize out the wiping if it knows that data won't be read back
+        //   I hope this is enough, suppressing inlining
+        //   but perhaps `RtlSecureZeroMemory` is needed
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        internal static void InternalWipe(byte[] data, int offset, int count)
+        {
+            Array.Clear(data, offset, count);
+        }
+
+        // shallow wipe of structs
+        [MethodImpl(MethodImplOptions.NoInlining)]
+        internal static void InternalWipe<T>(ref T data)
+            where T : struct
+        {
+            data = default(T);
+        }
+    }
+}
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Ed25519.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Ed25519.cs
new file mode 100644
index 000000000..613f8330a
--- /dev/null
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Ed25519.cs
@@ -0,0 +1,63 @@
+using System;
+using System.Security.Cryptography;
+using Chaos.NaCl.Internal.Ed25519Ref10;
+
+namespace Chaos.NaCl
+{
+    internal static class Ed25519
+    {
+        public static readonly int PublicKeySizeInBytes = 32;
+        public static readonly int SignatureSizeInBytes = 64;
+        public static readonly int ExpandedPrivateKeySizeInBytes = 32 * 2;
+        public static readonly int PrivateKeySeedSizeInBytes = 32;
+        public static readonly int SharedKeySizeInBytes = 32;
+
+        public static void Sign(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> expandedPrivateKey)
+        {
+            if (signature.Array == null)
+                throw new ArgumentNullException("signature.Array");
+            if (signature.Count != SignatureSizeInBytes)
+                throw new ArgumentException("signature.Count");
+            if (expandedPrivateKey.Array == null)
+                throw new ArgumentNullException("expandedPrivateKey.Array");
+            if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes)
+                throw new ArgumentException("expandedPrivateKey.Count");
+            if (message.Array == null)
+                throw new ArgumentNullException("message.Array");
+            Ed25519Operations.crypto_sign2(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, expandedPrivateKey.Array, expandedPrivateKey.Offset);
+        }
+
+        public static byte[] Sign(byte[] message, byte[] expandedPrivateKey)
+        {
+            var signature = new byte[SignatureSizeInBytes];
+            Sign(new ArraySegment<byte>(signature), new ArraySegment<byte>(message), new ArraySegment<byte>(expandedPrivateKey));
+            return signature;
+        }
+
+        public static byte[] ExpandedPrivateKeyFromSeed(byte[] privateKeySeed)
+        {
+            byte[] privateKey;
+            byte[] publicKey;
+            KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
+#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
+            CryptographicOperations.ZeroMemory(publicKey);
+#else
+            CryptoBytes.Wipe(publicKey);
+#endif
+            return privateKey;
+        }
+
+        public static void KeyPairFromSeed(out byte[] publicKey, out byte[] expandedPrivateKey, byte[] privateKeySeed)
+        {
+            if (privateKeySeed == null)
+                throw new ArgumentNullException("privateKeySeed");
+            if (privateKeySeed.Length != PrivateKeySeedSizeInBytes)
+                throw new ArgumentException("privateKeySeed");
+            var pk = new byte[PublicKeySizeInBytes];
+            var sk = new byte[ExpandedPrivateKeySizeInBytes];
+            Ed25519Operations.crypto_sign_keypair(pk, 0, sk, 0, privateKeySeed, 0);
+            publicKey = pk;
+            expandedPrivateKey = sk;
+        }
+    }
+}
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs
index 9ae034b6e..abeaca869 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/GroupElement.cs
@@ -1,52 +1,63 @@
-namespace Chaos.NaCl.Internal.Ed25519Ref10;
+﻿using System;
 
-/*
-ge means group element.
-
-Here the group is the set of pairs (x,y) of field elements (see fe.h)
-satisfying -x^2 + y^2 = 1 + d x^2y^2
-where d = -121665/121666.
+namespace Chaos.NaCl.Internal.Ed25519Ref10
+{
+	/*
+	ge means group element.
 
-Representations:
-  ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
-  ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
-  ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
-  ge_precomp (Duif): (y+x,y-x,2dxy)
-*/
+	Here the group is the set of pairs (x,y) of field elements (see fe.h)
+	satisfying -x^2 + y^2 = 1 + d x^2y^2
+	where d = -121665/121666.
 
-internal struct GroupElementP2
-{
-	public FieldElement X;
-	public FieldElement Y;
-	public FieldElement Z;
-} ;
+	Representations:
+	  ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
+	  ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
+	  ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
+	  ge_precomp (Duif): (y+x,y-x,2dxy)
+	*/
 
-internal struct GroupElementP3
-{
-	public FieldElement X;
-	public FieldElement Y;
-	public FieldElement Z;
-	public FieldElement T;
-} ;
+	internal struct GroupElementP2
+	{
+		public FieldElement X;
+		public FieldElement Y;
+		public FieldElement Z;
+	} ;
 
-internal struct GroupElementP1P1
-{
-	public FieldElement X;
-	public FieldElement Y;
-	public FieldElement Z;
-	public FieldElement T;
-} ;
+	internal struct GroupElementP3
+	{
+		public FieldElement X;
+		public FieldElement Y;
+		public FieldElement Z;
+		public FieldElement T;
+	} ;
 
-internal struct GroupElementPreComp
-{
-	public FieldElement yplusx;
-	public FieldElement yminusx;
-	public FieldElement xy2d;
+	internal struct GroupElementP1P1
+	{
+		public FieldElement X;
+		public FieldElement Y;
+		public FieldElement Z;
+		public FieldElement T;
+	} ;
 
-	public GroupElementPreComp(FieldElement yplusx, FieldElement yminusx, FieldElement xy2d)
+	internal struct GroupElementPreComp
+	{
+		public FieldElement yplusx;
+		public FieldElement yminusx;
+		public FieldElement xy2d;
+
+		public GroupElementPreComp(FieldElement yplusx, FieldElement yminusx, FieldElement xy2d)
+		{
+			this.yplusx = yplusx;
+			this.yminusx = yminusx;
+			this.xy2d = xy2d;
+		}
+	} ;
+
+	internal struct GroupElementCached
 	{
-		this.yplusx = yplusx;
-		this.yminusx = yminusx;
-		this.xy2d = xy2d;
-	}
-} ;
+		public FieldElement YplusX;
+		public FieldElement YminusX;
+		public FieldElement Z;
+		public FieldElement T2d;
+	} ;
+}
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/base2.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/base2.cs
new file mode 100644
index 000000000..03676e5cf
--- /dev/null
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/base2.cs
@@ -0,0 +1,50 @@
+﻿using System;
+
+namespace Chaos.NaCl.Internal.Ed25519Ref10
+{
+    internal static partial class LookupTables
+    {
+        internal static readonly GroupElementPreComp[] Base2 = new GroupElementPreComp[]{
+            new GroupElementPreComp(
+              new FieldElement( 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 ),
+            new FieldElement( -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 ),
+            new FieldElement( -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 )
+             ),
+            new GroupElementPreComp(
+            new FieldElement( 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 ),
+            new FieldElement( 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 ),
+            new FieldElement( 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 )
+             ),
+            new GroupElementPreComp(
+            new FieldElement( 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 ),
+            new FieldElement( 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 ),
+            new FieldElement( 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 )
+             ),
+            new GroupElementPreComp(
+            new FieldElement( 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 ),
+            new FieldElement( -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 ),
+            new FieldElement( 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 )
+             ),
+            new GroupElementPreComp(
+            new FieldElement( -22518993,-6692182,14201702,-8745502,-23510406,8844726,18474211,-1361450,-13062696,13821877 ),
+            new FieldElement( -6455177,-7839871,3374702,-4740862,-27098617,-10571707,31655028,-7212327,18853322,-14220951 ),
+            new FieldElement( 4566830,-12963868,-28974889,-12240689,-7602672,-2830569,-8514358,-10431137,2207753,-3209784 )
+             ),
+            new GroupElementPreComp(
+            new FieldElement( -25154831,-4185821,29681144,7868801,-6854661,-9423865,-12437364,-663000,-31111463,-16132436 ),
+            new FieldElement( 25576264,-2703214,7349804,-11814844,16472782,9300885,3844789,15725684,171356,6466918 ),
+            new FieldElement( 23103977,13316479,9739013,-16149481,817875,-15038942,8965339,-14088058,-30714912,16193877 )
+             ),
+            new GroupElementPreComp(
+            new FieldElement( -33521811,3180713,-2394130,14003687,-16903474,-16270840,17238398,4729455,-18074513,9256800 ),
+            new FieldElement( -25182317,-4174131,32336398,5036987,-21236817,11360617,22616405,9761698,-19827198,630305 ),
+            new FieldElement( -13720693,2639453,-24237460,-7406481,9494427,-5774029,-6554551,-15960994,-2449256,-14291300 )
+             ),
+            new GroupElementPreComp(
+            new FieldElement( -3151181,-5046075,9282714,6866145,-31907062,-863023,-18940575,15033784,25105118,-7894876 ),
+            new FieldElement( -24326370,15950226,-31801215,-14592823,-11662737,-5090925,1573892,-2625887,2198790,-15804619 ),
+            new FieldElement( -3099351,10324967,-2241613,7453183,-5446979,-2735503,-13812022,-16236442,-32461234,-12290683 )
+             )
+		};
+    }
+}
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs
new file mode 100644
index 000000000..037efcfd1
--- /dev/null
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/keypair.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Security.Cryptography;
+
+namespace Chaos.NaCl.Internal.Ed25519Ref10
+{
+    internal static partial class Ed25519Operations
+    {
+        public static void crypto_sign_keypair(byte[] pk, int pkoffset, byte[] sk, int skoffset, byte[] seed, int seedoffset)
+        {
+            GroupElementP3 A;
+            int i;
+
+            Array.Copy(seed, seedoffset, sk, skoffset, 32);
+#if NET5_0_OR_GREATER
+            byte[] h = SHA512.HashData(sk.AsSpan(skoffset, 32));
+#else
+            using var hash = SHA512.Create();
+            byte[] h = hash.ComputeHash(sk, skoffset, 32);
+#endif
+            ScalarOperations.sc_clamp(h, 0);
+
+            GroupOperations.ge_scalarmult_base(out A, h, 0);
+            GroupOperations.ge_p3_tobytes(pk, pkoffset, ref A);
+
+            for (i = 0; i < 32; ++i) sk[skoffset + 32 + i] = pk[pkoffset + i];
+#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
+            CryptographicOperations.ZeroMemory(h);
+#else
+            CryptoBytes.Wipe(h);
+#endif
+        }
+    }
+}
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sign.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sign.cs
new file mode 100644
index 000000000..a56625557
--- /dev/null
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sign.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Security.Cryptography;
+
+namespace Chaos.NaCl.Internal.Ed25519Ref10
+{
+	internal static partial class Ed25519Operations
+	{
+		public static void crypto_sign2(
+			byte[] sig, int sigoffset,
+			byte[] m, int moffset, int mlen,
+			byte[] sk, int skoffset)
+		{
+			byte[] az;
+			byte[] r;
+			byte[] hram;
+			GroupElementP3 R;
+			using (var hasher = SHA512.Create())
+			{
+				az = hasher.ComputeHash(sk, skoffset, 32);
+				ScalarOperations.sc_clamp(az, 0);
+
+				hasher.Initialize();
+				hasher.TransformBlock(az, 32, 32, null, 0);
+				hasher.TransformFinalBlock(m, moffset, mlen);
+				r = hasher.Hash;
+
+				ScalarOperations.sc_reduce(r);
+				GroupOperations.ge_scalarmult_base(out R, r, 0);
+				GroupOperations.ge_p3_tobytes(sig, sigoffset, ref R);
+
+				hasher.Initialize();
+				hasher.TransformBlock(sig, sigoffset, 32, null, 0);
+				hasher.TransformBlock(sk, skoffset + 32, 32, null, 0);
+				hasher.TransformFinalBlock(m, moffset, mlen);
+				hram = hasher.Hash;
+
+				ScalarOperations.sc_reduce(hram);
+				var s = new byte[32];//todo: remove allocation
+				Array.Copy(sig, sigoffset + 32, s, 0, 32);
+				ScalarOperations.sc_muladd(s, hram, az, r);
+				Array.Copy(s, 0, sig, sigoffset + 32, 32);
+#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
+				CryptographicOperations.ZeroMemory(s);
+#else
+				CryptoBytes.Wipe(s);
+#endif
+			}
+		}
+	}
+}
diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sqrtm1.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sqrtm1.cs
new file mode 100644
index 000000000..fb8b50122
--- /dev/null
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Internal/Ed25519Ref10/sqrtm1.cs
@@ -0,0 +1,9 @@
+﻿using System;
+
+namespace Chaos.NaCl.Internal.Ed25519Ref10
+{
+	internal static partial class LookupTables
+	{
+		internal static FieldElement sqrtm1 = new FieldElement(-32595792, -7943725, 9377950, 3500415, 12389472, -272473, -25146209, -2005654, 326686, 11406482);
+	}
+}
\ No newline at end of file
diff --git a/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml b/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml
index 738dbc799..e66567aca 100644
--- a/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml
+++ b/src/MySqlConnector.Authentication.Ed25519/CompatibilitySuppressions.xml
@@ -1,5 +1,5 @@
 ﻿<?xml version="1.0" encoding="utf-8"?>
-<!-- https://learn.microsoft.com/en-us/dotnet/fundamentals/package-validation/diagnostic-ids -->
+<!-- https://learn.microsoft.com/dotnet/fundamentals/package-validation/diagnostic-ids -->
 <Suppressions xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   <Suppression>
     <DiagnosticId>PKV006</DiagnosticId>
diff --git a/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj b/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj
index bd2122bb3..37349bfd2 100644
--- a/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj
+++ b/src/MySqlConnector.Authentication.Ed25519/MySqlConnector.Authentication.Ed25519.csproj
@@ -1,22 +1,30 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFrameworks>net462;netstandard2.0</TargetFrameworks>
+    <TargetFrameworks>net462;net472;netstandard2.0;netstandard2.1;net6.0</TargetFrameworks>
     <Title>MySqlConnector Ed25519 Authentication Plugin</Title>
-    <Description>Implements the client_ed25519 authentication plugin for MariaDB.</Description>
+    <Description>Implements the client_ed25519 and parsec authentication plugins for MariaDB.</Description>
     <Copyright>Copyright 2019–2024 Bradley Grainger</Copyright>
     <Authors>Bradley Grainger</Authors>
     <PackageReadmeFile>README.md</PackageReadmeFile>
-    <PackageTags>mariadb;mysqlconnector;authentication;ed25519</PackageTags>
-    <NoWarn>SA1001;SA1002;SA1005;SA1011;SA1012;SA1021;SA1025;SA1106;SA1107;SA1111;SA1119;SA1121;SA1300;SA1307;SA1312;SA1401;SA1413;SA1501;SA1505;SA1507;SA1508;SA1512;SA1518;SA1601</NoWarn>
+    <PackageTags>mariadb;mysqlconnector;authentication;ed25519;parsec</PackageTags>
+    <NoWarn>CA1305;CA1507;CA1802;CA2208;CS0649;IDE0049;SA1001;SA1002;SA1005;SA1011;SA1012;SA1021;SA1025;SA1028;SA1106;SA1107;SA1111;SA1119;SA1121;SA1124;SA1137;SA1214;SA1300;SA1307;SA1309;SA1312;SA1313;SA1401;SA1413;SA1501;SA1505;SA1507;SA1508;SA1509;SA1512;SA1515;SA1518;SA1520;SA1601</NoWarn>
   </PropertyGroup>
 
   <ItemGroup>
     <ProjectReference Include="..\MySqlConnector\MySqlConnector.csproj" />
   </ItemGroup>
 
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net462' or '$(TargetFramework)' == 'netstandard2.0' ">
+    <PackageReference Include="Microsoft.AspNetCore.Cryptography.KeyDerivation" />
+  </ItemGroup>
+
+  <ItemGroup Condition=" '$(TargetFramework)' == 'net6.0' or '$(TargetFramework)' == 'netstandard2.1' ">
+    <Compile Remove="Chaos.NaCl\CryptoBytes.cs" />
+  </ItemGroup>
+
   <ItemGroup>
-    <None Include="docs\README.md" Pack="true" PackagePath="\"/>
+    <None Include="docs\README.md" Pack="true" PackagePath="\" />
   </ItemGroup>
 
 </Project>
diff --git a/src/MySqlConnector.Authentication.Ed25519/ParsecAuthenticationPlugin.cs b/src/MySqlConnector.Authentication.Ed25519/ParsecAuthenticationPlugin.cs
new file mode 100644
index 000000000..91a579b3e
--- /dev/null
+++ b/src/MySqlConnector.Authentication.Ed25519/ParsecAuthenticationPlugin.cs
@@ -0,0 +1,90 @@
+using System;
+using System.Security.Cryptography;
+using System.Text;
+using System.Threading;
+
+namespace MySqlConnector.Authentication.Ed25519;
+
+/// <summary>
+/// Provides an implementation of the Parsec authentication plugin for MariaDB.
+/// </summary>
+public sealed class ParsecAuthenticationPlugin : IAuthenticationPlugin
+{
+	/// <summary>
+	/// Registers the Parsec authentication plugin with MySqlConnector. You must call this method once before
+	/// opening a connection that uses Parsec authentication.
+	/// </summary>
+	public static void Install()
+	{
+		if (Interlocked.CompareExchange(ref s_isInstalled, 1, 0) == 0)
+			AuthenticationPlugins.Register(new ParsecAuthenticationPlugin());
+	}
+
+	/// <summary>
+	/// Gets the authentication plugin name.
+	/// </summary>
+	public string Name => "parsec";
+
+	/// <summary>
+	/// Creates the authentication response.
+	/// </summary>
+	public byte[] CreateResponse(string password, ReadOnlySpan<byte> authenticationData)
+	{
+		// first 32 bytes are server scramble
+		var serverScramble = authenticationData.Slice(0, 32);
+
+		// generate client scramble
+#if NET6_0_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		Span<byte> clientScramble = stackalloc byte[32];
+		RandomNumberGenerator.Fill(clientScramble);
+#else
+		var clientScramble = new byte[32];
+		using var randomNumberGenerator = RandomNumberGenerator.Create();
+		randomNumberGenerator.GetBytes(clientScramble);
+#endif
+
+		// parse extended salt from remaining authentication data and verify format
+		var extendedSalt = authenticationData.Slice(32);
+		if (extendedSalt[0] != (byte) 'P')
+			throw new ArgumentException("Invalid extended salt", nameof(authenticationData));
+		if (extendedSalt[1] is not (>= 0 and <= 3))
+			throw new ArgumentException("Invalid iteration count", nameof(authenticationData));
+
+		var iterationCount = 1024 << extendedSalt[1];
+		var salt = extendedSalt.Slice(2);
+
+		// derive private key using PBKDF2-SHA512
+		byte[] privateKey;
+#if NET6_0_OR_GREATER
+		privateKey = Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(password), salt, iterationCount, HashAlgorithmName.SHA512, 32);
+#elif NET472_OR_GREATER || NETSTANDARD2_1_OR_GREATER
+		using (var pbkdf2 = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(password), salt.ToArray(), iterationCount, HashAlgorithmName.SHA512))
+			privateKey = pbkdf2.GetBytes(32);
+#else
+		privateKey = Microsoft.AspNetCore.Cryptography.KeyDerivation.KeyDerivation.Pbkdf2(
+			password, salt.ToArray(), Microsoft.AspNetCore.Cryptography.KeyDerivation.KeyDerivationPrf.HMACSHA512,
+			iterationCount, numBytesRequested: 32);
+#endif
+		var expandedPrivateKey = Chaos.NaCl.Ed25519.ExpandedPrivateKeyFromSeed(privateKey);
+
+		// generate Ed25519 keypair and sign concatenated scrambles
+		var message = new byte[serverScramble.Length + clientScramble.Length];
+		serverScramble.CopyTo(message);
+		clientScramble.CopyTo(message.AsSpan(serverScramble.Length));
+
+		var signature = Chaos.NaCl.Ed25519.Sign(message, expandedPrivateKey);
+
+		// return client scramble followed by signature
+		var response = new byte[clientScramble.Length + signature.Length];
+		clientScramble.CopyTo(response.AsSpan());
+		signature.CopyTo(response.AsSpan(clientScramble.Length));
+		
+		return response;
+	}
+
+	private ParsecAuthenticationPlugin()
+	{
+	}
+
+	private static int s_isInstalled;
+}
diff --git a/src/MySqlConnector.Authentication.Ed25519/docs/README.md b/src/MySqlConnector.Authentication.Ed25519/docs/README.md
index 0dd5a9af7..7f17e4c81 100644
--- a/src/MySqlConnector.Authentication.Ed25519/docs/README.md
+++ b/src/MySqlConnector.Authentication.Ed25519/docs/README.md
@@ -1,7 +1,13 @@
 ## About
 
-This package implements the `client_ed25519` [authentication plugin for MariaDB](https://mariadb.com/kb/en/authentication-plugin-ed25519/).
+This package implements the following authentication plugins for MariaDB:
+
+* [`client_ed25519`](https://mariadb.com/kb/en/authentication-plugin-ed25519/).
+* [PARSEC](https://mariadb.com/kb/en/authentication-plugin-parsec/)
 
 ## How to Use
 
-Call `Ed25519AuthenticationPlugin.Install()` from your application startup code to enable it.
+Call either the following methods from your application startup code to enable the corresponding authentication plugin:
+
+* `Ed25519AuthenticationPlugin.Install()`
+* `ParsecAuthenticationPlugin.Install()`
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 67e5a438e..6f740c38b 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -937,6 +937,21 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
+			case "parsec":
+				if (!AuthenticationPlugins.TryGetPlugin(switchRequest.Name, out var parsecPlugin))
+					throw new NotSupportedException("You must install the MySqlConnector.Authentication.Ed25519 package and call ParsecAuthenticationPlugin.Install to use parsec authentication.");
+				payload = new([]);
+				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
+				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+
+				Span<byte> combinedData = stackalloc byte[switchRequest.Data.Length + payload.Span.Length];
+				switchRequest.Data.CopyTo(combinedData);
+				payload.Span.CopyTo(combinedData.Slice(switchRequest.Data.Length));
+
+				payload = new(parsecPlugin.CreateResponse(password, combinedData));
+				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
+				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
+
 			default:
 				Log.AuthenticationMethodNotSupported(m_logger, Id, switchRequest.Name);
 				throw new NotSupportedException($"Authentication method '{switchRequest.Name}' is not supported.");
diff --git a/tests/IntegrationTests/ConnectAsync.cs b/tests/IntegrationTests/ConnectAsync.cs
index ce31fdf55..03e69afde 100644
--- a/tests/IntegrationTests/ConnectAsync.cs
+++ b/tests/IntegrationTests/ConnectAsync.cs
@@ -1,7 +1,4 @@
 using System.Security.Authentication;
-#if !MYSQL_DATA
-using MySqlConnector.Authentication.Ed25519;
-#endif
 
 namespace IntegrationTests;
 
@@ -429,7 +426,7 @@ public async Task CachingSha2WithoutSecureConnection()
 	[SkippableFact(ServerFeatures.Ed25519)]
 	public async Task Ed25519Authentication()
 	{
-		Ed25519AuthenticationPlugin.Install();
+		MySqlConnector.Authentication.Ed25519.Ed25519AuthenticationPlugin.Install();
 
 		var csb = AppConfig.CreateConnectionStringBuilder();
 		csb.UserID = "ed25519user";
@@ -442,7 +439,7 @@ public async Task Ed25519Authentication()
 	[SkippableFact(ServerFeatures.Ed25519)]
 	public async Task MultiAuthentication()
 	{
-		Ed25519AuthenticationPlugin.Install();
+		MySqlConnector.Authentication.Ed25519.Ed25519AuthenticationPlugin.Install();
 		var csb = AppConfig.CreateConnectionStringBuilder();
 		csb.UserID = "multiAuthUser";
 		csb.Password = "secret";
@@ -450,6 +447,18 @@ public async Task MultiAuthentication()
 		using var connection = new MySqlConnection(csb.ConnectionString);
 		await connection.OpenAsync();
 	}
+
+	[SkippableFact(ServerFeatures.ParsecAuthentication)]
+	public async Task Parsec()
+	{
+		MySqlConnector.Authentication.Ed25519.ParsecAuthenticationPlugin.Install();
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		csb.UserID = "parsec-user";
+		csb.Password = "P@rs3c-Pa55";
+		csb.Database = null;
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+	}
 #endif
 
 	// To create a MariaDB GSSAPI user for a current user
diff --git a/tests/IntegrationTests/ServerFeatures.cs b/tests/IntegrationTests/ServerFeatures.cs
index 120b541bf..f78c4829d 100644
--- a/tests/IntegrationTests/ServerFeatures.cs
+++ b/tests/IntegrationTests/ServerFeatures.cs
@@ -45,4 +45,9 @@ public enum ServerFeatures
 	/// Server provides hash of TLS certificate in first OK packet.
 	/// </summary>
 	TlsFingerprintValidation = 0x100_0000,
+
+	/// <summary>
+	/// Server supports the 'parsec' authentication plugin.
+	/// </summary>
+	ParsecAuthentication = 0x200_0000,
 }

From ec3619e31f2cb313d239051f425ddfd39c70c994 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Mon, 27 Jan 2025 09:47:57 -0800
Subject: [PATCH 385/393] Enable TLS validation with parsec.

Introduce new IAuthenticationPlugin3 interface and deprecate IAuthenticationPlugin2. Authentication plugins will now compute the password hash and the authentication response in one call, and the session will cache the password hash for later use.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Chaos.NaCl/Ed25519.cs                     | 13 ----
 .../Ed25519AuthenticationPlugin.cs            | 19 +++--
 .../ParsecAuthenticationPlugin.cs             | 38 ++++++----
 .../docs/README.md                            |  2 +-
 .../Authentication/IAuthenticationPlugin.cs   | 19 +++++
 src/MySqlConnector/Core/ServerSession.cs      | 69 ++++++++-----------
 .../Payloads/HandshakeResponse41Payload.cs    |  6 +-
 .../Serialization/AuthenticationUtility.cs    | 27 ++++----
 tests/IntegrationTests/SslTests.cs            | 14 ++++
 9 files changed, 121 insertions(+), 86 deletions(-)

diff --git a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Ed25519.cs b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Ed25519.cs
index 613f8330a..f3a0b7011 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Ed25519.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Ed25519.cs
@@ -34,19 +34,6 @@ public static byte[] Sign(byte[] message, byte[] expandedPrivateKey)
             return signature;
         }
 
-        public static byte[] ExpandedPrivateKeyFromSeed(byte[] privateKeySeed)
-        {
-            byte[] privateKey;
-            byte[] publicKey;
-            KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
-#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
-            CryptographicOperations.ZeroMemory(publicKey);
-#else
-            CryptoBytes.Wipe(publicKey);
-#endif
-            return privateKey;
-        }
-
         public static void KeyPairFromSeed(out byte[] publicKey, out byte[] expandedPrivateKey, byte[] privateKeySeed)
         {
             if (privateKeySeed == null)
diff --git a/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs b/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs
index f2ea62c3e..cd123acf2 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs
@@ -10,7 +10,9 @@ namespace MySqlConnector.Authentication.Ed25519;
 /// Provides an implementation of the <c>client_ed25519</c> authentication plugin for MariaDB.
 /// </summary>
 /// <remarks>See <a href="https://mariadb.com/kb/en/library/authentication-plugin-ed25519/">Authentication Plugin - ed25519</a>.</remarks>
-public sealed class Ed25519AuthenticationPlugin : IAuthenticationPlugin2
+#pragma warning disable CS0618 // Type or member is obsolete
+public sealed class Ed25519AuthenticationPlugin : IAuthenticationPlugin3, IAuthenticationPlugin2
+#pragma warning restore CS0618 // Type or member is obsolete
 {
 	/// <summary>
 	/// Registers the Ed25519 authentication plugin with MySqlConnector. You must call this method once before
@@ -32,7 +34,7 @@ public static void Install()
 	/// </summary>
 	public byte[] CreateResponse(string password, ReadOnlySpan<byte> authenticationData)
 	{
-		CreateResponseAndHash(password, authenticationData, out _, out var authenticationResponse);
+		CreateResponseAndPasswordHash(password, authenticationData, out var authenticationResponse, out _);
 		return authenticationResponse;
 	}
 
@@ -41,11 +43,20 @@ public byte[] CreateResponse(string password, ReadOnlySpan<byte> authenticationD
 	/// </summary>
 	public byte[] CreatePasswordHash(string password, ReadOnlySpan<byte> authenticationData)
 	{
-		CreateResponseAndHash(password, authenticationData, out var passwordHash, out _);
+		CreateResponseAndPasswordHash(password, authenticationData, out _, out var passwordHash);
 		return passwordHash;
 	}
 
-	private static void CreateResponseAndHash(string password, ReadOnlySpan<byte> authenticationData, out byte[] passwordHash, out byte[] authenticationResponse)
+	/// <summary>
+	/// Creates the authentication response and hashes the client's password (e.g., for TLS certificate fingerprint verification).
+	/// </summary>
+	/// <param name="password">The client's password.</param>
+	/// <param name="authenticationData">The authentication data supplied by the server; this is the <code>auth method data</code>
+	/// from the <a href="https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest">Authentication
+	/// Method Switch Request Packet</a>.</param>
+	/// <param name="authenticationResponse">The authentication response.</param>
+	/// <param name="passwordHash">The authentication-method-specific hash of the client's password.</param>
+	public void CreateResponseAndPasswordHash(string password, ReadOnlySpan<byte> authenticationData, out byte[] authenticationResponse, out byte[] passwordHash)
 	{
 		// Java reference: https://github.com/MariaDB/mariadb-connector-j/blob/master/src/main/java/org/mariadb/jdbc/internal/com/send/authentication/Ed25519PasswordPlugin.java
 		// C reference:  https://github.com/MariaDB/server/blob/592fe954ef82be1bc08b29a8e54f7729eb1e1343/plugin/auth_ed25519/ref10/sign.c#L7
diff --git a/src/MySqlConnector.Authentication.Ed25519/ParsecAuthenticationPlugin.cs b/src/MySqlConnector.Authentication.Ed25519/ParsecAuthenticationPlugin.cs
index 91a579b3e..d10011216 100644
--- a/src/MySqlConnector.Authentication.Ed25519/ParsecAuthenticationPlugin.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/ParsecAuthenticationPlugin.cs
@@ -8,7 +8,7 @@ namespace MySqlConnector.Authentication.Ed25519;
 /// <summary>
 /// Provides an implementation of the Parsec authentication plugin for MariaDB.
 /// </summary>
-public sealed class ParsecAuthenticationPlugin : IAuthenticationPlugin
+public sealed class ParsecAuthenticationPlugin : IAuthenticationPlugin3
 {
 	/// <summary>
 	/// Registers the Parsec authentication plugin with MySqlConnector. You must call this method once before
@@ -29,6 +29,15 @@ public static void Install()
 	/// Creates the authentication response.
 	/// </summary>
 	public byte[] CreateResponse(string password, ReadOnlySpan<byte> authenticationData)
+	{
+		CreateResponseAndPasswordHash(password, authenticationData, out var response, out _);
+		return response;
+	}
+
+	/// <summary>
+	/// Creates the authentication response.
+	/// </summary>
+	public void CreateResponseAndPasswordHash(string password, ReadOnlySpan<byte> authenticationData, out byte[] authenticationResponse, out byte[] passwordHash)
 	{
 		// first 32 bytes are server scramble
 		var serverScramble = authenticationData.Slice(0, 32);
@@ -54,32 +63,37 @@ public byte[] CreateResponse(string password, ReadOnlySpan<byte> authenticationD
 		var salt = extendedSalt.Slice(2);
 
 		// derive private key using PBKDF2-SHA512
-		byte[] privateKey;
+		byte[] privateKeySeed;
 #if NET6_0_OR_GREATER
-		privateKey = Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(password), salt, iterationCount, HashAlgorithmName.SHA512, 32);
+		privateKeySeed = Rfc2898DeriveBytes.Pbkdf2(Encoding.UTF8.GetBytes(password), salt, iterationCount, HashAlgorithmName.SHA512, 32);
 #elif NET472_OR_GREATER || NETSTANDARD2_1_OR_GREATER
 		using (var pbkdf2 = new Rfc2898DeriveBytes(Encoding.UTF8.GetBytes(password), salt.ToArray(), iterationCount, HashAlgorithmName.SHA512))
-			privateKey = pbkdf2.GetBytes(32);
+			privateKeySeed = pbkdf2.GetBytes(32);
 #else
-		privateKey = Microsoft.AspNetCore.Cryptography.KeyDerivation.KeyDerivation.Pbkdf2(
+		privateKeySeed = Microsoft.AspNetCore.Cryptography.KeyDerivation.KeyDerivation.Pbkdf2(
 			password, salt.ToArray(), Microsoft.AspNetCore.Cryptography.KeyDerivation.KeyDerivationPrf.HMACSHA512,
 			iterationCount, numBytesRequested: 32);
 #endif
-		var expandedPrivateKey = Chaos.NaCl.Ed25519.ExpandedPrivateKeyFromSeed(privateKey);
+		Chaos.NaCl.Ed25519.KeyPairFromSeed(out var publicKey, out var privateKey, privateKeySeed);
 
 		// generate Ed25519 keypair and sign concatenated scrambles
 		var message = new byte[serverScramble.Length + clientScramble.Length];
 		serverScramble.CopyTo(message);
 		clientScramble.CopyTo(message.AsSpan(serverScramble.Length));
 
-		var signature = Chaos.NaCl.Ed25519.Sign(message, expandedPrivateKey);
+		var signature = Chaos.NaCl.Ed25519.Sign(message, privateKey);
+
+#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
+		CryptographicOperations.ZeroMemory(privateKey);
+#endif
 
 		// return client scramble followed by signature
-		var response = new byte[clientScramble.Length + signature.Length];
-		clientScramble.CopyTo(response.AsSpan());
-		signature.CopyTo(response.AsSpan(clientScramble.Length));
-		
-		return response;
+		authenticationResponse = new byte[clientScramble.Length + signature.Length];
+		clientScramble.CopyTo(authenticationResponse.AsSpan());
+		signature.CopyTo(authenticationResponse.AsSpan(clientScramble.Length));
+
+		// "password hash" for parsec is the extended salt followed by the public key
+		passwordHash = [(byte) 'P', (byte) iterationCount, .. salt, .. publicKey];
 	}
 
 	private ParsecAuthenticationPlugin()
diff --git a/src/MySqlConnector.Authentication.Ed25519/docs/README.md b/src/MySqlConnector.Authentication.Ed25519/docs/README.md
index 7f17e4c81..b2d794296 100644
--- a/src/MySqlConnector.Authentication.Ed25519/docs/README.md
+++ b/src/MySqlConnector.Authentication.Ed25519/docs/README.md
@@ -7,7 +7,7 @@ This package implements the following authentication plugins for MariaDB:
 
 ## How to Use
 
-Call either the following methods from your application startup code to enable the corresponding authentication plugin:
+Call either of the following methods from your application startup code to enable the corresponding authentication plugin:
 
 * `Ed25519AuthenticationPlugin.Install()`
 * `ParsecAuthenticationPlugin.Install()`
diff --git a/src/MySqlConnector/Authentication/IAuthenticationPlugin.cs b/src/MySqlConnector/Authentication/IAuthenticationPlugin.cs
index 6dfface4e..bc432c0d1 100644
--- a/src/MySqlConnector/Authentication/IAuthenticationPlugin.cs
+++ b/src/MySqlConnector/Authentication/IAuthenticationPlugin.cs
@@ -24,6 +24,7 @@ public interface IAuthenticationPlugin
 /// <summary>
 /// <see cref="IAuthenticationPlugin2"/> is an extension to <see cref="IAuthenticationPlugin"/> that returns a hash of the client's password.
 /// </summary>
+[Obsolete("Use IAuthenticationPlugin3 instead.")]
 public interface IAuthenticationPlugin2 : IAuthenticationPlugin
 {
 	/// <summary>
@@ -36,3 +37,21 @@ public interface IAuthenticationPlugin2 : IAuthenticationPlugin
 	/// <returns>The authentication-method-specific hash of the client's password.</returns>
 	byte[] CreatePasswordHash(string password, ReadOnlySpan<byte> authenticationData);
 }
+
+/// <summary>
+/// <see cref="IAuthenticationPlugin3"/> is an extension to <see cref="IAuthenticationPlugin"/> that also returns a hash of the client's password.
+/// </summary>
+/// <remarks>If an authentication plugin supports this interface, the base <see cref="IAuthenticationPlugin.CreateResponse(string, ReadOnlySpan{byte})"/> method will not be called.</remarks>
+public interface IAuthenticationPlugin3 : IAuthenticationPlugin
+{
+	/// <summary>
+	/// Creates the authentication response and hashes the client's password (e.g., for TLS certificate fingerprint verification).
+	/// </summary>
+	/// <param name="password">The client's password.</param>
+	/// <param name="authenticationData">The authentication data supplied by the server; this is the <code>auth method data</code>
+	/// from the <a href="https://dev.mysql.com/doc/internals/en/connection-phase-packets.html#packet-Protocol::AuthSwitchRequest">Authentication
+	/// Method Switch Request Packet</a>.</param>
+	/// <param name="authenticationResponse">The authentication response.</param>
+	/// <param name="passwordHash">The authentication-method-specific hash of the client's password.</param>
+	void CreateResponseAndPasswordHash(string password, ReadOnlySpan<byte> authenticationData, out byte[] authenticationResponse, out byte[] passwordHash);
+}
diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 6f740c38b..23efbfb44 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -448,13 +448,13 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			var initialHandshake = InitialHandshakePayload.Create(payload.Span);
 
 			// if PluginAuth is supported, then use the specified auth plugin; else, fall back to protocol capabilities to determine the auth type to use
-			m_currentAuthenticationMethod = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0 ? initialHandshake.AuthPluginName! :
+			var currentAuthenticationMethod = (initialHandshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0 ? initialHandshake.AuthPluginName! :
 				(initialHandshake.ProtocolCapabilities & ProtocolCapabilities.SecureConnection) == 0 ? "mysql_old_password" :
 				"mysql_native_password";
-			Log.ServerSentAuthPluginName(m_logger, Id, m_currentAuthenticationMethod);
-			if (m_currentAuthenticationMethod is not "mysql_native_password" and not "sha256_password" and not "caching_sha2_password")
+			Log.ServerSentAuthPluginName(m_logger, Id, currentAuthenticationMethod);
+			if (currentAuthenticationMethod is not "mysql_native_password" and not "sha256_password" and not "caching_sha2_password")
 			{
-				Log.UnsupportedAuthenticationMethod(m_logger, Id, m_currentAuthenticationMethod);
+				Log.UnsupportedAuthenticationMethod(m_logger, Id, currentAuthenticationMethod);
 				throw new NotSupportedException($"Authentication method '{initialHandshake.AuthPluginName}' is not supported.");
 			}
 
@@ -528,11 +528,17 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 			if (m_supportsConnectionAttributes && cs.ConnectionAttributes is null)
 				cs.ConnectionAttributes = CreateConnectionAttributes(cs.ApplicationName);
 
+			var password = GetPassword(cs, connection);
+
 			// send a caching_sha2_password response if the server advertised support in the initial handshake
 			var useCachingSha2 = initialHandshake.AuthPluginName == "caching_sha2_password";
+			byte[] authenticationResponse;
+			if (useCachingSha2)
+				authenticationResponse = AuthenticationUtility.CreateScrambleResponse(Utility.TrimZeroByte(initialHandshake.AuthPluginData.AsSpan()), password);
+			else
+				AuthenticationUtility.CreateResponseAndPasswordHash(password, initialHandshake.AuthPluginData, out authenticationResponse, out m_passwordHash);
 
-			var password = GetPassword(cs, connection);
-			using (var handshakeResponsePayload = HandshakeResponse41Payload.Create(initialHandshake, cs, password, useCachingSha2, m_compressionMethod, connection.ZstandardPlugin?.CompressionLevel, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null))
+			using (var handshakeResponsePayload = HandshakeResponse41Payload.Create(initialHandshake, cs, authenticationResponse, m_compressionMethod, connection.ZstandardPlugin?.CompressionLevel, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null))
 				await SendReplyAsync(handshakeResponsePayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 			payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
@@ -553,7 +559,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 				}
 				else if (!m_isSecureConnection && password.Length != 0)
 				{
-					var publicKey = await GetRsaPublicKeyAsync(m_currentAuthenticationMethod, cs, ioBehavior, cancellationToken).ConfigureAwait(false);
+					var publicKey = await GetRsaPublicKeyAsync(currentAuthenticationMethod, cs, ioBehavior, cancellationToken).ConfigureAwait(false);
 					payload = await SendEncryptedPasswordAsync(AuthPluginData, publicKey, password, ioBehavior, cancellationToken).ConfigureAwait(false);
 				}
 				else
@@ -583,7 +589,7 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 					// there is no shared secret that can be used to validate the certificate
 					Log.CertificateErrorNoPassword(m_logger, Id, m_sslPolicyErrors);
 				}
-				else if (ValidateFingerprint(ok.StatusInfo, initialHandshake.AuthPluginData.AsSpan(0, 20), password))
+				else if (ValidateFingerprint(ok.StatusInfo, initialHandshake.AuthPluginData.AsSpan(0, 20)))
 				{
 					Log.CertificateErrorValidThumbprint(m_logger, Id, m_sslPolicyErrors);
 					ignoreCertificateError = true;
@@ -649,36 +655,20 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
 	/// </summary>
 	/// <param name="validationHash">The validation hash received from the server.</param>
 	/// <param name="challenge">The auth plugin data from the initial handshake.</param>
-	/// <param name="password">The user's password.</param>
 	/// <returns><c>true</c> if the validation hash matches the locally-computed value; otherwise, <c>false</c>.</returns>
-	private bool ValidateFingerprint(byte[]? validationHash, ReadOnlySpan<byte> challenge, string password)
+	private bool ValidateFingerprint(byte[]? validationHash, ReadOnlySpan<byte> challenge)
 	{
 		// expect 0x01 followed by 64 hex characters giving a SHA2 hash
 		if (validationHash?.Length != 65 || validationHash[0] != 1)
 			return false;
 
-		byte[]? passwordHashResult = null;
-		switch (m_currentAuthenticationMethod)
-		{
-			case "mysql_native_password":
-				passwordHashResult = AuthenticationUtility.HashPassword([], password, onlyHashPassword: true);
-				break;
-
-			case "client_ed25519":
-				AuthenticationPlugins.TryGetPlugin(m_currentAuthenticationMethod, out var ed25519Plugin);
-				if (ed25519Plugin is IAuthenticationPlugin2 plugin2)
-					passwordHashResult = plugin2.CreatePasswordHash(password, challenge);
-				break;
-		}
-		if (passwordHashResult is null)
+		// the authentication plugin must have provided a password hash (via IAuthenticationPlugin3) that we saved for future use
+		if (m_passwordHash is null)
 			return false;
 
-		Span<byte> combined = stackalloc byte[32 + challenge.Length + passwordHashResult.Length];
-		passwordHashResult.CopyTo(combined);
-		challenge.CopyTo(combined[passwordHashResult.Length..]);
-		m_remoteCertificateSha2Thumbprint!.CopyTo(combined[(passwordHashResult.Length + challenge.Length)..]);
-
+		// hash password hash || scramble || certificate thumbprint
 		Span<byte> hashBytes = stackalloc byte[32];
+		Span<byte> combined = [.. m_passwordHash, .. challenge, .. m_remoteCertificateSha2Thumbprint!];
 #if NET5_0_OR_GREATER
 		SHA256.TryHashData(combined, hashBytes, out _);
 #else
@@ -827,8 +817,8 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
 					DatabaseOverride = null;
 				}
 				var password = GetPassword(cs, connection);
-				var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData!, password);
-				using (var changeUserPayload = ChangeUserPayload.Create(cs.UserID, hashedPassword, cs.Database, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null))
+				AuthenticationUtility.CreateResponseAndPasswordHash(password, AuthPluginData, out var nativeResponse, out m_passwordHash);
+				using (var changeUserPayload = ChangeUserPayload.Create(cs.UserID, nativeResponse, cs.Database, m_characterSet, m_supportsConnectionAttributes ? cs.ConnectionAttributes : null))
 					await SendAsync(changeUserPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 				if (payload.HeaderByte == AuthenticationMethodSwitchRequestPayload.Signature)
@@ -872,13 +862,12 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 		// if the server didn't support the hashed password; rehash with the new challenge
 		var switchRequest = AuthenticationMethodSwitchRequestPayload.Create(payload.Span);
 		Log.SwitchingToAuthenticationMethod(m_logger, Id, switchRequest.Name);
-		m_currentAuthenticationMethod = switchRequest.Name;
 		switch (switchRequest.Name)
 		{
 			case "mysql_native_password":
 				AuthPluginData = switchRequest.Data;
-				var hashedPassword = AuthenticationUtility.CreateAuthenticationResponse(AuthPluginData, password);
-				payload = new(hashedPassword);
+				AuthenticationUtility.CreateResponseAndPasswordHash(password, AuthPluginData, out var nativeResponse, out m_passwordHash);
+				payload = new(nativeResponse);
 				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
@@ -931,14 +920,15 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 				throw new NotSupportedException("'MySQL Server is requesting the insecure pre-4.1 auth mechanism (mysql_old_password). The user password must be upgraded; see https://dev.mysql.com/doc/refman/5.7/en/account-upgrades.html.");
 
 			case "client_ed25519":
-				if (!AuthenticationPlugins.TryGetPlugin(switchRequest.Name, out var ed25519Plugin))
+				if (!AuthenticationPlugins.TryGetPlugin(switchRequest.Name, out var ed25519Plugin) || ed25519Plugin is not IAuthenticationPlugin3 ed25519Plugin3)
 					throw new NotSupportedException("You must install the MySqlConnector.Authentication.Ed25519 package and call Ed25519AuthenticationPlugin.Install to use client_ed25519 authentication.");
-				payload = new(ed25519Plugin.CreateResponse(password, switchRequest.Data));
+				ed25519Plugin3.CreateResponseAndPasswordHash(password, switchRequest.Data, out var ed25519Response, out m_passwordHash);
+				payload = new(ed25519Response);
 				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
 			case "parsec":
-				if (!AuthenticationPlugins.TryGetPlugin(switchRequest.Name, out var parsecPlugin))
+				if (!AuthenticationPlugins.TryGetPlugin(switchRequest.Name, out var parsecPlugin) || parsecPlugin is not IAuthenticationPlugin3 parsecPlugin3)
 					throw new NotSupportedException("You must install the MySqlConnector.Authentication.Ed25519 package and call ParsecAuthenticationPlugin.Install to use parsec authentication.");
 				payload = new([]);
 				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
@@ -948,7 +938,8 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
 				switchRequest.Data.CopyTo(combinedData);
 				payload.Span.CopyTo(combinedData.Slice(switchRequest.Data.Length));
 
-				payload = new(parsecPlugin.CreateResponse(password, combinedData));
+				parsecPlugin3.CreateResponseAndPasswordHash(password, combinedData, out var parsecResponse, out m_passwordHash);
+				payload = new(parsecResponse);
 				await SendReplyAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
 				return await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
 
@@ -2216,7 +2207,7 @@ protected override void OnStatementBegin(int index)
 	private PayloadData m_setNamesPayload;
 	private byte[]? m_pipelinedResetConnectionBytes;
 	private Dictionary<string, PreparedStatements>? m_preparedStatements;
-	private string? m_currentAuthenticationMethod;
+	private byte[]? m_passwordHash;
 	private byte[]? m_remoteCertificateSha2Thumbprint;
 	private SslPolicyErrors m_sslPolicyErrors;
 }
diff --git a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
index fdc5f2352..d817e0290 100644
--- a/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
+++ b/src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs
@@ -56,14 +56,12 @@ private static ByteBufferWriter CreateCapabilitiesPayload(ProtocolCapabilities s
 	public static PayloadData CreateWithSsl(ProtocolCapabilities serverCapabilities, ConnectionSettings cs, CompressionMethod compressionMethod, CharacterSet characterSet) =>
 		CreateCapabilitiesPayload(serverCapabilities, cs, compressionMethod, characterSet, ProtocolCapabilities.Ssl).ToPayloadData();
 
-	public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSettings cs, string password, bool useCachingSha2, CompressionMethod compressionMethod, int? compressionLevel, CharacterSet characterSet, byte[]? connectionAttributes)
+	public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSettings cs, byte[] authenticationResponse, CompressionMethod compressionMethod, int? compressionLevel, CharacterSet characterSet, byte[]? connectionAttributes)
 	{
 		// TODO: verify server capabilities
 		var writer = CreateCapabilitiesPayload(handshake.ProtocolCapabilities, cs, compressionMethod, characterSet);
 		writer.WriteNullTerminatedString(cs.UserID);
 
-		var authenticationResponse = useCachingSha2 ? AuthenticationUtility.CreateScrambleResponse(Utility.TrimZeroByte(handshake.AuthPluginData.AsSpan()), password) :
-			AuthenticationUtility.CreateAuthenticationResponse(handshake.AuthPluginData, password);
 		writer.Write((byte) authenticationResponse.Length);
 		writer.Write(authenticationResponse);
 
@@ -71,7 +69,7 @@ public static PayloadData Create(InitialHandshakePayload handshake, ConnectionSe
 			writer.WriteNullTerminatedString(cs.Database);
 
 		if ((handshake.ProtocolCapabilities & ProtocolCapabilities.PluginAuth) != 0)
-			writer.Write(useCachingSha2 ? "caching_sha2_password\0"u8 : "mysql_native_password\0"u8);
+			writer.Write(handshake.AuthPluginName == "caching_sha2_password" ? "caching_sha2_password\0"u8 : "mysql_native_password\0"u8);
 
 		if (connectionAttributes is not null)
 			writer.Write(connectionAttributes);
diff --git a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
index 659d2350d..d1f325682 100644
--- a/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
+++ b/src/MySqlConnector/Protocol/Serialization/AuthenticationUtility.cs
@@ -24,24 +24,26 @@ public static byte[] GetNullTerminatedPasswordBytes(string password)
 		return passwordBytes;
 	}
 
-	public static byte[] CreateAuthenticationResponse(ReadOnlySpan<byte> challenge, string password) =>
-		string.IsNullOrEmpty(password) ? [] : HashPassword(challenge, password, onlyHashPassword: false);
-
 	/// <summary>
 	/// Hashes a password with the "Secure Password Authentication" method.
 	/// </summary>
-	/// <param name="challenge">The 20-byte random challenge (from the "auth-plugin-data" in the initial handshake).</param>
 	/// <param name="password">The password to hash.</param>
-	/// <param name="onlyHashPassword">If true, <paramref name="challenge"/> is ignored and only the twice-hashed password
-	/// is returned, instead of performing the full "secure password authentication" algorithm that XORs the hashed password against
-	/// a hash derived from the challenge.</param>
-	/// <returns>A 20-byte password hash.</returns>
+	/// <param name="authenticationData">The 20-byte random challenge (from the "auth-plugin-data" in the initial handshake).</param>
+	/// <param name="authenticationResponse">The authentication response.</param>
+	/// <param name="passwordHash">The twice-hashed password.</param>
 	/// <remarks>See <a href="https://dev.mysql.com/doc/internals/en/secure-password-authentication.html">Secure Password Authentication</a>.</remarks>
 #if NET5_0_OR_GREATER
 	[SkipLocalsInit]
 #endif
-	public static byte[] HashPassword(ReadOnlySpan<byte> challenge, string password, bool onlyHashPassword)
+	public static void CreateResponseAndPasswordHash(string password, ReadOnlySpan<byte> authenticationData, out byte[] authenticationResponse, out byte[] passwordHash)
 	{
+		if (string.IsNullOrEmpty(password))
+		{
+			authenticationResponse = [];
+			passwordHash = [];
+			return;
+		}
+
 #if !NET5_0_OR_GREATER
 		using var sha1 = SHA1.Create();
 #endif
@@ -58,10 +60,9 @@ public static byte[] HashPassword(ReadOnlySpan<byte> challenge, string password,
 		sha1.TryComputeHash(passwordBytes, hashedPassword, out _);
 		sha1.TryComputeHash(hashedPassword, combined[20..], out _);
 #endif
-		if (onlyHashPassword)
-			return combined[20..].ToArray();
+		passwordHash = combined[20..].ToArray();
 
-		challenge[..20].CopyTo(combined);
+		authenticationData[..20].CopyTo(combined);
 		Span<byte> xorBytes = stackalloc byte[20];
 #if NET5_0_OR_GREATER
 		SHA1.TryHashData(combined, xorBytes, out _);
@@ -71,7 +72,7 @@ public static byte[] HashPassword(ReadOnlySpan<byte> challenge, string password,
 		for (var i = 0; i < hashedPassword.Length; i++)
 			hashedPassword[i] ^= xorBytes[i];
 
-		return hashedPassword.ToArray();
+		authenticationResponse = hashedPassword.ToArray();
 	}
 
 	public static byte[] CreateScrambleResponse(ReadOnlySpan<byte> nonce, string password) =>
diff --git a/tests/IntegrationTests/SslTests.cs b/tests/IntegrationTests/SslTests.cs
index b27742e02..be0dcf3da 100644
--- a/tests/IntegrationTests/SslTests.cs
+++ b/tests/IntegrationTests/SslTests.cs
@@ -249,6 +249,20 @@ public async Task ConnectZeroConfigurationSslEd25519()
 		using var connection = new MySqlConnection(csb.ConnectionString);
 		await connection.OpenAsync();
 	}
+
+	[SkippableFact(ServerFeatures.TlsFingerprintValidation | ServerFeatures.ParsecAuthentication)]
+	public async Task ConnectZeroConfigurationSslParsec()
+	{
+		MySqlConnector.Authentication.Ed25519.ParsecAuthenticationPlugin.Install();
+		var csb = AppConfig.CreateConnectionStringBuilder();
+		csb.CertificateFile = null;
+		csb.SslMode = MySqlSslMode.VerifyFull;
+		csb.SslCa = "";
+		csb.UserID = "parsec-user";
+		csb.Password = "P@rs3c-Pa55";
+		using var connection = new MySqlConnection(csb.ConnectionString);
+		await connection.OpenAsync();
+	}
 #endif
 
 	[SkippableFact(ConfigSettings.RequiresSsl)]

From 48238f8de2bb1cf6cfd9c7f540b2e9d3daefe1de Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sun, 18 May 2025 15:20:35 -0700
Subject: [PATCH 386/393] Use one-shot hash methods on modern .NET.

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../Ed25519AuthenticationPlugin.cs                   | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs b/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs
index cd123acf2..b7f718b17 100644
--- a/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs
+++ b/src/MySqlConnector.Authentication.Ed25519/Ed25519AuthenticationPlugin.cs
@@ -88,8 +88,12 @@ public void CreateResponseAndPasswordHash(string password, ReadOnlySpan<byte> au
 			az[31] |= 64;
 		*/
 
+#if NET5_0_OR_GREATER
+		byte[] az = SHA512.HashData(passwordBytes);
+#else
 		using var sha512 = SHA512.Create();
 		byte[] az = sha512.ComputeHash(passwordBytes);
+#endif
 		ScalarOperations.sc_clamp(az, 0);
 
 		/*** Java
@@ -115,7 +119,11 @@ public void CreateResponseAndPasswordHash(string password, ReadOnlySpan<byte> au
 		byte[] sm = new byte[64 + authenticationData.Length];
 		authenticationData.CopyTo(sm.AsSpan().Slice(64));
 		Buffer.BlockCopy(az, 32, sm, 32, 32);
+#if NET5_0_OR_GREATER
+		byte[] nonce = SHA512.HashData(sm.AsSpan(32, authenticationData.Length + 32));
+#else
 		byte[] nonce = sha512.ComputeHash(sm, 32, authenticationData.Length + 32);
+#endif
 
 		/*** Java
 			ScalarOps scalar = new ScalarOps();
@@ -173,7 +181,11 @@ public void CreateResponseAndPasswordHash(string password, ReadOnlySpan<byte> au
 
 			return 0;
 		*/
+#if NET5_0_OR_GREATER
+		var hram = SHA512.HashData(sm);
+#else
 		var hram = sha512.ComputeHash(sm);
+#endif
 		ScalarOperations.sc_reduce(hram);
 		var temp = new byte[32];
 		ScalarOperations.sc_muladd(temp, hram, az, nonce);

From 4cbcfc72063a74c02003a781174f9393660fe731 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Sat, 24 May 2025 13:56:57 -0700
Subject: [PATCH 387/393] Support VECTOR data type (#1551). Fixes #1549

---
 .ci/config/config.compression+ssl.json        |  2 +-
 .ci/config/config.compression.json            |  2 +-
 .ci/config/config.json                        |  2 +-
 .ci/config/config.ssl.json                    |  2 +-
 .ci/docker-run.sh                             |  2 +-
 azure-pipelines.yml                           | 24 +++---
 docs/content/home.md                          |  2 +-
 .../troubleshooting/parameter-types.md        |  1 +
 .../ColumnReaders/ColumnReader.cs             |  3 +
 .../ColumnReaders/VectorColumnReader.cs       | 37 +++++++++
 src/MySqlConnector/Core/Row.cs                |  2 +-
 .../Core/SingleCommandPayloadCreator.cs       |  4 +
 src/MySqlConnector/Core/TypeMapper.cs         |  8 ++
 src/MySqlConnector/MySqlDataReader.cs         |  2 +-
 src/MySqlConnector/MySqlDbColumn.cs           |  1 +
 src/MySqlConnector/MySqlDbType.cs             |  1 +
 src/MySqlConnector/MySqlParameter.cs          | 78 ++++++++++++++++++-
 src/MySqlConnector/Protocol/ColumnType.cs     |  1 +
 tests/IntegrationTests/CharacterSetTests.cs   |  2 +-
 tests/IntegrationTests/DataTypes.cs           | 55 +++++++++++++
 tests/IntegrationTests/DataTypesFixture.cs    | 22 ++++++
 tests/IntegrationTests/QueryTests.cs          | 64 +++++++++++++++
 tests/IntegrationTests/ServerFeatures.cs      | 10 +++
 .../IntegrationTests/StoredProcedureTests.cs  | 35 +++++++++
 24 files changed, 338 insertions(+), 24 deletions(-)
 create mode 100644 src/MySqlConnector/ColumnReaders/VectorColumnReader.cs

diff --git a/.ci/config/config.compression+ssl.json b/.ci/config/config.compression+ssl.json
index f7d4263c8..e0bf3504d 100644
--- a/.ci/config/config.compression+ssl.json
+++ b/.ci/config/config.compression+ssl.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin",
+      "UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin,Vector",
       "MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
       "CertificatesPath": "../../../../.ci/server/certs"
diff --git a/.ci/config/config.compression.json b/.ci/config/config.compression.json
index 448eb1b2b..fd312878b 100644
--- a/.ci/config/config.compression.json
+++ b/.ci/config/config.compression.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
+      "UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,Vector,ZeroDateTime",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
diff --git a/.ci/config/config.json b/.ci/config/config.json
index e32c72260..67eb66c79 100644
--- a/.ci/config/config.json
+++ b/.ci/config/config.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
+      "UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,Vector,ZeroDateTime",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
     }
diff --git a/.ci/config/config.ssl.json b/.ci/config/config.ssl.json
index a7511faa4..e136a243d 100644
--- a/.ci/config/config.ssl.json
+++ b/.ci/config/config.ssl.json
@@ -4,7 +4,7 @@
       "SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
       "PasswordlessUser": "no_password",
       "SecondaryDatabase": "testdb2",
-      "UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin",
+      "UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin,Vector",
       "MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
       "MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV",
       "CertificatesPath": "../../../../.ci/server/certs"
diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index 1247f47c2..6cb8a2efe 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -28,7 +28,7 @@ MYSQL=mysql
 if [[ "$IMAGE" == mariadb* ]]; then
   MYSQL_EXTRA='--in-predicate-conversion-threshold=100000 --plugin-maturity=beta'
 fi
-if [ "$IMAGE" == "mariadb:11.4" ] || [ "$IMAGE" == "mariadb:11.6" ]; then
+if [ "$IMAGE" == "mariadb:11.4" ] || [ "$IMAGE" == "mariadb:11.7" ]; then
   MYSQL='mariadb'
 fi
 
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index d7cba324e..9e914f07e 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -51,7 +51,7 @@ jobs:
       arguments: 'tests\IntegrationTests\IntegrationTests.csproj -c MySqlData'
       testRunTitle: 'MySql.Data integration tests'
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,StreamingResults,TlsFingerprintValidation,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,StreamingResults,TlsFingerprintValidation,UnixDomainSocket,Vector'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=root;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600'
       DATA__CERTIFICATESPATH: '$(Build.Repository.LocalPath)\.ci\server\certs\'
       DATA__MYSQLBULKLOADERLOCALCSVFILE: '$(Build.Repository.LocalPath)\tests\TestData\LoadData_UTF8_BOM_Unix.CSV'
@@ -120,7 +120,7 @@ jobs:
       arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net481/net9.0', 'No SSL') }}
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,Vector'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True'
 
 - job: windows_integration_tests_2
@@ -158,7 +158,7 @@ jobs:
       arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
       testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net8.0', 'No SSL') }}
     env:
-      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
+      DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,Vector'
       DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
 
 - job: linux_integration_tests
@@ -171,31 +171,31 @@ jobs:
       'MySQL 8.0':
         image: 'mysql:8.0'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,Vector,ZeroDateTime'
       'MySQL 8.4':
         image: 'mysql:8.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,Vector,ZeroDateTime'
       'MySQL 9.3':
         image: 'mysql:9.3'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
-        unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
+        unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,Vector,ZeroDateTime'
       'MariaDB 10.6':
         image: 'mariadb:10.6'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin,Vector'
       'MariaDB 10.11':
         image: 'mariadb:10.11'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin,Vector'
       'MariaDB 11.4':
         image: 'mariadb:11.4'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Sha256Password,Tls11,UuidToBin,Redirection'
-      'MariaDB 11.6':
-        image: 'mariadb:11.6'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,UuidToBin,Vector'
+      'MariaDB 11.7':
+        image: 'mariadb:11.7'
         connectionStringExtra: ''
-        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,UuidToBin'
+        unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,UuidToBin,VectorType'
   steps:
   - template: '.ci/integration-tests-steps.yml'
     parameters:
diff --git a/docs/content/home.md b/docs/content/home.md
index 10e2fa408..555afcf3a 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -64,7 +64,7 @@ Server  | Versions | Notes
 Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://mysqlconnector.net/troubleshooting/aurora-freeze/)
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
-MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**, **11.6**) |
+MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**, **11.7**) |
 MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.3**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
diff --git a/docs/content/troubleshooting/parameter-types.md b/docs/content/troubleshooting/parameter-types.md
index 8085b2d9c..82cd238d0 100644
--- a/docs/content/troubleshooting/parameter-types.md
+++ b/docs/content/troubleshooting/parameter-types.md
@@ -39,5 +39,6 @@ In some cases, this may be as simple as calling `.ToString()` or `.ToString(Cult
 * .NET primitives: `bool`, `byte`, `char`, `double`, `float`, `int`, `long`, `sbyte`, `short`, `uint`, `ulong`, `ushort`
 * Common types: `BigInteger`, `DateOnly`, `DateTime`, `DateTimeOffset`, `decimal`, `enum`, `Guid`, `string`, `TimeOnly`, `TimeSpan`
 * BLOB types: `ArraySegment<byte>`, `byte[]`, `Memory<byte>`, `ReadOnlyMemory<byte>`
+* Vector types: `float[]`, `Memory<float>`, `ReadOnlyMemory<float>`
 * String types: `Memory<char>`, `ReadOnlyMemory<char>`, `StringBuilder`
 * Custom MySQL types: `MySqlDateTime`, `MySqlDecimal`, `MySqlGeometry`
diff --git a/src/MySqlConnector/ColumnReaders/ColumnReader.cs b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
index 86ba9145f..24baba810 100644
--- a/src/MySqlConnector/ColumnReaders/ColumnReader.cs
+++ b/src/MySqlConnector/ColumnReaders/ColumnReader.cs
@@ -113,6 +113,9 @@ public static ColumnReader Create(bool isBinary, ColumnDefinitionPayload columnD
 			case ColumnType.Null:
 				return NullColumnReader.Instance;
 
+			case ColumnType.Vector:
+				return VectorColumnReader.Instance;
+
 			default:
 				throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
 		}
diff --git a/src/MySqlConnector/ColumnReaders/VectorColumnReader.cs b/src/MySqlConnector/ColumnReaders/VectorColumnReader.cs
new file mode 100644
index 000000000..23f574e72
--- /dev/null
+++ b/src/MySqlConnector/ColumnReaders/VectorColumnReader.cs
@@ -0,0 +1,37 @@
+using System.Buffers.Binary;
+using System.Runtime.InteropServices;
+using MySqlConnector.Protocol.Payloads;
+
+namespace MySqlConnector.ColumnReaders;
+
+internal sealed class VectorColumnReader : ColumnReader
+{
+	public static VectorColumnReader Instance { get; } = new();
+
+	public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
+	{
+		if (BitConverter.IsLittleEndian)
+		{
+			return new ReadOnlyMemory<float>(MemoryMarshal.Cast<byte, float>(data).ToArray());
+		}
+		else
+		{
+			var floats = new float[data.Length / 4];
+
+#if !NET5_0_OR_GREATER
+			var bytes = data.ToArray();
+#endif
+			for (var i = 0; i < floats.Length; i++)
+			{
+#if NET5_0_OR_GREATER
+				floats[i] = BinaryPrimitives.ReadSingleLittleEndian(data.Slice(i * 4));
+#else
+				Array.Reverse(bytes, i * 4, 4);
+				floats[i] = BitConverter.ToSingle(bytes, i * 4);
+#endif
+			}
+
+			return new ReadOnlyMemory<float>(floats);
+		}
+	}
+}
diff --git a/src/MySqlConnector/Core/Row.cs b/src/MySqlConnector/Core/Row.cs
index bdbc81658..a9f424c9d 100644
--- a/src/MySqlConnector/Core/Row.cs
+++ b/src/MySqlConnector/Core/Row.cs
@@ -455,7 +455,7 @@ private void CheckBinaryColumn(int ordinal)
 		if ((column.ColumnFlags & ColumnFlags.Binary) == 0 ||
 			(columnType != ColumnType.String && columnType != ColumnType.VarString && columnType != ColumnType.TinyBlob &&
 			columnType != ColumnType.Blob && columnType != ColumnType.MediumBlob && columnType != ColumnType.LongBlob &&
-			columnType != ColumnType.Geometry))
+			columnType != ColumnType.Geometry && columnType != ColumnType.Vector))
 		{
 			throw new InvalidCastException($"Can't convert {columnType} to bytes.");
 		}
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index 49b81a7b0..4cc7b12db 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -165,6 +165,10 @@ private static void WriteBinaryParameters(ByteBufferWriter writer, MySqlParamete
 				mySqlDbType = TypeMapper.Instance.GetMySqlDbTypeForDbType(dbType);
 			}
 
+			// HACK: MariaDB doesn't have a dedicated Vector type so mark it as binary data
+			if (mySqlDbType == MySqlDbType.Vector && command.Connection!.Session.ServerVersion.IsMariaDb)
+				mySqlDbType = MySqlDbType.LongBlob;
+
 			writer.Write(TypeMapper.ConvertToColumnTypeAndFlags(mySqlDbType, command.Connection!.GuidFormat));
 
 			if (supportsQueryAttributes)
diff --git a/src/MySqlConnector/Core/TypeMapper.cs b/src/MySqlConnector/Core/TypeMapper.cs
index cfb6e97f1..9d361d930 100644
--- a/src/MySqlConnector/Core/TypeMapper.cs
+++ b/src/MySqlConnector/Core/TypeMapper.cs
@@ -55,6 +55,10 @@ private TypeMapper()
 		AddColumnTypeMetadata(new("DOUBLE", typeDouble, MySqlDbType.Double));
 		AddColumnTypeMetadata(new("FLOAT", typeFloat, MySqlDbType.Float));
 
+		 // vector
+		var typeFloatReadOnlyMemory = AddDbTypeMapping(new(typeof(ReadOnlyMemory<float>), [DbType.Object]));
+		AddColumnTypeMetadata(new("VECTOR", typeFloatReadOnlyMemory, MySqlDbType.Vector, binary: true, simpleDataTypeName: "VECTOR", createFormat: "VECTOR({0})"));
+
 		// string
 		var typeFixedString = AddDbTypeMapping(new(typeof(string), [DbType.StringFixedLength, DbType.AnsiStringFixedLength], convert: Convert.ToString!));
 		var typeString = AddDbTypeMapping(new(typeof(string), [DbType.String, DbType.AnsiString, DbType.Xml], convert: Convert.ToString!));
@@ -311,6 +315,9 @@ public static MySqlDbType ConvertToMySqlDbType(ColumnDefinitionPayload columnDef
 			case ColumnType.Set:
 				return MySqlDbType.Set;
 
+			case ColumnType.Vector:
+				return MySqlDbType.Vector;
+
 			default:
 				throw new NotImplementedException($"ConvertToMySqlDbType for {columnDefinition.ColumnType} is not implemented");
 		}
@@ -347,6 +354,7 @@ public static ushort ConvertToColumnTypeAndFlags(MySqlDbType dbType, MySqlGuidFo
 			MySqlDbType.NewDecimal => ColumnType.NewDecimal,
 			MySqlDbType.Geometry => ColumnType.Geometry,
 			MySqlDbType.Null => ColumnType.Null,
+			MySqlDbType.Vector => ColumnType.Vector,
 			_ => throw new NotImplementedException($"ConvertToColumnTypeAndFlags for {dbType} is not implemented"),
 		};
 		return (ushort) ((byte) columnType | (isUnsigned ? 0x8000 : 0));
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 800fafe47..98e07fdd0 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -671,7 +671,7 @@ private static async Task ReadOutParametersAsync(IMySqlCommand command, ResultSe
 			if (param.HasSetDbType && !row.IsDBNull(columnIndex))
 			{
 				var dbTypeMapping = TypeMapper.Instance.GetDbTypeMapping(param.DbType);
-				if (dbTypeMapping is not null)
+				if (dbTypeMapping is not null && param.DbType is not DbType.Object)
 				{
 					param.Value = dbTypeMapping.DoConversion(row.GetValue(columnIndex));
 					continue;
diff --git a/src/MySqlConnector/MySqlDbColumn.cs b/src/MySqlConnector/MySqlDbColumn.cs
index b55e9a02a..9696951ed 100644
--- a/src/MySqlConnector/MySqlDbColumn.cs
+++ b/src/MySqlConnector/MySqlDbColumn.cs
@@ -15,6 +15,7 @@ internal MySqlDbColumn(int ordinal, ColumnDefinitionPayload column, bool allowZe
 		var type = columnTypeMetadata.DbTypeMapping.ClrType;
 		var columnSize = type == typeof(string) || type == typeof(Guid) ?
 			column.ColumnLength / ProtocolUtility.GetBytesPerCharacter(column.CharacterSet) :
+			column.ColumnType == ColumnType.Vector ? column.ColumnLength / 4 :
 			column.ColumnLength;
 
 		AllowDBNull = (column.ColumnFlags & ColumnFlags.NotNull) == 0;
diff --git a/src/MySqlConnector/MySqlDbType.cs b/src/MySqlConnector/MySqlDbType.cs
index 77f990383..f1d0708a0 100644
--- a/src/MySqlConnector/MySqlDbType.cs
+++ b/src/MySqlConnector/MySqlDbType.cs
@@ -37,6 +37,7 @@ public enum MySqlDbType
 	VarChar,
 	String,
 	Geometry,
+	Vector = 242,
 	UByte = 501,
 	UInt16,
 	UInt32,
diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 2c97527f7..3913c48ce 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -1,9 +1,11 @@
+using System.Buffers.Binary;
 using System.Buffers.Text;
 using System.Data.Common;
 using System.Diagnostics;
 using System.Diagnostics.CodeAnalysis;
 using System.Globalization;
 using System.Numerics;
+using System.Runtime.InteropServices;
 using System.Text;
 #if NET8_0_OR_GREATER
 using System.Text.Unicode;
@@ -284,7 +286,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 		{
 			writer.WriteString(ulongValue);
 		}
-		else if (Value is byte[] or ReadOnlyMemory<byte> or Memory<byte> or ArraySegment<byte> or MySqlGeometry or MemoryStream)
+		else if (Value is byte[] or ReadOnlyMemory<byte> or Memory<byte> or ArraySegment<byte> or MySqlGeometry or MemoryStream or float[] or ReadOnlyMemory<float> or Memory<float>)
 		{
 			var inputSpan = Value switch
 			{
@@ -293,6 +295,9 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 				Memory<byte> memory => memory.Span,
 				MySqlGeometry geometry => geometry.ValueSpan,
 				MemoryStream memoryStream => memoryStream.TryGetBuffer(out var streamBuffer) ? streamBuffer.AsSpan() : memoryStream.ToArray().AsSpan(),
+				float[] floatArray => ConvertFloatsToBytes(floatArray.AsSpan()),
+				Memory<float> memory => ConvertFloatsToBytes(memory.Span),
+				ReadOnlyMemory<float> memory => ConvertFloatsToBytes(memory.Span),
 				_ => ((ReadOnlyMemory<byte>) Value).Span,
 			};
 
@@ -725,11 +730,52 @@ private void AppendBinary(ByteBufferWriter writer, object value, StatementPrepar
 		}
 		else if (value is float floatValue)
 		{
-			writer.Write(BitConverter.GetBytes(floatValue));
+#if NET5_0_OR_GREATER
+			Span<byte> bytes = stackalloc byte[4];
+			BinaryPrimitives.WriteSingleLittleEndian(bytes, floatValue);
+			writer.Write(bytes);
+#else
+			// convert float to bytes with correct endianness (MySQL uses little-endian)
+			var bytes = BitConverter.GetBytes(floatValue);
+			if (!BitConverter.IsLittleEndian)
+				Array.Reverse(bytes);
+			writer.Write(bytes);
+#endif
 		}
 		else if (value is double doubleValue)
 		{
-			writer.Write(unchecked((ulong) BitConverter.DoubleToInt64Bits(doubleValue)));
+#if NET5_0_OR_GREATER
+			Span<byte> bytes = stackalloc byte[8];
+			BinaryPrimitives.WriteDoubleLittleEndian(bytes, doubleValue);
+			writer.Write(bytes);
+#else
+			if (BitConverter.IsLittleEndian)
+			{
+				writer.Write(unchecked((ulong) BitConverter.DoubleToInt64Bits(doubleValue)));
+			}
+			else
+			{
+				// convert double to bytes with correct endianness (MySQL uses little-endian)
+				var bytes = BitConverter.GetBytes(doubleValue);
+				Array.Reverse(bytes);
+				writer.Write(bytes);
+			}
+#endif
+		}
+		else if (value is float[] floatArrayValue)
+		{
+			writer.WriteLengthEncodedInteger(unchecked((ulong) floatArrayValue.Length * 4));
+			writer.Write(ConvertFloatsToBytes(floatArrayValue.AsSpan()));
+		}
+		else if (value is Memory<float> floatMemory)
+		{
+			writer.WriteLengthEncodedInteger(unchecked((ulong) floatMemory.Length * 4));
+			writer.Write(ConvertFloatsToBytes(floatMemory.Span));
+		}
+		else if (value is ReadOnlyMemory<float> floatReadOnlyMemory)
+		{
+			writer.WriteLengthEncodedInteger(unchecked((ulong) floatReadOnlyMemory.Length * 4));
+			writer.Write(ConvertFloatsToBytes(floatReadOnlyMemory.Span));
 		}
 		else if (value is decimal decimalValue)
 		{
@@ -948,6 +994,32 @@ private static void WriteTime(ByteBufferWriter writer, TimeSpan timeSpan)
 		}
 	}
 
+	private static ReadOnlySpan<byte> ConvertFloatsToBytes(ReadOnlySpan<float> floats)
+	{
+		if (BitConverter.IsLittleEndian)
+		{
+			return MemoryMarshal.AsBytes(floats);
+		}
+		else
+		{
+			// for big-endian platforms, we need to convert each float individually
+			var bytes = new byte[floats.Length * 4];
+
+			for (var i = 0; i < floats.Length; i++)
+			{
+#if NET5_0_OR_GREATER
+				BinaryPrimitives.WriteSingleLittleEndian(bytes.AsSpan(i * 4), floats[i]);
+#else
+				var floatBytes = BitConverter.GetBytes(floats[i]);
+				Array.Reverse(floatBytes);
+				floatBytes.CopyTo(bytes, i * 4);
+#endif
+			}
+
+			return bytes;
+		}
+	}
+
 	private static ReadOnlySpan<byte> BinaryBytes => "_binary'"u8;
 
 	private DbType m_dbType;
diff --git a/src/MySqlConnector/Protocol/ColumnType.cs b/src/MySqlConnector/Protocol/ColumnType.cs
index 4b3701106..b031a41a5 100644
--- a/src/MySqlConnector/Protocol/ColumnType.cs
+++ b/src/MySqlConnector/Protocol/ColumnType.cs
@@ -24,6 +24,7 @@ internal enum ColumnType
 	Bit = 16,
 	Timestamp2 = 17,
 	DateTime2 = 18,
+	Vector = 242,
 	Json = 0xF5,
 	NewDecimal = 0xF6,
 	Enum = 0xF7,
diff --git a/tests/IntegrationTests/CharacterSetTests.cs b/tests/IntegrationTests/CharacterSetTests.cs
index 2dcb685a8..317d49ddc 100644
--- a/tests/IntegrationTests/CharacterSetTests.cs
+++ b/tests/IntegrationTests/CharacterSetTests.cs
@@ -79,7 +79,7 @@ public void CollationConnection(bool reopenConnection)
 
 		var collation = connection.Query<string>(@"select @@collation_connection;").Single();
 		var expected = connection.ServerVersion.Substring(0, 2) is "8." or "9." ? "utf8mb4_0900_ai_ci" :
-			connection.ServerVersion.StartsWith("11.4.", StringComparison.Ordinal) || connection.ServerVersion.StartsWith("11.6.", StringComparison.Ordinal) ? "utf8mb4_uca1400_ai_ci" :
+			connection.ServerVersion.StartsWith("11.4.", StringComparison.Ordinal) || connection.ServerVersion.StartsWith("11.7.", StringComparison.Ordinal) ? "utf8mb4_uca1400_ai_ci" :
 			"utf8mb4_general_ci";
 		Assert.Equal(expected, collation);
 	}
diff --git a/tests/IntegrationTests/DataTypes.cs b/tests/IntegrationTests/DataTypes.cs
index 6d5b1e170..c105cf7ff 100644
--- a/tests/IntegrationTests/DataTypes.cs
+++ b/tests/IntegrationTests/DataTypes.cs
@@ -1,4 +1,6 @@
 using System.Globalization;
+using System.Runtime.InteropServices;
+
 #if MYSQL_DATA
 using MySql.Data.Types;
 #endif
@@ -1145,6 +1147,11 @@ private static object CreateGeometry(byte[] data)
 	[InlineData("Int64", "datatypes_integers", MySqlDbType.Int64, 20, typeof(long), "N", 0, 0)]
 	[InlineData("UInt64", "datatypes_integers", MySqlDbType.UInt64, 20, typeof(ulong), "N", 0, 0)]
 	[InlineData("value", "datatypes_json_core", MySqlDbType.JSON, int.MaxValue, typeof(string), "LN", 0, 0)]
+#if MYSQL_DATA
+	[InlineData("value", "datatypes_vector", MySqlDbType.Vector, 12, typeof(byte[]), "N", 0, 31)]
+#else
+	[InlineData("value", "datatypes_vector", MySqlDbType.Vector, 3, typeof(ReadOnlyMemory<float>), "N", 0, 31)]
+#endif
 	[InlineData("Single", "datatypes_reals", MySqlDbType.Float, 12, typeof(float), "N", 0, 31)]
 	[InlineData("Double", "datatypes_reals", MySqlDbType.Double, 22, typeof(double), "N", 0, 31)]
 	[InlineData("SmallDecimal", "datatypes_reals", MySqlDbType.NewDecimal, 7, typeof(decimal), "N", 5, 2)]
@@ -1197,6 +1204,17 @@ private void DoGetSchemaTable(string column, string table, MySqlDbType mySqlDbTy
 	{
 		if (table == "datatypes_json_core" && !AppConfig.SupportsJson)
 			return;
+		if (table == "datatypes_vector" && !AppConfig.SupportedFeatures.HasFlag(ServerFeatures.Vector))
+			return;
+
+		// adjust for databases that don't have a dedicated on-the-wire type for VECTOR(n)
+		if (mySqlDbType == MySqlDbType.Vector && !AppConfig.SupportedFeatures.HasFlag(ServerFeatures.VectorType))
+		{
+			mySqlDbType = MySqlDbType.VarBinary;
+			columnSize *= 4;
+			dataType = typeof(byte[]);
+			scale = 0;
+		}
 
 		var isAutoIncrement = flags.IndexOf('A') != -1;
 		var isKey = flags.IndexOf('K') != -1;
@@ -1605,6 +1623,43 @@ public void QueryJson(string column, string[] expected)
 		DoQuery("json_core", column, dataTypeName, expected, reader => reader.GetString(0), omitWhereTest: true);
 	}
 
+	[SkippableTheory(ServerFeatures.Vector)]
+	[InlineData("value", new[] { null, "0,0,0", "1,1,1", "1,2,3", "-1,-1,-1" })]
+	public void QueryVector(string column, string[] expected)
+	{
+		var hasVectorType = AppConfig.SupportedFeatures.HasFlag(ServerFeatures.VectorType);
+		string dataTypeName = hasVectorType ? "VECTOR" : "BLOB";
+		DoQuery("vector", column, dataTypeName,
+			expected.Select(x =>
+#if !MYSQL_DATA
+				hasVectorType ? (GetFloatArray(x) is float[] a ? (object) new ReadOnlyMemory<float>(a) : null) : GetByteArray(x))
+#else
+				// Connector/NET returns the float array as a byte[]
+				GetByteArray(x))
+#endif
+			.ToArray(),
+#if !MYSQL_DATA
+			x => hasVectorType ? (ReadOnlyMemory<float>) x.GetValue(0) : (byte[]) x.GetValue(0),
+#else
+			// NOTE: Connector/NET returns 'null' for NULL so simulate an exception for the tests
+			x => x.IsDBNull(0) ? throw new GetValueWhenNullException() : x.GetValue(0),
+#endif
+			assertEqual: (l, r) =>
+			{
+				if (l is ReadOnlyMemory<float> roml)
+					l = roml.ToArray();
+				if (r is ReadOnlyMemory<float> romr)
+					r = romr.ToArray();
+				Assert.Equal(l, r);
+			},
+			omitWhereTest: true);
+
+		static float[] GetFloatArray(string value) => value?.Split(',').Select(x => float.Parse(x, CultureInfo.InvariantCulture)).ToArray();
+
+		static byte[] GetByteArray(string value) =>
+			GetFloatArray(value) is { } floats ? MemoryMarshal.AsBytes<float>(floats).ToArray() : null;
+	}
+
 	[SkippableTheory(MySqlData = "https://bugs.mysql.com/bug.php?id=97067")]
 	[InlineData(false, "MIN", 0)]
 	[InlineData(false, "MAX", uint.MaxValue)]
diff --git a/tests/IntegrationTests/DataTypesFixture.cs b/tests/IntegrationTests/DataTypesFixture.cs
index 471e519a8..13ac73aaa 100644
--- a/tests/IntegrationTests/DataTypesFixture.cs
+++ b/tests/IntegrationTests/DataTypesFixture.cs
@@ -244,6 +244,28 @@ insert into datatypes_json_core (value)
   ('{""a"": ""b""}');
 ");
 		}
+
+		if (AppConfig.SupportedFeatures.HasFlag(ServerFeatures.Vector))
+		{
+			// create a helper function for MariaDB 11.7+
+			if (Connection.ServerVersion.StartsWith("11.7.", StringComparison.Ordinal))
+				Connection.Execute("create function if not exists STRING_TO_VECTOR(s text) returns vector(3) deterministic return Vec_FromText(s);");
+
+			Connection.Execute("""
+				drop table if exists datatypes_vector;
+				create table datatypes_vector (
+					rowid integer not null primary key auto_increment,
+					value vector(3) null
+				);
+				insert into datatypes_vector (value)
+				values
+					(null),
+					(STRING_TO_VECTOR('[0, 0, 0]')),
+					(STRING_TO_VECTOR('[1, 1, 1]')),
+					(STRING_TO_VECTOR('[1, 2, 3]')),
+					(STRING_TO_VECTOR('[-1, -1, -1]'));
+				""");
+		}
 		Connection.Close();
 	}
 }
diff --git a/tests/IntegrationTests/QueryTests.cs b/tests/IntegrationTests/QueryTests.cs
index a6077f5fe..eae6175da 100644
--- a/tests/IntegrationTests/QueryTests.cs
+++ b/tests/IntegrationTests/QueryTests.cs
@@ -1,3 +1,5 @@
+using System.Runtime.InteropServices;
+
 namespace IntegrationTests;
 
 public class QueryTests : IClassFixture<DatabaseFixture>, IDisposable
@@ -1688,6 +1690,68 @@ public void GetBytesByName()
 	}
 #endif
 
+	[SkippableTheory(ServerFeatures.Vector)]
+	[InlineData(false, 0)]
+	[InlineData(false, 1)]
+	[InlineData(false, 2)]
+	[InlineData(true, 0)]
+	[InlineData(true, 1)]
+	[InlineData(true, 2)]
+	public void QueryVector(bool prepare, int dataFormat)
+	{
+		using var connection = new MySqlConnection(AppConfig.ConnectionString);
+		connection.Open();
+
+		connection.Execute("""
+			drop table if exists test_vector;
+			create table test_vector(id int auto_increment not null primary key, vec vector(3) not null);
+			""");
+
+		using var cmd = m_database.Connection.CreateCommand();
+		cmd.CommandText = "INSERT INTO test_vector(vec) VALUES(@vec)";
+		cmd.Parameters.Add(new MySqlParameter
+		{
+			ParameterName = "@vec",
+			MySqlDbType = MySqlDbType.Vector,
+		});
+
+		var floatArray = new[] { 1.2f, 3.4f, 5.6f };
+#if MYSQL_DATA
+		// Connector/NET requires the float vector to be passed as a byte array
+		cmd.Parameters[0].Value = MemoryMarshal.AsBytes<float>(floatArray).ToArray();
+		Assert.InRange(dataFormat, 0, 2);
+#else
+		cmd.Parameters[0].Value = dataFormat switch
+		{
+			0 => floatArray,
+			1 => new Memory<float>(floatArray),
+			2 => new ReadOnlyMemory<float>(floatArray),
+			_ => throw new NotSupportedException(),
+		};
+#endif
+
+		if (prepare)
+			cmd.Prepare();
+		cmd.ExecuteNonQuery();
+
+		// Select and verify the value
+		cmd.CommandText = "SELECT vec FROM test_vector";
+		if (prepare)
+			cmd.Prepare();
+
+		using var reader = cmd.ExecuteReader();
+		Assert.True(reader.Read());
+		var value = reader.GetValue(0);
+
+#if MYSQL_DATA
+		var result = MemoryMarshal.Cast<byte, float>((byte[]) value).ToArray();
+#else
+		var result = AppConfig.SupportedFeatures.HasFlag(ServerFeatures.VectorType) ? (ReadOnlyMemory<float>) value :
+			MemoryMarshal.Cast<byte, float>((byte[]) value).ToArray();
+#endif
+		Assert.Equal(floatArray, result);
+	}
+
 	private class BoolTest
 	{
 		public int Id { get; set; }
diff --git a/tests/IntegrationTests/ServerFeatures.cs b/tests/IntegrationTests/ServerFeatures.cs
index f78c4829d..2ab997c65 100644
--- a/tests/IntegrationTests/ServerFeatures.cs
+++ b/tests/IntegrationTests/ServerFeatures.cs
@@ -50,4 +50,14 @@ public enum ServerFeatures
 	/// Server supports the 'parsec' authentication plugin.
 	/// </summary>
 	ParsecAuthentication = 0x200_0000,
+
+	/// <summary>
+	/// Server supports the <c>VECTOR</c> SQL type.
+	/// </summary>
+	Vector = 0x400_0000,
+
+	/// <summary>
+	/// Server has a dedicated type on the wire for <c>VECTOR</c>.
+	/// </summary>
+	VectorType = 0x800_0000,
 }
diff --git a/tests/IntegrationTests/StoredProcedureTests.cs b/tests/IntegrationTests/StoredProcedureTests.cs
index 0a1ae418d..bbbdec53d 100644
--- a/tests/IntegrationTests/StoredProcedureTests.cs
+++ b/tests/IntegrationTests/StoredProcedureTests.cs
@@ -1,3 +1,5 @@
+using System.Runtime.InteropServices;
+
 namespace IntegrationTests;
 
 public class StoredProcedureTests : IClassFixture<StoredProcedureFixture>
@@ -761,6 +763,39 @@ public void PassJsonParameter()
 		Assert.False(reader.Read());
 	}
 
+	[SkippableTheory(ServerFeatures.Vector | ServerFeatures.VectorType)]
+	[InlineData(false)]
+	[InlineData(true)]
+	public void VectorOutputParameter(bool prepare)
+	{
+		using var cmd = m_database.Connection.CreateCommand();
+		cmd.CommandText = """
+			DROP PROCEDURE IF EXISTS sp_vector_out;
+			CREATE PROCEDURE sp_vector_out (OUT vec VECTOR)
+			BEGIN
+				SELECT STRING_TO_VECTOR('[1.2, 3.4, 5.6]') INTO vec;
+			END;
+			""";
+		cmd.ExecuteNonQuery();
+
+		cmd.CommandText = "sp_vector_out";
+		cmd.CommandType = CommandType.StoredProcedure;
+		cmd.Parameters.Add(new MySqlParameter
+		{
+			Direction = ParameterDirection.Output,
+			MySqlDbType = MySqlDbType.Vector,
+			ParameterName = "@vec",
+		});
+
+		if (prepare)
+			cmd.Prepare();
+		cmd.ExecuteNonQuery();
+
+		var value = cmd.Parameters[0].Value;
+		var result = Assert.IsType<byte[]>(value);
+		Assert.Equal(new float[] { 1.2f, 3.4f, 5.6f }, MemoryMarshal.Cast<byte, float>(result).ToArray());
+	}
+
 	private static Action<MySqlParameter> AssertParameter(string name, ParameterDirection direction, MySqlDbType mySqlDbType)
 	{
 		return x =>

From c1bec1c77253ddf1369cb5ce0fb24ada1fbe6e0b Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Thu, 3 Jul 2025 15:22:02 -0700
Subject: [PATCH 388/393] Bump
 advanced-security/component-detection-dependency-submission-action. (#1577)

Bumps [advanced-security/component-detection-dependency-submission-action](https://github.com/advanced-security/component-detection-dependency-submission-action) from 0.0.6 to 0.0.7.
- [Release notes](https://github.com/advanced-security/component-detection-dependency-submission-action/releases)
- [Commits](https://github.com/advanced-security/component-detection-dependency-submission-action/compare/v0.0.6...v0.0.7)

---
updated-dependencies:
- dependency-name: advanced-security/component-detection-dependency-submission-action
  dependency-version: 0.0.7
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 727a20971..f3b1370e9 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,7 +25,7 @@ jobs:
       run: dotnet build --configuration Release --no-restore
 
     - name: Component detection
-      uses: advanced-security/component-detection-dependency-submission-action@v0.0.6
+      uses: advanced-security/component-detection-dependency-submission-action@v0.0.7
 
   native-aot:
     name: Native AOT

From 7f853e3113d3813797eed6aedfd7164bdc08b106 Mon Sep 17 00:00:00 2001
From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com>
Date: Tue, 15 Jul 2025 19:40:58 -0700
Subject: [PATCH 389/393] Bump
 advanced-security/component-detection-dependency-submission-action. (#1578)

Bumps [advanced-security/component-detection-dependency-submission-action](https://github.com/advanced-security/component-detection-dependency-submission-action) from 0.0.7 to 0.1.0.
- [Release notes](https://github.com/advanced-security/component-detection-dependency-submission-action/releases)
- [Commits](https://github.com/advanced-security/component-detection-dependency-submission-action/compare/v0.0.7...v0.1.0)

---
updated-dependencies:
- dependency-name: advanced-security/component-detection-dependency-submission-action
  dependency-version: 0.1.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
---
 .github/workflows/ci.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index f3b1370e9..b69c1e7a7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -25,7 +25,7 @@ jobs:
       run: dotnet build --configuration Release --no-restore
 
     - name: Component detection
-      uses: advanced-security/component-detection-dependency-submission-action@v0.0.7
+      uses: advanced-security/component-detection-dependency-submission-action@v0.1.0
 
   native-aot:
     name: Native AOT

From fd8fb0f6b9af370651d8c150b2397a26c0179ab7 Mon Sep 17 00:00:00 2001
From: djeman <djemouli@gmail.com>
Date: Wed, 23 Jul 2025 00:56:37 +0200
Subject: [PATCH 390/393] Implement COM_STMT_SEND_LONG_DATA. Fixes #943 (#1579)

Signed-off-by: Bradley Grainger <bgrainger@gmail.com>
Co-authored-by: Didier Fracassi <d.fracassi@acd-groupe.fr>
Co-authored-by: Bradley Grainger <bgrainger@gmail.com>
---
 .../troubleshooting/parameter-types.md        |   3 +-
 src/MySqlConnector/Core/CommandExecutor.cs    |   2 +
 .../Core/ConcatenatedCommandPayloadCreator.cs |   3 +
 .../Core/ICommandPayloadCreator.cs            |  10 ++
 .../Core/SingleCommandPayloadCreator.cs       |  79 +++++++++++
 src/MySqlConnector/MySqlDataReader.cs         |   4 +-
 src/MySqlConnector/MySqlParameter.cs          |   8 ++
 src/MySqlConnector/Protocol/CommandKind.cs    |   1 +
 tests/IntegrationTests/ChunkStream.cs         | 130 ++++++++++++++++++
 tests/IntegrationTests/InsertTests.cs         | 124 +++++++++++++++++
 10 files changed, 362 insertions(+), 2 deletions(-)
 create mode 100644 tests/IntegrationTests/ChunkStream.cs

diff --git a/docs/content/troubleshooting/parameter-types.md b/docs/content/troubleshooting/parameter-types.md
index 82cd238d0..ae5123b0b 100644
--- a/docs/content/troubleshooting/parameter-types.md
+++ b/docs/content/troubleshooting/parameter-types.md
@@ -38,7 +38,8 @@ In some cases, this may be as simple as calling `.ToString()` or `.ToString(Cult
 
 * .NET primitives: `bool`, `byte`, `char`, `double`, `float`, `int`, `long`, `sbyte`, `short`, `uint`, `ulong`, `ushort`
 * Common types: `BigInteger`, `DateOnly`, `DateTime`, `DateTimeOffset`, `decimal`, `enum`, `Guid`, `string`, `TimeOnly`, `TimeSpan`
-* BLOB types: `ArraySegment<byte>`, `byte[]`, `Memory<byte>`, `ReadOnlyMemory<byte>`
+* BLOB types: `ArraySegment<byte>`, `byte[]`, `Memory<byte>`, `ReadOnlyMemory<byte>`. `MemoryStream`
+  * NOTE: `System.IO.Stream` and derived types (other than `MemoryStream`) are supported only when `MySqlCommand.Prepare` is called first. The data in the `Stream` will be streamed to the database server as binary data.
 * Vector types: `float[]`, `Memory<float>`, `ReadOnlyMemory<float>`
 * String types: `Memory<char>`, `ReadOnlyMemory<char>`, `StringBuilder`
 * Custom MySQL types: `MySqlDateTime`, `MySqlDecimal`, `MySqlGeometry`
diff --git a/src/MySqlConnector/Core/CommandExecutor.cs b/src/MySqlConnector/Core/CommandExecutor.cs
index a2581334a..732fec43c 100644
--- a/src/MySqlConnector/Core/CommandExecutor.cs
+++ b/src/MySqlConnector/Core/CommandExecutor.cs
@@ -39,6 +39,8 @@ public static async ValueTask<MySqlDataReader> ExecuteReaderAsync(CommandListPos
 				}
 			}
 
+			await payloadCreator.SendCommandPrologueAsync(connection, commandListPosition, ioBehavior, cancellationToken).ConfigureAwait(false);
+
 			var writer = new ByteBufferWriter();
 			//// cachedProcedures will be non-null if there is a stored procedure, which is also the only time it will be read
 			if (!payloadCreator.WriteQueryCommand(ref commandListPosition, cachedProcedures!, writer, false))
diff --git a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
index 20e0717f6..c87f59080 100644
--- a/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/ConcatenatedCommandPayloadCreator.cs
@@ -8,6 +8,9 @@ internal sealed class ConcatenatedCommandPayloadCreator : ICommandPayloadCreator
 {
 	public static ICommandPayloadCreator Instance { get; } = new ConcatenatedCommandPayloadCreator();
 
+	public ValueTask SendCommandPrologueAsync(MySqlConnection connection, CommandListPosition commandListPosition, IOBehavior ioBehavior, CancellationToken cancellationToken) =>
+		default;
+
 	public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDictionary<string, CachedProcedure?> cachedProcedures, ByteBufferWriter writer, bool appendSemicolon)
 	{
 		if (commandListPosition.CommandIndex == commandListPosition.CommandCount)
diff --git a/src/MySqlConnector/Core/ICommandPayloadCreator.cs b/src/MySqlConnector/Core/ICommandPayloadCreator.cs
index c6e32d04a..14d6fb9c2 100644
--- a/src/MySqlConnector/Core/ICommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/ICommandPayloadCreator.cs
@@ -7,6 +7,16 @@ namespace MySqlConnector.Core;
 /// </summary>
 internal interface ICommandPayloadCreator
 {
+	/// <summary>
+	/// Sends any data that is required to be sent to the server before the current command in the command list.
+	/// </summary>
+	/// <param name="connection">The <see cref="MySqlConnection"/> to which the data will be written.</param>
+	/// <param name="commandListPosition">The <see cref="CommandListPosition"/> giving the current command and current prepared statement.</param>
+	/// <param name="ioBehavior">The IO behavior.</param>
+	/// <param name="cancellationToken">A cancellation token to cancel the asynchronous operation.</param>
+	/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation or a completed <see cref="ValueTask"/> if no data needed to be sent.</returns>
+	ValueTask SendCommandPrologueAsync(MySqlConnection connection, CommandListPosition commandListPosition, IOBehavior ioBehavior, CancellationToken cancellationToken);
+
 	/// <summary>
 	/// Writes the payload for an "execute query" command to <paramref name="writer"/>.
 	/// </summary>
diff --git a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
index 4cc7b12db..122b4ccc8 100644
--- a/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
+++ b/src/MySqlConnector/Core/SingleCommandPayloadCreator.cs
@@ -1,3 +1,6 @@
+using System.Buffers;
+using System.Buffers.Binary;
+using System.Runtime.InteropServices;
 using MySqlConnector.Logging;
 using MySqlConnector.Protocol;
 using MySqlConnector.Protocol.Serialization;
@@ -12,6 +15,81 @@ internal sealed class SingleCommandPayloadCreator : ICommandPayloadCreator
 	// with this as the first column name, the result set will be treated as 'out' parameters for the previous command.
 	public static string OutParameterSentinelColumnName => "\uE001\b\x0B";
 
+	public async ValueTask SendCommandPrologueAsync(MySqlConnection connection, CommandListPosition commandListPosition, IOBehavior ioBehavior, CancellationToken cancellationToken)
+	{
+		// get the current command and check for prepared statements
+		var command = commandListPosition.CommandAt(commandListPosition.CommandIndex);
+		var preparedStatements = commandListPosition.PreparedStatements ?? command.TryGetPreparedStatements();
+		if (preparedStatements is not null)
+		{
+			// get the current prepared statement; WriteQueryCommand will advance this
+			var preparedStatement = preparedStatements.Statements[commandListPosition.PreparedStatementIndex];
+			if (preparedStatement.Parameters is { } parameters)
+			{
+				byte[]? buffer = null;
+				try
+				{
+					// check each parameter
+					for (var i = 0; i < parameters.Length; i++)
+					{
+						// look up this parameter in the command's parameter collection and check if it is a Stream
+						// NOTE: full parameter checks will be performed (and throw any necessary exceptions) in WritePreparedStatement
+						var parameterName = preparedStatement.Statement.NormalizedParameterNames![i];
+						var parameterIndex = parameterName is not null ? (command.RawParameters?.UnsafeIndexOf(parameterName) ?? -1) : preparedStatement.Statement.ParameterIndexes[i];
+						if (command.RawParameters is { } rawParameters && parameterIndex >= 0 && parameterIndex < rawParameters.Count && rawParameters[parameterIndex] is { Value: Stream stream and not MemoryStream })
+						{
+							// seven-byte COM_STMT_SEND_LONG_DATA header: https://dev.mysql.com/doc/dev/mysql-server/latest/page_protocol_com_stmt_send_long_data.html
+							const int packetHeaderLength = 7;
+
+							// send almost-full packets, but don't send exactly ProtocolUtility.MaxPacketSize bytes in one payload (as that's ambiguous to whether another packet follows).
+							const int maxDataSize = 16_000_000;
+							int totalBytesRead;
+							while (true)
+							{
+								buffer ??= ArrayPool<byte>.Shared.Rent(packetHeaderLength + maxDataSize);
+								buffer[0] = (byte) CommandKind.StatementSendLongData;
+								BinaryPrimitives.WriteInt32LittleEndian(buffer.AsSpan(1), preparedStatement.StatementId);
+								BinaryPrimitives.WriteUInt16LittleEndian(buffer.AsSpan(5), (ushort) i);
+
+								// keep reading from the stream until we've filled the buffer to send
+#if NET7_0_OR_GREATER
+								if (ioBehavior == IOBehavior.Synchronous)
+									totalBytesRead = stream.ReadAtLeast(buffer.AsSpan(packetHeaderLength, maxDataSize), maxDataSize, throwOnEndOfStream: false);
+								else
+									totalBytesRead = await stream.ReadAtLeastAsync(buffer.AsMemory(packetHeaderLength, maxDataSize), maxDataSize, throwOnEndOfStream: false, cancellationToken).ConfigureAwait(false);
+#else
+								totalBytesRead = 0;
+								int bytesRead;
+								do
+								{
+									var sizeToRead = maxDataSize - totalBytesRead;
+									if (ioBehavior == IOBehavior.Synchronous)
+										bytesRead = stream.Read(buffer, packetHeaderLength + totalBytesRead, sizeToRead);
+									else
+										bytesRead = await stream.ReadAsync(buffer, packetHeaderLength + totalBytesRead, sizeToRead, cancellationToken).ConfigureAwait(false);
+									totalBytesRead += bytesRead;
+								} while (bytesRead > 0 && totalBytesRead < maxDataSize);
+#endif
+
+								if (totalBytesRead == 0)
+									break;
+
+								// send StatementSendLongData; MySQL Server will keep appending the sent data to the parameter value
+								using var payload = new PayloadData(buffer.AsMemory(0, packetHeaderLength + totalBytesRead), isPooled: false);
+								await connection.Session.SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
+							}
+						}
+					}
+				}
+				finally
+				{
+					if (buffer is not null)
+						ArrayPool<byte>.Shared.Return(buffer);
+				}
+			}
+		}
+	}
+
 	public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDictionary<string, CachedProcedure?> cachedProcedures, ByteBufferWriter writer, bool appendSemicolon)
 	{
 		if (commandListPosition.CommandIndex == commandListPosition.CommandCount)
@@ -58,6 +136,7 @@ public bool WriteQueryCommand(ref CommandListPosition commandListPosition, IDict
 			{
 				commandListPosition.CommandIndex++;
 				commandListPosition.PreparedStatementIndex = 0;
+				commandListPosition.PreparedStatements = null;
 			}
 		}
 		return true;
diff --git a/src/MySqlConnector/MySqlDataReader.cs b/src/MySqlConnector/MySqlDataReader.cs
index 98e07fdd0..fc34ca365 100644
--- a/src/MySqlConnector/MySqlDataReader.cs
+++ b/src/MySqlConnector/MySqlDataReader.cs
@@ -74,8 +74,10 @@ internal async Task<bool> NextResultAsync(IOBehavior ioBehavior, CancellationTok
 						Command = m_commandListPosition.CommandAt(m_commandListPosition.CommandIndex);
 						using (Command.CancellableCommand.RegisterCancel(cancellationToken))
 						{
+							await m_payloadCreator!.SendCommandPrologueAsync(Command.Connection!, m_commandListPosition, ioBehavior, cancellationToken).ConfigureAwait(false);
+
 							var writer = new ByteBufferWriter();
-							if (!Command.Connection!.Session.IsCancelingQuery && m_payloadCreator!.WriteQueryCommand(ref m_commandListPosition, m_cachedProcedures!, writer, false))
+							if (!Command.Connection!.Session.IsCancelingQuery && m_payloadCreator.WriteQueryCommand(ref m_commandListPosition, m_cachedProcedures!, writer, false))
 							{
 								using var payload = writer.ToPayloadData();
 								await Command.Connection.Session.SendAsync(payload, ioBehavior, cancellationToken).ConfigureAwait(false);
diff --git a/src/MySqlConnector/MySqlParameter.cs b/src/MySqlConnector/MySqlParameter.cs
index 3913c48ce..b217eb814 100644
--- a/src/MySqlConnector/MySqlParameter.cs
+++ b/src/MySqlConnector/MySqlParameter.cs
@@ -331,6 +331,10 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
 			Debug.Assert(index == length, "index == length");
 			writer.Advance(index);
 		}
+		else if (Value is Stream)
+		{
+			throw new NotSupportedException($"Parameter type {Value.GetType().Name} can only be used after calling MySqlCommand.Prepare.");
+		}
 		else if (Value is bool boolValue)
 		{
 			writer.Write(boolValue ? "true"u8 : "false"u8);
@@ -728,6 +732,10 @@ private void AppendBinary(ByteBufferWriter writer, object value, StatementPrepar
 			writer.WriteLengthEncodedInteger(unchecked((ulong) streamBuffer.Count));
 			writer.Write(streamBuffer);
 		}
+		else if (value is Stream)
+		{
+			// do nothing; this will be sent via CommandKind.StatementSendLongData
+		}
 		else if (value is float floatValue)
 		{
 #if NET5_0_OR_GREATER
diff --git a/src/MySqlConnector/Protocol/CommandKind.cs b/src/MySqlConnector/Protocol/CommandKind.cs
index 6e384a733..0d546721c 100644
--- a/src/MySqlConnector/Protocol/CommandKind.cs
+++ b/src/MySqlConnector/Protocol/CommandKind.cs
@@ -9,5 +9,6 @@ internal enum CommandKind
 	ChangeUser = 17,
 	StatementPrepare = 22,
 	StatementExecute = 23,
+	StatementSendLongData = 24,
 	ResetConnection = 31,
 }
diff --git a/tests/IntegrationTests/ChunkStream.cs b/tests/IntegrationTests/ChunkStream.cs
new file mode 100644
index 000000000..9c44d3ede
--- /dev/null
+++ b/tests/IntegrationTests/ChunkStream.cs
@@ -0,0 +1,130 @@
+namespace IntegrationTests;
+
+internal sealed class ChunkStream : Stream
+{
+	public ChunkStream(byte[] data, int chunkLength)
+	{
+		if (data is null)
+			throw new ArgumentNullException(nameof(data));
+		if (chunkLength <= 0)
+			throw new ArgumentOutOfRangeException(nameof(chunkLength));
+
+		m_data = data;
+		m_chunkLength = chunkLength;
+		m_position = 0;
+	}
+
+	public override bool CanRead => true;
+	public override bool CanSeek => false;
+	public override bool CanWrite => false;
+	public override long Length => m_data.Length;
+	public override long Position
+	{
+		get => m_position;
+		set => throw new NotSupportedException();
+	}
+
+	public override int Read(byte[] buffer, int offset, int count)
+	{
+		if (buffer is null)
+			throw new ArgumentNullException(nameof(buffer));
+		if (offset < 0 || offset > buffer.Length)
+			throw new ArgumentOutOfRangeException(nameof(offset));
+		if (count < 0 || offset + count > buffer.Length)
+			throw new ArgumentOutOfRangeException(nameof(count));
+
+		return Read(buffer.AsSpan(offset, count));
+	}
+
+	public
+#if NETSTANDARD2_1 || NETCOREAPP2_1_OR_GREATER
+		override
+#endif
+		int Read(Span<byte> buffer)
+	{
+		if (m_position >= m_data.Length)
+			return 0;
+
+		// Read at most chunkLength bytes
+		var bytesToRead = Math.Min(buffer.Length, Math.Min(m_chunkLength, m_data.Length - m_position));
+
+		// Copy data from the actual data array
+		m_data.AsSpan(m_position, bytesToRead).CopyTo(buffer);
+
+		m_position += bytesToRead;
+		return bytesToRead;
+	}
+
+	public override int ReadByte()
+	{
+		Span<byte> buffer = stackalloc byte[1];
+		var bytesRead = Read(buffer);
+		return bytesRead == 0 ? -1 : buffer[0];
+	}
+
+	public override Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
+	{
+		if (buffer is null)
+			throw new ArgumentNullException(nameof(buffer));
+		if (offset < 0 || offset > buffer.Length)
+			throw new ArgumentOutOfRangeException(nameof(offset));
+		if (count < 0 || offset + count > buffer.Length)
+			throw new ArgumentOutOfRangeException(nameof(count));
+
+		if (cancellationToken.IsCancellationRequested)
+			return Task.FromCanceled<int>(cancellationToken);
+
+		try
+		{
+			return Task.FromResult(Read(buffer.AsSpan(offset, count)));
+		}
+		catch (Exception ex)
+		{
+			return Task.FromException<int>(ex);
+		}
+	}
+
+	public
+#if NETSTANDARD2_1 || NETCOREAPP2_1_OR_GREATER
+		override
+#endif
+		ValueTask<int> ReadAsync(Memory<byte> buffer, CancellationToken cancellationToken = default)
+	{
+		if (cancellationToken.IsCancellationRequested)
+			return new(Task.FromCanceled<int>(cancellationToken));
+
+		try
+		{
+			return new(Read(buffer.Span));
+		}
+		catch (Exception ex)
+		{
+			return new(Task.FromException<int>(ex));
+		}
+	}
+
+	public override void Write(byte[] buffer, int offset, int count) =>
+		throw new NotSupportedException();
+
+	public override void WriteByte(byte value) =>
+		throw new NotSupportedException();
+
+	public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) =>
+		throw new NotSupportedException();
+
+	public override void SetLength(long value) =>
+		throw new NotSupportedException();
+
+	public override long Seek(long offset, SeekOrigin origin) =>
+		throw new NotSupportedException();
+
+	public override void Flush() =>
+		throw new NotSupportedException();
+
+	public override Task FlushAsync(CancellationToken cancellationToken) =>
+		throw new NotSupportedException();
+
+	private readonly byte[] m_data;
+	private readonly int m_chunkLength;
+	private int m_position;
+}
diff --git a/tests/IntegrationTests/InsertTests.cs b/tests/IntegrationTests/InsertTests.cs
index 0557a19e6..63191eacd 100644
--- a/tests/IntegrationTests/InsertTests.cs
+++ b/tests/IntegrationTests/InsertTests.cs
@@ -475,6 +475,130 @@ public void InsertMySqlDecimalAsDecimal(bool prepare)
 		var val = ((decimal) reader.GetValue(0)).ToString(CultureInfo.InvariantCulture);
 		Assert.Equal(value, val);
 	}
+
+	[Theory]
+	[InlineData(1_000_000, 1024, true)]
+	[InlineData(1_000_000, 1024, false)]
+	[InlineData(1_000_000, int.MaxValue, true)]
+	[InlineData(1_000_000, int.MaxValue, false)]
+	[InlineData(0xff_fff8, 299593, true)]
+	[InlineData(0xff_fff8, 299593, false)]
+	[InlineData(0xff_fff8, 300000, true)]
+	[InlineData(0xff_fff8, 300000, false)]
+	[InlineData(0xff_fff8, int.MaxValue, true)]
+	[InlineData(0xff_fff8, int.MaxValue, false)]
+	[InlineData(0xff_fff9, int.MaxValue, true)]
+	[InlineData(0xff_fff9, int.MaxValue, false)]
+	[InlineData(0x1ff_fff0, 299593, true)]
+	[InlineData(0x1ff_fff0, 299593, false)]
+	[InlineData(0x1ff_fff0, 300000, true)]
+	[InlineData(0x1ff_fff0, 300000, false)]
+	[InlineData(15_999_999, int.MaxValue, true)]
+	[InlineData(15_999_999, int.MaxValue, false)]
+	[InlineData(16_000_000, int.MaxValue, true)]
+	[InlineData(16_000_000, int.MaxValue, false)]
+	[InlineData(16_000_001, int.MaxValue, true)]
+	[InlineData(16_000_001, int.MaxValue, false)]
+	[InlineData(31_999_999, 999_999, true)]
+	[InlineData(31_999_999, 1_000_000, false)]
+	[InlineData(32_000_000, 1_000_001, true)]
+	[InlineData(32_000_000, 1_000_002, false)]
+	[InlineData(32_000_001, 1_000_003, true)]
+	[InlineData(32_000_001, 1_000_004, false)]
+	public async Task SendLongData(int dataLength, int chunkLength, bool isAsync)
+	{
+		using MySqlConnection connection = new MySqlConnection(AppConfig.ConnectionString);
+		connection.Open();
+		connection.Execute("""
+			drop table if exists insert_mysql_long_data;
+			create table insert_mysql_long_data(rowid integer not null primary key auto_increment, value longblob);
+			""");
+
+		var random = new Random(dataLength);
+		var data = new byte[dataLength];
+		random.NextBytes(data);
+
+		using var chunkStream = new ChunkStream(data, chunkLength);
+
+		using var writeCommand = new MySqlCommand("""
+			insert into insert_mysql_long_data(value) values(@value);
+			select length(value) from insert_mysql_long_data order by rowid;
+			""", connection);
+		writeCommand.Parameters.AddWithValue("@value", chunkStream);
+		writeCommand.Prepare();
+		using (var reader = isAsync ? await writeCommand.ExecuteReaderAsync().ConfigureAwait(true) : writeCommand.ExecuteReader())
+		{
+			Assert.True(reader.Read());
+			Assert.Equal(1, reader.FieldCount);
+			Assert.Equal(dataLength, reader.GetInt32(0));
+			Assert.False(reader.Read());
+		}
+
+		using var readCommand = new MySqlCommand("select value from insert_mysql_long_data order by rowid;", connection);
+		using (var reader = readCommand.ExecuteReader())
+		{
+			Assert.True(reader.Read());
+			var readData = (byte[]) reader.GetValue(0);
+			Assert.True(data.AsSpan().SequenceEqual(readData)); // much faster than Assert.Equal
+		}
+	}
+
+	[Theory]
+	[InlineData(false)]
+	[InlineData(true)]
+	public async Task SendLongDataMultipleStatements(bool isAsync)
+	{
+		using MySqlConnection connection = new MySqlConnection(AppConfig.ConnectionString);
+		connection.Open();
+		connection.Execute("""
+			drop table if exists insert_mysql_long_data;
+			create table insert_mysql_long_data(rowid integer not null primary key auto_increment, value longblob);
+			""");
+
+		var data1 = new byte[1000];
+		var data2 = new byte[2000];
+		var data3 = new byte[3000];
+		var random = new Random(1);
+		random.NextBytes(data1);
+		random.NextBytes(data2);
+		random.NextBytes(data3);
+
+		using var chunkStream1 = new ChunkStream(data1, int.MaxValue);
+		using var chunkStream2 = new ChunkStream(data2, int.MaxValue);
+		using var chunkStream3 = new ChunkStream(data3, int.MaxValue);
+
+		using var writeCommand = new MySqlCommand("""
+			insert into insert_mysql_long_data(rowid, value) values(1, @value1);
+			insert into insert_mysql_long_data(rowid, value) values(2, @value2);
+			insert into insert_mysql_long_data(rowid, value) values(3, @value3);
+			""", connection);
+		writeCommand.Parameters.AddWithValue("@value1", chunkStream1);
+		writeCommand.Parameters.AddWithValue("@value2", chunkStream2);
+		writeCommand.Parameters.AddWithValue("@value3", chunkStream3);
+		writeCommand.Prepare();
+		if (isAsync)
+			await writeCommand.ExecuteNonQueryAsync();
+		else
+			writeCommand.ExecuteNonQuery();
+
+		using var readCommand = new MySqlCommand("select value from insert_mysql_long_data order by rowid;", connection);
+		using (var reader = readCommand.ExecuteReader())
+		{
+			Assert.True(reader.Read());
+			var readData = (byte[]) reader.GetValue(0);
+			Assert.True(data1.AsSpan().SequenceEqual(readData));
+
+			Assert.True(reader.Read());
+			readData = (byte[]) reader.GetValue(0);
+			Assert.True(data2.AsSpan().SequenceEqual(readData));
+
+			Assert.True(reader.Read());
+			readData = (byte[]) reader.GetValue(0);
+			Assert.True(data3.AsSpan().SequenceEqual(readData));
+
+			Assert.False(reader.Read());
+		}
+	}
 #endif
 
 	[Theory]

From 6d19d7f795557f868fe88c8ae894387db24a2b19 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 22 Jul 2025 16:09:43 -0700
Subject: [PATCH 391/393] Update MariaDB tests to v11.8.

This is the latest LTS version.
---
 .ci/docker-run.sh                           | 2 +-
 azure-pipelines.yml                         | 4 ++--
 docs/content/home.md                        | 2 +-
 tests/IntegrationTests/CharacterSetTests.cs | 2 +-
 tests/IntegrationTests/DataTypesFixture.cs  | 2 +-
 5 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/.ci/docker-run.sh b/.ci/docker-run.sh
index 6cb8a2efe..08e4efead 100755
--- a/.ci/docker-run.sh
+++ b/.ci/docker-run.sh
@@ -28,7 +28,7 @@ MYSQL=mysql
 if [[ "$IMAGE" == mariadb* ]]; then
   MYSQL_EXTRA='--in-predicate-conversion-threshold=100000 --plugin-maturity=beta'
 fi
-if [ "$IMAGE" == "mariadb:11.4" ] || [ "$IMAGE" == "mariadb:11.7" ]; then
+if [ "$IMAGE" == "mariadb:11.4" ] || [ "$IMAGE" == "mariadb:11.8" ]; then
   MYSQL='mariadb'
 fi
 
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 9e914f07e..3777127fe 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -192,8 +192,8 @@ jobs:
         image: 'mariadb:11.4'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,UuidToBin,Vector'
-      'MariaDB 11.7':
-        image: 'mariadb:11.7'
+      'MariaDB 11.8':
+        image: 'mariadb:11.8'
         connectionStringExtra: ''
         unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,UuidToBin,VectorType'
   steps:
diff --git a/docs/content/home.md b/docs/content/home.md
index 555afcf3a..93f67afd1 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -64,7 +64,7 @@ Server  | Versions | Notes
 Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://mysqlconnector.net/troubleshooting/aurora-freeze/)
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
-MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**, **11.7**) |
+MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**, **11.8**) |
 MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.3**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
diff --git a/tests/IntegrationTests/CharacterSetTests.cs b/tests/IntegrationTests/CharacterSetTests.cs
index 317d49ddc..e64f31b4b 100644
--- a/tests/IntegrationTests/CharacterSetTests.cs
+++ b/tests/IntegrationTests/CharacterSetTests.cs
@@ -79,7 +79,7 @@ public void CollationConnection(bool reopenConnection)
 
 		var collation = connection.Query<string>(@"select @@collation_connection;").Single();
 		var expected = connection.ServerVersion.Substring(0, 2) is "8." or "9." ? "utf8mb4_0900_ai_ci" :
-			connection.ServerVersion.StartsWith("11.4.", StringComparison.Ordinal) || connection.ServerVersion.StartsWith("11.7.", StringComparison.Ordinal) ? "utf8mb4_uca1400_ai_ci" :
+			connection.ServerVersion.StartsWith("11.4.", StringComparison.Ordinal) || connection.ServerVersion.StartsWith("11.8.", StringComparison.Ordinal) ? "utf8mb4_uca1400_ai_ci" :
 			"utf8mb4_general_ci";
 		Assert.Equal(expected, collation);
 	}
diff --git a/tests/IntegrationTests/DataTypesFixture.cs b/tests/IntegrationTests/DataTypesFixture.cs
index 13ac73aaa..de346a0f1 100644
--- a/tests/IntegrationTests/DataTypesFixture.cs
+++ b/tests/IntegrationTests/DataTypesFixture.cs
@@ -248,7 +248,7 @@ insert into datatypes_json_core (value)
 		if (AppConfig.SupportedFeatures.HasFlag(ServerFeatures.Vector))
 		{
 			// create a helper function for MariaDB 11.7+
-			if (Connection.ServerVersion.StartsWith("11.7.", StringComparison.Ordinal))
+			if (Connection.ServerVersion.StartsWith("11.8.", StringComparison.Ordinal))
 				Connection.Execute("create function if not exists STRING_TO_VECTOR(s text) returns vector(3) deterministic return Vec_FromText(s);");
 
 			Connection.Execute("""

From 9db104f70d161521ce59e95aa89cb102390fbf78 Mon Sep 17 00:00:00 2001
From: Bradley Grainger <bgrainger@gmail.com>
Date: Tue, 22 Jul 2025 16:19:05 -0700
Subject: [PATCH 392/393] Update MySQL versions to 9.4.0.

---
 .github/workflows/ci.yml                               | 2 +-
 Directory.Packages.props                               | 2 +-
 azure-pipelines.yml                                    | 4 ++--
 docs/content/home.md                                   | 2 +-
 docs/content/tutorials/migrating-from-connector-net.md | 6 +++---
 tests/Benchmark/Benchmark.csproj                       | 2 +-
 tests/IntegrationTests/ConnectAsync.cs                 | 2 +-
 tests/IntegrationTests/app.config                      | 2 +-
 tests/README.md                                        | 2 +-
 9 files changed, 12 insertions(+), 12 deletions(-)

diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index b69c1e7a7..b9995083e 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -35,7 +35,7 @@ jobs:
         tfm: [ 'net8.0', 'net9.0' ]
     services:
       mysql:
-        image: mysql:9.3
+        image: mysql:9.4
         env:
           MYSQL_DATABASE: testdb
           MYSQL_ROOT_PASSWORD: pass
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 9f75afc5a..c66ca1f40 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -17,7 +17,7 @@
     <PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.2" />
     <PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.2" />
     <PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
-    <PackageVersion Include="MySql.Data" Version="9.3.0" />
+    <PackageVersion Include="MySql.Data" Version="9.4.0" />
     <PackageVersion Include="NLog" Version="4.7.15" />
     <PackageVersion Include="Serilog" Version="2.12.0" />
     <PackageVersion Include="System.Diagnostics.DiagnosticSource" Version="8.0.1" />
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index 3777127fe..e579d4de3 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -176,8 +176,8 @@ jobs:
         image: 'mysql:8.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,Vector,ZeroDateTime'
-      'MySQL 9.3':
-        image: 'mysql:9.3'
+      'MySQL 9.4':
+        image: 'mysql:9.4'
         connectionStringExtra: 'AllowPublicKeyRetrieval=True'
         unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,Vector,ZeroDateTime'
       'MariaDB 10.6':
diff --git a/docs/content/home.md b/docs/content/home.md
index 93f67afd1..cb1899de0 100644
--- a/docs/content/home.md
+++ b/docs/content/home.md
@@ -65,7 +65,7 @@ Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://m
 Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
 Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
 MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**, **11.8**) |
-MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.3**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
+MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.4**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
 Percona Server | 5.6, 5.7, 8.0 |
 PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)
 ProxySQL | 2.x | Some [compatibility issues](https://github.com/search?q=repo%3Amysql-net%2FMySqlConnector+proxysql&type=issues)
diff --git a/docs/content/tutorials/migrating-from-connector-net.md b/docs/content/tutorials/migrating-from-connector-net.md
index 8666d78ea..270ddc7ea 100644
--- a/docs/content/tutorials/migrating-from-connector-net.md
+++ b/docs/content/tutorials/migrating-from-connector-net.md
@@ -41,7 +41,7 @@ In a .NET Framework application, make the following `app.config` change to regis
     <!-- add name="MySQL Data Provider"
       invariant="MySql.Data.MySqlClient"
       description=".Net Framework Data Provider for MySQL"
-      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.3.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
+      type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.4.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" / -->
 
     <!-- ADD THIS -->
     <add name="MySqlConnector"
@@ -245,7 +245,6 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector.
 * [#109331](https://bugs.mysql.com/bug.php?id=109331): MySQL Connector/NET is incompatible with MariaDB 10.10 and later
 * [#109390](https://bugs.mysql.com/bug.php?id=109390): Transaction lock held after connection timeout exception
 * [#109476](https://bugs.mysql.com/bug.php?id=109476): `TransactionScope.Dispose` throws "Connection must be valid and open to rollback"
-* [#110789](https://bugs.mysql.com/bug.php?id=110789): `OpenAsync` throws unhandled exception from thread pool
 * [#110790](https://bugs.mysql.com/bug.php?id=110790): `ExecuteReaderAsync` hangs instead of cancelling query after `CommandTimeout`
 * [#110791](https://bugs.mysql.com/bug.php?id=110791): `OpenAsync(CancellationToken)` doesn't throw for cancelled token
 * [#111797](https://bugs.mysql.com/bug.php?id=111797): Certain sequence of special characters can break connection string validation
@@ -258,7 +257,6 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector.
 * [#116146](https://bugs.mysql.com/bug.php?id=116146): `MySqlConnection.OpenAsync` throws exception when `UseCompression = true`
 * [#116257](https://bugs.mysql.com/bug.php?id=116257): `NullReferenceException` thrown when using prepared statement under OpenTelemetry
 * [#116382](https://bugs.mysql.com/bug.php?id=116382): `ExecuteNonQueryAsync` hangs with DDL if `UseCompression=True`
-* [#117020](https://bugs.mysql.com/bug.php?id=117020): `MinPoolSize` configuration is not honored
 
 ### Fixed First by MySqlConnector
 
@@ -346,4 +344,6 @@ These bugs are fixed in the latest version of MySQL Connector/NET, but were fixe
 * [#109683](https://bugs.mysql.com/bug.php?id=109683): `MySqlCommand.LastInsertedId` is incorrect if multiple rows are inserted.
 * [#109716](https://bugs.mysql.com/bug.php?id=109716), [#109727](https://bugs.mysql.com/bug.php?id=109727): Failed to add reference to 'comerr64'
 * [#110717](https://bugs.mysql.com/bug.php?id=110717), [#111759](https://bugs.mysql.com/bug.php?id=111759): MySql.Data is not thread-safe
+* [#110789](https://bugs.mysql.com/bug.php?id=110789): `OpenAsync` throws unhandled exception from thread pool
 * [#114527](https://bugs.mysql.com/bug.php?id=114527): Connector/NET hangs on second connection attempt to non-running database
+* [#117020](https://bugs.mysql.com/bug.php?id=117020): `MinPoolSize` configuration is not honored
diff --git a/tests/Benchmark/Benchmark.csproj b/tests/Benchmark/Benchmark.csproj
index c872995fc..0fc9f463a 100644
--- a/tests/Benchmark/Benchmark.csproj
+++ b/tests/Benchmark/Benchmark.csproj
@@ -8,7 +8,7 @@
   <ItemGroup>
     <PackageReference Include="BenchmarkDotNet" Version="0.14.0" />
     <PackageReference Include="MySqlConnector" Version="2.4.0" />
-    <PackageReference Include="MySql.Data" Version="9.3.0" />
+    <PackageReference Include="MySql.Data" Version="9.4.0" />
   </ItemGroup>
 
 <!--  <ItemGroup>-->
diff --git a/tests/IntegrationTests/ConnectAsync.cs b/tests/IntegrationTests/ConnectAsync.cs
index 03e69afde..eb5a99b76 100644
--- a/tests/IntegrationTests/ConnectAsync.cs
+++ b/tests/IntegrationTests/ConnectAsync.cs
@@ -110,7 +110,7 @@ public async Task ConnectKeepAlive()
 		await Task.Delay(3000);
 	}
 
-	[SkippableFact(ServerFeatures.Timeout, MySqlData = "https://bugs.mysql.com/bug.php?id=110789")]
+	[SkippableFact(ServerFeatures.Timeout)]
 	public async Task ConnectTimeoutAsync()
 	{
 		var csb = new MySqlConnectionStringBuilder
diff --git a/tests/IntegrationTests/app.config b/tests/IntegrationTests/app.config
index 0f88d3075..15ee770dd 100644
--- a/tests/IntegrationTests/app.config
+++ b/tests/IntegrationTests/app.config
@@ -4,7 +4,7 @@
     <DbProviderFactories>
       <add name="MySqlConnector" invariant="MySqlConnector" description="Async MySQL ADO.NET Connector" type="MySqlConnector.MySqlConnectorFactory, MySqlConnector, Culture=neutral, PublicKeyToken=d33d3e53aa5f8c92" />
       <remove invariant="MySql.Data.MySqlClient" />
-      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.3.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
+      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=9.4.0.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
     </DbProviderFactories>
   </system.data>
 </configuration>
diff --git a/tests/README.md b/tests/README.md
index 4ea82a640..4bc3082a9 100644
--- a/tests/README.md
+++ b/tests/README.md
@@ -7,7 +7,7 @@ It also verifies that MySqlConnector and MySQL Connector/NET (MySql.Data) have s
 
 The tests require a MySQL server. The simplest way to run one is with [Docker](https://www.docker.com/community-edition):
 
-    docker run -d --rm --pull always --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.3 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
+    docker run -d --rm --pull always --name mysqlconnector -e MYSQL_ROOT_PASSWORD=pass -p 3306:3306 --tmpfs /var/lib/mysql mysql:9.4 --max-allowed-packet=96M --character-set-server=utf8mb4 --disable-log-bin --local-infile=1 --max-connections=250
     docker exec mysqlconnector mysql -uroot -ppass -e "INSTALL COMPONENT 'file://component_query_attributes'; CREATE USER 'caching-sha2-user'@'%' IDENTIFIED WITH caching_sha2_password BY 'Cach!ng-Sh@2-Pa55'; GRANT ALL PRIVILEGES ON *.* TO 'caching-sha2-user'@'%';"
 
 Copy the file `IntegrationTests/config.json.example` to `IntegrationTests/config.json`, then edit

From 1834cfd3982f6f672d3c5a4045a358c5a08d62c3 Mon Sep 17 00:00:00 2001
From: Pete Dishman <peter.dishman@netcall.com>
Date: Tue, 29 Jul 2025 14:06:22 +0100
Subject: [PATCH 393/393] Detect connections to AWS RDS Proxy and update Server
 ID on connection

Signed-off-by: Pete Dishman <peter.dishman@netcall.com>
---
 src/MySqlConnector/Core/ServerSession.cs | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/MySqlConnector/Core/ServerSession.cs b/src/MySqlConnector/Core/ServerSession.cs
index 23efbfb44..eb7826021 100644
--- a/src/MySqlConnector/Core/ServerSession.cs
+++ b/src/MySqlConnector/Core/ServerSession.cs
@@ -1898,6 +1898,13 @@ private bool ShouldGetRealServerDetails(ConnectionSettings cs)
 				HostName.EndsWith(".mysql.database.chinacloudapi.cn", StringComparison.OrdinalIgnoreCase);
 		}
 
+		// detect AWS RDS Proxy, if hostname like <name>.proxy-<random-chars>.<region>.rds.amazonaws.com
+		if (HostName.EndsWith(".rds.amazonaws.com", StringComparison.OrdinalIgnoreCase) &&
+			HostName.Contains(".proxy-", StringComparison.OrdinalIgnoreCase))
+		{
+			return true;
+		}
+
 		return false;
 	}
 
