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