epgsql_codec.erl 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. %%% @doc
  2. %%% Behaviour for postgresql datatype codecs.
  3. %%%
  4. %%% XXX: this module and callbacks "know nothing" about OIDs.
  5. %%% XXX: state of codec shouldn't leave epgsql_sock process. If you need to
  6. %%% return "pointer" to data type/codec, it's better to return OID or type name.
  7. %%% @end
  8. %%% @see epgsql_binary
  9. %%% @end
  10. %%% Created : 12 Oct 2017 by Sergey Prokhorov <me@seriyps.ru>
  11. -module(epgsql_codec).
  12. -export([init_mods/2, encode/4, decode/4, decode_text/4]).
  13. -export_type([codec_state/0, codec_mod/0, codec_entry/0]).
  14. %%
  15. %% Behaviour
  16. %%
  17. -type codec_state() :: any().
  18. -type codec_mod() :: module().
  19. -optional_callbacks([decode_text/3]).
  20. %% Called on connection start-up
  21. -callback init(any(), epgsql_sock:pg_sock()) -> codec_state().
  22. %% List of supported type names
  23. -callback names() -> [epgsql:type_name()].
  24. %% Encode Erlang representation to PG binary
  25. %% Called for each parameter, binary protocol (equery)
  26. -callback encode(Cell :: any(), epgsql:type_name(), codec_state()) -> iodata().
  27. %% Decode PG binary to erlang representation
  28. %% Called for each cell in each row, binary protocol (equery)
  29. -callback decode(Cell :: binary(), epgsql:type_name(), codec_state()) -> any().
  30. %% Decode PG string representation (text protocol) to erlang term.
  31. %% Called for each cell in each row, text protocol (squery)
  32. -callback decode_text(Cell :: binary(), epgsql:type_name(), codec_state()) ->
  33. any().
  34. %% ==========
  35. -type codec_entry() :: {epgsql:type_name(),
  36. Mod :: codec_mod(),
  37. CallbackState :: any()}.
  38. -spec init_mods([{codec_mod(), any()}], epgsql_sock:pg_sock()) ->
  39. ordsets:ordset(codec_entry()).
  40. init_mods(Codecs, PgSock) ->
  41. ModState = [{Mod, Mod:init(Opts, PgSock)} || {Mod, Opts} <- Codecs],
  42. build_mapping(ModState, sets:new(), []).
  43. build_mapping([{Mod, _State} = MS | ModStates], Set, Acc) ->
  44. Names = Mod:names(),
  45. {Set1, Acc1} = add_names(Names, MS, Set, Acc),
  46. build_mapping(ModStates, Set1, Acc1);
  47. build_mapping([], _, Acc) ->
  48. ordsets:from_list(Acc).
  49. add_names([Name | Names], {Mod, State} = MS, Set, Acc) ->
  50. case sets:is_element(Name, Set) of
  51. true ->
  52. add_names(Names, MS, Set, Acc);
  53. false ->
  54. Set1 = sets:add_element(Name, Set),
  55. Acc1 = [{Name, Mod, State} | Acc],
  56. add_names(Names, MS, Set1, Acc1)
  57. end;
  58. add_names([], _, Set, Acc) ->
  59. {Set, Acc}.
  60. -spec encode(codec_mod(), any(), epgsql:type_name(), codec_state()) -> iodata().
  61. encode(Mod, Cell, TypeName, CodecState) ->
  62. Mod:encode(Cell, TypeName, CodecState).
  63. -spec decode(codec_mod(), binary(), epgsql:type_name(), codec_state()) -> any().
  64. decode(Mod, Cell, TypeName, CodecState) ->
  65. Mod:decode(Cell, TypeName, CodecState).
  66. -spec decode_text(codec_mod(), binary(), epgsql:type_name(), codec_state()) -> any().
  67. decode_text(Mod, Cell, TypeName, CodecState) ->
  68. Mod:decode(Cell, TypeName, CodecState).