Skip to content

Commit 3764da4

Browse files
committed
Implement MySqlGeometry. Fixes #677
1 parent 9d578a4 commit 3764da4

File tree

5 files changed

+78
-4
lines changed

5 files changed

+78
-4
lines changed

docs/content/tutorials/migrating-from-connector-net.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,3 +192,6 @@ The following bugs in Connector/NET are fixed by switching to MySqlConnector. (~
192192
* [#95436](https://bugs.mysql.com/bug.php?id=95436): Client doesn't authenticate with PEM certificate
193193
* [#95984](https://bugs.mysql.com/bug.php?id=95984): “Incorrect arguments to mysqld_stmt_execute” using prepared statement with `MySqlDbType.JSON`
194194
* [#95986](https://bugs.mysql.com/bug.php?id=95986): “Incorrect integer value” using prepared statement with `MySqlDbType.Int24`
195+
* [#96498](https://bugs.mysql.com/bug.php?id=96498): `WHERE` clause using `MySqlGeometry` as parameter finds no rows
196+
* [#96499](https://bugs.mysql.com/bug.php?id=96499): `MySqlException` when inserting a `MySqlGeometry` value
197+
* [#96500](https://bugs.mysql.com/bug.php?id=96500): `MySqlDataReader.GetFieldValue<MySqlGeometry>` throws `InvalidCastException`

src/MySqlConnector/Core/Row.cs

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,14 @@ public MySqlDateTime GetMySqlDateTime(int ordinal)
313313
return (MySqlDateTime) value;
314314
}
315315

316+
public MySqlGeometry GetMySqlGeometry(int ordinal)
317+
{
318+
var value = GetValue(ordinal);
319+
if (value is byte[] bytes && ResultSet.ColumnDefinitions[ordinal].ColumnType == ColumnType.Geometry)
320+
return new MySqlGeometry(bytes);
321+
throw new InvalidCastException("Can't convert {0} to MySqlGeometry.".FormatInvariant(ResultSet.ColumnDefinitions[ordinal].ColumnType));
322+
}
323+
316324
public int GetValues(object[] values)
317325
{
318326
int count = Math.Min(values.Length, ResultSet.ColumnDefinitions.Length);
@@ -372,9 +380,9 @@ private void CheckBinaryColumn(int ordinal)
372380
var column = ResultSet.ColumnDefinitions[ordinal];
373381
var columnType = column.ColumnType;
374382
if ((column.ColumnFlags & ColumnFlags.Binary) == 0 ||
375-
(columnType != ColumnType.String && columnType != ColumnType.VarString && columnType != ColumnType.TinyBlob &&
376-
columnType != ColumnType.Blob && columnType != ColumnType.MediumBlob && columnType != ColumnType.LongBlob &&
377-
columnType != ColumnType.Geometry))
383+
(columnType != ColumnType.String && columnType != ColumnType.VarString && columnType != ColumnType.TinyBlob &&
384+
columnType != ColumnType.Blob && columnType != ColumnType.MediumBlob && columnType != ColumnType.LongBlob &&
385+
columnType != ColumnType.Geometry))
378386
{
379387
throw new InvalidCastException("Can't convert {0} to bytes.".FormatInvariant(columnType));
380388
}

src/MySqlConnector/MySql.Data.MySqlClient/MySqlDataReader.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ public override long GetChars(int ordinal, long dataOffset, char[] buffer, int b
234234
public MySqlDateTime GetMySqlDateTime(int ordinal) => GetResultSet().GetCurrentRow().GetMySqlDateTime(ordinal);
235235
public MySqlDateTime GetMySqlDateTime(string name) => GetMySqlDateTime(GetOrdinal(name));
236236

237+
public MySqlGeometry GetMySqlGeometry(int ordinal) => GetResultSet().GetCurrentRow().GetMySqlGeometry(ordinal);
238+
public MySqlGeometry GetMySqlGeometry(string name) => GetMySqlGeometry(GetOrdinal(name));
239+
237240
public TimeSpan GetTimeSpan(int ordinal) => (TimeSpan) GetValue(ordinal);
238241
public TimeSpan GetTimeSpan(string name) => GetTimeSpan(GetOrdinal(name));
239242

@@ -295,6 +298,8 @@ public override T GetFieldValue<T>(int ordinal)
295298
return (T) (object) GetTextReader(ordinal);
296299
if (typeof(T) == typeof(Stream))
297300
return (T) (object) GetStream(ordinal);
301+
if (typeof(T) == typeof(MySqlGeometry))
302+
return (T) (object) GetMySqlGeometry(ordinal);
298303

299304
return base.GetFieldValue<T>(ordinal);
300305
}

src/MySqlConnector/MySql.Data.MySqlClient/MySqlParameter.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,11 +254,12 @@ internal void AppendSqlString(ByteBufferWriter writer, StatementPreparerOptions
254254
{
255255
writer.WriteString(ulongValue);
256256
}
257-
else if (Value is byte[] || Value is ReadOnlyMemory<byte> || Value is Memory<byte> || Value is ArraySegment<byte>)
257+
else if (Value is byte[] || Value is ReadOnlyMemory<byte> || Value is Memory<byte> || Value is ArraySegment<byte> || Value is MySqlGeometry)
258258
{
259259
var inputSpan = Value is byte[] byteArray ? byteArray.AsSpan() :
260260
Value is ArraySegment<byte> arraySegment ? arraySegment.AsSpan() :
261261
Value is Memory<byte> memory ? memory.Span :
262+
Value is MySqlGeometry geometry ? geometry.Value :
262263
((ReadOnlyMemory<byte>) Value).Span;
263264

264265
// determine the number of bytes to be written
@@ -475,6 +476,11 @@ internal void AppendBinary(ByteBufferWriter writer, StatementPreparerOptions opt
475476
writer.WriteLengthEncodedInteger(unchecked((ulong) arraySegmentValue.Count));
476477
writer.Write(arraySegmentValue);
477478
}
479+
else if (Value is MySqlGeometry geometry)
480+
{
481+
writer.WriteLengthEncodedInteger(unchecked((ulong) geometry.Value.Length));
482+
writer.Write(geometry.Value);
483+
}
478484
else if (Value is float floatValue)
479485
{
480486
writer.Write(BitConverter.GetBytes(floatValue));
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System;
2+
using System.Buffers.Binary;
3+
4+
namespace MySql.Data.Types
5+
{
6+
/// <summary>
7+
/// Represents MySQL's internal GEOMETRY format: https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html#gis-internal-format
8+
/// </summary>
9+
public sealed class MySqlGeometry
10+
{
11+
/// <summary>
12+
/// Constructs a <see cref="MySqlGeometry"/> from a SRID and Well-known Binary bytes.
13+
/// </summary>
14+
/// <param name="srid">The SRID (Spatial Reference System ID).</param>
15+
/// <param name="wkb">The Well-known Binary serialization of the geometry.</param>
16+
/// <returns>A new <see cref="MySqlGeometry"/> containing the specified geometry.</returns>
17+
public static MySqlGeometry FromWkb(int srid, ReadOnlySpan<byte> wkb)
18+
{
19+
var bytes = new byte[wkb.Length + 4];
20+
BinaryPrimitives.WriteInt32LittleEndian(bytes, srid);
21+
wkb.CopyTo(bytes.AsSpan().Slice(4));
22+
return new MySqlGeometry(bytes);
23+
}
24+
25+
/// <summary>
26+
/// Constructs a <see cref="MySqlGeometry"/> from MySQL's internal format.
27+
/// </summary>
28+
/// <param name="value">The raw bytes of MySQL's internal GEOMETRY format.</param>
29+
/// <returns>A new <see cref="MySqlGeometry"/> containing the specified geometry.</returns>
30+
/// <remarks>See <a href="https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html#gis-internal-format">Internal Geometry Storage Format</a>.</remarks>
31+
public static MySqlGeometry FromMySql(ReadOnlySpan<byte> value) => new MySqlGeometry(value.ToArray());
32+
33+
/// <summary>
34+
/// The Spatial Reference System ID of this geometry.
35+
/// </summary>
36+
public int SRID => BinaryPrimitives.ReadInt32LittleEndian(m_bytes);
37+
38+
/// <summary>
39+
/// The Well-known Binary serialization of this geometry.
40+
/// </summary>
41+
public ReadOnlySpan<byte> WKB => Value.Slice(4);
42+
43+
/// <summary>
44+
/// The internal MySQL form of this geometry.
45+
/// </summary>
46+
public ReadOnlySpan<byte> Value => m_bytes;
47+
48+
internal MySqlGeometry(byte[] bytes) => m_bytes = bytes;
49+
50+
readonly byte[] m_bytes;
51+
}
52+
}

0 commit comments

Comments
 (0)