Packages
postgrex
0.13.5
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/numeric.ex
defmodule Postgrex.Extensions.Numeric do
@moduledoc false
import Postgrex.BinaryUtils, warn: false
use Postgrex.BinaryExtension, send: "numeric_send"
def encode(_) do
quote location: :keep do
%Decimal{} = decimal ->
data = unquote(__MODULE__).encode_numeric(decimal)
[<<IO.iodata_length(data)::int32>> | data]
n when is_number(n) ->
data = unquote(__MODULE__).encode_numeric(Decimal.new(n))
[<<IO.iodata_length(data)::int32>> | data]
end
end
def decode(_) do
quote location: :keep do
<<len :: int32, data :: binary-size(len)>> ->
unquote(__MODULE__).decode_numeric(data)
end
end
## Helpers
def encode_numeric(%Decimal{coef: coef}) when coef in [:qNaN, :sNaN] do
<<0 :: int16, 0 :: int16, 0xC000 :: uint16, 0 :: int16>>
end
def encode_numeric(%Decimal{sign: sign, coef: coef, exp: exp}) do
sign = encode_sign(sign)
scale = -exp
{integer, float, scale} = split_parts(coef, scale)
integer_digits = encode_digits(integer, [])
float_digits = encode_float(float, scale)
digits = integer_digits ++ float_digits
num_digits = length(digits)
weight = max(length(integer_digits) - 1, 0)
bin = for digit <- digits, into: "", do: <<digit :: uint16>>
[<<num_digits :: int16, weight :: int16, sign :: uint16, scale :: int16>> | bin]
end
defp encode_sign(1), do: 0x0000
defp encode_sign(-1), do: 0x4000
defp split_parts(coef, scale) when scale >= 0 do
integer_base = pow10(scale)
{div(coef, integer_base), rem(coef, integer_base), scale}
end
defp split_parts(coef, scale) when scale < 0 do
integer_base = pow10(-scale)
{coef * integer_base, 0, 0}
end
defp encode_float(float, scale) do
pending = pending_scale(float, scale)
float_prefix = div(pending, 4)
float_suffix = 4 - rem(scale, 4)
float = float * pow10(float_suffix)
List.duplicate(0, float_prefix) ++ encode_digits(float, [])
end
defp pending_scale(0, scale), do: scale
defp pending_scale(num, scale), do: pending_scale(div(num, 10), scale - 1)
defp encode_digits(coef, digits) when coef < 10000 do
[coef|digits]
end
defp encode_digits(coef, digits) do
digit = rem(coef, 10000)
coef = div(coef, 10000)
encode_digits(coef, [digit|digits])
end
def decode_numeric(<<ndigits :: int16, weight :: int16, sign :: uint16, scale :: int16, tail :: binary>>) do
decode_numeric(ndigits, weight, sign, scale, tail)
end
defp decode_numeric(0, _weight, 0xC000, _scale, "") do
Decimal.new(1, :qNaN, 0)
end
defp decode_numeric(_num_digits, weight, sign, scale, bin) do
{value, weight} = decode_numeric_int(bin, weight, 0)
sign = decode_sign(sign)
coef = scale(value, (weight + 1) * 4 + scale)
Decimal.new(sign, coef, -scale)
end
defp decode_sign(0x0000), do: 1
defp decode_sign(0x4000), do: -1
defp scale(coef, 0), do: coef
defp scale(coef, diff) when diff < 0, do: div(coef, pow10(-diff))
defp scale(coef, diff) when diff > 0, do: coef * pow10(diff)
Enum.reduce 0..100, 1, fn x, acc ->
defp pow10(unquote(x)), do: unquote(acc)
acc * 10
end
defp pow10(num) when num > 100, do: pow10(100) * pow10(num-100)
defp decode_numeric_int("", weight, acc), do: {acc, weight}
defp decode_numeric_int(<<digit :: int16, tail :: binary>>, weight, acc) do
acc = (acc * 10000) + digit
decode_numeric_int(tail, weight - 1, acc)
end
end