Browse Source

Use maps as oid DB on Erlang >=18

Сергей Прохоров 7 years ago
parent
commit
d3c2478cd1
3 changed files with 61 additions and 15 deletions
  1. 2 1
      rebar.config
  2. 2 1
      src/datatypes/epgsql_codec_hstore.erl
  3. 57 13
      src/epgsql_oid_db.erl

+ 2 - 1
rebar.config

@@ -1,4 +1,5 @@
-{erl_opts, [{platform_define, "^[0-9]+", have_maps}]}.
+{erl_opts, [{platform_define, "^[0-9]+", have_maps},
+            {platform_define, "^(1[89])|^([2-9][0-9])", 'FAST_MAPS'}]}. % Erlang >=18
 
 {eunit_opts, [verbose]}.
 

+ 2 - 1
src/datatypes/epgsql_codec_hstore.erl

@@ -24,13 +24,14 @@
 -type data_in() :: { [{key_in(), binary()}] }.
 -type data_out() :: { [{Key :: binary(), Value :: binary()}] }.
 
+-dialyzer([{nowarn_function, [encode/3]}, no_improper_lists]).
+
 %% TODO: option for output format: proplist | jiffy-object | map
 init(_, _) -> [].
 
 names() ->
     [hstore].
 
--dialyzer([{nowarn_function, [encode/3]}, no_improper_lists]).
 encode({Hstore}, hstore, _) when is_list(Hstore) ->
     Size = length(Hstore),
     Body = [[encode_key(K) | encode_value(V)]

+ 57 - 13
src/epgsql_oid_db.erl

@@ -20,8 +20,8 @@
          codec :: module(),
          codec_state :: any()}).
 -record(oid_db,
-        {by_oid :: dict:dict(oid(), #type{}),
-         by_name :: dict:dict(epgsql:type_name(), #type{})}).
+        {by_oid :: kv(oid(), #type{}),
+         by_name :: kv(epgsql:type_name(), #type{})}).
 
 -type oid() :: non_neg_integer().
 -type oid_entry() :: {epgsql:type_name(), Oid :: oid(), ArrayOid :: oid()}.
@@ -89,21 +89,21 @@ do_join([], _) ->
 
 -spec from_list([type_info()]) -> db().
 from_list(Types) ->
-    #oid_db{by_oid = dict:from_list(
+    #oid_db{by_oid = kv_from_list(
                        [{Oid, Type} || #type{oid = Oid} = Type <- Types]),
-            by_name = dict:from_list(
+            by_name = kv_from_list(
                         [{{Name, IsArray}, Oid}
                          || #type{name = Name, is_array = IsArray, oid = Oid}
                                 <- Types])}.
 
 to_list(#oid_db{by_oid = Dict}) ->
-    [Type || {_Oid, Type} <- dict:to_list(Dict)].
+    [Type || {_Oid, Type} <- kv_to_list(Dict)].
 
 -spec update([type_info()], db()) -> db().
 update(Types, #oid_db{by_oid = OldByOid, by_name = OldByName} = Store) ->
     #oid_db{by_oid = NewByOid, by_name = NewByName} = from_list(Types),
-    ByOid = dict:merge(fun(_, _, V2) -> V2 end, OldByOid, NewByOid),
-    ByName = dict:merge(fun(_, _, V2) -> V2 end, OldByName, NewByName),
+    ByOid = kv_merge(OldByOid, NewByOid),
+    ByName = kv_merge(OldByName, NewByName),
     Store#oid_db{by_oid = ByOid,
                  by_name = ByName}.
 
@@ -111,19 +111,16 @@ update(Types, #oid_db{by_oid = OldByOid, by_name = OldByName} = Store) ->
 %% find_by_oid(?RECORD_OID, _) ->
 %%     '$record';
 find_by_oid(Oid, #oid_db{by_oid = Dict}) ->
-    case dict:find(Oid, Dict) of
-        {ok, Type} -> Type;
-        error -> undefined
-    end.
+    kv_get(Oid, Dict, undefined).
 
 -spec find_by_name(epgsql:type_name(), boolean(), db()) -> type_info().
 find_by_name(Name, IsArray, #oid_db{by_oid = ByOid} = Db) ->
     Oid = oid_by_name(Name, IsArray, Db),
-    dict:fetch(Oid, ByOid).                  % or maybe find_by_oid(Oid, Store)
+    kv_get(Oid, ByOid).                  % or maybe find_by_oid(Oid, Store)
 
 -spec oid_by_name(epgsql:type_name(), boolean(), db()) -> oid().
 oid_by_name(Name, IsArray, #oid_db{by_name = ByName}) ->
-    dict:fetch({Name, IsArray}, ByName).
+    kv_get({Name, IsArray}, ByName).
 
 -spec type_to_codec_entry(type_info()) -> epgsql_codec:codec_entry().
 type_to_codec_entry(#type{name = Name, codec = Codec, codec_state = State}) ->
@@ -144,3 +141,50 @@ join(Sep, [H | T]) -> [H | join_prepend(Sep, T)].
 
 join_prepend(_Sep, []) -> [];
 join_prepend(Sep, [H | T]) -> [Sep, H | join_prepend(Sep, T)].
+
+
+%% K-V storage
+%% In Erlang 17 map access time is O(n), so, it's faster to use dicts.
+%% In Erlang >=18 maps are the most eficient choice
+-ifdef(FAST_MAPS).
+
+-type kv(K, V) :: #{K => V}.
+
+kv_from_list(L) ->
+    maps:from_list(L).
+
+kv_to_list(Map) ->
+    maps:to_list(Map).
+
+kv_get(Key, Map) ->
+    maps:get(Key, Map).
+
+kv_get(Key, Map, Default) ->
+    maps:get(Key, Map, Default).
+
+kv_merge(Map1, Map2) ->
+    maps:merge(Map1, Map2).
+
+-else.
+
+-type kv(K, V) :: dict:dict(K, V).
+
+kv_from_list(L) ->
+    dict:from_list(L).
+
+kv_to_list(Dict) ->
+    dict:to_list(Dict).
+
+kv_get(Key, Dict) ->
+    dict:fetch(Key, Dict).
+
+kv_get(Key, Dict, Default) ->
+    case dict:find(Key, Dict) of
+        {ok, Value} -> Value;
+        error -> Default
+    end.
+
+kv_merge(Dict1, Dict2) ->
+    dict:merge(fun(_, _, V2) -> V2 end, Dict1, Dict2).
+
+-endif.