From 0ce5e37d7768249e702c866d79fbc9579a328c93 Mon Sep 17 00:00:00 2001 From: Wojtek Mach Date: Mon, 25 Nov 2019 13:53:54 +0100 Subject: [PATCH] Make geo optional --- README.md | 26 +++++++++++++++----- lib/myxql/protocol/values.ex | 46 ++++++++++++++++++++++++------------ mix.exs | 2 +- 3 files changed, 52 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index d3206551..855dca85 100644 --- a/README.md +++ b/README.md @@ -113,15 +113,13 @@ point, polygon, ... %Geo.Point{coordinates: {0.0, 1.0}}, ... ***** \*\*\*\* MySQL added a native JSON type in version 5.7.8, if you're using earlier versions, remember to use TEXT column for your JSON field. -\*\*\*\*\* Encoding/decoding between `Geo.*` structs and the OpenGIS WKB binary format is -done using the [Geo](https://github.com/bryanjos/geo) package. If you're using MyXQL geometry -types with Ecto and need to for example accept a WKT format as user input, consider implementing an -[custom Ecto type](https://hexdocs.pm/ecto/Ecto.Type.html). Note, some structs like `%Geo.PointZ{}` -does not have equivalent on the MySQL server side and thus shouldn't be used. +\*\*\*\*\* See "Geometry support" section below ## JSON support -MyXQL comes with JSON support out of the box via the [Jason](https://github.com/michalmuskala/jason) library. To use it, add `:jason` to your dependencies: +MyXQL comes with JSON support via the [Jason](https://github.com/michalmuskala/jason) library. + +To use it, add `:jason` to your dependencies: ```elixir {:jason, "~> 1.0"} @@ -133,6 +131,22 @@ You can customize it to use another library via the `:json_library` configuratio config :myxql, :json_library, SomeJSONModule ``` +## Geometry support + +MyXQL comes with Geometry types support via the [Geo](https://github.com/bryanjos/geo) package. + +To use it, add `:geo` to your dependencies: + +```elixir +{:geo, "~> 3.3"} +``` + +Note, some structs like `%Geo.PointZ{}` does not have equivalent on the MySQL server side and thus +shouldn't be used. + +If you're using MyXQL geometry types with Ecto and need to for example accept a WKT format as user +input, consider implementing an [custom Ecto type](https://hexdocs.pm/ecto/Ecto.Type.html). + ## Contributing Run tests: diff --git a/lib/myxql/protocol/values.ex b/lib/myxql/protocol/values.ex index 9d290d11..7250e572 100644 --- a/lib/myxql/protocol/values.ex +++ b/lib/myxql/protocol/values.ex @@ -223,13 +223,15 @@ defmodule MyXQL.Protocol.Values do {:mysql_type_tiny, <<0>>} end - def encode_binary_value(%Geo.Point{} = geo), do: encode_geometry(geo) - def encode_binary_value(%Geo.MultiPoint{} = geo), do: encode_geometry(geo) - def encode_binary_value(%Geo.LineString{} = geo), do: encode_geometry(geo) - def encode_binary_value(%Geo.MultiLineString{} = geo), do: encode_geometry(geo) - def encode_binary_value(%Geo.Polygon{} = geo), do: encode_geometry(geo) - def encode_binary_value(%Geo.MultiPolygon{} = geo), do: encode_geometry(geo) - def encode_binary_value(%Geo.GeometryCollection{} = geo), do: encode_geometry(geo) + if Code.ensure_loaded?(Geo) do + def encode_binary_value(%Geo.Point{} = geo), do: encode_geometry(geo) + def encode_binary_value(%Geo.MultiPoint{} = geo), do: encode_geometry(geo) + def encode_binary_value(%Geo.LineString{} = geo), do: encode_geometry(geo) + def encode_binary_value(%Geo.MultiLineString{} = geo), do: encode_geometry(geo) + def encode_binary_value(%Geo.Polygon{} = geo), do: encode_geometry(geo) + def encode_binary_value(%Geo.MultiPolygon{} = geo), do: encode_geometry(geo) + def encode_binary_value(%Geo.GeometryCollection{} = geo), do: encode_geometry(geo) + end def encode_binary_value(term) when is_list(term) or is_map(term) do string = json_library().encode!(term) @@ -240,10 +242,12 @@ defmodule MyXQL.Protocol.Values do raise ArgumentError, "query has invalid parameter #{inspect(other)}" end - defp encode_geometry(geo) do - srid = geo.srid || 0 - binary = %{geo | srid: nil} |> Geo.WKB.encode!(:ndr) |> Base.decode16!() - {:mysql_type_geometry, encode_string_lenenc(<>)} + if Code.ensure_loaded?(Geo) do + defp encode_geometry(geo) do + srid = geo.srid || 0 + binary = %{geo | srid: nil} |> Geo.WKB.encode!(:ndr) |> Base.decode16!() + {:mysql_type_geometry, encode_string_lenenc(<>)} + end end ## Time/DateTime @@ -389,10 +393,22 @@ defmodule MyXQL.Protocol.Values do Enum.reverse(acc) end - # https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html#gis-internal-format - defp decode_geometry(<>) do - srid = if srid == 0, do: nil, else: srid - r |> Base.encode16() |> Geo.WKB.decode!() |> Map.put(:srid, srid) + if Code.ensure_loaded?(Geo) do + # https://dev.mysql.com/doc/refman/8.0/en/gis-data-formats.html#gis-internal-format + defp decode_geometry(<>) do + srid = if srid == 0, do: nil, else: srid + r |> Base.encode16() |> Geo.WKB.decode!() |> Map.put(:srid, srid) + end + else + defp decode_geometry(_) do + raise """ + encoding/decoding geometry types requires :geo package, add: + + {:geo, "~> 3.3"} + + to your mix.exs and run `mix deps.compile --force myxql`. + """ + end end defp decode_int1(<>, null_bitmap, t, acc), diff --git a/mix.exs b/mix.exs index d0cd234f..ff1074e0 100644 --- a/mix.exs +++ b/mix.exs @@ -51,7 +51,7 @@ defmodule MyXQL.MixProject do {:db_connection, "~> 2.0", db_connection_opts()}, {:decimal, "~> 1.6"}, {:jason, "~> 1.0", optional: true}, - {:geo, "~> 3.3"}, + {:geo, "~> 3.3", optional: true}, {:binpp, ">= 0.0.0", only: [:dev, :test]}, {:dialyxir, "~> 1.0-rc", only: :dev, runtime: false}, {:ex_doc, ">= 0.0.0", only: :dev, runtime: false},