Packages
postgrex
0.11.0
1.0.0-rc.1
retired
1.0.0-rc.0
retired
0.22.3
0.22.2
0.22.1
0.22.0
0.21.1
0.21.0
0.20.0
0.19.3
0.19.2
0.19.1
0.19.0
0.18.0
0.17.5
0.17.4
0.17.3
0.17.2
0.17.1
0.17.0
0.16.5
0.16.4
0.16.3
0.16.2
0.16.1
0.16.0
0.15.13
0.15.12
0.15.11
0.15.10
0.15.9
0.15.8
0.15.7
0.15.6
0.15.5
0.15.4
0.15.3
0.15.2
0.15.1
0.15.0
0.14.3
0.14.2
0.14.1
0.14.0
0.14.0-rc.1
0.14.0-rc.0
0.13.5
0.13.4
0.13.3
0.13.2
0.13.1
0.13.0
0.13.0-rc.0
0.12.2
0.12.1
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.9.1
0.9.0
0.8.4
0.8.3
0.8.2
0.8.1
0.8.0
0.7.0
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.2
PostgreSQL driver for Elixir
Current section
Files
Jump to
Current section
Files
lib/postgrex/extensions/array.ex
defmodule Postgrex.Extensions.Array do
@moduledoc false
alias Postgrex.TypeInfo
alias Postgrex.Types
import Postgrex.BinaryUtils
use Postgrex.BinaryExtension, [] # send: "array_send" hard coded in types
def encode(%TypeInfo{array_elem: elem_oid}, list, types, _) when is_list(list),
do: encode_array(list, elem_oid, types)
def encode(type_info, value, _, _) do
raise ArgumentError, Postgrex.Utils.encode_msg(type_info, value, "a list")
end
def decode(_, bin, types, _),
do: decode_array(bin, types)
## Helpers
defp encode_array(list, elem_oid, types) do
encoder = &Types.encode(elem_oid, &1, types)
{data, ndims, lengths} = encode_array(list, 0, [], encoder)
lengths = for len <- Enum.reverse(lengths), do: <<len :: int32, 1 :: int32>>
[<<ndims :: int32, 0 :: int32, elem_oid :: uint32>>, lengths, data]
end
defp encode_array([], ndims, lengths, _encoder) do
{"", ndims, lengths}
end
defp encode_array([head|tail]=list, ndims, lengths, encoder)
when is_list(head) do
lengths = [length(list)|lengths]
{data, ndims, lengths} = encode_array(head, ndims, lengths, encoder)
[dimlength|_] = lengths
rest = Enum.reduce(tail, [], fn sublist, acc ->
{data, _, [len|_]} = encode_array(sublist, ndims, lengths, encoder)
if len != dimlength do
raise ArgumentError, "nested lists must have lists with matching lengths"
end
[acc|data]
end)
{[data|rest], ndims+1, lengths}
end
defp encode_array(list, ndims, lengths, encoder) do
{data, length} =
Enum.map_reduce(list, 0, fn
nil, length ->
{<<-1::int32>>, length + 1}
elem, length ->
data = encoder.(elem)
data = [<<IO.iodata_length(data)::int32>>, data]
{data, length + 1}
end)
{data, ndims+1, [length|lengths]}
end
defp decode_array(<<ndims :: int32, _has_null :: int32, oid :: uint32,
dims :: size(ndims)-binary-unit(64), rest :: binary>>, types) do
lengths = for <<len :: int32, _lbound :: int32 <- dims>>, do: len
decoder = &Types.decode(oid, &1, types)
{array, ""} = decode_array(rest, lengths, decoder)
array
end
defp decode_array("", [], _decoder) do
{[], ""}
end
defp decode_array(rest, [len], decoder) do
array_elements(rest, len, [], decoder)
end
defp decode_array(rest, [len|lens], decoder) do
Enum.map_reduce(1..len, rest, fn _, rest ->
decode_array(rest, lens, decoder)
end)
end
defp array_elements(rest, 0, acc, _decoder) do
{Enum.reverse(acc), rest}
end
defp array_elements(<<-1 :: int32, rest :: binary>>, count, acc, decoder) do
array_elements(rest, count-1, [nil|acc], decoder)
end
defp array_elements(<<size :: int32, elem :: binary(size), rest :: binary>>,
count, acc, decoder) do
value = decoder.(elem)
array_elements(rest, count-1, [value|acc], decoder)
end
end