diff --git a/.travis.yml b/.travis.yml index 0b827e4..3d5add7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,14 +12,14 @@ notifications: sudo: false env: matrix: - - DB=mariadb:10.1 - - DB=mariadb:10.2 - - DB=mariadb:10.3 - - DB=mysql:8.0 - - DB=mysql:5.7 - - DB=mysql:5.6 + - DB=mariadb:10.1 JSON_SUPPORT=false + - DB=mariadb:10.2 JSON_SUPPORT=false + - DB=mariadb:10.3 JSON_SUPPORT=false + - DB=mysql:8.0 JSON_SUPPORT=true + - DB=mysql:5.7 JSON_SUPPORT=true + - DB=mysql:5.6 JSON_SUPPORT=false before_install: - sudo service mysql stop - docker pull $DB || true - docker run --name mariadb -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -d $DB -script: "mix test --cover" +script: "JSON_SUPPORT=$JSON_SUPPORT mix test --cover" diff --git a/README.md b/README.md index 8be3a2b..ee80d34 100644 --- a/README.md +++ b/README.md @@ -36,3 +36,13 @@ After you are done, run `mix deps.get` in your shell to fetch and compile Mariae Important configuration, which depends on used charset for support unicode chars, see `:binary_as` in `Mariaex.start_link/1` + +### JSON library + +As default, [Poison](https://github.com/devinus/poison) is used for JSON library in mariaex to support JSON column. + +If you want to use another library, please set `config.exs` like below. + +```elixir +config :mariaex, json_library: SomeLibrary +``` diff --git a/lib/mariaex/messages.ex b/lib/mariaex/messages.ex index 85d1905..d410d66 100644 --- a/lib/mariaex/messages.ex +++ b/lib/mariaex/messages.ex @@ -67,6 +67,8 @@ defmodule Mariaex.Messages do field_type_blob: 0xfc, field_type_var_string: 0xfd, field_type_string: 0xfe], + json: + [field_type_json: 0xf5], null: [field_type_null: 0x06] ] @@ -229,32 +231,32 @@ defmodule Mariaex.Messages do do: {nil, rest} def decode_bin_rows(<< len :: size(24)-little-integer, seqnum :: size(8)-integer, body :: size(len)-binary, rest :: binary>>, - fields, nullbin_size, rows, datetime) do + fields, nullbin_size, rows, datetime, json_library) do case body do <<0 :: 8, nullbin::size(nullbin_size)-little-unit(8), values :: binary>> -> - row = Mariaex.RowParser.decode_bin_rows(values, fields, nullbin, datetime) - decode_bin_rows(rest, fields, nullbin_size, [row | rows], datetime) + row = Mariaex.RowParser.decode_bin_rows(values, fields, nullbin, datetime, json_library) + decode_bin_rows(rest, fields, nullbin_size, [row | rows], datetime, json_library) body -> msg = decode_msg(body, :bin_rows) {:ok, packet(size: len, seqnum: seqnum, msg: msg, body: body), rows, rest} end end - def decode_bin_rows(<>, _fields, _nullbin_size, rows, _datetime) do + def decode_bin_rows(<>, _fields, _nullbin_size, rows, _datetime, _json_library) do {:more, rows, rest} end def decode_text_rows(<< len :: size(24)-little-integer, seqnum :: size(8)-integer, body :: size(len)-binary, rest :: binary>>, - fields, rows, datetime) do + fields, rows, datetime, json_library) do case body do << 254 :: 8, _ :: binary >> = body when byte_size(body) < 9 -> msg = decode_msg(body, :text_rows) {:ok, packet(size: len, seqnum: seqnum, msg: msg, body: body), rows, rest} body -> - row = Mariaex.RowParser.decode_text_rows(body, fields, datetime) - decode_text_rows(rest, fields, [row | rows], datetime) + row = Mariaex.RowParser.decode_text_rows(body, fields, datetime, json_library) + decode_text_rows(rest, fields, [row | rows], datetime, json_library) end end - def decode_text_rows(<>, _fields, rows, _datetime) do + def decode_text_rows(<>, _fields, rows, _datetime, _json_library) do {:more, rows, rest} end diff --git a/lib/mariaex/protocol.ex b/lib/mariaex/protocol.ex index f301034..50619ec 100644 --- a/lib/mariaex/protocol.ex +++ b/lib/mariaex/protocol.ex @@ -62,6 +62,7 @@ defmodule Mariaex.Protocol do cursors: %{}, seqnum: 0, datetime: :structs, + json_library: Poison, ssl_conn_state: :undefined # :undefined | :not_used | :ssl_handshake | :connected @doc """ @@ -76,6 +77,7 @@ defmodule Mariaex.Protocol do connect_opts = [host, opts[:port], opts[:socket_options], opts[:timeout]] binary_as = opts[:binary_as] || :field_type_var_string datetime = opts[:datetime] || :structs + json_library = Application.get_env(:mariaex, :json_library, Poison) case apply(sock_mod, :connect, connect_opts) do {:ok, sock} -> @@ -88,6 +90,7 @@ defmodule Mariaex.Protocol do lru_cache: reset_lru_cache(opts[:cache_size]), timeout: opts[:timeout], datetime: datetime, + json_library: json_library, opts: opts} handshake_recv(s, %{opts: opts}) {:error, reason} -> @@ -531,8 +534,8 @@ defmodule Mariaex.Protocol do end end - defp text_row_decode(%{datetime: datetime} = s, fields, rows, buffer) do - case decode_text_rows(buffer, fields, rows, datetime) do + defp text_row_decode(%{datetime: datetime, json_library: json_library} = s, fields, rows, buffer) do + case decode_text_rows(buffer, fields, rows, datetime, json_library) do {:ok, packet, rows, rest} -> {:ok, packet, rows, %{s | buffer: rest}} {:more, rows, rest} -> @@ -638,8 +641,8 @@ defmodule Mariaex.Protocol do end end - defp binary_row_decode(%{datetime: datetime} = s, fields, nullbin_size, rows, buffer) do - case decode_bin_rows(buffer, fields, nullbin_size, rows, datetime) do + defp binary_row_decode(%{datetime: datetime, json_library: json_library} = s, fields, nullbin_size, rows, buffer) do + case decode_bin_rows(buffer, fields, nullbin_size, rows, datetime, json_library) do {:ok, packet, rows, rest} -> {:ok, packet, rows, %{s | buffer: rest}} {:more, rows, rest} -> diff --git a/lib/mariaex/row_parser.ex b/lib/mariaex/row_parser.ex index fe3c23c..bd21580 100644 --- a/lib/mariaex/row_parser.ex +++ b/lib/mariaex/row_parser.ex @@ -20,8 +20,8 @@ defmodule Mariaex.RowParser do {fields, div(length(fields) + 7 + 2, 8)} end - def decode_bin_rows(row, fields, nullint, datetime) do - decode_bin_rows(row, fields, nullint >>> 2, [], datetime) + def decode_bin_rows(row, fields, nullint, datetime, json_library) do + decode_bin_rows(row, fields, nullint >>> 2, [], datetime, json_library) end ## Helpers @@ -66,6 +66,10 @@ defmodule Mariaex.RowParser do :int64 end + defp type_to_atom({:json, :field_type_json}, _) do + :json + end + defp type_to_atom({:string, _mysql_type}, _), do: :string defp type_to_atom({:integer, :field_type_year}, _), do: :uint16 defp type_to_atom({:time, :field_type_time}, _), do: :time @@ -78,252 +82,276 @@ defmodule Mariaex.RowParser do defp type_to_atom({:bit, :field_type_bit}, _), do: :bit defp type_to_atom({:null, :field_type_null}, _), do: nil - defp decode_bin_rows(<>, [_ | fields], nullint, acc, datetime) when (nullint &&& 1) === 1 do - decode_bin_rows(rest, fields, nullint >>> 1, [nil | acc], datetime) + defp decode_bin_rows(<>, [_ | fields], nullint, acc, datetime, json_library) when (nullint &&& 1) === 1 do + decode_bin_rows(rest, fields, nullint >>> 1, [nil | acc], datetime, json_library) + end + + defp decode_bin_rows(<>, [:string | fields], null_bitfield, acc, datetime, json_library) do + decode_string(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:string | fields], null_bitfield, acc, datetime) do - decode_string(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:uint8 | fields], null_bitfield, acc, datetime, json_library) do + decode_uint8(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:uint8 | fields], null_bitfield, acc, datetime) do - decode_uint8(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:int8 | fields], null_bitfield, acc, datetime, json_library) do + decode_int8(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:int8 | fields], null_bitfield, acc, datetime) do - decode_int8(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:uint16 | fields], null_bitfield, acc, datetime, json_library) do + decode_uint16(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:uint16 | fields], null_bitfield, acc, datetime) do - decode_uint16(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:int16 | fields], null_bitfield, acc, datetime, json_library) do + decode_int16(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:int16 | fields], null_bitfield, acc, datetime) do - decode_int16(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:uint32 | fields], null_bitfield, acc, datetime, json_library) do + decode_uint32(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:uint32 | fields], null_bitfield, acc, datetime) do - decode_uint32(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:int32 | fields], null_bitfield, acc, datetime, json_library) do + decode_int32(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:int32 | fields], null_bitfield, acc, datetime) do - decode_int32(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:uint64 | fields], null_bitfield, acc, datetime, json_library) do + decode_uint64(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:uint64 | fields], null_bitfield, acc, datetime) do - decode_uint64(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:int64 | fields], null_bitfield, acc, datetime, json_library) do + decode_int64(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:int64 | fields], null_bitfield, acc, datetime) do - decode_int64(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:time | fields], null_bitfield, acc, datetime, json_library) do + decode_time(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:time | fields], null_bitfield, acc, datetime) do - decode_time(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:date | fields], null_bitfield, acc, datetime, json_library) do + decode_date(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:date | fields], null_bitfield, acc, datetime) do - decode_date(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:datetime | fields], null_bitfield, acc, datetime, json_library) do + decode_datetime(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:datetime | fields], null_bitfield, acc, datetime) do - decode_datetime(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:decimal | fields], null_bitfield, acc, datetime, json_library) do + decode_decimal(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:decimal | fields], null_bitfield, acc, datetime) do - decode_decimal(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:float32 | fields], null_bitfield, acc, datetime, json_library) do + decode_float32(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:float32 | fields], null_bitfield, acc, datetime) do - decode_float32(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:float64 | fields], null_bitfield, acc, datetime, json_library) do + decode_float64(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:float64 | fields], null_bitfield, acc, datetime) do - decode_float64(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:bit | fields], null_bitfield, acc, datetime, json_library) do + decode_string(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:bit | fields], null_bitfield, acc, datetime) do - decode_string(rest, fields, null_bitfield >>> 1, acc, datetime) + defp decode_bin_rows(<>, [:json | fields], null_bitfield, acc, datetime, json_library) do + decode_json(rest, fields, null_bitfield >>> 1, acc, datetime, json_library) end - defp decode_bin_rows(<>, [:nil | fields], null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield >>> 1, [nil | acc], datetime) + defp decode_bin_rows(<>, [:nil | fields], null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield >>> 1, [nil | acc], datetime, json_library) end - defp decode_bin_rows(<<>>, [], _, acc, _datetime) do + defp decode_bin_rows(<<>>, [], _, acc, _datetime, _json_library) do Enum.reverse(acc) end - defp decode_string(<>, fields, nullint, acc, datetime) when len <= 250 do - decode_bin_rows(rest, fields, nullint, [string | acc], datetime) + defp decode_string(<>, fields, nullint, acc, datetime, json_library) when len <= 250 do + decode_bin_rows(rest, fields, nullint, [string | acc], datetime, json_library) end - defp decode_string(<<252::8, len::16-little, string::size(len)-binary, rest::bits>>, fields, nullint, acc, datetime) do - decode_bin_rows(rest, fields, nullint, [string | acc], datetime) + defp decode_string(<<252::8, len::16-little, string::size(len)-binary, rest::bits>>, fields, nullint, acc, datetime, json_library) do + decode_bin_rows(rest, fields, nullint, [string | acc], datetime, json_library) end - defp decode_string(<<253::8, len::24-little, string::size(len)-binary, rest::bits>>, fields, nullint, acc, datetime) do - decode_bin_rows(rest, fields, nullint, [string | acc], datetime) + defp decode_string(<<253::8, len::24-little, string::size(len)-binary, rest::bits>>, fields, nullint, acc, datetime, json_library) do + decode_bin_rows(rest, fields, nullint, [string | acc], datetime, json_library) end - defp decode_string(<<254::8, len::64-little, string::size(len)-binary, rest::bits>>, fields, nullint, acc, datetime) do - decode_bin_rows(rest, fields, nullint, [string | acc], datetime) + defp decode_string(<<254::8, len::64-little, string::size(len)-binary, rest::bits>>, fields, nullint, acc, datetime, json_library) do + decode_bin_rows(rest, fields, nullint, [string | acc], datetime, json_library) end - defp decode_float32(<>, fields, null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield, [value | acc], datetime) + defp decode_float32(<>, fields, null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield, [value | acc], datetime, json_library) end - defp decode_float64(<>, fields, null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield, [value | acc], datetime) + defp decode_float64(<>, fields, null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield, [value | acc], datetime, json_library) end defp decode_uint8(<>, - fields, null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime) + fields, null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime, json_library) end defp decode_int8(<>, - fields, null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime) + fields, null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime, json_library) end defp decode_uint16(<>, - fields, null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime) + fields, null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime, json_library) end defp decode_int16(<>, - fields, null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime) + fields, null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime, json_library) end defp decode_uint32(<>, - fields, null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime) + fields, null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime, json_library) end defp decode_int32(<>, - fields, null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime) + fields, null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime, json_library) end defp decode_uint64(<>, - fields, null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime) + fields, null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime, json_library) end defp decode_int64(<>, - fields, null_bitfield, acc, datetime) do - decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime) + fields, null_bitfield, acc, datetime, json_library) do + decode_bin_rows(rest, fields, null_bitfield , [value | acc], datetime, json_library) end defp decode_decimal(<>, - fields, null_bitfield, acc, datetime) do + fields, null_bitfield, acc, datetime, json_library) do value = Decimal.new(raw_value) - decode_bin_rows(rest, fields, null_bitfield, [value | acc], datetime) + decode_bin_rows(rest, fields, null_bitfield, [value | acc], datetime, json_library) end defp decode_time(<< 0::8-little, rest::bits>>, - fields, null_bitfield, acc, :structs) do + fields, null_bitfield, acc, :structs, json_library) do time = %Time{hour: 0, minute: 0, second: 0} - decode_bin_rows(rest, fields, null_bitfield, [time | acc], :structs) + decode_bin_rows(rest, fields, null_bitfield, [time | acc], :structs, json_library) end defp decode_time(<<8::8-little, _::8-little, _::32-little, hour::8-little, min::8-little, sec::8-little, rest::bits>>, - fields, null_bitfield, acc, :structs) do + fields, null_bitfield, acc, :structs, json_library) do time = %Time{hour: hour, minute: min, second: sec} - decode_bin_rows(rest, fields, null_bitfield, [time | acc], :structs) + decode_bin_rows(rest, fields, null_bitfield, [time | acc], :structs, json_library) end defp decode_time(<< 12::8, _::32-little, _::8-little, hour::8-little, min::8-little, sec::8-little, msec::32-little, rest::bits >>, - fields, null_bitfield, acc, :structs) do + fields, null_bitfield, acc, :structs, json_library) do time = %Time{hour: hour, minute: min, second: sec, microsecond: {msec, 6}} - decode_bin_rows(rest, fields, null_bitfield, [time | acc], :structs) + decode_bin_rows(rest, fields, null_bitfield, [time | acc], :structs, json_library) end defp decode_time(<< 0::8-little, rest::bits>>, - fields, null_bitfield, acc, :tuples) do - decode_bin_rows(rest, fields, null_bitfield, [{0, 0, 0, 0} | acc], :tuples) + fields, null_bitfield, acc, :tuples, json_library) do + decode_bin_rows(rest, fields, null_bitfield, [{0, 0, 0, 0} | acc], :tuples, json_library) end defp decode_time(<<8::8-little, _::8-little, _::32-little, hour::8-little, min::8-little, sec::8-little, rest::bits>>, - fields, null_bitfield, acc, :tuples) do - decode_bin_rows(rest, fields, null_bitfield, [{hour, min, sec, 0} | acc], :tuples) + fields, null_bitfield, acc, :tuples, json_library) do + decode_bin_rows(rest, fields, null_bitfield, [{hour, min, sec, 0} | acc], :tuples, json_library) end defp decode_time(<< 12::8, _::32-little, _::8-little, hour::8-little, min::8-little, sec::8-little, msec::32-little, rest::bits >>, - fields, null_bitfield, acc, :tuples) do + fields, null_bitfield, acc, :tuples, json_library) do - decode_bin_rows(rest, fields, null_bitfield, [{hour, min, sec, msec} | acc], :tuples) + decode_bin_rows(rest, fields, null_bitfield, [{hour, min, sec, msec} | acc], :tuples, json_library) end defp decode_date(<< 0::8-little, rest::bits >>, - fields, null_bitfield, acc, :structs) do + fields, null_bitfield, acc, :structs, json_library) do date = %Date{year: 0, month: 1, day: 1} - decode_bin_rows(rest, fields, null_bitfield, [date | acc], :structs) + decode_bin_rows(rest, fields, null_bitfield, [date | acc], :structs, json_library) end defp decode_date(<< 4::8-little, year::16-little, month::8-little, day::8-little, rest::bits >>, - fields, null_bitfield, acc, :structs) do + fields, null_bitfield, acc, :structs, json_library) do date = %Date{year: year, month: month, day: day} - decode_bin_rows(rest, fields, null_bitfield, [date | acc], :structs) + decode_bin_rows(rest, fields, null_bitfield, [date | acc], :structs, json_library) end defp decode_date(<< 0::8-little, rest::bits >>, - fields, null_bitfield, acc, :tuples) do - decode_bin_rows(rest, fields, null_bitfield, [{0, 0, 0} | acc], :tuples) + fields, null_bitfield, acc, :tuples, json_library) do + decode_bin_rows(rest, fields, null_bitfield, [{0, 0, 0} | acc], :tuples, json_library) end defp decode_date(<< 4::8-little, year::16-little, month::8-little, day::8-little, rest::bits >>, - fields, null_bitfield, acc, :tuples) do + fields, null_bitfield, acc, :tuples, json_library) do - decode_bin_rows(rest, fields, null_bitfield, [{year, month, day} | acc], :tuples) + decode_bin_rows(rest, fields, null_bitfield, [{year, month, day} | acc], :tuples, json_library) end defp decode_datetime(<< 0::8-little, rest::bits >>, - fields, null_bitfield, acc, :structs) do + fields, null_bitfield, acc, :structs, json_library) do datetime = %NaiveDateTime{year: 0, month: 1, day: 1, hour: 0, minute: 0, second: 0} - decode_bin_rows(rest, fields, null_bitfield, [datetime | acc], :structs) + decode_bin_rows(rest, fields, null_bitfield, [datetime | acc], :structs, json_library) end defp decode_datetime(<<4::8-little, year::16-little, month::8-little, day::8-little, rest::bits >>, - fields, null_bitfield, acc, :structs) do + fields, null_bitfield, acc, :structs, json_library) do datetime = %NaiveDateTime{year: year, month: month, day: day, hour: 0, minute: 0, second: 0} - decode_bin_rows(rest, fields, null_bitfield, [datetime | acc], :structs) + decode_bin_rows(rest, fields, null_bitfield, [datetime | acc], :structs, json_library) end defp decode_datetime(<< 7::8-little, year::16-little, month::8-little, day::8-little, hour::8-little, min::8-little, sec::8-little, rest::bits >>, - fields, null_bitfield, acc, :structs) do + fields, null_bitfield, acc, :structs, json_library) do datetime = %NaiveDateTime{year: year, month: month, day: day, hour: hour, minute: min, second: sec} - decode_bin_rows(rest, fields, null_bitfield, [datetime | acc], :structs) + decode_bin_rows(rest, fields, null_bitfield, [datetime | acc], :structs, json_library) end defp decode_datetime(<<11::8-little, year::16-little, month::8-little, day::8-little, hour::8-little, min::8-little, sec::8-little, msec::32-little, rest::bits >>, - fields, null_bitfield, acc, :structs) do + fields, null_bitfield, acc, :structs, json_library) do datetime = %NaiveDateTime{year: year, month: month, day: day, hour: hour, minute: min, second: sec, microsecond: {msec, 6}} - decode_bin_rows(rest, fields, null_bitfield, [datetime | acc], :structs) + decode_bin_rows(rest, fields, null_bitfield, [datetime | acc], :structs, json_library) end defp decode_datetime(<< 0::8-little, rest::bits >>, - fields, null_bitfield, acc, :tuples) do - decode_bin_rows(rest, fields, null_bitfield, [{{0, 0, 0}, {0, 0, 0, 0}} | acc], :tuples) + fields, null_bitfield, acc, :tuples, json_library) do + decode_bin_rows(rest, fields, null_bitfield, [{{0, 0, 0}, {0, 0, 0, 0}} | acc], :tuples, json_library) end defp decode_datetime(<<4::8-little, year::16-little, month::8-little, day::8-little, rest::bits >>, - fields, null_bitfield, acc, :tuples) do - decode_bin_rows(rest, fields, null_bitfield, [{{year, month, day}, {0, 0, 0, 0}} | acc], :tuples) + fields, null_bitfield, acc, :tuples, json_library) do + decode_bin_rows(rest, fields, null_bitfield, [{{year, month, day}, {0, 0, 0, 0}} | acc], :tuples, json_library) end defp decode_datetime(<< 7::8-little, year::16-little, month::8-little, day::8-little, hour::8-little, min::8-little, sec::8-little, rest::bits >>, - fields, null_bitfield, acc, :tuples) do - decode_bin_rows(rest, fields, null_bitfield, [{{year, month, day}, {hour, min, sec, 0}} | acc], :tuples) + fields, null_bitfield, acc, :tuples, json_library) do + decode_bin_rows(rest, fields, null_bitfield, [{{year, month, day}, {hour, min, sec, 0}} | acc], :tuples, json_library) end defp decode_datetime(<<11::8-little, year::16-little, month::8-little, day::8-little, hour::8-little, min::8-little, sec::8-little, msec::32-little, rest::bits >>, - fields, null_bitfield, acc, :tuples) do - decode_bin_rows(rest, fields, null_bitfield, [{{year, month, day}, {hour, min, sec, msec}} | acc], :tuples) + fields, null_bitfield, acc, :tuples, json_library) do + decode_bin_rows(rest, fields, null_bitfield, [{{year, month, day}, {hour, min, sec, msec}} | acc], :tuples, json_library) + end + + defp decode_json(<< len::8, string::size(len)-binary, rest::bits >>, fields, nullint, acc, datetime, json_library) when len <= 250 do + json = json_library.decode!(string) + decode_bin_rows(rest, fields, nullint, [json | acc], datetime, json_library) + end + + defp decode_json(<< 252::8, len::16-little, string::size(len)-binary, rest::bits >>, fields, nullint, acc, datetime, json_library) do + json = json_library.decode!(string) + decode_bin_rows(rest, fields, nullint, [json | acc], datetime, json_library) + end + + defp decode_json(<< 253::8, len::24-little, string::size(len)-binary, rest::bits >>, fields, nullint, acc, datetime, json_library) do + json = json_library.decode!(string) + decode_bin_rows(rest, fields, nullint, [json | acc], datetime, json_library) + end + + defp decode_json(<< 254::8, len::64-little, string::size(len)-binary, rest::bits >>, fields, nullint, acc, datetime, json_library) do + json = json_library.decode!(string) + decode_bin_rows(rest, fields, nullint, [json | acc], datetime, json_library) end ### TEXT ROW PARSER @@ -335,93 +363,102 @@ defmodule Mariaex.RowParser do end end - def decode_text_rows(binary, fields, datetime) do - decode_text_part(binary, fields, [], datetime) + def decode_text_rows(binary, fields, datetime, json_library) do + decode_text_part(binary, fields, [], datetime, json_library) end ### IMPLEMENTATION - defp decode_text_part(<>, fields, acc, datetime) when len <= 250 do - decode_text_rows(string, rest, fields, acc, datetime) + defp decode_text_part(<>, fields, acc, datetime, json_library) when len <= 250 do + decode_text_rows(string, rest, fields, acc, datetime, json_library) end - defp decode_text_part(<<252::8, len::16-little, string::size(len)-binary, rest::bits>>, fields, acc, datetime) do - decode_text_rows(string, rest, fields, acc, datetime) + defp decode_text_part(<<252::8, len::16-little, string::size(len)-binary, rest::bits>>, fields, acc, datetime, json_library) do + decode_text_rows(string, rest, fields, acc, datetime, json_library) end - defp decode_text_part(<<253::8, len::24-little, string::size(len)-binary, rest::bits>>, fields, acc, datetime) do - decode_text_rows(string, rest, fields, acc, datetime) + defp decode_text_part(<<253::8, len::24-little, string::size(len)-binary, rest::bits>>, fields, acc, datetime, json_library) do + decode_text_rows(string, rest, fields, acc, datetime, json_library) end - defp decode_text_part(<<254::8, len::64-little, string::size(len)-binary, rest::bits>>, fields, acc, datetime) do - decode_text_rows(string, rest, fields, acc, datetime) + defp decode_text_part(<<254::8, len::64-little, string::size(len)-binary, rest::bits>>, fields, acc, datetime, json_library) do + decode_text_rows(string, rest, fields, acc, datetime, json_library) end - defp decode_text_part(<<>>, [], acc, _datetime) do + defp decode_text_part(<<>>, [], acc, _datetime, _json_library) do Enum.reverse(acc) end - defp decode_text_rows(string, rest, [:string | fields], acc, datetime) do - decode_text_part(rest, fields, [string | acc], datetime) + defp decode_text_rows(string, rest, [:string | fields], acc, datetime, json_library) do + decode_text_part(rest, fields, [string | acc], datetime, json_library) end - defp decode_text_rows(string, rest, [type | fields], acc, datetime) + defp decode_text_rows(string, rest, [type | fields], acc, datetime, json_library) when type in [:uint8, :int8, :uint16, :int16, :uint32, :int32, :uint64, :int64] do - decode_text_part(rest, fields, [:erlang.binary_to_integer(string) | acc], datetime) + decode_text_part(rest, fields, [:erlang.binary_to_integer(string) | acc], datetime, json_library) end - defp decode_text_rows(string, rest, [type | fields], acc, datetime) + defp decode_text_rows(string, rest, [type | fields], acc, datetime, json_library) when type in [:float32, :float64, :decimal] do - decode_text_part(rest, fields, [:erlang.binary_to_float(string) | acc], datetime) + decode_text_part(rest, fields, [:erlang.binary_to_float(string) | acc], datetime, json_library) end - defp decode_text_rows(string, rest, [:bit | fields], acc, datetime) do - decode_text_part(rest, fields, [string | acc], datetime) + defp decode_text_rows(string, rest, [:bit | fields], acc, datetime, json_library) do + decode_text_part(rest, fields, [string | acc], datetime, json_library) end - defp decode_text_rows(string, rest, [:time | fields], acc, datetime) do - decode_text_time(string, rest, fields, acc, datetime) + defp decode_text_rows(string, rest, [:time | fields], acc, datetime, json_library) do + decode_text_time(string, rest, fields, acc, datetime, json_library) end - defp decode_text_rows(string, rest, [:date | fields], acc, datetime) do - decode_text_date(string, rest, fields, acc, datetime) + defp decode_text_rows(string, rest, [:date | fields], acc, datetime, json_library) do + decode_text_date(string, rest, fields, acc, datetime, json_library) end - defp decode_text_rows(string, rest, [:datetime | fields], acc, datetime) do - decode_text_datetime(string, rest, fields, acc, datetime) + defp decode_text_rows(string, rest, [:datetime | fields], acc, datetime, json_library) do + decode_text_datetime(string, rest, fields, acc, datetime, json_library) + end + + defp decode_text_rows(string, rest, [:json | fields], acc, datetime, json_library) do + decode_text_json(string, rest, fields, acc, datetime, json_library) end defmacrop to_int(value) do quote do: :erlang.binary_to_integer(unquote(value)) end - defp decode_text_date(<>, rest, fields, acc, :structs) do + defp decode_text_date(<>, rest, fields, acc, :structs, json_library) do date = %Date{year: to_int(year), month: to_int(month), day: to_int(day)} - decode_text_part(rest, fields, [date | acc], :structs) + decode_text_part(rest, fields, [date | acc], :structs, json_library) end - defp decode_text_date(<>, rest, fields, acc, :tuples) do - decode_text_part(rest, fields, [{to_int(year), to_int(month), to_int(day)} | acc], :tuples) + defp decode_text_date(<>, rest, fields, acc, :tuples, json_library) do + decode_text_part(rest, fields, [{to_int(year), to_int(month), to_int(day)} | acc], :tuples, json_library) end - defp decode_text_time(<>, rest, fields, acc, :structs) do + defp decode_text_time(<>, rest, fields, acc, :structs, json_library) do time = %Time{hour: to_int(hour), minute: to_int(min), second: to_int(sec)} - decode_text_part(rest, fields, [time | acc], :structs) + decode_text_part(rest, fields, [time | acc], :structs, json_library) end - defp decode_text_time(<>, rest, fields, acc, :tuples) do - decode_text_part(rest, fields, [{to_int(hour), to_int(min), to_int(sec), 0} | acc], :tuples) + defp decode_text_time(<>, rest, fields, acc, :tuples, json_library) do + decode_text_part(rest, fields, [{to_int(hour), to_int(min), to_int(sec), 0} | acc], :tuples, json_library) end defp decode_text_datetime(<>, rest, fields, acc, :structs) do + _::8-little, hour::2-bytes, ?:, min::2-bytes, ?:, sec::2-bytes>>, rest, fields, acc, :structs, json_library) do datetime = %NaiveDateTime{year: to_int(year), month: to_int(month), day: to_int(day), hour: to_int(hour), minute: to_int(min), second: to_int(sec)} - decode_text_part(rest, fields, [datetime | acc], :structs) + decode_text_part(rest, fields, [datetime | acc], :structs, json_library) end defp decode_text_datetime(<>, rest, fields, acc, :tuples) do - decode_text_part(rest, fields, [{{to_int(year), to_int(month), to_int(day)}, {to_int(hour), to_int(min), to_int(sec), 0}} | acc], :tuples) + _::8-little, hour::2-bytes, ?:, min::2-bytes, ?:, sec::2-bytes>>, rest, fields, acc, :tuples, json_library) do + decode_text_part(rest, fields, [{{to_int(year), to_int(month), to_int(day)}, {to_int(hour), to_int(min), to_int(sec), 0}} | acc], :tuples, json_library) + end + + defp decode_text_json(string, rest, fields, acc, datetime, json_library) do + json = json_library.decode!(string) + decode_text_part(rest, fields, [json | acc], datetime, json_library) end end diff --git a/mix.exs b/mix.exs index 8edef9a..4d3e45a 100644 --- a/mix.exs +++ b/mix.exs @@ -22,7 +22,8 @@ defmodule Mariaex.Mixfile do [{:decimal, "~> 1.0"}, {:db_connection, "~> 1.1"}, {:coverex, "~> 1.4.10", only: :test}, - {:ex_doc, ">= 0.0.0", only: :dev}] + {:ex_doc, ">= 0.0.0", only: :dev}, + {:poison, ">= 0.0.0", optional: true}] end defp description do diff --git a/test/query_test.exs b/test/query_test.exs index e02894b..4eda2c0 100644 --- a/test/query_test.exs +++ b/test/query_test.exs @@ -685,4 +685,20 @@ defmodule QueryTest do assert :ok = query("REPLACE INTO test_replace VALUES (1, 'Old', '2014-08-20 18:47:00');", []) assert :ok = query("REPLACE INTO test_replace VALUES (1, 'New', ?);", [timestamp]) end + + @tag :json + test "encode and decode json", context do + map = %{"hoge" => "1", "huga" => "2"} + map_string = ~s|{"hoge": "1", "huga": "2"}| + + table = "test_jsons" + + sql = ~s{CREATE TABLE #{table} (id int, map json)} + :ok = query(sql, []) + + insert = ~s{INSERT INTO #{table} (id, map) VALUES (?, ?)} + :ok = query(insert, [1, map_string]) + + assert query("SELECT map FROM #{table} WHERE id = 1", []) == [[map]] + end end diff --git a/test/test_helper.exs b/test/test_helper.exs index cf5ecd6..b55c8b8 100644 --- a/test/test_helper.exs +++ b/test/test_helper.exs @@ -1,4 +1,4 @@ -ExUnit.configure exclude: [:ssl_tests] +ExUnit.configure exclude: [ssl_tests: :true, json: System.get_env("JSON_SUPPORT") != "true"] ExUnit.start() run_cmd = fn cmd -> diff --git a/test/text_query_test.exs b/test/text_query_test.exs index 1e1edc7..e1a71a2 100644 --- a/test/text_query_test.exs +++ b/test/text_query_test.exs @@ -28,6 +28,24 @@ defmodule TextQueryTest do (2, false, b'11', 'goodbye', 'earth', 1.2, '2016-09-26T16:36:07', '0001-01-01 00:00:01') """ {:ok, _} = Mariaex.query(pid, insert, [], [query_type: :text]) + + if System.get_env("JSON_SUPPORT") === "true" do + create = """ + CREATE TABLE test_text_json_query_table ( + id serial, + map json, + dt datetime + ) + """ + {:ok, _} = Mariaex.query(pid, create, [], [query_type: :text]) + insert = """ + INSERT INTO test_text_json_query_table (id, map, dt) + VALUES + (1, '{"hoge": "1", "huga": "2"}', '2017-01-01 00:00:00'), + (2, '{"hoge": "3", "huga": "4"}', '2017-01-01 00:00:01') + """ + {:ok, _} = Mariaex.query(pid, insert, [], [query_type: :text]) + end {:ok, [pid: pid]} end @@ -83,4 +101,12 @@ defmodule TextQueryTest do {:ok, %{rows: rows}} = Mariaex.query(pid, "SELECT dt FROM test_text_query_table", [], query_type: :text) assert(rows == [[{{1,1,1}, {0,0,0,0}}], [{{1,1,1}, {0,0,1,0}}]]) end + + @tag :json + test "select json", context do + opts = [json_library: Poison] + + rows = execute_text("SELECT map FROM test_text_json_query_table", [], opts) + assert(rows == [[%{"hoge" => "1", "huga" => "2"}], [%{"hoge" => "3", "huga" => "4"}]]) + end end