@@ -3,15 +3,21 @@ defmodule Postgrex.Extensions.Interval do
33 import Postgrex.BinaryUtils , warn: false
44 use Postgrex.BinaryExtension , send: "interval_send"
55
6+ @ int64_max 9_223_372_036_854_775_807
7+ @ int64_min - 9_223_372_036_854_775_808
8+ @ int32_max 2_147_483_647
9+ @ int32_min - 2_147_483_648
10+
611 def init ( opts ) do
7- case Keyword . get ( opts , :interval_decode_type , Postgrex.Interval ) do
8- type when type in [ Postgrex.Interval , Duration ] ->
9- type
12+ infinity? = Keyword . get ( opts , :allow_infinite_intervals , false )
13+ type = Keyword . get ( opts , :interval_decode_type , Postgrex.Interval )
1014
11- other ->
12- raise ArgumentError ,
13- "#{ inspect ( other ) } is not valid for `:interval_decode_type`. Please use either `Postgrex.Interval` or `Duration`"
15+ if type not in [ Postgrex.Interval , Duration ] do
16+ raise ArgumentError ,
17+ "#{ inspect ( type ) } is not valid for `:interval_decode_type`. Please use either `Postgrex.Interval` or `Duration`"
1418 end
19+
20+ { type , infinity? }
1521 end
1622
1723 if Code . ensure_loaded? ( Duration ) do
@@ -22,8 +28,18 @@ defmodule Postgrex.Extensions.Interval do
2228 # nil: coming from a super type that does not pass modifier for sub-type
2329 @ unspecified_precision [ 0xFFFF , nil ]
2430
25- def encode ( _ ) do
31+ def encode ( { _type , infinity? } ) do
2632 quote location: :keep do
33+ :inf ->
34+ if unquote ( infinity? ) ,
35+ do: unquote ( __MODULE__ ) . infinity_binary ( :inf ) ,
36+ else: unquote ( __MODULE__ ) . raise_encode_infinity ( :inf )
37+
38+ :"-inf" ->
39+ if unquote ( infinity? ) ,
40+ do: unquote ( __MODULE__ ) . infinity_binary ( :"-inf" ) ,
41+ else: unquote ( __MODULE__ ) . raise_encode_infinity ( :"-inf" )
42+
2743 % Postgrex.Interval { months: months , days: days , secs: seconds , microsecs: microseconds } ->
2844 microseconds = 1_000_000 * seconds + microseconds
2945 << 16 :: int32 ( ) , microseconds :: int64 ( ) , days :: int32 ( ) , months :: int32 ( ) >>
@@ -49,22 +65,31 @@ defmodule Postgrex.Extensions.Interval do
4965 end
5066 end
5167
52- def decode ( type ) do
68+ def decode ( { type , infinity? } ) do
5369 quote location: :keep , generated: true do
5470 << 16 :: int32 ( ) , microseconds :: int64 ( ) , days :: int32 ( ) , months :: int32 ( ) >> ->
5571 unquote ( __MODULE__ ) . decode_interval (
5672 microseconds ,
5773 days ,
5874 months ,
5975 var! ( mod ) ,
60- unquote ( type )
76+ unquote ( type ) ,
77+ unquote ( infinity? )
6178 )
6279 end
6380 end
6481
6582 ## Helpers
6683
67- def decode_interval ( microseconds , days , months , _type_mod , Postgrex.Interval ) do
84+ def decode_interval ( @ int64_max , @ int32_max , @ int32_max , _type_mod , _struct , infinity? ) do
85+ if infinity? , do: :inf , else: raise_decode_infinity ( "infinity" )
86+ end
87+
88+ def decode_interval ( @ int64_min , @ int32_min , @ int32_min , _type_mod , _struct , infinity? ) do
89+ if infinity? , do: :"-inf" , else: raise_decode_infinity ( "-infinity" )
90+ end
91+
92+ def decode_interval ( microseconds , days , months , _type_mod , Postgrex.Interval , _infinity? ) do
6893 seconds = div ( microseconds , 1_000_000 )
6994 microseconds = rem ( microseconds , 1_000_000 )
7095
@@ -76,7 +101,7 @@ defmodule Postgrex.Extensions.Interval do
76101 }
77102 end
78103
79- def decode_interval ( microseconds , days , months , type_mod , Duration ) do
104+ def decode_interval ( microseconds , days , months , type_mod , Duration , _infinity? ) do
80105 seconds = div ( microseconds , 1_000_000 )
81106 microseconds = rem ( microseconds , 1_000_000 )
82107 precision = if type_mod , do: type_mod &&& unquote ( @ precision_mask )
@@ -94,8 +119,18 @@ defmodule Postgrex.Extensions.Interval do
94119 )
95120 end
96121 else
97- def encode ( _ ) do
122+ def encode ( { _type , infinity? } ) do
98123 quote location: :keep do
124+ :inf ->
125+ if unquote ( infinity? ) ,
126+ do: unquote ( __MODULE__ ) . infinity_binary ( :inf ) ,
127+ else: unquote ( __MODULE__ ) . raise_encode_infinity ( :inf )
128+
129+ :"-inf" ->
130+ if unquote ( infinity? ) ,
131+ do: unquote ( __MODULE__ ) . infinity_binary ( :"-inf" ) ,
132+ else: unquote ( __MODULE__ ) . raise_encode_infinity ( :"-inf" )
133+
99134 % Postgrex.Interval { months: months , days: days , secs: seconds , microsecs: microseconds } ->
100135 microseconds = 1_000_000 * seconds + microseconds
101136 << 16 :: int32 ( ) , microseconds :: int64 ( ) , days :: int32 ( ) , months :: int32 ( ) >>
@@ -105,16 +140,30 @@ defmodule Postgrex.Extensions.Interval do
105140 end
106141 end
107142
108- def decode ( _ ) do
143+ def decode ( { _type , infinity? } ) do
109144 quote location: :keep do
110145 << 16 :: int32 ( ) , microseconds :: int64 ( ) , days :: int32 ( ) , months :: int32 ( ) >> ->
111- unquote ( __MODULE__ ) . decode_interval ( microseconds , days , months , Postgrex.Interval )
146+ unquote ( __MODULE__ ) . decode_interval (
147+ microseconds ,
148+ days ,
149+ months ,
150+ Postgrex.Interval ,
151+ unquote ( infinity? )
152+ )
112153 end
113154 end
114155
115156 ## Helpers
116157
117- def decode_interval ( microseconds , days , months , Postgrex.Interval ) do
158+ def decode_interval ( @ int64_max , @ int32_max , @ int32_max , _struct , infinity? ) do
159+ if infinity? , do: :inf , else: raise_decode_infinity ( "infinity" )
160+ end
161+
162+ def decode_interval ( @ int64_min , @ int32_min , @ int32_min , _struct , infinity? ) do
163+ if infinity? , do: :"-inf" , else: raise_decode_infinity ( "-infinity" )
164+ end
165+
166+ def decode_interval ( microseconds , days , months , Postgrex.Interval , _infinity? ) do
118167 seconds = div ( microseconds , 1_000_000 )
119168 microseconds = rem ( microseconds , 1_000_000 )
120169
@@ -126,4 +175,38 @@ defmodule Postgrex.Extensions.Interval do
126175 }
127176 end
128177 end
178+
179+ def infinity_binary ( :inf ) do
180+ << 16 :: int32 ( ) , @ int64_max :: int64 ( ) , @ int32_max :: int32 ( ) , @ int32_max :: int32 ( ) >>
181+ end
182+
183+ def infinity_binary ( :"-inf" ) do
184+ << 16 :: int32 ( ) , @ int64_min :: int64 ( ) , @ int32_min :: int32 ( ) , @ int32_min :: int32 ( ) >>
185+ end
186+
187+ def raise_encode_infinity ( type ) do
188+ raise ArgumentError , """
189+ got query parameter value of `#{ inspect ( type ) } `. If you want to support infinite intervals \
190+ in your application, you can enable them by defining your own types:
191+
192+ Postgrex.Types.define(MyApp.PostgrexTypes, [], allow_infinite_intervals: true)
193+
194+ And then configuring your database to use it:
195+
196+ types: MyApp.PostgrexTypes
197+ """
198+ end
199+
200+ defp raise_decode_infinity ( type ) do
201+ raise ArgumentError , """
202+ got \" #{ type } \" from PostgreSQL. If you want to support infinite intervals \
203+ in your application, you can enable them by defining your own types:
204+
205+ Postgrex.Types.define(MyApp.PostgrexTypes, [], allow_infinite_intervals: true)
206+
207+ And then configuring your database to use it:
208+
209+ types: MyApp.PostgrexTypes
210+ """
211+ end
129212end
0 commit comments