Skip to content

Commit aeaf900

Browse files
authored
Merge pull request #1497 from mysql-net/avoid-set-names
Avoid SET NAMES commands if not needed. For MariaDB >= 11.5, session variables are sent in the initial OK Packet, permitting MySqlConnector to avoid initial SET NAMES query, since charset is already known.
2 parents e6bb115 + 3f50c14 commit aeaf900

File tree

5 files changed

+109
-46
lines changed

5 files changed

+109
-46
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace MySqlConnector.Core;
2+
3+
internal interface IServerCapabilities
4+
{
5+
bool SupportsDeprecateEof { get; }
6+
bool SupportsSessionTrack { get; }
7+
}

src/MySqlConnector/Core/ResultSet.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
3838
var firstByte = payload.HeaderByte;
3939
if (firstByte == OkPayload.Signature)
4040
{
41-
var ok = OkPayload.Create(payload.Span, Session.SupportsDeprecateEof, Session.SupportsSessionTrack);
41+
var ok = OkPayload.Create(payload.Span, Session);
4242

4343
// if we've read a result set header then this is a SELECT statement, so we shouldn't overwrite RecordsAffected
4444
// (which should be -1 for SELECT) unless the server reports a non-zero count
@@ -252,9 +252,9 @@ public async Task<bool> ReadAsync(IOBehavior ioBehavior, CancellationToken cance
252252

253253
if (payload.HeaderByte == EofPayload.Signature)
254254
{
255-
if (Session.SupportsDeprecateEof && OkPayload.IsOk(payload.Span, Session.SupportsDeprecateEof))
255+
if (Session.SupportsDeprecateEof && OkPayload.IsOk(payload.Span, Session))
256256
{
257-
var ok = OkPayload.Create(payload.Span, Session.SupportsDeprecateEof, Session.SupportsSessionTrack);
257+
var ok = OkPayload.Create(payload.Span, Session);
258258
BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
259259
return null;
260260
}

src/MySqlConnector/Core/ServerSession.cs

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ namespace MySqlConnector.Core;
2323

2424
#pragma warning disable CA1001 // Types that own disposable fields should be disposable
2525

26-
internal sealed partial class ServerSession
26+
internal sealed partial class ServerSession : IServerCapabilities
2727
{
2828
public ServerSession(ILogger logger)
2929
: this(logger, null, 0, Interlocked.Increment(ref s_lastId))
@@ -320,7 +320,7 @@ public void FinishQuerying()
320320
SendAsync(payload, IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
321321
payload = ReceiveReplyAsync(IOBehavior.Synchronous, CancellationToken.None).GetAwaiter().GetResult();
322322
#pragma warning restore CA2012
323-
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
323+
OkPayload.Verify(payload.Span, this);
324324
}
325325

326326
lock (m_lock)
@@ -532,19 +532,30 @@ public async Task DisposeAsync(IOBehavior ioBehavior, CancellationToken cancella
532532
payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
533533
}
534534

535-
var ok = OkPayload.Create(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
535+
var ok = OkPayload.Create(payload.Span, this);
536536
var statusInfo = ok.StatusInfo;
537537

538538
if (m_useCompression)
539539
m_payloadHandler = new CompressedPayloadHandler(m_payloadHandler.ByteHandler);
540540

541-
// set 'collation_connection' to the server default
542-
await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
543-
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
544-
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
541+
// 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)
542+
if (ok.NewCharacterSet != (ServerVersion.Version >= ServerVersions.SupportsUtf8Mb4 ? CharacterSet.Utf8Mb4Binary : CharacterSet.Utf8Mb3Binary))
543+
{
544+
// set 'collation_connection' to the server default
545+
await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
546+
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
547+
OkPayload.Verify(payload.Span, this);
548+
}
545549

546550
if (ShouldGetRealServerDetails(cs))
551+
{
547552
await GetRealServerDetailsAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
553+
}
554+
else if (ok.NewConnectionId is int newConnectionId && newConnectionId != ConnectionId)
555+
{
556+
Log.ChangingConnectionId(m_logger, Id, ConnectionId, newConnectionId, ServerVersion.OriginalString, ServerVersion.OriginalString);
557+
ConnectionId = newConnectionId;
558+
}
548559

549560
m_payloadHandler.ByteHandler.RemainingTimeout = Constants.InfiniteTimeout;
550561
return statusInfo;
@@ -584,18 +595,18 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
584595

585596
// read two OK replies
586597
payload = await ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
587-
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
598+
OkPayload.Verify(payload.Span, this);
588599

589600
payload = await ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
590-
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
601+
OkPayload.Verify(payload.Span, this);
591602

592603
return true;
593604
}
594605

595606
Log.SendingResetConnectionRequest(m_logger, Id, ServerVersion.OriginalString);
596607
await SendAsync(ResetConnectionPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
597608
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
598-
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
609+
OkPayload.Verify(payload.Span, this);
599610
}
600611
else
601612
{
@@ -619,13 +630,13 @@ public async Task<bool> TryResetConnectionAsync(ConnectionSettings cs, MySqlConn
619630
Log.OptimisticReauthenticationFailed(m_logger, Id);
620631
payload = await SwitchAuthenticationAsync(cs, password, payload, ioBehavior, cancellationToken).ConfigureAwait(false);
621632
}
622-
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
633+
OkPayload.Verify(payload.Span, this);
623634
}
624635

625636
// set 'collation_connection' to the server default
626637
await SendAsync(m_setNamesPayload, ioBehavior, cancellationToken).ConfigureAwait(false);
627638
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
628-
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
639+
OkPayload.Verify(payload.Span, this);
629640

630641
return true;
631642
}
@@ -684,7 +695,7 @@ private async Task<PayloadData> SwitchAuthenticationAsync(ConnectionSettings cs,
684695
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
685696

686697
// OK payload can be sent immediately (e.g., if password is empty) bypassing even the fast authentication path
687-
if (OkPayload.IsOk(payload.Span, SupportsDeprecateEof))
698+
if (OkPayload.IsOk(payload.Span, this))
688699
return payload;
689700

690701
var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload.Span);
@@ -824,7 +835,7 @@ public async ValueTask<bool> TryPingAsync(bool logInfo, IOBehavior ioBehavior, C
824835
Log.PingingServer(m_logger, Id);
825836
await SendAsync(PingPayload.Instance, ioBehavior, cancellationToken).ConfigureAwait(false);
826837
var payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
827-
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
838+
OkPayload.Verify(payload.Span, this);
828839
Log.SuccessfullyPingedServer(m_logger, logInfo ? LogLevel.Information : LogLevel.Trace, Id);
829840
return true;
830841
}
@@ -1662,8 +1673,8 @@ static void ReadRow(ReadOnlySpan<byte> span, out int? connectionId, out ServerVe
16621673

16631674
// OK/EOF payload
16641675
payload = await ReceiveReplyAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
1665-
if (OkPayload.IsOk(payload.Span, SupportsDeprecateEof))
1666-
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
1676+
if (OkPayload.IsOk(payload.Span, this))
1677+
OkPayload.Verify(payload.Span, this);
16671678
else
16681679
EofPayload.Create(payload.Span);
16691680

src/MySqlConnector/MySqlConnection.cs

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -161,23 +161,23 @@ private async ValueTask<MySqlTransaction> BeginTransactionAsync(IsolationLevel i
161161

162162
// read the two OK replies
163163
var payload = await m_session.ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
164-
OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
164+
OkPayload.Verify(payload.Span, m_session);
165165

166166
payload = await m_session.ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
167-
OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
167+
OkPayload.Verify(payload.Span, m_session);
168168
}
169169
else
170170
{
171171
// send the two packets separately
172172
await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(4, startTransactionPayload.Span[0])), ioBehavior, cancellationToken).ConfigureAwait(false);
173173

174174
var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
175-
OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
175+
OkPayload.Verify(payload.Span, m_session);
176176

177177
await m_session.SendAsync(new Protocol.PayloadData(startTransactionPayload.Slice(8 + startTransactionPayload.Span[0], startTransactionPayload.Span[startTransactionPayload.Span[0] + 4])), ioBehavior, cancellationToken).ConfigureAwait(false);
178178

179179
payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
180-
OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
180+
OkPayload.Verify(payload.Span, m_session);
181181
}
182182

183183
var transaction = new MySqlTransaction(this, isolationLevel, m_transactionLogger);
@@ -487,7 +487,9 @@ private async Task ChangeDatabaseAsync(IOBehavior ioBehavior, string databaseNam
487487
using (var initDatabasePayload = InitDatabasePayload.Create(databaseName))
488488
await m_session!.SendAsync(initDatabasePayload, ioBehavior, cancellationToken).ConfigureAwait(false);
489489
var payload = await m_session.ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
490-
OkPayload.Verify(payload.Span, m_session.SupportsDeprecateEof, m_session.SupportsSessionTrack);
490+
OkPayload.Verify(payload.Span, m_session);
491+
492+
// for non session tracking servers
491493
m_session.DatabaseOverride = databaseName;
492494
}
493495

@@ -603,7 +605,7 @@ public async ValueTask ResetConnectionAsync(CancellationToken cancellationToken
603605
Log.ResettingConnection(m_logger, session.Id);
604606
await session.SendAsync(ResetConnectionPayload.Instance, AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
605607
var payload = await session.ReceiveReplyAsync(AsyncIOBehavior, cancellationToken).ConfigureAwait(false);
606-
OkPayload.Verify(payload.Span, session.SupportsDeprecateEof, session.SupportsSessionTrack);
608+
OkPayload.Verify(payload.Span, session);
607609
}
608610

609611
[AllowNull]

0 commit comments

Comments
 (0)