Packages
postgrex
0.18.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
Security advisory:
This version has known vulnerabilities.
View advisories
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, generated: true do
%Decimal{} = decimal ->
data = unquote(__MODULE__).encode_numeric(decimal)
[<<IO.iodata_length(data)::int32()>> | data]
n when is_float(n) ->
data = unquote(__MODULE__).encode_numeric(Decimal.from_float(n))
[<<IO.iodata_length(data)::int32()>> | data]
n when is_integer(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
# TODO: remove qNaN and sNaN when we depend on Decimal 2.0
def encode_numeric(%Decimal{coef: coef}) when coef in [:NaN, :qNaN, :sNaN] do
<<0::int16(), 0::int16(), 0xC000::uint16(), 0::int16()>>
end
def encode_numeric(%Decimal{sign: 1, coef: :inf}) do
<<0::int16(), 0::int16(), 0xD000::uint16(), 0::int16()>>
end
def encode_numeric(%Decimal{sign: -1, coef: :inf}) do
<<0::int16(), 0::int16(), 0xF000::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 < 10_000 do
[coef | digits]
end
defp encode_digits(coef, digits) do
digit = rem(coef, 10_000)
coef = div(coef, 10_000)
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
@nan Decimal.new("NaN")
@positive_inf Decimal.new("Inf")
@negative_inf Decimal.new("-Inf")
defp decode_numeric(0, _weight, 0xC000, _scale, "") do
@nan
end
defp decode_numeric(0, _weight, 0xD000, _scale, "") do
@positive_inf
end
defp decode_numeric(0, _weight, 0xF000, _scale, "") do
@negative_inf
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 * 10_000 + digit
decode_numeric_int(tail, weight - 1, acc)
end
end