Skip to content

Commit f175c95

Browse files
committed
Use sql_select_limit for CommandBehavior.SingleRow. Fixes #679
1 parent 561d30f commit f175c95

File tree

5 files changed

+78
-5
lines changed

5 files changed

+78
-5
lines changed

src/MySqlConnector/Core/IMySqlCommand.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ internal interface IMySqlCommand
1111
{
1212
string? CommandText { get; }
1313
CommandType CommandType { get; }
14+
CommandBehavior CommandBehavior { get; }
1415
MySqlParameterCollection? RawParameters { get; }
1516
PreparedStatements? TryGetPreparedStatements();
1617
MySqlConnection? Connection { get; }

src/MySqlConnector/Core/SingleCommandPayloadCreator.cs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,18 @@ private static bool WriteStoredProcedure(IMySqlCommand command, IDictionary<stri
191191

192192
private static bool WriteCommand(IMySqlCommand command, ByteBufferWriter writer)
193193
{
194+
var isSingleRow = (command.CommandBehavior & CommandBehavior.SingleRow) != 0;
195+
if (isSingleRow)
196+
writer.Write(SetSqlSelectLimit);
194197
var preparer = new StatementPreparer(command.CommandText!, command.RawParameters, command.CreateStatementPreparerOptions());
195-
return preparer.ParseAndBindParameters(writer);
198+
var isComplete = preparer.ParseAndBindParameters(writer);
199+
if (isComplete && isSingleRow)
200+
writer.Write(ClearSqlSelectLimit);
201+
return isComplete;
196202
}
197203

204+
static ReadOnlySpan<byte> SetSqlSelectLimit => new byte[] { 83, 69, 84, 32, 115, 113, 108, 95, 115, 101, 108, 101, 99, 116, 95, 108, 105, 109, 105, 116, 61, 49, 59, 10 }; // SET sql_select_limit=1;\n
205+
static ReadOnlySpan<byte> ClearSqlSelectLimit => new byte[] { 10, 83, 69, 84, 32, 115, 113, 108, 95, 115, 101, 108, 101, 99, 116, 95, 108, 105, 109, 105, 116, 61, 100, 101, 102, 97, 117, 108, 116, 59 }; // \nSET sql_select_limit=default;
198206
static readonly IMySqlConnectorLogger Log = MySqlConnectorLogManager.CreateLogger(nameof(SingleCommandPayloadCreator));
199207
}
200208
}

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

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,8 @@ internal Task<DbDataReader> ExecuteReaderAsync(CommandBehavior behavior, IOBehav
271271
if (!IsValid(out var exception))
272272
return Utility.TaskFromException<DbDataReader>(exception);
273273

274-
return CommandExecutor.ExecuteReaderAsync(new IMySqlCommand[] { this }, new SingleCommandPayloadCreator(), behavior, ioBehavior, cancellationToken);
274+
m_commandBehavior = behavior;
275+
return CommandExecutor.ExecuteReaderAsync(new IMySqlCommand[] { this }, SingleCommandPayloadCreator.Instance, behavior, ioBehavior, cancellationToken);
275276
}
276277

277278
public MySqlCommand Clone() => new MySqlCommand(this);
@@ -354,8 +355,8 @@ private bool IsValid(out Exception exception)
354355
PreparedStatements IMySqlCommand.TryGetPreparedStatements() => CommandType == CommandType.Text && !string.IsNullOrWhiteSpace(CommandText) && m_connection is object &&
355356
m_connection.State == ConnectionState.Open ? m_connection.Session.TryGetPreparedStatement(CommandText) : null;
356357

358+
CommandBehavior IMySqlCommand.CommandBehavior => m_commandBehavior;
357359
MySqlParameterCollection IMySqlCommand.OutParameters { get; set; }
358-
359360
MySqlParameter IMySqlCommand.ReturnParameter { get; set; }
360361

