Skip to content

Commit 2c5ec00

Browse files
committed
Prevent the same reader being closed twice. Fixes #423
Dispose(bool) calls DoClose then (on certain frameworks) base.Dispose(bool) calls Close, which calls DoClose again. This normally wouldn't be a problem except that when a stored procedure has OUT parameters, they are read from a nested MySqlDataReader. This reentrancy allows a reader to be closed twice if an exception is thrown, which masks the underlying exception and throws InvalidOperationExcepion "Expected state to be Failed but was Connected" instead.
1 parent 239dfe6 commit 2c5ec00

File tree

3 files changed

+30
-2
lines changed

3 files changed

+30
-2
lines changed

src/MySqlConnector/MySql.Data.MySqlClient/MySqlDataReader.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -396,8 +396,10 @@ private MySqlDataReader(MySqlCommand command, CommandBehavior behavior)
396396

397397
private void DoClose()
398398
{
399-
if (Command != null)
399+
if (!m_closed)
400400
{
401+
m_closed = true;
402+
401403
if (m_resultSet != null)
402404
{
403405
Command.Connection.Session.SetTimeout(Constants.InfiniteTimeout);
@@ -443,6 +445,7 @@ private ResultSet GetResultSet()
443445
}
444446

445447
readonly CommandBehavior m_behavior;
448+
bool m_closed;
446449
int? m_recordsAffected;
447450
ResultSet m_resultSet;
448451
ResultSet m_resultSetBuffered;

tests/SideBySide/StoredProcedureFixture.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Dapper;
1+
using Dapper;
22

33
namespace SideBySide
44
{
@@ -48,6 +48,13 @@ OUT shape VARCHAR(63)
4848
SELECT 'circle' INTO shape;
4949
SELECT CONCAT(name, shape);
5050
END");
51+
Connection.Execute(@"DROP PROCEDURE IF EXISTS out_string;
52+
CREATE PROCEDURE out_string(
53+
OUT value VARCHAR(100)
54+
)
55+
BEGIN
56+
SELECT 'test value' INTO value;
57+
END");
5158
Connection.Execute(@"drop table if exists sproc_multiple_rows;
5259
create table sproc_multiple_rows (
5360
value integer not null primary key auto_increment,

tests/SideBySide/StoredProcedureTests.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,24 @@ public async Task StoredProcedureEchoException(string procedureType)
117117
}
118118
}
119119

120+
[Fact]
121+
public async Task StoredProcedureOutIncorrectType()
122+
{
123+
using (var cmd = m_database.Connection.CreateCommand())
124+
{
125+
cmd.CommandText = "out_string";
126+
cmd.CommandType = CommandType.StoredProcedure;
127+
cmd.Parameters.Add(new MySqlParameter
128+
{
129+
ParameterName = "@value",
130+
DbType = DbType.Double,
131+
Direction = ParameterDirection.Output,
132+
});
133+
134+
await Assert.ThrowsAsync<FormatException>(cmd.ExecuteNonQueryAsync);
135+
}
136+
}
137+
120138
[Theory]
121139
[InlineData("NonQuery")]
122140
[InlineData("Scalar")]

0 commit comments

Comments
 (0)