Skip to content

Commit 0eb4e95

Browse files
authored
Extend AssertType to cover sync and async read/write converter paths (#6505)
1 parent 9acf81a commit 0eb4e95

2 files changed

Lines changed: 52 additions & 7 deletions

File tree

src/Npgsql/NpgsqlDataReader.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,17 @@ public sealed class NpgsqlDataReader : DbDataReader, IDbColumnSchemaGenerator
8989
/// </summary>
9090
bool _isRowBuffered;
9191

92+
/// <summary>
93+
/// Gets or sets whether the current row is fully buffered in memory.
94+
/// When <see langword="false"/>, async reads will go through the real async converter path rather than the sync shortcut.
95+
/// </summary>
96+
/// <remarks>Settable for testing purposes.</remarks>
97+
internal bool IsRowBuffered
98+
{
99+
get => _isRowBuffered;
100+
set => _isRowBuffered = value;
101+
}
102+
92103
/// <summary>
93104
/// The RowDescription message for the current resultset being processed
94105
/// </summary>

test/Npgsql.Tests/Support/TestBase.cs

Lines changed: 41 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -339,14 +339,31 @@ static async Task AssertTypeWriteCore<T>(
339339
cmd.CommandText = "SELECT " + string.Join(", ", Enumerable.Range(1, cmd.Parameters.Count).Select(i =>
340340
"pg_typeof($1)::text, $1::text".Replace("$1", $"${i}")));
341341

342-
await using var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess);
343-
await reader.ReadAsync();
342+
// Async execution: tests async write paths in converters
343+
{
344+
await using var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess);
345+
await reader.ReadAsync();
346+
AssertWriteResults(reader);
347+
}
344348

345-
for (var i = 0; i < cmd.Parameters.Count * 2; i += 2)
349+
// Sync execution: tests sync write paths in converters.
350+
// Reset parameter values first so that one-shot values (e.g. streams) can be re-read from the start.
351+
for (var i = 0; i < cmd.Parameters.Count; i++)
352+
cmd.Parameters[i].Value = valueFactory();
346353
{
347-
var error = errorIdentifier[i / 2];
348-
Assert.That(reader[i], Is.EqualTo(dataTypeNameWithoutFacets), $"Got wrong data type name when writing with {error}");
349-
Assert.That(reader[i+1], Is.EqualTo(sqlLiteral), $"Got wrong SQL literal when writing with {error}");
354+
using var reader = cmd.ExecuteReader(CommandBehavior.SequentialAccess);
355+
reader.Read();
356+
AssertWriteResults(reader);
357+
}
358+
359+
void AssertWriteResults(NpgsqlDataReader reader)
360+
{
361+
for (var i = 0; i < cmd.Parameters.Count * 2; i += 2)
362+
{
363+
var error = errorIdentifier[i / 2];
364+
Assert.That(reader[i], Is.EqualTo(dataTypeNameWithoutFacets), $"Got wrong data type name when writing with {error}");
365+
Assert.That(reader[i + 1], Is.EqualTo(sqlLiteral), $"Got wrong SQL literal when writing with {error}");
366+
}
350367
}
351368

352369
void DataTypeAsserts()
@@ -465,6 +482,8 @@ static async Task<T> AssertTypeReadCore<T>(
465482
sqlLiteral = sqlLiteral.Replace("'", "''");
466483

467484
await using var cmd = new NpgsqlCommand($"SELECT '{sqlLiteral}'::{dataTypeName}", connection);
485+
486+
// Async execution: tests async and sync column reads within a single buffered query.
468487
await using var reader = await cmd.ExecuteReaderAsync();
469488
await reader.ReadAsync();
470489

@@ -491,6 +510,15 @@ static async Task<T> AssertTypeReadCore<T>(
491510
T actual;
492511
if (valueTypeEqualsFieldType)
493512
{
513+
// Set IsRowBuffered=false before the first read so _column is still -1, ensuring GetFieldValueAsync
514+
// goes through the real async converter path (converter.ReadAsObjectAsync) rather than the sync shortcut.
515+
reader.IsRowBuffered = false;
516+
actual = (T)await reader.GetFieldValueAsync<object>(0);
517+
Assert.That(actual, comparer is null ? Is.EqualTo(value) : Is.EqualTo(value).Using<T>(CreateEqualityComparer(comparer)),
518+
$"Got wrong result from GetFieldValueAsync<object>() value when reading '{truncatedSqlLiteral}'");
519+
520+
// Restore IsRowBuffered so subsequent sync reads use the normal buffered code path.
521+
reader.IsRowBuffered = true;
494522
actual = (T)reader.GetValue(0);
495523
Assert.That(actual, comparer is null ? Is.EqualTo(value) : Is.EqualTo(value).Using<T>(CreateEqualityComparer(comparer!)),
496524
$"Got wrong result from GetValue() value when reading '{truncatedSqlLiteral}'");
@@ -502,8 +530,14 @@ static async Task<T> AssertTypeReadCore<T>(
502530
return actual;
503531
}
504532

505-
actual = reader.GetFieldValue<T>(0);
533+
// See comment above about IsRowBuffered.
534+
reader.IsRowBuffered = false;
535+
actual = await reader.GetFieldValueAsync<T>(0);
536+
Assert.That(actual, comparer is null ? Is.EqualTo(value) : Is.EqualTo(value).Using<T>(CreateEqualityComparer(comparer!)),
537+
$"Got wrong result from GetFieldValueAsync<T>() value when reading '{truncatedSqlLiteral}'");
506538

539+
reader.IsRowBuffered = true;
540+
actual = reader.GetFieldValue<T>(0);
507541
Assert.That(actual, comparer is null ? Is.EqualTo(value) : Is.EqualTo(value).Using<T>(CreateEqualityComparer(comparer!)),
508542
$"Got wrong result from GetFieldValue<T>() value when reading '{truncatedSqlLiteral}'");
509543

0 commit comments

Comments
 (0)