Skip to content

Commit 1268dab

Browse files
committed
Use ExceptionDispatchInfo to preserve the stack trace.
This avoid unnecessarily rewrapping a MySqlException in another MySqlException.
1 parent 2e3fd52 commit 1268dab

File tree

3 files changed

+14
-7
lines changed

3 files changed

+14
-7
lines changed

src/MySqlConnector/Core/ResultSet.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Data;
55
using System.Globalization;
66
using System.IO;
7+
using System.Runtime.ExceptionServices;
78
using System.Threading;
89
using System.Threading.Tasks;
910
using MySqlConnector.Protocol;
@@ -110,7 +111,7 @@ public async Task ReadResultSetHeaderAsync(IOBehavior ioBehavior)
110111
catch (Exception ex)
111112
{
112113
// store the exception, to be thrown after reading the response packet from the server
113-
ReadResultSetHeaderException = new("Error during LOAD DATA LOCAL INFILE", ex);
114+
ReadResultSetHeaderException = ExceptionDispatchInfo.Capture(new MySqlException("Error during LOAD DATA LOCAL INFILE", ex));
114115
}
115116

116117
await Session.SendReplyAsync(EmptyPayload.Instance, ioBehavior, CancellationToken.None).ConfigureAwait(false);
@@ -166,7 +167,7 @@ int ReadColumnCount(ReadOnlySpan<byte> span)
166167
}
167168
catch (Exception ex)
168169
{
169-
ReadResultSetHeaderException = ex;
170+
ReadResultSetHeaderException = ExceptionDispatchInfo.Capture(ex);
170171
}
171172
finally
172173
{
@@ -425,7 +426,7 @@ public Row GetCurrentRow()
425426
}
426427

427428
public readonly MySqlDataReader DataReader;
428-
public Exception? ReadResultSetHeaderException { get; private set; }
429+
public ExceptionDispatchInfo? ReadResultSetHeaderException { get; private set; }
429430
public IMySqlCommand Command => DataReader.Command!;
430431
public MySqlConnection Connection => DataReader.Connection!;
431432
public ServerSession Session => DataReader.Session!;

src/MySqlConnector/MySqlDataReader.cs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.Globalization;
88
using System.IO;
99
using System.Linq;
10+
using System.Runtime.ExceptionServices;
1011
using System.Threading;
1112
using System.Threading.Tasks;
1213
using MySqlConnector.Core;
@@ -117,12 +118,12 @@ private void ActivateResultSet(CancellationToken cancellationToken)
117118
{
118119
if (m_resultSet!.ReadResultSetHeaderException is not null)
119120
{
120-
var mySqlException = m_resultSet.ReadResultSetHeaderException as MySqlException;
121+
var mySqlException = m_resultSet.ReadResultSetHeaderException.SourceException as MySqlException;
121122

122123
// for any exception not created from an ErrorPayload, mark the session as failed (because we can't guarantee that all data
123124
// has been read from the connection and that the socket is still usable)
124125
if (mySqlException?.SqlState is null)
125-
Command!.Connection!.SetSessionFailed(m_resultSet.ReadResultSetHeaderException);
126+
Command!.Connection!.SetSessionFailed(m_resultSet.ReadResultSetHeaderException.SourceException);
126127

127128
if (mySqlException?.ErrorCode == MySqlErrorCode.QueryInterrupted && cancellationToken.IsCancellationRequested)
128129
throw new OperationCanceledException(mySqlException.Message, mySqlException, cancellationToken);
@@ -131,9 +132,9 @@ private void ActivateResultSet(CancellationToken cancellationToken)
131132
throw MySqlException.CreateForTimeout(mySqlException);
132133

133134
if (mySqlException is not null)
134-
throw new MySqlException(mySqlException.ErrorCode, mySqlException.SqlState, mySqlException.Message, mySqlException);
135+
m_resultSet.ReadResultSetHeaderException.Throw();
135136

136-
throw new MySqlException("Failed to read the result set.", m_resultSet.ReadResultSetHeaderException);
137+
throw new MySqlException("Failed to read the result set.", m_resultSet.ReadResultSetHeaderException.SourceException);
137138
}
138139

139140
Command!.SetLastInsertedId(m_resultSet.LastInsertId);

tests/MySqlConnector.Tests/CancellationTests.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Collections.Generic;
33
using System.Data;
44
using System.Diagnostics;
5+
using System.Net.Sockets;
56
using System.Threading;
67
using System.Threading.Tasks;
78
using Xunit;
@@ -224,6 +225,7 @@ public void Test(int step, int method)
224225
Assert.InRange(stopwatch.ElapsedMilliseconds, 2900, 3500);
225226
Assert.Equal(MySqlErrorCode.CommandTimeoutExpired, ex.ErrorCode);
226227
Assert.True(ex.IsTransient);
228+
Assert.Null(ex.InnerException);
227229

228230
// connection is unusable
229231
Assert.Equal(ConnectionState.Closed, connection.State);
@@ -245,6 +247,7 @@ public async Task Test(int step, int method)
245247
var ex = await Assert.ThrowsAsync<MySqlException>(async () => await s_executeAsyncMethods[method](command, default));
246248
Assert.InRange(stopwatch.ElapsedMilliseconds, 2900, 3500);
247249
Assert.Equal(MySqlErrorCode.CommandTimeoutExpired, ex.ErrorCode);
250+
Assert.IsType<SocketException>(ex.InnerException);
248251

249252
// connection is unusable
250253
Assert.Equal(ConnectionState.Closed, connection.State);
@@ -267,6 +270,7 @@ public void Test(int step, int method)
267270
var ex = Assert.Throws<MySqlException>(() => s_executeMethods[method](command));
268271
Assert.InRange(stopwatch.ElapsedMilliseconds, 900, 1500);
269272
Assert.Equal(MySqlErrorCode.CommandTimeoutExpired, ex.ErrorCode);
273+
Assert.Null(ex.InnerException);
270274

271275
// connection is unusable
272276
Assert.Equal(ConnectionState.Closed, connection.State);
@@ -289,6 +293,7 @@ public async Task Test(int step, int method)
289293
var ex = await Assert.ThrowsAsync<MySqlException>(async () => await s_executeAsyncMethods[method](command, default));
290294
Assert.InRange(stopwatch.ElapsedMilliseconds, 900, 1500);
291295
Assert.Equal(MySqlErrorCode.CommandTimeoutExpired, ex.ErrorCode);
296+
Assert.IsType<SocketException>(ex.InnerException);
292297

293298
// connection is unusable
294299
Assert.Equal(ConnectionState.Closed, connection.State);

0 commit comments

Comments
 (0)