diff --git a/NEW/src/JdeScoping.DataSync/Etl/Transformers/DataTransformerBase.cs b/NEW/src/JdeScoping.DataSync/Etl/Transformers/DataTransformerBase.cs
index f3819f7..a105846 100644
--- a/NEW/src/JdeScoping.DataSync/Etl/Transformers/DataTransformerBase.cs
+++ b/NEW/src/JdeScoping.DataSync/Etl/Transformers/DataTransformerBase.cs
@@ -71,4 +71,58 @@ public abstract class DataTransformerBase : IDataTransformer
/// The source data reader.
/// The corresponding source ordinal, or -1 for computed columns.
public virtual int MapOrdinal(int transformedOrdinal, IDataReader source) => transformedOrdinal;
+
+ ///
+ /// Gets bytes from a field at the specified ordinal.
+ /// Throws NotSupportedException for computed columns (where MapOrdinal returns -1).
+ ///
+ public virtual long GetBytes(int ordinal, long fieldOffset, byte[]? buffer,
+ int bufferOffset, int length, IDataReader source)
+ {
+ var sourceOrdinal = MapOrdinal(ordinal, source);
+ if (sourceOrdinal < 0)
+ throw new NotSupportedException(
+ $"GetBytes not supported for computed column at ordinal {ordinal}.");
+ return source.GetBytes(sourceOrdinal, fieldOffset, buffer, bufferOffset, length);
+ }
+
+ ///
+ /// Gets characters from a field at the specified ordinal.
+ /// Throws NotSupportedException for computed columns (where MapOrdinal returns -1).
+ ///
+ public virtual long GetChars(int ordinal, long fieldOffset, char[]? buffer,
+ int bufferOffset, int length, IDataReader source)
+ {
+ var sourceOrdinal = MapOrdinal(ordinal, source);
+ if (sourceOrdinal < 0)
+ throw new NotSupportedException(
+ $"GetChars not supported for computed column at ordinal {ordinal}.");
+ return source.GetChars(sourceOrdinal, fieldOffset, buffer, bufferOffset, length);
+ }
+
+ ///
+ /// Gets nested data reader for a field at the specified ordinal.
+ /// Throws NotSupportedException for computed columns (where MapOrdinal returns -1).
+ ///
+ public virtual IDataReader GetData(int ordinal, IDataReader source)
+ {
+ var sourceOrdinal = MapOrdinal(ordinal, source);
+ if (sourceOrdinal < 0)
+ throw new NotSupportedException(
+ $"GetData not supported for computed column at ordinal {ordinal}.");
+ return source.GetData(sourceOrdinal);
+ }
+
+ ///
+ /// Gets the data type name for a field at the specified ordinal.
+ /// Throws NotSupportedException for computed columns (where MapOrdinal returns -1).
+ ///
+ public virtual string GetDataTypeName(int ordinal, IDataReader source)
+ {
+ var sourceOrdinal = MapOrdinal(ordinal, source);
+ if (sourceOrdinal < 0)
+ throw new NotSupportedException(
+ $"GetDataTypeName not supported for computed column at ordinal {ordinal}.");
+ return source.GetDataTypeName(sourceOrdinal);
+ }
}
diff --git a/NEW/src/JdeScoping.DataSync/Etl/Transformers/TransformingDataReader.cs b/NEW/src/JdeScoping.DataSync/Etl/Transformers/TransformingDataReader.cs
index 04770fd..c400bc9 100644
--- a/NEW/src/JdeScoping.DataSync/Etl/Transformers/TransformingDataReader.cs
+++ b/NEW/src/JdeScoping.DataSync/Etl/Transformers/TransformingDataReader.cs
@@ -50,8 +50,8 @@ internal sealed class TransformingDataReader : IDataReader
public long GetInt64(int i) => (long)GetValue(i);
public string GetString(int i) => (string)GetValue(i);
- // Schema and bulk data access - delegated to source
- public string GetDataTypeName(int i) => _source.GetDataTypeName(i);
+ // Schema and bulk data access - delegated to transformer
+ public string GetDataTypeName(int i) => _transformer.GetDataTypeName(i, _source);
public int GetValues(object[] values)
{
@@ -62,12 +62,12 @@ internal sealed class TransformingDataReader : IDataReader
}
public long GetBytes(int i, long fieldOffset, byte[]? buffer, int bufferoffset, int length)
- => _source.GetBytes(i, fieldOffset, buffer, bufferoffset, length);
+ => _transformer.GetBytes(i, fieldOffset, buffer, bufferoffset, length, _source);
public long GetChars(int i, long fieldoffset, char[]? buffer, int bufferoffset, int length)
- => _source.GetChars(i, fieldoffset, buffer, bufferoffset, length);
+ => _transformer.GetChars(i, fieldoffset, buffer, bufferoffset, length, _source);
- public IDataReader GetData(int i) => _source.GetData(i);
+ public IDataReader GetData(int i) => _transformer.GetData(i, _source);
public DataTable? GetSchemaTable() => _source.GetSchemaTable();
}
diff --git a/NEW/tests/JdeScoping.DataSync.Tests/Etl/Transformers/TransformingDataReaderTests.cs b/NEW/tests/JdeScoping.DataSync.Tests/Etl/Transformers/TransformingDataReaderTests.cs
index 4b9ad52..67dac3c 100644
--- a/NEW/tests/JdeScoping.DataSync.Tests/Etl/Transformers/TransformingDataReaderTests.cs
+++ b/NEW/tests/JdeScoping.DataSync.Tests/Etl/Transformers/TransformingDataReaderTests.cs
@@ -225,8 +225,162 @@ public class TransformingDataReaderTests
Assert.Equal("PassThrough", transformer.TransformerName);
}
+ [Fact]
+ public void GetBytes_ComputedColumn_ThrowsNotSupportedException()
+ {
+ // Arrange - transformer that returns -1 for ordinal 0 (computed)
+ var source = Substitute.For();
+ source.FieldCount.Returns(2);
+ source.GetName(0).Returns("Computed");
+ source.GetName(1).Returns("B");
+ var transformer = new ComputedColumnTransformer();
+ var reader = transformer.Transform(source);
+
+ // Act & Assert
+ Assert.Throws(() =>
+ reader.GetBytes(0, 0, null, 0, 0));
+ }
+
+ [Fact]
+ public void GetChars_ComputedColumn_ThrowsNotSupportedException()
+ {
+ // Arrange - transformer that returns -1 for ordinal 0 (computed)
+ var source = Substitute.For();
+ source.FieldCount.Returns(2);
+ source.GetName(0).Returns("Computed");
+ source.GetName(1).Returns("B");
+ var transformer = new ComputedColumnTransformer();
+ var reader = transformer.Transform(source);
+
+ // Act & Assert
+ Assert.Throws(() =>
+ reader.GetChars(0, 0, null, 0, 0));
+ }
+
+ [Fact]
+ public void GetData_ComputedColumn_ThrowsNotSupportedException()
+ {
+ // Arrange - transformer that returns -1 for ordinal 0 (computed)
+ var source = Substitute.For();
+ source.FieldCount.Returns(2);
+ source.GetName(0).Returns("Computed");
+ source.GetName(1).Returns("B");
+ var transformer = new ComputedColumnTransformer();
+ var reader = transformer.Transform(source);
+
+ // Act & Assert
+ Assert.Throws(() =>
+ reader.GetData(0));
+ }
+
+ [Fact]
+ public void GetDataTypeName_ComputedColumn_ThrowsNotSupportedException()
+ {
+ // Arrange - transformer that returns -1 for ordinal 0 (computed)
+ var source = Substitute.For();
+ source.FieldCount.Returns(2);
+ source.GetName(0).Returns("Computed");
+ source.GetName(1).Returns("B");
+ var transformer = new ComputedColumnTransformer();
+ var reader = transformer.Transform(source);
+
+ // Act & Assert
+ Assert.Throws(() =>
+ reader.GetDataTypeName(0));
+ }
+
+ [Fact]
+ public void GetBytes_NonComputedColumn_DelegatesToSource()
+ {
+ // Arrange
+ var source = Substitute.For();
+ source.FieldCount.Returns(2);
+ source.GetName(0).Returns("A");
+ source.GetName(1).Returns("B");
+ var expectedBytes = new byte[] { 1, 2, 3 };
+ source.GetBytes(1, 0, Arg.Any(), 0, 3).Returns(3L);
+ var transformer = new PassThroughTransformer();
+ var reader = transformer.Transform(source);
+
+ // Act
+ var buffer = new byte[3];
+ var result = reader.GetBytes(1, 0, buffer, 0, 3);
+
+ // Assert
+ Assert.Equal(3L, result);
+ source.Received(1).GetBytes(1, 0, buffer, 0, 3);
+ }
+
+ [Fact]
+ public void GetChars_NonComputedColumn_DelegatesToSource()
+ {
+ // Arrange
+ var source = Substitute.For();
+ source.FieldCount.Returns(2);
+ source.GetName(0).Returns("A");
+ source.GetName(1).Returns("B");
+ source.GetChars(1, 0, Arg.Any(), 0, 3).Returns(3L);
+ var transformer = new PassThroughTransformer();
+ var reader = transformer.Transform(source);
+
+ // Act
+ var buffer = new char[3];
+ var result = reader.GetChars(1, 0, buffer, 0, 3);
+
+ // Assert
+ Assert.Equal(3L, result);
+ source.Received(1).GetChars(1, 0, buffer, 0, 3);
+ }
+
+ [Fact]
+ public void GetData_NonComputedColumn_DelegatesToSource()
+ {
+ // Arrange
+ var source = Substitute.For();
+ source.FieldCount.Returns(2);
+ source.GetName(0).Returns("A");
+ source.GetName(1).Returns("B");
+ var nestedReader = Substitute.For();
+ source.GetData(1).Returns(nestedReader);
+ var transformer = new PassThroughTransformer();
+ var reader = transformer.Transform(source);
+
+ // Act
+ var result = reader.GetData(1);
+
+ // Assert
+ Assert.Same(nestedReader, result);
+ source.Received(1).GetData(1);
+ }
+
+ [Fact]
+ public void GetDataTypeName_NonComputedColumn_DelegatesToSource()
+ {
+ // Arrange
+ var source = Substitute.For();
+ source.FieldCount.Returns(2);
+ source.GetName(0).Returns("A");
+ source.GetName(1).Returns("B");
+ source.GetDataTypeName(1).Returns("nvarchar");
+ var transformer = new PassThroughTransformer();
+ var reader = transformer.Transform(source);
+
+ // Act
+ var result = reader.GetDataTypeName(1);
+
+ // Assert
+ Assert.Equal("nvarchar", result);
+ source.Received(1).GetDataTypeName(1);
+ }
+
private class PassThroughTransformer : DataTransformerBase
{
public override string TransformerName => "PassThrough";
}
+
+ private class ComputedColumnTransformer : DataTransformerBase
+ {
+ public override string TransformerName => "Computed";
+ public override int MapOrdinal(int ordinal, IDataReader source) => ordinal == 0 ? -1 : ordinal;
+ }
}