feat(DbExporter): add counting data reader for accurate row count
Wrap IDataReader with CountingDataReader to track rows as they're read during protobuf serialization, fixing the export returning 0 rows.
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
using System.Data;
|
||||||
|
|
||||||
|
namespace DbExporter;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wraps an IDataReader to count rows as they're read.
|
||||||
|
/// </summary>
|
||||||
|
internal sealed class CountingDataReader : IDataReader
|
||||||
|
{
|
||||||
|
private readonly IDataReader _inner;
|
||||||
|
private int _rowCount;
|
||||||
|
|
||||||
|
public CountingDataReader(IDataReader inner)
|
||||||
|
{
|
||||||
|
_inner = inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int RowCount => _rowCount;
|
||||||
|
|
||||||
|
public bool Read()
|
||||||
|
{
|
||||||
|
var result = _inner.Read();
|
||||||
|
if (result) _rowCount++;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate all other members to inner reader
|
||||||
|
public object this[int i] => _inner[i];
|
||||||
|
public object this[string name] => _inner[name];
|
||||||
|
public int Depth => _inner.Depth;
|
||||||
|
public bool IsClosed => _inner.IsClosed;
|
||||||
|
public int RecordsAffected => _inner.RecordsAffected;
|
||||||
|
public int FieldCount => _inner.FieldCount;
|
||||||
|
public void Close() => _inner.Close();
|
||||||
|
public void Dispose() => _inner.Dispose();
|
||||||
|
public bool GetBoolean(int i) => _inner.GetBoolean(i);
|
||||||
|
public byte GetByte(int i) => _inner.GetByte(i);
|
||||||
|
public long GetBytes(int i, long fieldOffset, byte[]? buffer, int bufferoffset, int length) => _inner.GetBytes(i, fieldOffset, buffer, bufferoffset, length);
|
||||||
|
public char GetChar(int i) => _inner.GetChar(i);
|
||||||
|
public long GetChars(int i, long fieldoffset, char[]? buffer, int bufferoffset, int length) => _inner.GetChars(i, fieldoffset, buffer, bufferoffset, length);
|
||||||
|
public IDataReader GetData(int i) => _inner.GetData(i);
|
||||||
|
public string GetDataTypeName(int i) => _inner.GetDataTypeName(i);
|
||||||
|
public DateTime GetDateTime(int i) => _inner.GetDateTime(i);
|
||||||
|
public decimal GetDecimal(int i) => _inner.GetDecimal(i);
|
||||||
|
public double GetDouble(int i) => _inner.GetDouble(i);
|
||||||
|
public Type GetFieldType(int i) => _inner.GetFieldType(i);
|
||||||
|
public float GetFloat(int i) => _inner.GetFloat(i);
|
||||||
|
public Guid GetGuid(int i) => _inner.GetGuid(i);
|
||||||
|
public short GetInt16(int i) => _inner.GetInt16(i);
|
||||||
|
public int GetInt32(int i) => _inner.GetInt32(i);
|
||||||
|
public long GetInt64(int i) => _inner.GetInt64(i);
|
||||||
|
public string GetName(int i) => _inner.GetName(i);
|
||||||
|
public int GetOrdinal(string name) => _inner.GetOrdinal(name);
|
||||||
|
public DataTable GetSchemaTable() => _inner.GetSchemaTable()!;
|
||||||
|
public string GetString(int i) => _inner.GetString(i);
|
||||||
|
public object GetValue(int i) => _inner.GetValue(i);
|
||||||
|
public int GetValues(object[] values) => _inner.GetValues(values);
|
||||||
|
public bool IsDBNull(int i) => _inner.IsDBNull(i);
|
||||||
|
public bool NextResult() => _inner.NextResult();
|
||||||
|
}
|
||||||
@@ -26,7 +26,8 @@ public sealed class DatabaseExporter
|
|||||||
command.CommandText = definition.Query;
|
command.CommandText = definition.Query;
|
||||||
command.CommandTimeout = 0; // No timeout for large exports
|
command.CommandTimeout = 0; // No timeout for large exports
|
||||||
|
|
||||||
await using var reader = await command.ExecuteReaderAsync(cancellationToken);
|
await using var baseReader = await command.ExecuteReaderAsync(cancellationToken);
|
||||||
|
var reader = new CountingDataReader(baseReader);
|
||||||
|
|
||||||
long uncompressedSize = 0;
|
long uncompressedSize = 0;
|
||||||
|
|
||||||
@@ -51,9 +52,7 @@ public sealed class DatabaseExporter
|
|||||||
|
|
||||||
var compressedSize = new FileInfo(definition.OutputPath).Length;
|
var compressedSize = new FileInfo(definition.OutputPath).Length;
|
||||||
|
|
||||||
// Row count requires a separate pass or we estimate from verify
|
return new ExportResult(reader.RowCount, uncompressedSize, compressedSize, hash);
|
||||||
// Return 0 for now, verify will get accurate count
|
|
||||||
return new ExportResult(0, uncompressedSize, compressedSize, hash);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static DbConnection CreateConnection(string providerType, string connectionString)
|
private static DbConnection CreateConnection(string providerType, string connectionString)
|
||||||
|
|||||||
Reference in New Issue
Block a user