Skip to content

Commit d0771b8

Browse files
committed
wip
1 parent 2c1a2a8 commit d0771b8

File tree

3 files changed

+95
-9
lines changed

3 files changed

+95
-9
lines changed

lib/myxql/geometry.ex

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
defmodule MyXQL.Geometry.Point do
2+
defstruct [:x, :y]
3+
end
4+
5+
defmodule MyXQL.Geometry.Multipoint do
6+
defstruct [:points]
7+
end

lib/myxql/protocol/values.ex

+76-9
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ defmodule MyXQL.Protocol.Values do
8383
defp column_def_to_type(column_def(type: :mysql_type_string)), do: :binary
8484
defp column_def_to_type(column_def(type: :mysql_type_bit, length: length)), do: {:bit, length}
8585
defp column_def_to_type(column_def(type: :mysql_type_null)), do: :null
86+
defp column_def_to_type(column_def(type: :mysql_type_geometry)), do: :geometry
8687

8788
# Text values
8889

@@ -308,7 +309,7 @@ defmodule MyXQL.Protocol.Values do
308309
end
309310

310311
defp decode_binary_row(<<r::bits>>, null_bitmap, [:binary | t], acc),
311-
do: decode_string_lenenc(r, null_bitmap, t, acc)
312+
do: decode_string_lenenc(r, null_bitmap, t, acc, & &1)
312313

313314
defp decode_binary_row(<<r::bits>>, null_bitmap, [:int1 | t], acc),
314315
do: decode_int1(r, null_bitmap, t, acc)
@@ -361,10 +362,58 @@ defmodule MyXQL.Protocol.Values do
361362
defp decode_binary_row(<<r::bits>>, null_bitmap, [{:bit, size} | t], acc),
362363
do: decode_bit(r, size, null_bitmap, t, acc)
363364

365+
defp decode_binary_row(<<r::bits>>, null_bitmap, [:geometry | t], acc),
366+
do: decode_string_lenenc(r, null_bitmap, t, acc, &decode_geometry/1)
367+
364368
defp decode_binary_row(<<>>, _null_bitmap, [], acc) do
365369
Enum.reverse(acc)
366370
end
367371

372+
# this looks similar to WKB [1] but instead of:
373+
#
374+
# <<byte_order::8, type::32, ...>>
375+
#
376+
# we have:
377+
#
378+
# <<0::32, byte_order::8, type::32, ...>>
379+
#
380+
# so seems there's some extra padding in front. Maybe it's a 4-byte srid?
381+
#
382+
# byte_order is allegedly big endian (0x01) but we need to decode floats as little-endian. (lol.)
383+
#
384+
# Looking at [2]
385+
#
386+
# > Values should be stored in internal geometry format, but you can convert them to that
387+
# > format from either Well-Known Text (WKT) or Well-Known Binary (WKB) format.
388+
#
389+
# so yeah, looks like mysql might be storing it in an internal format that is not WKB.
390+
# There's a ST_ToBinary() function that allegedly returns data in WKB so this might be helpful
391+
# for debugging.
392+
#
393+
# [1] https://en.wikipedia.org/wiki/Well-known_text_representation_of_geometry#Well-known_binary
394+
# [2] https://dev.mysql.com/doc/refman/8.0/en/populating-spatial-columns.html
395+
defp decode_geometry(<<0::uint4, 1::uint1, type::uint4, r::bits>>) do
396+
# ^ ^ big endian
397+
# |
398+
# some padding? maybe srid?
399+
decode_geometry(type, r)
400+
end
401+
402+
defp decode_geometry(1, <<x::64-signed-little-float, y::64-signed-little-float>>),
403+
do: %MyXQL.Geometry.Point{x: x, y: y}
404+
405+
defp decode_geometry(4, <<size::uint4, r::bits>>),
406+
do: decode_multipoint(r, size, [])
407+
408+
# <<1, 1, 0, 0, 0>> in front seems like some kind of header?
409+
defp decode_multipoint(<<1, 1, 0, 0, 0, x::64-signed-little-float, y::64-signed-little-float, r::bits>>, size, acc) do
410+
decode_multipoint(r, size - 1, [{x, y} | acc])
411+
end
412+
413+
defp decode_multipoint("", 0, acc) do
414+
%MyXQL.Geometry.Multipoint{points: Enum.reverse(acc)}
415+
end
416+
368417
defp decode_int1(<<v::int1, r::bits>>, null_bitmap, t, acc),
369418
do: decode_binary_row(r, null_bitmap >>> 1, t, [v | acc])
370419

