Skip to content

Commit 4cbcfc7

Browse files
authored
Support VECTOR data type (#1551). Fixes #1549
1 parent 8f44eba commit 4cbcfc7

24 files changed

+338
-24
lines changed

.ci/config/config.compression+ssl.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
55
"PasswordlessUser": "no_password",
66
"SecondaryDatabase": "testdb2",
7-
"UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin",
7+
"UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin,Vector",
88
"MySqlBulkLoaderLocalCsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
99
"MySqlBulkLoaderLocalTsvFile": "../../../TestData/LoadData_UTF8_BOM_Unix.TSV",
1010
"CertificatesPath": "../../../../.ci/server/certs"

.ci/config/config.compression.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
55
"PasswordlessUser": "no_password",
66
"SecondaryDatabase": "testdb2",
7-
"UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
7+
"UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,Vector,ZeroDateTime",
88
"MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
99
"MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
1010
}

.ci/config/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
55
"PasswordlessUser": "no_password",
66
"SecondaryDatabase": "testdb2",
7-
"UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
7+
"UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,Vector,ZeroDateTime",
88
"MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
99
"MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
1010
}

.ci/config/config.ssl.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
"SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
55
"PasswordlessUser": "no_password",
66
"SecondaryDatabase": "testdb2",
7-
"UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin",
7+
"UnsupportedFeatures": "CachingSha2Password,Redirection,RsaEncryption,Tls12,Tls13,TlsFingerprintValidation,UuidToBin,Vector",
88
"MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
99
"MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV",
1010
"CertificatesPath": "../../../../.ci/server/certs"

.ci/docker-run.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ MYSQL=mysql
2828
if [[ "$IMAGE" == mariadb* ]]; then
2929
MYSQL_EXTRA='--in-predicate-conversion-threshold=100000 --plugin-maturity=beta'
3030
fi
31-
if [ "$IMAGE" == "mariadb:11.4" ] || [ "$IMAGE" == "mariadb:11.6" ]; then
31+
if [ "$IMAGE" == "mariadb:11.4" ] || [ "$IMAGE" == "mariadb:11.7" ]; then
3232
MYSQL='mariadb'
3333
fi
3434

azure-pipelines.yml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151
arguments: 'tests\IntegrationTests\IntegrationTests.csproj -c MySqlData'
5252
testRunTitle: 'MySql.Data integration tests'
5353
env:
54-
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,StreamingResults,TlsFingerprintValidation,UnixDomainSocket'
54+
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,StreamingResults,TlsFingerprintValidation,UnixDomainSocket,Vector'
5555
DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=root;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600'
5656
DATA__CERTIFICATESPATH: '$(Build.Repository.LocalPath)\.ci\server\certs\'
5757
DATA__MYSQLBULKLOADERLOCALCSVFILE: '$(Build.Repository.LocalPath)\tests\TestData\LoadData_UTF8_BOM_Unix.CSV'
@@ -120,7 +120,7 @@ jobs:
120120
arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
121121
testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net481/net9.0', 'No SSL') }}
122122
env:
123-
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
123+
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,Vector'
124124
DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True'
125125

126126
- job: windows_integration_tests_2
@@ -158,7 +158,7 @@ jobs:
158158
arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
159159
testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net8.0', 'No SSL') }}
160160
env:
161-
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
161+
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,Vector'
162162
DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'
163163

164164
- job: linux_integration_tests
@@ -171,31 +171,31 @@ jobs:
171171
'MySQL 8.0':
172172
image: 'mysql:8.0'
173173
connectionStringExtra: 'AllowPublicKeyRetrieval=True'
174-
unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
174+
unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,Vector,ZeroDateTime'
175175
'MySQL 8.4':
176176
image: 'mysql:8.4'
177177
connectionStringExtra: 'AllowPublicKeyRetrieval=True'
178-
unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
178+
unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,Vector,ZeroDateTime'
179179
'MySQL 9.3':
180180
image: 'mysql:9.3'
181181
connectionStringExtra: 'AllowPublicKeyRetrieval=True'
182-
unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
182+
unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,Vector,ZeroDateTime'
183183
'MariaDB 10.6':
184184
image: 'mariadb:10.6'
185185
connectionStringExtra: ''
186-
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
186+
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin,Vector'
187187
'MariaDB 10.11':
188188
image: 'mariadb:10.11'
189189
connectionStringExtra: ''
190-
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
190+
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin,Vector'
191191
'MariaDB 11.4':
192192
image: 'mariadb:11.4'
193193
connectionStringExtra: ''
194-
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Sha256Password,Tls11,UuidToBin,Redirection'
195-
'MariaDB 11.6':
196-
image: 'mariadb:11.6'
194+
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,UuidToBin,Vector'
195+
'MariaDB 11.7':
196+
image: 'mariadb:11.7'
197197
connectionStringExtra: ''
198-
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,UuidToBin'
198+
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,UuidToBin,VectorType'
199199
steps:
200200
- template: '.ci/integration-tests-steps.yml'
201201
parameters:

