Skip to content

Commit e70dda2

Browse files
committed
Parse BIT columns sent as text. Fixes #708
1 parent db3af37 commit e70dda2

File tree

4 files changed

+41
-13
lines changed

4 files changed

+41
-13
lines changed

src/MySqlConnector/Core/BinaryRow.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,7 @@ protected override object GetValueCore(ReadOnlySpan<byte> data, ColumnDefinition
8787
return isUnsigned ? (object) MemoryMarshal.Read<ulong>(data) : MemoryMarshal.Read<long>(data);
8888

8989
case ColumnType.Bit:
90-
// BIT column is transmitted as MSB byte array
91-
ulong bitValue = 0;
92-
for (int i = 0; i < data.Length; i++)
93-
bitValue = bitValue * 256 + data[i];
94-
return bitValue;
90+
return ReadBit(data, columnDefinition.ColumnFlags);
9591

9692
case ColumnType.String:
9793
if (Connection.GuidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)

src/MySqlConnector/Core/Row.cs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Buffers.Text;
23
using System.IO;
34
using System.Runtime.InteropServices;
45
using MySql.Data.MySqlClient;
@@ -436,6 +437,26 @@ protected static Guid CreateGuidFromBytes(MySqlGuidFormat guidFormat, ReadOnlySp
436437
#endif
437438
}
438439

440+
protected static object ReadBit(ReadOnlySpan<byte> data, ColumnFlags columnFlags)
441+
{
442+
if ((columnFlags & ColumnFlags.Binary) == 0)
443+
{
444+
// when the Binary flag IS NOT set, the BIT column is transmitted as MSB byte array
445+
ulong bitValue = 0;
446+
for (int i = 0; i < data.Length; i++)
447+
bitValue = bitValue * 256 + data[i];
448+
return bitValue;
449+
}
450+
else
451+
{
452+
// when the Binary flag IS set, the BIT column is transmitted as text
453+
return ParseUInt64(data);
454+
}
455+
}
456+
457+
protected static ulong ParseUInt64(ReadOnlySpan<byte> data) =>
458+
!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
459+
439460
private void CheckBinaryColumn(int ordinal)
440461
{
441462
if (m_dataOffsets[ordinal] == -1)

src/MySqlConnector/Core/TextRow.cs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,7 @@ protected override object GetValueCore(ReadOnlySpan<byte> data, ColumnDefinition
5050
return isUnsigned ? (object) ParseUInt64(data) : ParseInt64(data);
5151

5252
case ColumnType.Bit:
53-
// BIT column is transmitted as MSB byte array
54-
ulong bitValue = 0;
55-
for (int i = 0; i < data.Length; i++)
56-
bitValue = bitValue * 256 + data[i];
57-
return bitValue;
53+
return ReadBit(data, columnDefinition.ColumnFlags);
5854

5955
case ColumnType.String:
6056
if (Connection.GuidFormat == MySqlGuidFormat.Char36 && columnDefinition.ColumnLength / ProtocolUtility.GetBytesPerCharacter(columnDefinition.CharacterSet) == 36)
@@ -129,9 +125,6 @@ private static uint ParseUInt32(ReadOnlySpan<byte> data) =>
129125
private static long ParseInt64(ReadOnlySpan<byte> data) =>
130126
!Utf8Parser.TryParse(data, out long value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
131127

132-
private static ulong ParseUInt64(ReadOnlySpan<byte> data) =>
133-
!Utf8Parser.TryParse(data, out ulong value, out var bytesConsumed) || bytesConsumed != data.Length ? throw new FormatException() : value;
134-
135128
private object ParseDateTime(ReadOnlySpan<byte> value)
136129
{
137130
Exception? exception = null;

tests/SideBySide/DataTypes.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1391,6 +1391,24 @@ public void QueryJson(string column, string[] expected)
13911391
DoQuery("json_core", column, dataTypeName, expected, reader => reader.GetString(0), omitWhereTest: true);
13921392
}
13931393

1394+
[SkippableTheory(Baseline = "https://bugs.mysql.com/bug.php?id=97067")]
1395+
[InlineData(false, "MIN", 0)]
1396+
[InlineData(false, "MAX", uint.MaxValue)]
1397+
[InlineData(true, "MIN", 0)]
1398+
[InlineData(true, "MAX", uint.MaxValue)]
1399+
public void QueryAggregateBit(bool shouldPrepare, string aggregation, ulong expected)
1400+
{
1401+
var csb = AppConfig.CreateConnectionStringBuilder();
1402+
csb.IgnorePrepare = !shouldPrepare;
1403+
using var connection = new MySqlConnection(csb.ConnectionString);
1404+
connection.Open();
1405+
using var command = new MySqlCommand($@"SELECT {aggregation}(Bit32) FROM datatypes_bits;", connection);
1406+
if (shouldPrepare)
1407+
command.Prepare();
1408+
var result = command.ExecuteScalar();
1409+
Assert.Equal(expected, result);
1410+
}
1411+
13941412
private static byte[] GetBytes(DbDataReader reader)
13951413
{
13961414
var size = reader.GetBytes(0, 0, null, 0, 0);

0 commit comments

Comments
 (0)