Skip to content

Commit

Permalink
Respect precision for intervals (#699)
Browse files Browse the repository at this point in the history
  • Loading branch information
greg-rychlewski authored Jul 27, 2024
1 parent e5c3a34 commit db2e0a2
Show file tree
Hide file tree
Showing 6 changed files with 53 additions and 15 deletions.
16 changes: 15 additions & 1 deletion lib/postgrex/extensions/interval.ex
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,13 @@ defmodule Postgrex.Extensions.Interval do
def init(opts), do: Keyword.get(opts, :interval_decode_type, Postgrex.Interval)

if Code.ensure_loaded?(Duration) do
import Bitwise, warn: false
@default_precision 6
@precision_mask 0xFFFF
# 0xFFFF: user did not specify precision (2's complement version of -1)
# nil: coming from a super type that does not pass modifier for sub-type
@unspecified_precision [0xFFFF, nil]

def encode(_) do
quote location: :keep do
%Postgrex.Interval{months: months, days: days, secs: seconds, microsecs: microseconds} ->
Expand Down Expand Up @@ -57,6 +64,13 @@ defmodule Postgrex.Extensions.Interval do
seconds = rem(seconds, 60)
hours = div(minutes, 60)
minutes = rem(minutes, 60)
type_mod = var!(mod)
precision = if type_mod, do: type_mod &&& unquote(@precision_mask)

precision =
if precision in unquote(@unspecified_precision),
do: unquote(@default_precision),
else: precision

Duration.new!(
year: years,
Expand All @@ -66,7 +80,7 @@ defmodule Postgrex.Extensions.Interval do
hour: hours,
minute: minutes,
second: seconds,
microsecond: {microseconds, 6}
microsecond: {microseconds, precision}
)
end
end
Expand Down
8 changes: 4 additions & 4 deletions lib/postgrex/extensions/time.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ defmodule Postgrex.Extensions.Time do
use Postgrex.BinaryExtension, send: "time_send"

@default_precision 6
# -1: user did not specify precision
# nil: coming from a super type that does not pass modifier for sub-type
@unspecified_precision [-1, nil]

def encode(_) do
quote location: :keep do
Expand Down Expand Up @@ -31,10 +34,7 @@ defmodule Postgrex.Extensions.Time do
end

def microsecond_to_elixir(microsec, precision) do
# use the default precision if the precision modifier from postgres is -1 (this means no precision specified)
# or if the precision is missing because we are in a super type which does not give us the sub-type's modifier
precision = if precision in [-1, nil], do: @default_precision, else: precision

precision = if precision in @unspecified_precision, do: @default_precision, else: precision
sec = div(microsec, 1_000_000)
microsec = rem(microsec, 1_000_000)

Expand Down
7 changes: 4 additions & 3 deletions lib/postgrex/extensions/timestamp.ex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ defmodule Postgrex.Extensions.Timestamp do
@plus_infinity 9_223_372_036_854_775_807
@minus_infinity -9_223_372_036_854_775_808
@default_precision 6
# -1: user did not specify precision
# nil: coming from a super type that does not pass modifier for sub-type
@unspecified_precision [-1, nil]

def init(opts), do: Keyword.get(opts, :allow_infinite_timestamps, false)

Expand Down Expand Up @@ -77,9 +80,7 @@ defmodule Postgrex.Extensions.Timestamp do
end

defp split(secs, microsecs, precision) do
# use the default precision if the precision modifier from postgres is -1 (this means no precision specified)
# or if the precision is missing because we are in a super type which does not give us the sub-type's modifier
precision = if precision in [-1, nil], do: @default_precision, else: precision
precision = if precision in @unspecified_precision, do: @default_precision, else: precision
NaiveDateTime.from_gregorian_seconds(secs + @gs_epoch, {microsecs, precision})
end

Expand Down
7 changes: 4 additions & 3 deletions lib/postgrex/extensions/timestamptz.ex
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ defmodule Postgrex.Extensions.TimestampTZ do
@plus_infinity 9_223_372_036_854_775_807
@minus_infinity -9_223_372_036_854_775_808
@default_precision 6
# -1: user did not specify precision
# nil: coming from a super type that does not pass modifier for sub-type
@unspecified_precision [-1, nil]

def init(opts), do: Keyword.get(opts, :allow_infinite_timestamps, false)

Expand Down Expand Up @@ -67,9 +70,7 @@ defmodule Postgrex.Extensions.TimestampTZ do
end

defp split(secs, microsecs, precision) do
# use the default precision if the precision modifier from postgres is -1 (this means no precision specified)
# or if the precision is missing because we are in a super type which does not give us the sub-type's modifier
precision = if precision in [-1, nil], do: @default_precision, else: precision
precision = if precision in @unspecified_precision, do: @default_precision, else: precision
DateTime.from_gregorian_seconds(secs + @gs_epoch, {microsecs, precision})
end

Expand Down
8 changes: 4 additions & 4 deletions lib/postgrex/extensions/timetz.ex
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ defmodule Postgrex.Extensions.TimeTZ do

@day (:calendar.time_to_seconds({23, 59, 59}) + 1) * 1_000_000
@default_precision 6
# -1: user did not specify precision
# nil: coming from a super type that does not pass modifier for sub-type
@unspecified_precision [-1, nil]

def encode(_) do
quote location: :keep do
Expand Down Expand Up @@ -51,10 +54,7 @@ defmodule Postgrex.Extensions.TimeTZ do
end

defp microsecond_to_elixir(microsec, precision) do
# use the default precision if the precision modifier from postgres is -1 (this means no precision specified)
# or if the precision is missing because we are in a super type which does not give us the sub-type's modifier
precision = if precision in [-1, nil], do: @default_precision, else: precision

precision = if precision in @unspecified_precision, do: @default_precision, else: precision
sec = div(microsec, 1_000_000)
microsec = rem(microsec, 1_000_000)

Expand Down
22 changes: 22 additions & 0 deletions test/query_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,28 @@ defmodule QueryTest do
assert [[[%Duration{second: 10, microsecond: {240_000, 6}}]]] =
P.query!(pid, "SELECT ARRAY[interval '10240000 microseconds']", []).rows
end

test "decode interval with Elixir Duration: precision is given" do
opts = [database: "postgrex_test", backoff_type: :stop, types: Postgrex.ElixirDurationTypes]
{:ok, pid} = P.start_link(opts)

assert [[%Duration{second: 10, microsecond: {240_000, 2}}]] =
P.query!(pid, "SELECT interval(2) '10240000 microseconds'", []).rows

assert [[[%Duration{second: 10, microsecond: {0, 0}}]]] =
P.query!(pid, "SELECT ARRAY[interval(0) '10240000 microseconds']", []).rows
end

test "decode interval with Elixir Duration: field is given but not precision" do
opts = [database: "postgrex_test", backoff_type: :stop, types: Postgrex.ElixirDurationTypes]
{:ok, pid} = P.start_link(opts)

assert [[%Duration{week: 1, day: 3, microsecond: {0, 6}}]] =
P.query!(pid, "SELECT interval '10' DAY", []).rows

assert [[[%Duration{week: 1, day: 3, microsecond: {0, 6}}]]] =
P.query!(pid, "SELECT ARRAY[interval '10' DAY]", []).rows
end
end

test "decode point", context do
Expand Down

0 comments on commit db2e0a2

Please sign in to comment.