docs/content/home.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ Server | Versions | Notes
6464
Amazon Aurora RDS | 2.x, 3.x | Use `Pipelining=False` [for Aurora 2.x](https://mysqlconnector.net/troubleshooting/aurora-freeze/)
6565
Azure Database for MySQL | 5.7, 8.0 | Single Server and Flexible Server
6666
Google Cloud SQL for MySQL | 5.6, 5.7, 8.0 |
67-
MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**, **11.6**) |
67+
MariaDB | 10.x (**10.6**, **10.11**), 11.x (**11.4**, **11.7**) |
6868
MySQL | 5.5, 5.6, 5.7, 8.x (**8.0**, **8.4**), 9.x (**9.3**) | 5.5 is EOL and has some [compatibility issues](https://github.com/mysql-net/MySqlConnector/issues/1192); 5.6 and 5.7 are EOL
6969
Percona Server | 5.6, 5.7, 8.0 |
7070
PlanetScale | | See PlanetScale [MySQL compatibility notes](https://planetscale.com/docs/reference/mysql-compatibility)

docs/content/troubleshooting/parameter-types.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,6 @@ In some cases, this may be as simple as calling `.ToString()` or `.ToString(Cult
3939
* .NET primitives: `bool`, `byte`, `char`, `double`, `float`, `int`, `long`, `sbyte`, `short`, `uint`, `ulong`, `ushort`
4040
* Common types: `BigInteger`, `DateOnly`, `DateTime`, `DateTimeOffset`, `decimal`, `enum`, `Guid`, `string`, `TimeOnly`, `TimeSpan`
4141
* BLOB types: `ArraySegment<byte>`, `byte[]`, `Memory<byte>`, `ReadOnlyMemory<byte>`
42+
* Vector types: `float[]`, `Memory<float>`, `ReadOnlyMemory<float>`
4243
* String types: `Memory<char>`, `ReadOnlyMemory<char>`, `StringBuilder`
4344
* Custom MySQL types: `MySqlDateTime`, `MySqlDecimal`, `MySqlGeometry`

src/MySqlConnector/ColumnReaders/ColumnReader.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,9 @@ public static ColumnReader Create(bool isBinary, ColumnDefinitionPayload columnD
113113
case ColumnType.Null:
114114
return NullColumnReader.Instance;
115115

116+
case ColumnType.Vector:
117+
return VectorColumnReader.Instance;
118+
116119
default:
117120
throw new NotImplementedException($"Reading {columnDefinition.ColumnType} not implemented");
118121
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
using System.Buffers.Binary;
2+
using System.Runtime.InteropServices;
3+
using MySqlConnector.Protocol.Payloads;
4+
5+
namespace MySqlConnector.ColumnReaders;
6+
7+
internal sealed class VectorColumnReader : ColumnReader
8+
{
9+
public static VectorColumnReader Instance { get; } = new();
10+
11+
public override object ReadValue(ReadOnlySpan<byte> data, ColumnDefinitionPayload columnDefinition)
12+
{
13+
if (BitConverter.IsLittleEndian)
14+
{
15+
return new ReadOnlyMemory<float>(MemoryMarshal.Cast<byte, float>(data).ToArray());
16+
}
17+
else
18+
{
19+
var floats = new float[data.Length / 4];
20+
21+
#if !NET5_0_OR_GREATER
22+
var bytes = data.ToArray();
23+
#endif
24+
for (var i = 0; i < floats.Length; i++)
25+
{
26+
#if NET5_0_OR_GREATER
27+
floats[i] = BinaryPrimitives.ReadSingleLittleEndian(data.Slice(i * 4));
28+
#else
29+
Array.Reverse(bytes, i * 4, 4);
30+
floats[i] = BitConverter.ToSingle(bytes, i * 4);
31+
#endif
32+
}
33+
34+
return new ReadOnlyMemory<float>(floats);
35+
}
36+
}
37+
}

src/MySqlConnector/Core/Row.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,7 @@ private void CheckBinaryColumn(int ordinal)
455455
if ((column.ColumnFlags & ColumnFlags.Binary) == 0 ||
456456
(columnType != ColumnType.String && columnType != ColumnType.VarString && columnType != ColumnType.TinyBlob &&
457457
columnType != ColumnType.Blob && columnType != ColumnType.MediumBlob && columnType != ColumnType.LongBlob &&
458-
columnType != ColumnType.Geometry))
458+
columnType != ColumnType.Geometry && columnType != ColumnType.Vector))
459459
{
460460
throw new InvalidCastException($"Can't convert {columnType} to bytes.");
461461
}

src/MySqlConnector/Core/SingleCommandPayloadCreator.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ private static void WriteBinaryParameters(ByteBufferWriter writer, MySqlParamete
165165
mySqlDbType = TypeMapper.Instance.GetMySqlDbTypeForDbType(dbType);
166166
}
167167

168+
// HACK: MariaDB doesn't have a dedicated Vector type so mark it as binary data
169+
if (mySqlDbType == MySqlDbType.Vector && command.Connection!.Session.ServerVersion.IsMariaDb)
170+
mySqlDbType = MySqlDbType.LongBlob;
171+
168172
writer.Write(TypeMapper.ConvertToColumnTypeAndFlags(mySqlDbType, command.Connection!.GuidFormat));
169173

170174
if (supportsQueryAttributes)

src/MySqlConnector/Core/TypeMapper.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,10 @@ private TypeMapper()
5555
AddColumnTypeMetadata(new("DOUBLE", typeDouble, MySqlDbType.Double));
5656
AddColumnTypeMetadata(new("FLOAT", typeFloat, MySqlDbType.Float));
5757

58+
// vector
59+
var typeFloatReadOnlyMemory = AddDbTypeMapping(new(typeof(ReadOnlyMemory<float>), [DbType.Object]));
60+
AddColumnTypeMetadata(new("VECTOR", typeFloatReadOnlyMemory, MySqlDbType.Vector, binary: true, simpleDataTypeName: "VECTOR", createFormat: "VECTOR({0})"));
61+
5862
// string
5963
var typeFixedString = AddDbTypeMapping(new(typeof(string), [DbType.StringFixedLength, DbType.AnsiStringFixedLength], convert: Convert.ToString!));
6064
var typeString = AddDbTypeMapping(new(typeof(string), [DbType.String, DbType.AnsiString, DbType.Xml], convert: Convert.ToString!));
@@ -311,6 +315,9 @@ public static MySqlDbType ConvertToMySqlDbType(ColumnDefinitionPayload columnDef
311315
case ColumnType.Set:
312316
return MySqlDbType.Set;
313317

318+
case ColumnType.Vector:
319+
return MySqlDbType.Vector;
320+
314321
default:
315322
throw new NotImplementedException($"ConvertToMySqlDbType for {columnDefinition.ColumnType} is not implemented");
316323
}
@@ -347,6 +354,7 @@ public static ushort ConvertToColumnTypeAndFlags(MySqlDbType dbType, MySqlGuidFo
347354
MySqlDbType.NewDecimal => ColumnType.NewDecimal,
348355
MySqlDbType.Geometry => ColumnType.Geometry,
349356
MySqlDbType.Null => ColumnType.Null,
357+
MySqlDbType.Vector => ColumnType.Vector,
350358
_ => throw new NotImplementedException($"ConvertToColumnTypeAndFlags for {dbType} is not implemented"),
351359
};
352360
return (ushort) ((byte) columnType | (isUnsigned ? 0x8000 : 0));

src/MySqlConnector/MySqlDataReader.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -671,7 +671,7 @@ private static async Task ReadOutParametersAsync(IMySqlCommand command, ResultSe
671671
if (param.HasSetDbType && !row.IsDBNull(columnIndex))
672672
{
673673
var dbTypeMapping = TypeMapper.Instance.GetDbTypeMapping(param.DbType);
674-
if (dbTypeMapping is not null)
674+
if (dbTypeMapping is not null && param.DbType is not DbType.Object)
675675
{
676676
param.Value = dbTypeMapping.DoConversion(row.GetValue(columnIndex));
677677
continue;

src/MySqlConnector/MySqlDbColumn.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ internal MySqlDbColumn(int ordinal, ColumnDefinitionPayload column, bool allowZe
1515
var type = columnTypeMetadata.DbTypeMapping.ClrType;
1616
var columnSize = type == typeof(string) || type == typeof(Guid) ?
1717
column.ColumnLength / ProtocolUtility.GetBytesPerCharacter(column.CharacterSet) :
18+
column.ColumnType == ColumnType.Vector ? column.ColumnLength / 4 :
1819
column.ColumnLength;
1920

2021
AllowDBNull = (column.ColumnFlags & ColumnFlags.NotNull) == 0;

src/MySqlConnector/MySqlDbType.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public enum MySqlDbType
3737
VarChar,
3838
String,
3939
Geometry,
40+
Vector = 242,
4041
UByte = 501,
4142
UInt16,
4243
UInt32,

src/MySqlConnector/MySqlParameter.cs

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
using System.Buffers.Binary;
12
using System.Buffers.Text;
23
using System.Data.Common;
34
using System.Diagnostics;
45
using System.Diagnostics.CodeAnalysis;
56
using System.Globalization;
67
using System.Numerics;
8+
using System.Runtime.InteropServices;
79
using System.Text;
810
#if NET8_0_OR_GREATER
911
using System.Text.Unicode;
@@ -284,7 +286,7 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
284286
{
285287
writer.WriteString(ulongValue);
286288
}
287-
else if (Value is byte[] or ReadOnlyMemory<byte> or Memory<byte> or ArraySegment<byte> or MySqlGeometry or MemoryStream)
289+
else if (Value is byte[] or ReadOnlyMemory<byte> or Memory<byte> or ArraySegment<byte> or MySqlGeometry or MemoryStream or float[] or ReadOnlyMemory<float> or Memory<float>)
288290
{
289291
var inputSpan = Value switch
290292
{
@@ -293,6 +295,9 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
293295
Memory<byte> memory => memory.Span,
294296
MySqlGeometry geometry => geometry.ValueSpan,
295297
MemoryStream memoryStream => memoryStream.TryGetBuffer(out var streamBuffer) ? streamBuffer.AsSpan() : memoryStream.ToArray().AsSpan(),
298+
float[] floatArray => ConvertFloatsToBytes(floatArray.AsSpan()),
299+
Memory<float> memory => ConvertFloatsToBytes(memory.Span),
300+
ReadOnlyMemory<float> memory => ConvertFloatsToBytes(memory.Span),
296301
_ => ((ReadOnlyMemory<byte>) Value).Span,
297302
};
298303

@@ -725,11 +730,52 @@ private void AppendBinary(ByteBufferWriter writer, object value, StatementPrepar
725730
}
726731
else if (value is float floatValue)
727732
{
728-
writer.Write(BitConverter.GetBytes(floatValue));
733+
#if NET5_0_OR_GREATER
734+
Span<byte> bytes = stackalloc byte[4];
735+
BinaryPrimitives.WriteSingleLittleEndian(bytes, floatValue);
736+
writer.Write(bytes);
737+
#else
738+
// convert float to bytes with correct endianness (MySQL uses little-endian)
739+
var bytes = BitConverter.GetBytes(floatValue);
740+
if (!BitConverter.IsLittleEndian)
741+
Array.Reverse(bytes);
742+
writer.Write(bytes);
743+
#endif
729744
}
730745
else if (value is double doubleValue)
731746
{
732-
writer.Write(unchecked((ulong) BitConverter.DoubleToInt64Bits(doubleValue)));
747+
#if NET5_0_OR_GREATER
748+
Span<byte> bytes = stackalloc byte[8];
749+
BinaryPrimitives.WriteDoubleLittleEndian(bytes, doubleValue);
750+
writer.Write(bytes);
751+
#else
752+
if (BitConverter.IsLittleEndian)
753+
{
754+
writer.Write(unchecked((ulong) BitConverter.DoubleToInt64Bits(doubleValue)));
755+
}
756+
else
757+
{
758+
// convert double to bytes with correct endianness (MySQL uses little-endian)
759+
var bytes = BitConverter.GetBytes(doubleValue);
760+
Array.Reverse(bytes);
761+
writer.Write(bytes);
762+
}
763+
#endif
764+
}
765+
else if (value is float[] floatArrayValue)
766+
{
767+
writer.WriteLengthEncodedInteger(unchecked((ulong) floatArrayValue.Length * 4));
768+
writer.Write(ConvertFloatsToBytes(floatArrayValue.AsSpan()));
769+
}
770+
else if (value is Memory<float> floatMemory)
771+
{
772+
writer.WriteLengthEncodedInteger(unchecked((ulong) floatMemory.Length * 4));
773+
writer.Write(ConvertFloatsToBytes(floatMemory.Span));
774+
}
775+
else if (value is ReadOnlyMemory<float> floatReadOnlyMemory)
776+
{
777+
writer.WriteLengthEncodedInteger(unchecked((ulong) floatReadOnlyMemory.Length * 4));
778+
writer.Write(ConvertFloatsToBytes(floatReadOnlyMemory.Span));
733779
}
734780
else if (value is decimal decimalValue)
735781
{
@@ -948,6 +994,32 @@ private static void WriteTime(ByteBufferWriter writer, TimeSpan timeSpan)
948994
}
949995
}
950996

997+
private static ReadOnlySpan<byte> ConvertFloatsToBytes(ReadOnlySpan<float> floats)
998+
{
999+
if (BitConverter.IsLittleEndian)
1000+
{
1001+
return MemoryMarshal.AsBytes(floats);
1002+
}
1003+
else
1004+
{
1005+
// for big-endian platforms, we need to convert each float individually
1006+
var bytes = new byte[floats.Length * 4];
1007+
1008+
for (var i = 0; i < floats.Length; i++)
1009+
{
1010+
#if NET5_0_OR_GREATER
1011+
BinaryPrimitives.WriteSingleLittleEndian(bytes.AsSpan(i * 4), floats[i]);
1012+
#else
1013+
var floatBytes = BitConverter.GetBytes(floats[i]);
1014+
Array.Reverse(floatBytes);
1015+
floatBytes.CopyTo(bytes, i * 4);
1016+
#endif
1017+
}
1018+
1019+
return bytes;
1020+
}
1021+
}
1022+
9511023
private static ReadOnlySpan<byte> BinaryBytes => "_binary'"u8;
9521024

9531025
private DbType m_dbType;

src/MySqlConnector/Protocol/ColumnType.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ internal enum ColumnType
2424
Bit = 16,
2525
Timestamp2 = 17,
2626
DateTime2 = 18,
27+
Vector = 242,
2728
Json = 0xF5,
2829
NewDecimal = 0xF6,
2930
Enum = 0xF7,

tests/IntegrationTests/CharacterSetTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public void CollationConnection(bool reopenConnection)
7979

8080
var collation = connection.Query<string>(@"select @@collation_connection;").Single();
8181
var expected = connection.ServerVersion.Substring(0, 2) is "8." or "9." ? "utf8mb4_0900_ai_ci" :
82-
connection.ServerVersion.StartsWith("11.4.", StringComparison.Ordinal) || connection.ServerVersion.StartsWith("11.6.", StringComparison.Ordinal) ? "utf8mb4_uca1400_ai_ci" :
82+
connection.ServerVersion.StartsWith("11.4.", StringComparison.Ordinal) || connection.ServerVersion.StartsWith("11.7.", StringComparison.Ordinal) ? "utf8mb4_uca1400_ai_ci" :
8383
"utf8mb4_general_ci";
8484
Assert.Equal(expected, collation);
8585
}

0 commit comments

Comments
 (0)