Browse Source

int4range support

Jean Rouge 10 years ago
parent
commit
d4334ce4a1
4 changed files with 56 additions and 1 deletions
  1. 26 0
      src/epgsql_binary.erl
  2. 2 0
      src/epgsql_types.erl
  3. 26 0
      test/epgsql_tests.erl
  4. 2 1
      test_data/test_schema.sql

+ 26 - 0
src/epgsql_binary.erl

@@ -77,6 +77,7 @@ encode(point, {X,Y}, _)                     -> encode_point({X,Y});
 encode(geometry, Data, _)                   -> encode_geometry(Data);
 encode(cidr, B, Codec)                      -> encode(bytea, encode_net(B), Codec);
 encode(inet, B, Codec)                      -> encode(bytea, encode_net(B), Codec);
+encode(int4range, R, _) when is_tuple(R)    -> encode_int4range(R);
 encode(Type, L, Codec) when is_list(L)      -> encode(Type, list_to_binary(L), Codec);
 encode(_Type, _Value, _)                    -> {error, unsupported}.
 
@@ -102,6 +103,7 @@ decode(cidr, B, _)                             -> decode_net(B);
 decode({array, _Type}, B, Codec)               -> decode_array(B, Codec);
 decode(point, B, _)                            -> decode_point(B);
 decode(geometry, B, _)                         -> ewkb:decode_geometry(B);
+decode(int4range, B, _)                        -> decode_int4range(B);
 decode(_Other, Bin, _)                         -> Bin.
 
 encode_array(Type, Oid, A, Codec) ->
@@ -232,6 +234,30 @@ decode_net(<<?INET, ?MAX_IP_MASK, 0, ?IP_SIZE, Bin/binary>>) ->
 decode_net(<<?INET6, ?MAX_IP6_MASK, 0, ?IP6_SIZE, Bin/binary>>) ->
     list_to_tuple([X || <<X:16>> <= Bin]).
 
+%% @doc encode an int4range
+encode_int4range({minus_infinity, plus_infinity}) ->
+    <<1:?int32, 24:1/big-signed-unit:8>>;
+encode_int4range({From, plus_infinity}) ->
+    FromInt = to_int(From),
+    <<9:?int32, 18:1/big-signed-unit:8, 4:?int32, FromInt:?int32>>;
+encode_int4range({minus_infinity, To}) ->
+    ToInt = to_int(To),
+    <<9:?int32, 8:1/big-signed-unit:8, 4:?int32, ToInt:?int32>>;
+encode_int4range({From, To}) ->
+    FromInt = to_int(From),
+    ToInt = to_int(To),
+    <<17:?int32, 2:1/big-signed-unit:8, 4:?int32, FromInt:?int32, 4:?int32, ToInt:?int32>>.
+
+to_int(N) when is_integer(N) -> N;
+to_int(S) when is_list(S) -> erlang:list_to_integer(S);
+to_int(B) when is_binary(B) -> erlang:binary_to_integer(B).
+
+%% @doc decode an int4range
+decode_int4range(<<2:1/big-signed-unit:8, 4:?int32, From:?int32, 4:?int32, To:?int32>>) -> {From, To};
+decode_int4range(<<8:1/big-signed-unit:8, 4:?int32, To:?int32>>) -> {minus_infinity, To};
+decode_int4range(<<18:1/big-signed-unit:8, 4:?int32, From:?int32>>) -> {From, plus_infinity};
+decode_int4range(<<24:1/big-signed-unit:8>>) -> {minus_infinity, plus_infinity}.
+
 supports(bool)    -> true;
 supports(bpchar)  -> true;
 supports(int2)    -> true;

+ 2 - 0
src/epgsql_types.erl

@@ -95,6 +95,7 @@ oid2type(2776)  -> anynonarray;
 oid2type(2950)  -> uuid;
 oid2type(2951)  -> {array, uuid};
 oid2type(3500)  -> anyenum;
+oid2type(3904)  -> int4range;
 oid2type(Oid)   -> {unknown_oid, Oid}.
 
 type2oid(bool)                  -> 16;
@@ -190,4 +191,5 @@ type2oid(anynonarray)           -> 2776;
 type2oid(uuid)                  -> 2950;
 type2oid({array, uuid})         -> 2951;
 type2oid(anyenum)               -> 3500;
+type2oid(int4range)             -> 3904;
 type2oid(Type)                  -> {unknown_type, Type}.

+ 26 - 0
test/epgsql_tests.erl

@@ -761,6 +761,17 @@ application_test(_Module) ->
     ok = application:start(epgsql),
     ok = application:stop(epgsql).
 
+range_type_test(Module) ->
+    with_min_version(
+      Module,
+      9.2,
+      fun(_C) ->
+          check_type(Module, int4range, "int4range(10, 20)", <<"[10,20)">>,
+                     [{1, 58}, {-1, 12}, {-985521, 5412687}, {minus_infinity, 0},
+                      {984655, plus_infinity}, {minus_infinity, plus_infinity}])
+      end,
+      []).
+
 %% -- run all tests --
 
 run_tests() ->
@@ -905,8 +916,23 @@ compare(Type, V1 = {_, _, MS}, {D2, {H2, M2, S2}}) when Type == timestamp;
                                                         Type == timestamptz ->
     {D1, {H1, M1, S1}} = calendar:now_to_universal_time(V1),
     ({D1, H1, M1} =:= {D2, H2, M2}) and (abs(S1 + MS/1000000 - S2) < 0.000000000000001);
+compare(int4range, {Lower, Upper}, Result) ->
+  translate_infinities(Lower, Upper) =:= Result;
 compare(_Type, V1, V2)     -> V1 =:= V2.
 
+translate_infinities(Lower, Upper) ->
+  iolist_to_binary([lower(Lower), [","], upper(Upper)]).
+
+lower(minus_infinity) ->
+  "(";
+lower(Val) ->
+  io_lib:format("[~p", [Val]).
+
+upper(plus_infinity) ->
+  ")";
+upper(Val) ->
+  io_lib:format("~p)", [Val]).
+
 format_hstore({Hstore}) -> Hstore;
 format_hstore(Hstore) ->
     [{format_hstore_key(Key), format_hstore_value(Value)} || {Key, Value} <- Hstore].

+ 2 - 1
test_data/test_schema.sql

@@ -62,7 +62,8 @@ CREATE TABLE test_table2 (
   c_point point,
   c_geometry geometry,
   c_cidr cidr,
-  c_inet inet);
+  c_inet inet,
+  c_int4range int4range);
 
 CREATE LANGUAGE plpgsql;