_site/cover/pgmp_types.COVER.html

1 %% Copyright (c) 2022 Peter Morgan <peter.james.morgan@gmail.com>
2 %%
3 %% Licensed under the Apache License, Version 2.0 (the "License");
4 %% you may not use this file except in compliance with the License.
5 %% You may obtain a copy of the License at
6 %%
7 %% http://www.apache.org/licenses/LICENSE-2.0
8 %%
9 %% Unless required by applicable law or agreed to in writing, software
10 %% distributed under the License is distributed on an "AS IS" BASIS,
11 %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 %% See the License for the specific language governing permissions and
13 %% limitations under the License.
14
15
16 -module(pgmp_types).
17
18
19 %% -export([write_file/1]).
20 -behaviour(gen_statem).
21 -export([cache/1]).
22 -export([callback_mode/0]).
23 -export([handle_event/4]).
24 -export([init/1]).
25 -export([server_ref/1]).
26 -export([start_link/1]).
27 -export([when_ready/1]).
28 -export_type([cache/0]).
29 -import(pgmp_statem, [nei/1]).
30 -include("pgmp_types.hrl").
31
32
33 -type cache() :: #{pgmp:oid() => #{binary() => any()}}.
34
35
36 start_link(Arg) ->
37 10 gen_statem:start_link(
38 {local, server_ref(Arg)},
39 ?MODULE,
40 [Arg],
41 envy_gen:options(?MODULE)).
42
43
44 -spec server_ref(pgmp_dbs_sup:db()) -> gen_statem:server_ref().
45
46 server_ref(#{application_name := ApplicationName}) ->
47 76 pgmp_util:snake_case([binary_to_list(ApplicationName), ?MODULE]).
48
49
50 when_ready(Arg) ->
51 66 send_request(
52 maps:merge(
53 Arg,
54 #{request => ?FUNCTION_NAME})).
55
56
57 send_request(#{label := _} = Arg) ->
58
:-(
pgmp_statem:send_request(Arg);
59
60 send_request(Arg) ->
61 66 pgmp_statem:send_request(Arg#{label => ?MODULE}).
62
63
64 %% write_file(Filename) ->
65 %% {ok, Header} = file:read_file("HEADER.txt"),
66
67 %% file:write_file(
68 %% Filename,
69 %% [io_lib:fwrite("%% -*- mode: erlang -*-~n", []),
70 %% Header,
71 %% maps:fold(
72 %% fun
73 %% (K, V, A) ->
74 %% [io_lib:fwrite("~n~p.~n", [{K, V}]) | A]
75 %% end,
76 %% [],
77 %% cache())]).
78
79
80 -spec cache(pgmp_dbs_sup:db()) -> cache().
81
82 cache(Arg) ->
83 8129 persistent_term:get(persistent_term_name(Arg)).
84
85
86 persistent_term_name(#{application_name := ApplicationName}) ->
87 8139 pgmp_util:snake_case([binary_to_list(ApplicationName), ?MODULE]).
88
89
90 init([Arg]) ->
91 10 case pgmp_config:enabled(?MODULE) of
92 true ->
93 10 {ok,
94 unready,
95 #{cache => ets:new(?MODULE, []),
96 config => Arg,
97 requests => gen_statem:reqids_new()},
98 nei(refresh)};
99
100 false ->
101
:-(
ignore
102 end.
103
104
105 callback_mode() ->
106 10 handle_event_function.
107
108
109 handle_event({call, _}, when_ready, unready, _) ->
110 20 {keep_state_and_data, postpone};
111
112 handle_event({call, From}, when_ready, ready, _) ->
113 66 {keep_state_and_data, {reply, From, ready}};
114
115 handle_event(internal,
116 refresh,
117 _,
118 #{config := Config, requests := Requests} = Data) ->
119 10 {keep_state,
120 Data#{requests := pgmp_connection:query(
121 #{sql=> <<?TYPE_SQL>>,
122 label => types,
123 server_ref => pgmp_connection:server_ref(Config),
124 requests => Requests})}};
125
126 handle_event(info, Msg, _, #{requests := Existing} = Data) ->
127 10 case gen_statem:check_response(Msg, Existing, true) of
128 {{reply, Reply}, Label, Updated} ->
129 10 {keep_state,
130 Data#{requests := Updated},
131 nei({response, #{label => Label, reply => Reply}})};
132
133 {{error, {Reason, ServerRef}}, Label, UpdatedRequests} ->
134
:-(
{stop,
135 #{reason => Reason,
136 server_ref => ServerRef,
137 label => Label},
138 Data#{requests := UpdatedRequests}}
139 end;
140
141 handle_event(internal,
142 {response, #{label := types, reply := Replies}},
143 _,
144 _) ->
145 10 {keep_state_and_data, [nei(Reply) || Reply <- Replies]};
146
147 handle_event(internal,
148 {command_complete, {select, _}},
149 _,
150 #{config := Config, cache := Cache} = Data) ->
151 10 persistent_term:put(
152 persistent_term_name(Config),
153 ets:foldl(
154 fun
155 ({{type, OID}, Description}, A) ->
156 6130 A#{OID => Description}
157 end,
158 #{},
159 Cache)),
160
161 10 {next_state, ready, maps:without([columns], Data), hibernate};
162
163 handle_event(internal,
164 {data_row, Columns},
165 _,
166 #{cache := Cache, columns := Names}) ->
167 6130 #{<<"oid">> := OID} = Row = maps:map(
168 fun data_row/2,
169 maps:from_list(
170 lists:zip(Names, Columns))),
171 6130 ets:insert(Cache, {{type, OID}, maps:without([oid], Row)}),
172 6130 keep_state_and_data;
173
174 handle_event(internal, {row_description, Columns}, _, Data) ->
175 10 {keep_state, Data#{columns => Columns}}.
176
177
178 data_row(Column, Value) when Column == <<"oid">>;
179 Column == <<"typnamespace">>;
180 Column == <<"typowner">>;
181 Column == <<"typlen">>;
182 Column == <<"typrelid">>;
183 Column == <<"typelem">>;
184 Column == <<"typarray">>;
185 Column == <<"typbasetype">>;
186 Column == <<"typtypmod">>;
187 Column == <<"typndims">>;
188 Column == <<"typcollation">> ->
189 67430 binary_to_integer(Value);
190
191 data_row(_, Value) ->
192 128730 Value.
Line Hits Source