@@ -510,18 +559,36 @@ defmodule MyXQL.Protocol.Values do
510559
}
511560
end
512561

513-
defp decode_string_lenenc(<<n::uint1, v::string(n), r::bits>>, null_bitmap, t, acc)
562+
defp decode_string_lenenc(<<n::uint1, v::string(n), r::bits>>, null_bitmap, t, acc, decoder)
514563
when n < 251,
515-
do: decode_binary_row(r, null_bitmap >>> 1, t, [v | acc])
564+
do: decode_binary_row(r, null_bitmap >>> 1, t, [decoder.(v) | acc])
516565

517-
defp decode_string_lenenc(<<0xFC, n::uint2, v::string(n), r::bits>>, null_bitmap, t, acc),
518-
do: decode_binary_row(r, null_bitmap >>> 1, t, [v | acc])
566+
defp decode_string_lenenc(
567+
<<0xFC, n::uint2, v::string(n), r::bits>>,
568+
null_bitmap,
569+
t,
570+
acc,
571+
decoder
572+
),
573+
do: decode_binary_row(r, null_bitmap >>> 1, t, [decoder.(v) | acc])
519574

520-
defp decode_string_lenenc(<<0xFD, n::uint3, v::string(n), r::bits>>, null_bitmap, t, acc),
521-
do: decode_binary_row(r, null_bitmap >>> 1, t, [v | acc])
575+
defp decode_string_lenenc(
576+
<<0xFD, n::uint3, v::string(n), r::bits>>,
577+
null_bitmap,
578+
t,
579+
acc,
580+
decoder
581+
),
582+
do: decode_binary_row(r, null_bitmap >>> 1, t, [decoder.(v) | acc])
522583

523-
defp decode_string_lenenc(<<0xFE, n::uint8, v::string(n), r::bits>>, null_bitmap, t, acc),
524-
do: decode_binary_row(r, null_bitmap >>> 1, t, [v | acc])
584+
defp decode_string_lenenc(
585+
<<0xFE, n::uint8, v::string(n), r::bits>>,
586+
null_bitmap,
587+
t,
588+
acc,
589+
decoder
590+
),
591+
do: decode_binary_row(r, null_bitmap >>> 1, t, [decoder.(v) | acc])
525592

526593
defp decode_json(<<n::uint1, v::string(n), r::bits>>, null_bitmap, t, acc) when n < 251,
527594
do: decode_binary_row(r, null_bitmap >>> 1, t, [decode_json(v) | acc])

test/myxql/protocol/values_test.exs

+12
Original file line numberDiff line numberDiff line change
@@ -273,6 +273,18 @@ defmodule MyXQL.Protocol.ValueTest do
273273
test "CHAR", c do
274274
assert_roundtrip(c, "my_char", "é")
275275
end
276+
277+
if @protocol == :binary do
278+
test "POINT", c do
279+
assert query!(c, "SELECT ST_GeomFromText('POINT(1 2.2)')").rows == [
280+
[%MyXQL.Geometry.Point{x: 1.0, y: 2.2}]
281+
]
282+
283+
assert query!(c, "SELECT ST_GeomFromText('MULTIPOINT(1 1, 2 2)')").rows == [
284+
[%MyXQL.Geometry.Multipoint{points: [{1.0, 1.0}, {2.0, 2.0}]}]
285+
]
286+
end
287+
end
276288
end
277289
end
278290

0 commit comments

Comments
 (0)