Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid SET NAMES commands if not needed #1497

Merged
merged 7 commits into from
Jul 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/MySqlConnector/Core/IServerCapabilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace MySqlConnector.Core;

internal interface IServerCapabilities
{
bool SupportsDeprecateEof { get; }
bool SupportsSessionTrack { get; }
}
6 changes: 3 additions & 3 deletions src/MySqlConnector/Core/ResultSet.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

// 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
Expand Down Expand Up @@ -252,9 +252,9 @@ 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.SupportsDeprecateEof && OkPayload.IsOk(payload.Span, Session))
{
var ok = OkPayload.Create(payload.Span, Session.SupportsDeprecateEof, Session.SupportsSessionTrack);
var ok = OkPayload.Create(payload.Span, Session);
BufferState = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0 ? ResultSetState.NoMoreData : ResultSetState.HasMoreData;
return null;
}
Expand Down
43 changes: 27 additions & 16 deletions src/MySqlConnector/Core/ServerSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down Expand Up @@ -320,7 +320,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.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
OkPayload.Verify(payload.Span, this);
}

lock (m_lock)
Expand Down Expand Up @@ -532,19 +532,30 @@ 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, this);
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);
// 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);
payload = await ReceiveReplyAsync(ioBehavior, cancellationToken).ConfigureAwait(false);
OkPayload.Verify(payload.Span, this);
}

if (ShouldGetRealServerDetails(cs))
{
await GetRealServerDetailsAsync(ioBehavior, CancellationToken.None).ConfigureAwait(false);
}
else if (ok.NewConnectionId 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;
Expand Down Expand Up @@ -584,18 +595,18 @@ 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, this);

payload = await ReceiveReplyAsync(1, ioBehavior, cancellationToken).ConfigureAwait(false);
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
OkPayload.Verify(payload.Span, this);

return true;
}

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, this);
}
else
{
Expand All @@ -619,13 +630,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, 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, SupportsDeprecateEof, SupportsSessionTrack);
OkPayload.Verify(payload.Span, this);

return true;
}
Expand Down Expand Up @@ -684,7 +695,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, this))
return payload;

var cachingSha2ServerResponsePayload = CachingSha2ServerResponsePayload.Create(payload.Span);
Expand Down Expand Up @@ -824,7 +835,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, this);
Log.SuccessfullyPingedServer(m_logger, logInfo ? LogLevel.Information : LogLevel.Trace, Id);
return true;
}
Expand Down Expand Up @@ -1662,8 +1673,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, SupportsDeprecateEof))
OkPayload.Verify(payload.Span, SupportsDeprecateEof, SupportsSessionTrack);
if (OkPayload.IsOk(payload.Span, this))
OkPayload.Verify(payload.Span, this);
else
EofPayload.Create(payload.Span);

Expand Down
14 changes: 8 additions & 6 deletions src/MySqlConnector/MySqlConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,23 +161,23 @@ 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);

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);
}
else
{
// 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);
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.SupportsDeprecateEof, m_session.SupportsSessionTrack);
OkPayload.Verify(payload.Span, m_session);
}

var transaction = new MySqlTransaction(this, isolationLevel, m_transactionLogger);
Expand Down Expand Up @@ -487,7 +487,9 @@ 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);
OkPayload.Verify(payload.Span, m_session);

// for non session tracking servers
m_session.DatabaseOverride = databaseName;
}

Expand Down Expand Up @@ -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);
}

[AllowNull]
Expand Down
Loading