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
12 changes: 10 additions & 2 deletions DuckDB.NET.Data/DataChunk/Reader/StringVectorDataReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ protected override T GetValidValue<T>(ulong offset)
return DuckDBType switch
{
DuckDBType.Bit => GetBitString<T>(offset),
DuckDBType.Blob => (T)(object)GetStream(offset),
DuckDBType.Blob => typeof(T) == typeof(byte[])
? (T)(object)GetBytes(offset)
: (T)(object)GetStream(offset),
DuckDBType.Varchar => (T)(object)GetString(offset),
_ => base.GetValidValue<T>(offset)
};
Expand All @@ -31,7 +33,7 @@ internal override object GetValue(ulong offset, Type targetType)
return DuckDBType switch
{
DuckDBType.Bit => GetBitString(offset),
DuckDBType.Blob => GetStream(offset),
DuckDBType.Blob => targetType == typeof(byte[]) ? GetBytes(offset) : GetStream(offset),
DuckDBType.Varchar => GetString(offset),
_ => base.GetValue(offset, targetType)
};
Expand Down Expand Up @@ -219,4 +221,10 @@ private unsafe Stream GetStream(ulong offset)

return new UnmanagedMemoryStream((byte*)data->Data, data->Length, data->Length, FileAccess.Read);
}

private unsafe byte[] GetBytes(ulong offset)
{
var data = (DuckDBString*)DataPointer + offset;
return new ReadOnlySpan<byte>((void*)data->Data, checked((int)data->Length)).ToArray();
}
}
63 changes: 62 additions & 1 deletion DuckDB.NET.Test/Parameters/BlobParameterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,62 @@ public void SimpleTest()
reader.GetFieldType(0).Should().Be(typeof(Stream));
}

[Fact]
public void GetFieldValueByteArrayReadsBlob()
{
Command.CommandText = "SELECT '\\x01\\x02\\x03\\x04'::BLOB;";

using var reader = Command.ExecuteReader();
reader.Read().Should().BeTrue();

reader.GetFieldValue<byte[]>(0).Should().Equal(1, 2, 3, 4);
reader.GetValue(0).Should().BeOfType<UnmanagedMemoryStream>();
}

[Fact]
public void GetFieldValueByteArrayReadsEmptyBlob()
{
Command.CommandText = "SELECT ''::BLOB;";

using var reader = Command.ExecuteReader();
reader.Read().Should().BeTrue();

reader.GetFieldValue<byte[]>(0).Should().BeEmpty();
}

[Fact]
public void GetFieldValueByteArrayReadsBlobInAStruct()
{
Command.CommandText = "SELECT {'value': '\\x01\\x02\\x03'::BLOB};";

using var reader = Command.ExecuteReader();
reader.Read().Should().BeTrue();

reader.GetFieldValue<BlobStruct>(0).Value.Should().Equal(1, 2, 3);
}

[Fact]
public void GetFieldValueByteArrayReadsBlobInAMap()
{
Command.CommandText = "SELECT MAP {'value': '\\x04\\x05'::BLOB};";

using var reader = Command.ExecuteReader();
reader.Read().Should().BeTrue();

reader.GetFieldValue<Dictionary<string, byte[]>>(0)["value"].Should().Equal(4, 5);
}

[Fact]
public void GetFieldValueByteArrayReadsBlobInAList()
{
Command.CommandText = "SELECT ['\\x06\\x07'::BLOB, '\\x08'::BLOB];";

using var reader = Command.ExecuteReader();
reader.Read().Should().BeTrue();

reader.GetFieldValue<List<byte[]>>(0).Should().BeEquivalentTo(new[] { new byte[] { 6, 7 }, new byte[] { 8 } });
}

[Fact]
public void SeekTest()
{
Expand Down Expand Up @@ -140,4 +196,9 @@ public void BindParameterWithoutTable()

resultBytes.Should().BeEquivalentTo(value);
}
}

private sealed class BlobStruct
{
public byte[] Value { get; set; } = [];
}
}
Loading