361362
readonly int m_commandId;
@@ -365,6 +366,7 @@ PreparedStatements IMySqlCommand.TryGetPreparedStatements() => CommandType == Co
365366
MySqlParameterCollection m_parameterCollection;
366367
int? m_commandTimeout;
367368
CommandType m_commandType;
369+
CommandBehavior m_commandBehavior;
368370
Action m_cancelAction;
369371
}
370372
}

tests/SideBySide/BatchTests.cs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,48 @@ public void ExecuteInvalidSqlBatch()
250250
}
251251
}
252252
}
253+
254+
[Fact]
255+
public void SingleRow()
256+
{
257+
using (var connection = new MySqlConnection(AppConfig.ConnectionString))
258+
{
259+
connection.Open();
260+
using (var command = new MySqlCommand(@"drop table if exists batch_single_row;
261+
create table batch_single_row(id integer not null primary key);
262+
insert into batch_single_row(id) values(1),(2),(3);", connection))
263+
{
264+
command.ExecuteNonQuery();
265+
}
266+
267+
using (var batch = new MySqlBatch(connection)
268+
{
269+
BatchCommands =
270+
{
271+
new MySqlBatchCommand("SELECT id FROM batch_single_row ORDER BY id"),
272+
new MySqlBatchCommand("SELECT id FROM batch_single_row ORDER BY id") { CommandBehavior = CommandBehavior.SingleRow },
273+
},
274+
})
275+
using (var reader = batch.ExecuteReader())
276+
{
277+
Assert.True(reader.Read());
278+
Assert.Equal(1, reader.GetInt32(0));
279+
Assert.True(reader.Read());
280+
Assert.Equal(2, reader.GetInt32(0));
281+
Assert.True(reader.Read());
282+
Assert.Equal(3, reader.GetInt32(0));
283+
Assert.False(reader.Read());
284+
285+
Assert.True(reader.NextResult());
286+
Assert.True(reader.Read());
287+
Assert.Equal(1, reader.GetInt32(0));
288+
Assert.False(reader.Read());
289+
290+
Assert.False(reader.NextResult());
291+
}
292+
}
293+
}
294+
253295
[Fact]
254296
public void PrepareNeedsConnection()
255297
{

tests/SideBySide/QueryTests.cs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ public async Task MultipleReaders()
333333
}
334334
}
335335
}
336-
336+
337337
[Fact]
338338
public async Task UndisposedReader()
339339
{
@@ -573,7 +573,7 @@ public void DapperNullableBoolNullFirst()
573573
Assert.True(rows[1].IsBold);
574574
Assert.Null(rows[2].IsBold);
575575
}
576-
576+
577577
[SkippableFact(Baseline = "https://bugs.mysql.com/bug.php?id=78760")]
578578
public void TabsAndNewLines()
579579
{
@@ -1182,6 +1182,26 @@ public void CommandBehaviorCloseConnection()
11821182
}
11831183
}
11841184

1185+
[Fact]
1186+
public void CommandBehaviorSingleRow()
1187+
{
1188+
using (var connection = new MySqlConnection(AppConfig.ConnectionString))
1189+
{
1190+
connection.Open();
1191+
connection.Execute(@"drop table if exists command_behavior_single_row;
1192+
create table command_behavior_single_row(id integer not null primary key);
1193+
insert into command_behavior_single_row(id) values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10);");
1194+
1195+
using (var cmd = new MySqlCommand("SELECT id FROM command_behavior_single_row ORDER BY id;", connection))
1196+
using (var reader = cmd.ExecuteReader(CommandBehavior.SingleRow))
1197+
{
1198+
Assert.True(reader.Read());
1199+
Assert.Equal(1, reader.GetInt32(0));
1200+
Assert.False(reader.Read());
1201+
}
1202+
}
1203+
}
1204+
11851205
class BoolTest
11861206
{
11871207
public int Id { get; set; }

0 commit comments

Comments
 (0)