Skip to content
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
10 changes: 8 additions & 2 deletions src/SingleStoreConnector/SingleStoreCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,10 @@ private SingleStoreCommand(SingleStoreCommand other)
/// <returns>The number of rows affected.</returns>
/// <remarks>For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command.
/// For stored procedures, the return value is the number of rows affected by the last statement in the stored procedure,
/// or zero if the last statement is a SELECT. For all other types of statements, the return value is -1.</remarks>
/// or zero if the last statement is a SELECT. For all other types of statements, the return value is -1.
/// If the number of affected rows exceeds <see cref="int.MaxValue"/>, the return value is <see cref="int.MaxValue"/>;
Comment thread
francoisjehl marked this conversation as resolved.
Resolved
/// to get the exact count, use <see cref="ExecuteReader()"/>, call <see cref="SingleStoreDataReader.NextResult"/> until it returns <c>false</c>,
/// then read <see cref="SingleStoreDataReader.RealRecordsAffected"/>.</remarks>
public override int ExecuteNonQuery() => ExecuteNonQueryAsync(IOBehavior.Synchronous, CancellationToken.None).Result;

public override object? ExecuteScalar() => ExecuteScalarAsync(IOBehavior.Synchronous, CancellationToken.None).Result;
Expand Down Expand Up @@ -292,7 +295,10 @@ protected override DbDataReader ExecuteDbDataReader(CommandBehavior behavior) =>
/// <returns>A task representing the asynchronous operation.</returns>
/// <remarks>For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command.
/// For stored procedures, the return value is the number of rows affected by the last statement in the stored procedure,
/// or zero if the last statement is a SELECT. For all other types of statements, the return value is -1.</remarks>
/// or zero if the last statement is a SELECT. For all other types of statements, the return value is -1.
/// If the number of affected rows exceeds <see cref="int.MaxValue"/>, the return value is <see cref="int.MaxValue"/>;
/// to get the exact count, use <see cref="ExecuteReaderAsync(CancellationToken)"/>, call <see cref="SingleStoreDataReader.NextResultAsync(CancellationToken)"/> until it returns <c>false</c>,
/// then read <see cref="SingleStoreDataReader.RealRecordsAffected"/>.</remarks>
public override Task<int> ExecuteNonQueryAsync(CancellationToken cancellationToken) =>
ExecuteNonQueryAsync(AsyncIOBehavior, cancellationToken).AsTask();

Expand Down
12 changes: 9 additions & 3 deletions src/SingleStoreConnector/SingleStoreDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,10 @@ public override bool HasRows
/// </summary>
/// <remarks>For UPDATE, INSERT, and DELETE statements, the return value is the number of rows affected by the command.
/// For stored procedures, the return value is the number of rows affected by the last statement in the stored procedure,
/// or zero if the last statement is a SELECT. For all other types of statements, the return value is -1.</remarks>
public override int RecordsAffected => RealRecordsAffected is ulong recordsAffected ? checked((int) recordsAffected) : -1;
/// or zero if the last statement is a SELECT. For all other types of statements, the return value is -1.
/// If the number of affected rows exceeds <see cref="int.MaxValue"/>, the return value is <see cref="int.MaxValue"/>;
/// use <see cref="RealRecordsAffected"/> to get the exact count.</remarks>
public override int RecordsAffected => RealRecordsAffected is ulong recordsAffected ? (int) Math.Min(recordsAffected, int.MaxValue) : -1;
Comment thread
francoisjehl marked this conversation as resolved.
Resolved
public override int GetOrdinal(string name) => GetResultSet().GetOrdinal(name);

public override bool GetBoolean(int ordinal) => GetResultSet().GetCurrentRow().GetBoolean(ordinal);
Expand Down Expand Up @@ -463,7 +465,11 @@ protected override void Dispose(bool disposing)
internal Activity? Activity { get; private set; }
internal ISingleStoreCommand? Command { get; private set; }
internal SingleStoreConnection? Connection => Command?.Connection;
internal ulong? RealRecordsAffected { get; set; }
/// <summary>
/// Gets the number of rows changed, inserted, or deleted by execution of the SQL statement, without truncation to <see cref="int"/>.
/// </summary>
/// <remarks>Returns <c>null</c> when <see cref="RecordsAffected"/> would return -1.</remarks>
public ulong? RealRecordsAffected { get; internal set; }
internal ServerSession? Session => Command?.Connection!.Session;

internal async Task InitAsync(CommandListPosition commandListPosition, ICommandPayloadCreator payloadCreator, IDictionary<string, CachedProcedure?>? cachedProcedures, ISingleStoreCommand command, CommandBehavior behavior, Activity? activity, IOBehavior ioBehavior, CancellationToken cancellationToken)
Expand Down
12 changes: 12 additions & 0 deletions tests/SingleStoreConnector.Tests/ConnectionTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ public void Dispose()
m_server.Stop();
}

[Fact]
public void RecordsAffectedAboveInt32MaxValueIsClamped()
{
using var connection = new SingleStoreConnection(m_csb.ConnectionString);
connection.Open();
using var command = new SingleStoreCommand("ALTER TABLE big_table ADD COLUMN c INT", connection);
Assert.Equal(int.MaxValue, command.ExecuteNonQuery());
using var reader = command.ExecuteReader();
Assert.Equal(int.MaxValue, reader.RecordsAffected);
Assert.Equal(4_000_000_000UL, reader.RealRecordsAffected);
}

[Fact]
public void PooledConnectionIsReturnedToPool()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,10 @@ public async Task RunAsync(TcpClient client, CancellationToken token)
var wasSet = CancelQueryEvent.Wait(0, token);
await SendAsync(stream, 1, WriteOk);
}
else if (query == "ALTER TABLE big_table ADD COLUMN c INT")
Comment thread
francoisjehl marked this conversation as resolved.
Resolved
{
await SendAsync(stream, 1, x => WriteOk(x, 4_000_000_000UL));
}
else if (query == "select infinity")
{
var packets = new[]
Expand Down Expand Up @@ -295,10 +299,20 @@ private void WriteInitialHandshake(BinaryWriter writer)
writer.Write((byte) 0);
}

private static void WriteOk(BinaryWriter writer)
private static void WriteOk(BinaryWriter writer) => WriteOk(writer, 0);

private static void WriteOk(BinaryWriter writer, ulong affectedRows)
{
writer.Write((byte) 0); // signature
writer.Write((byte) 0); // 0 rows affected
if (affectedRows < 251)
{
writer.Write((byte) affectedRows); // rows affected
}
else
{
writer.Write((byte) 0xFE); // 8-byte length-encoded integer
writer.Write(affectedRows); // rows affected
}
writer.Write((byte) 0); // last insert ID
writer.Write((ushort) 0); // server status
writer.Write((ushort) 0); // warning count
Expand Down
Loading