./ct_report/coverage/mod_roster_rdbms.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : mod_roster_rdbms.erl
3 %%% Author : MichaƂ Piotrowski <michal.piotrowski@erlang-solutions.com>
4 %%% Purpose : mod_roster_rdbms rdbms backend
5 %%%
6 %%%
7 %%% ejabberd, Copyright (C) 2002-2014 ProcessOne
8 %%% MongooseIM, Copyright (C) 2015 Erlang Solutions Ltd.
9 %%%
10 %%%----------------------------------------------------------------------
11 -module(mod_roster_rdbms).
12
13 -include("mod_roster.hrl").
14
15 -behaviour(mod_roster_backend).
16
17 %% API
18 -export([init/2,
19 transaction/2,
20 read_roster_version/3,
21 write_roster_version/5,
22 get_roster/3,
23 get_roster_entry/6,
24 get_subscription_lists/3,
25 roster_subscribe_t/2,
26 update_roster_t/2,
27 del_roster_t/4,
28 remove_user_t/3,
29 remove_domain_t/2]).
30
31 %% mod_roster backend API
32
33 -spec init(mongooseim:host_type(), gen_mod:module_opts()) -> ok.
34 init(HostType, _Opts) ->
35
:-(
prepare_queries(HostType),
36
:-(
ok.
37
38 -spec transaction(mongooseim:host_type(), fun(() -> any())) ->
39 {aborted, any()} | {atomic, any()} | {error, any()}.
40 transaction(HostType, F) ->
41
:-(
mongoose_rdbms:sql_transaction(HostType, F).
42
43 -spec read_roster_version(mongooseim:host_type(), jid:luser(), jid:lserver()) -> binary() | error.
44 read_roster_version(HostType, LUser, LServer) ->
45
:-(
case mongoose_rdbms:execute_successfully(HostType, roster_version_get, [LServer, LUser]) of
46
:-(
{selected, [{Version}]} -> Version;
47
:-(
{selected, []} -> error
48 end.
49
50 -spec write_roster_version(mongooseim:host_type(), jid:luser(), jid:lserver(),
51 mod_roster:transaction_state(), mod_roster:version()) -> ok.
52 write_roster_version(HostType, LUser, LServer, _TransactionState, Ver) ->
53
:-(
version_upsert(HostType, LUser, LServer, Ver),
54
:-(
ok.
55
56 -spec get_roster(mongooseim:host_type(), jid:luser(), jid:lserver()) -> [mod_roster:roster()].
57 get_roster(HostType, LUser, LServer) ->
58
:-(
{selected, Rows} = execute_roster_get(HostType, LUser, LServer),
59
:-(
{selected, GroupRows} = execute_roster_group_get(HostType, LUser, LServer),
60
:-(
decode_roster_rows(LServer, LUser, Rows, GroupRows).
61
62 -spec get_roster_entry(mongooseim:host_type(), jid:luser(), jid:lserver(), mod_roster:contact(),
63 mod_roster:transaction_state(), mod_roster:entry_format()) ->
64 mod_roster:roster() | does_not_exist.
65 get_roster_entry(HostType, LUser, LServer, LJID, _TransactionState, full) ->
66
:-(
BinJID = jid:to_binary(LJID),
67
:-(
case execute_roster_get_by_jid(HostType, LUser, LServer, BinJID) of
68 {selected, []} ->
69
:-(
does_not_exist;
70 {selected, [Row]} ->
71
:-(
Groups = get_groups_by_jid(HostType, LUser, LServer, BinJID),
72
:-(
row_to_record(LServer, LUser, Row, #{BinJID => Groups})
73 end;
74 get_roster_entry(HostType, LUser, LServer, LJID, _TransactionState, short) ->
75
:-(
BinJID = jid:to_binary(LJID),
76
:-(
case execute_roster_get_by_jid(HostType, LUser, LServer, BinJID) of
77 {selected, []} ->
78
:-(
does_not_exist;
79 {selected, [Row]} ->
80
:-(
row_to_record(LServer, LUser, Row, #{})
81 end.
82
83 -spec get_subscription_lists(mongoose_acc:t(), jid:luser(), jid:lserver()) -> [mod_roster:roster()].
84 get_subscription_lists(Acc, LUser, LServer) ->
85
:-(
HostType = mongoose_acc:host_type(Acc),
86
:-(
{selected, Rows} = execute_roster_get(HostType, LUser, LServer),
87
:-(
[row_to_record(LServer, LUser, Row, #{}) || Row <- Rows].
88
89 -spec roster_subscribe_t(mongooseim:host_type(), mod_roster:roster()) -> ok.
90 roster_subscribe_t(HostType, Item) ->
91
:-(
RosterRow = record_to_row(Item),
92
:-(
roster_upsert(HostType, RosterRow),
93
:-(
ok.
94
95 -spec update_roster_t(mongooseim:host_type(), mod_roster:roster()) -> ok.
96 update_roster_t(HostType, Item) ->
97
:-(
RosterRow = [LServer, LUser, BinJID | _] = record_to_row(Item),
98
:-(
GroupRows = groups_to_rows(Item),
99
:-(
roster_upsert(HostType, RosterRow),
100
:-(
mongoose_rdbms:execute_successfully(HostType, roster_group_delete_by_jid,
101 [LServer, LUser, BinJID]),
102
:-(
[mongoose_rdbms:execute_successfully(HostType, roster_group_insert, GroupRow)
103
:-(
|| GroupRow <- GroupRows],
104
:-(
ok.
105
106 -spec del_roster_t(mongooseim:host_type(), jid:luser(), jid:lserver(), mod_roster:contact()) -> ok.
107 del_roster_t(HostType, LUser, LServer, LJID) ->
108
:-(
BinJID = jid:to_binary(LJID),
109
:-(
mongoose_rdbms:execute_successfully(
110 HostType, roster_delete_by_jid, [LServer, LUser, BinJID]),
111
:-(
mongoose_rdbms:execute_successfully(
112 HostType, roster_group_delete_by_jid, [LServer, LUser, BinJID]),
113
:-(
ok.
114
115 -spec remove_user_t(mongooseim:host_type(), jid:luser(), jid:lserver()) -> ok.
116 remove_user_t(HostType, LUser, LServer) ->
117
:-(
mongoose_rdbms:execute_successfully(HostType, roster_delete, [LServer, LUser]),
118
:-(
mongoose_rdbms:execute_successfully(HostType, roster_group_delete, [LServer, LUser]),
119
:-(
ok.
120
121 -spec remove_domain_t(mongooseim:host_type(), jid:lserver()) -> ok.
122 remove_domain_t(HostType, Domain) ->
123
:-(
mongoose_rdbms:execute_successfully(HostType, rosterusers_remove_domain, [Domain]),
124
:-(
mongoose_rdbms:execute_successfully(HostType, rostergroups_remove_domain, [Domain]),
125
:-(
mongoose_rdbms:execute_successfully(HostType, roster_version_remove_domain, [Domain]),
126
:-(
ok.
127
128 %% Query preparation
129
130 prepare_queries(HostType) ->
131
:-(
mongoose_rdbms:prepare(roster_group_insert, rostergroups, [server, username, jid, grp],
132 <<"INSERT INTO rostergroups(server, username, jid, grp) "
133 "VALUES (?, ?, ?, ?)">>),
134
:-(
mongoose_rdbms:prepare(roster_version_get, roster_version, [server, username],
135 <<"SELECT version FROM roster_version "
136 "WHERE server = ? AND username = ?">>),
137
:-(
mongoose_rdbms:prepare(roster_get, rosterusers, [server, username],
138 <<"SELECT ", (roster_fields())/binary,
139 " FROM rosterusers WHERE server = ? AND username = ?">>),
140
:-(
mongoose_rdbms:prepare(roster_get_by_jid, rosterusers, [server, username, jid],
141 <<"SELECT ", (roster_fields())/binary,
142 " FROM rosterusers WHERE server = ? AND username = ? AND jid = ?">>),
143
:-(
mongoose_rdbms:prepare(roster_group_get, rostergroups, [server, username],
144 <<"SELECT jid, grp FROM rostergroups WHERE server = ? AND username = ?">>),
145
:-(
mongoose_rdbms:prepare(roster_group_get_by_jid, rostergroups, [server, username, jid],
146 <<"SELECT grp FROM rostergroups "
147 "WHERE server = ? AND username = ? AND jid = ?">>),
148
:-(
mongoose_rdbms:prepare(roster_delete, rosterusers, [server, username],
149 <<"DELETE FROM rosterusers WHERE server = ? AND username = ?">>),
150
:-(
mongoose_rdbms:prepare(roster_group_delete, rostergroups, [server, username],
151 <<"DELETE FROM rostergroups WHERE server = ? AND username = ?">>),
152
:-(
mongoose_rdbms:prepare(roster_delete_by_jid, rosterusers, [server, username, jid],
153 <<"DELETE FROM rosterusers"
154 " WHERE server = ? AND username = ? AND jid = ?">>),
155
:-(
mongoose_rdbms:prepare(roster_group_delete_by_jid, rostergroups, [server, username, jid],
156 <<"DELETE FROM rostergroups"
157 " WHERE server = ? AND username = ? AND jid = ?">>),
158
:-(
mongoose_rdbms:prepare(rosterusers_remove_domain, rosterusers, [server],
159 <<"DELETE FROM rosterusers WHERE server = ?">>),
160
:-(
mongoose_rdbms:prepare(rostergroups_remove_domain, rostergroups, [server],
161 <<"DELETE FROM rostergroups WHERE server = ?">>),
162
:-(
mongoose_rdbms:prepare(roster_version_remove_domain, roster_version, [server],
163 <<"DELETE FROM roster_version WHERE server = ?">>),
164
:-(
prepare_roster_upsert(HostType),
165
:-(
prepare_version_upsert(HostType),
166
:-(
ok.
167
168 prepare_roster_upsert(HostType) ->
169
:-(
Fields = [<<"nick">>, <<"subscription">>, <<"ask">>, <<"askmessage">>],
170
:-(
Filter = [<<"server">>, <<"username">>, <<"jid">>],
171
:-(
rdbms_queries:prepare_upsert(HostType, roster_upsert, rosterusers,
172 Filter ++ Fields, Fields, Filter).
173
174 prepare_version_upsert(HostType) ->
175
:-(
Fields = [<<"version">>],
176
:-(
Filter = [<<"server">>, <<"username">>],
177
:-(
rdbms_queries:prepare_upsert(HostType, roster_version_upsert, roster_version,
178 Filter ++ Fields, Fields, Filter).
179
180 %% Query Helpers
181
182 -spec execute_roster_get(mongooseim:host_type(), jid:luser(), jid:lserver()) ->
183 mongoose_rdbms:query_result().
184 execute_roster_get(HostType, LUser, LServer) ->
185
:-(
mongoose_rdbms:execute_successfully(HostType, roster_get, [LServer, LUser]).
186
187 -spec execute_roster_group_get(mongooseim:host_type(), jid:luser(), jid:lserver()) ->
188 mongoose_rdbms:query_result().
189 execute_roster_group_get(HostType, LUser, LServer) ->
190
:-(
mongoose_rdbms:execute_successfully(HostType, roster_group_get, [LServer, LUser]).
191
192 -spec execute_roster_get_by_jid(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid()) ->
193 mongoose_rdbms:query_result().
194 execute_roster_get_by_jid(HostType, LUser, LServer, BinJID) ->
195
:-(
mongoose_rdbms:execute_successfully(HostType, roster_get_by_jid, [LServer, LUser, BinJID]).
196
197 -spec execute_roster_get_groups_by_jid(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid()) ->
198 mongoose_rdbms:query_result().
199 execute_roster_get_groups_by_jid(HostType, LUser, LServer, BinJID) ->
200
:-(
mongoose_rdbms:execute_successfully(HostType, roster_group_get_by_jid, [LServer, LUser, BinJID]).
201
202 -spec roster_upsert(mongooseim:host_type(), list()) -> mongoose_rdbms:query_result().
203 roster_upsert(HostType, [LServer, LUser, BinJID | Rest] = RosterRow) ->
204
:-(
InsertParams = RosterRow,
205
:-(
UpdateParams = Rest,
206
:-(
UniqueKeyValues = [LServer, LUser, BinJID],
207
:-(
{updated, _} = rdbms_queries:execute_upsert(HostType, roster_upsert,
208 InsertParams, UpdateParams, UniqueKeyValues).
209
210 -spec version_upsert(mongooseim:host_type(), jid:luser(), jid:lserver(), mod_roster:version()) ->
211 mongoose_rdbms:query_result().
212 version_upsert(HostType, LUser, LServer, Version) ->
213
:-(
InsertParams = [LServer, LUser, Version],
214
:-(
UpdateParams = [Version],
215
:-(
UniqueKeyValues = [LServer, LUser],
216
:-(
{updated, _} = rdbms_queries:execute_upsert(HostType, roster_version_upsert,
217 InsertParams, UpdateParams, UniqueKeyValues).
218
219 -spec get_groups_by_jid(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid()) ->
220 [binary()].
221 get_groups_by_jid(HostType, LUser, LServer, BinJID) ->
222
:-(
{selected, Rows} = execute_roster_get_groups_by_jid(HostType, LUser, LServer, BinJID),
223
:-(
[Group || {Group} <- Rows].
224
225 %%==============================================================================
226 %% Helper functions
227 %%==============================================================================
228
229
:-(
decode_subscription($B) -> both;
230
:-(
decode_subscription($T) -> to;
231
:-(
decode_subscription($F) -> from;
232
:-(
decode_subscription($N) -> none.
233
234
:-(
encode_subscription(both) -> <<"B">>;
235
:-(
encode_subscription(to) -> <<"T">>;
236
:-(
encode_subscription(from) -> <<"F">>;
237
:-(
encode_subscription(none) -> <<"N">>.
238
239
:-(
decode_ask($S) -> subscribe;
240
:-(
decode_ask($U) -> unsubscribe;
241
:-(
decode_ask($B) -> both;
242
:-(
decode_ask($O) -> out;
243
:-(
decode_ask($I) -> in;
244
:-(
decode_ask($N) -> none.
245
246
:-(
encode_ask(subscribe) -> <<"S">>;
247
:-(
encode_ask(unsubscribe) -> <<"U">>;
248
:-(
encode_ask(both) -> <<"B">>;
249
:-(
encode_ask(out) -> <<"O">>;
250
:-(
encode_ask(in) -> <<"I">>;
251
:-(
encode_ask(none) -> <<"N">>.
252
253 record_to_row(#roster{us = {LUser, LServer},
254 jid = JID, name = Nick, subscription = Subscription,
255 ask = Ask, askmessage = AskMessage}) ->
256
:-(
BinJID = jid:to_binary(jid:to_lower(JID)),
257
:-(
ExtSubscription = encode_subscription(Subscription),
258
:-(
ExtAsk = encode_ask(Ask),
259
:-(
[LServer, LUser, BinJID, Nick, ExtSubscription, ExtAsk, AskMessage].
260
261 groups_to_rows(#roster{us = {LUser, LServer}, jid = JID, groups = Groups}) ->
262
:-(
BinJID = jid:to_binary(jid:to_lower(JID)),
263
:-(
lists:foldl(fun (<<>>, Acc) -> Acc;
264
:-(
(Group, Acc) -> [[LServer, LUser, BinJID, Group] | Acc]
265 end, [], Groups).
266
267 %% We don't care about `server, subscribe, type' fields
268 roster_fields() ->
269
:-(
<<"jid, nick, subscription, ask, askmessage">>.
270
271 %% Decode fields from `roster_fields()' into a record
272 row_to_record(LServer, LUser,
273 {BinJID, Nick, ExtSubscription, ExtAsk, AskMessage}, GroupsPerJID) ->
274
:-(
JID = jid:from_binary_noprep(BinJID), %% We trust the DB has correct jids
275
:-(
LJID = jid:to_lower(JID), %% Convert to tuple {U,S,R}
276
:-(
Subscription = decode_subscription(mongoose_rdbms:character_to_integer(ExtSubscription)),
277
:-(
Ask = decode_ask(mongoose_rdbms:character_to_integer(ExtAsk)),
278
:-(
US = {LUser, LServer},
279
:-(
USJ = {US, LJID},
280
:-(
Groups = maps:get(BinJID, GroupsPerJID, []),
281
:-(
#roster{usj = USJ, us = US, jid = LJID, name = Nick,
282 subscription = Subscription, ask = Ask, groups = Groups, askmessage = AskMessage}.
283
284 decode_roster_rows(LServer, LUser, Rows, JIDGroups) ->
285
:-(
GroupsPerJID = group_per_jid(JIDGroups),
286
:-(
[row_to_record(LServer, LUser, Row, GroupsPerJID) || Row <- Rows].
287
288 group_per_jid(Pairs) ->
289
:-(
F = fun ({Jid, Group}, Acc) ->
290
:-(
case Acc of
291 #{Jid := Groups} ->
292
:-(
Acc#{Jid := [Group | Groups]};
293 _ ->
294
:-(
Acc#{Jid => [Group]}
295 end
296 end,
297
:-(
lists:foldl(F, #{}, Pairs).
Line Hits Source