diff --git a/src/FirebirdSql.Data.FirebirdClient.Tests/FbDataReaderTests.cs b/src/FirebirdSql.Data.FirebirdClient.Tests/FbDataReaderTests.cs
index c87c804b..224e24a6 100644
--- a/src/FirebirdSql.Data.FirebirdClient.Tests/FbDataReaderTests.cs
+++ b/src/FirebirdSql.Data.FirebirdClient.Tests/FbDataReaderTests.cs
@@ -535,4 +535,60 @@ public async Task GetFieldValueTest()
await transaction.RollbackAsync();
}
}
+
+ [Test]
+ public async Task RecordsAffectedIsMinusOneForSelectTest()
+ {
+ // DbDataReader.RecordsAffected: "-1 for SELECT statements; 0 if no rows were affected or the statement failed."
+ await using (var command = new FbCommand("select INT_FIELD from TEST where 1 = 0", Connection))
+ {
+ var reader = await command.ExecuteReaderAsync();
+ while (await reader.ReadAsync())
+ { }
+ await reader.CloseAsync();
+ Assert.AreEqual(-1, reader.RecordsAffected);
+ }
+ }
+
+ [Test]
+ public async Task RecordsAffectedForInsertReturningTest()
+ {
+ // INSERT ... RETURNING is classified by Firebird as a stored procedure execution, not as an INSERT.
+ await using (var transaction = await Connection.BeginTransactionAsync())
+ {
+ await using (var command = new FbCommand("insert into test (int_field) values (100001) returning int_field", Connection, transaction))
+ {
+ await using (var reader = await command.ExecuteReaderAsync())
+ {
+ while (await reader.ReadAsync())
+ { }
+ Assert.AreEqual(1, reader.RecordsAffected);
+ }
+ }
+ await transaction.RollbackAsync();
+ }
+ }
+
+ [Test]
+ public async Task ExecuteNonQueryForStatementWithoutRowChangesIsMinusOneTest()
+ {
+ // Stored procedure execution that changes no rows keeps the -1 of "all other types of statements".
+ await using (var command = new FbCommand("execute block as begin end", Connection))
+ {
+ Assert.AreEqual(-1, await command.ExecuteNonQueryAsync());
+ }
+ }
+
+ [Test]
+ public async Task ExecuteNonQueryForInsertReturningTest()
+ {
+ await using (var transaction = await Connection.BeginTransactionAsync())
+ {
+ await using (var command = new FbCommand("insert into test (int_field) values (100002) returning int_field", Connection, transaction))
+ {
+ Assert.AreEqual(1, await command.ExecuteNonQueryAsync());
+ }
+ await transaction.RollbackAsync();
+ }
+ }
}
diff --git a/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbCommand.cs b/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbCommand.cs
index 551c6ea6..7bb0c0b1 100644
--- a/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbCommand.cs
+++ b/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbCommand.cs
@@ -263,6 +263,27 @@ internal int RecordsAffected
}
}
+ ///
+ /// The rows an INSERT, UPDATE or DELETE affected, or -1 for any other statement (DbCommand.ExecuteNonQuery: "For all
+ /// other types of statements, the return value is -1"; DbDataReader.RecordsAffected: "-1 for SELECT statements").
+ /// Firebird classifies INSERT ... RETURNING and DML inside an execute block as stored procedure execution; the server's
+ /// count is reported for those only when it is positive, so procedures that change no rows keep -1.
+ ///
+ internal int StatementRecordsAffected
+ {
+ get
+ {
+ return _statement?.StatementType switch
+ {
+ DbStatementType.Insert => RecordsAffected,
+ DbStatementType.Update => RecordsAffected,
+ DbStatementType.Delete => RecordsAffected,
+ DbStatementType.StoredProcedure when RecordsAffected > 0 => RecordsAffected,
+ _ => -1,
+ };
+ }
+ }
+
internal bool IsDisposed
{
get { return _disposed; }
@@ -529,13 +550,7 @@ public override int ExecuteNonQuery()
}
}
- return _statement.StatementType switch
- {
- DbStatementType.Insert => RecordsAffected,
- DbStatementType.Update => RecordsAffected,
- DbStatementType.Delete => RecordsAffected,
- _ => -1,
- };
+ return StatementRecordsAffected;
}
public override async Task ExecuteNonQueryAsync(CancellationToken cancellationToken)
{
@@ -566,13 +581,7 @@ public override async Task ExecuteNonQueryAsync(CancellationToken cancellat
}
}
- return _statement.StatementType switch
- {
- DbStatementType.Insert => RecordsAffected,
- DbStatementType.Update => RecordsAffected,
- DbStatementType.Delete => RecordsAffected,
- _ => -1,
- };
+ return StatementRecordsAffected;
}
public new FbDataReader ExecuteReader() => ExecuteReader(CommandBehavior.Default);
diff --git a/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbDataReader.cs b/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbDataReader.cs
index 0cf1d40c..aa800870 100644
--- a/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbDataReader.cs
+++ b/src/FirebirdSql.Data.FirebirdClient/FirebirdClient/FbDataReader.cs
@@ -84,6 +84,7 @@ internal FbDataReader(FbCommand command, FbConnection connection, CommandBehavio
_connection = connection;
_commandBehavior = commandBehavior;
_fields = _command.GetFieldsDescriptor();
+ _recordsAffected = -1;
UpdateRecordsAffected();
}
@@ -1016,10 +1017,11 @@ private void UpdateRecordsAffected()
{
if (_command != null && !_command.IsDisposed)
{
- if (_command.RecordsAffected != -1)
+ var recordsAffected = _command.StatementRecordsAffected;
+ if (recordsAffected != -1)
{
_recordsAffected = _recordsAffected == -1 ? 0 : _recordsAffected;
- _recordsAffected += _command.RecordsAffected;
+ _recordsAffected += recordsAffected;
}
}
}