./ct_report/coverage/mod_muc_light_db_rdbms.COVER.html

1 %%%----------------------------------------------------------------------
2 %%% File : mod_muc_light_db_rdbms.erl
3 %%% Author : Piotr Nosek <piotr.nosek@erlang-solutions.com>
4 %%% Purpose : RDBMS backend for mod_muc_light
5 %%% Created : 24 Nov 2016 by Piotr Nosek <piotr.nosek@erlang-solutions.com>
6 %%%
7 %%% This program is free software; you can redistribute it and/or
8 %%% modify it under the terms of the GNU General Public License as
9 %%% published by the Free Software Foundation; either version 2 of the
10 %%% License, or (at your option) any later version.
11 %%%
12 %%% This program is distributed in the hope that it will be useful,
13 %%% but WITHOUT ANY WARRANTY; without even the implied warranty of
14 %%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 %%% General Public License for more details.
16 %%%
17 %%% You should have received a copy of the GNU General Public License
18 %%% along with this program; if not, write to the Free Software
19 %%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 %%%
21 %%%----------------------------------------------------------------------
22
23 -module(mod_muc_light_db_rdbms).
24 -author('piotr.nosek@erlang-solutions.com').
25
26 -behaviour(mod_muc_light_db_backend).
27
28 %% API
29 -export([
30 start/2,
31 stop/1,
32 create_room/5,
33 destroy_room/2,
34 room_exists/2,
35 get_user_rooms/3,
36 get_user_rooms_count/2,
37 remove_user/3,
38 remove_domain/3,
39 get_config/2,
40 set_config/4,
41 get_blocking/3,
42 get_blocking/4,
43 set_blocking/4,
44 get_aff_users/2,
45 modify_aff_users/5,
46 get_info/2
47 ]).
48
49 %% Extra API for testing
50 -export([force_clear/0,
51 select_room_id/3,
52 select_affs_by_room_id/2,
53 select_config_by_room_id/2
54 ]).
55
56 -ignore_xref([force_clear/0,
57 select_room_id/3,
58 select_affs_by_room_id/2,
59 select_config_by_room_id/2]).
60
61 -type room_id() :: non_neg_integer().
62
63 -include("mongoose.hrl").
64 -include("mod_muc_light.hrl").
65
66 %%====================================================================
67 %% API
68 %%====================================================================
69
70 %% ------------------------ Backend start/stop ------------------------
71
72 -spec start(HostType :: mongooseim:host_type(), gen_mod:module_opts()) -> ok.
73 start(_HostType, _) ->
74 71 prepare_queries(),
75 71 ok.
76
77 -spec stop(HostType :: mongooseim:host_type()) -> ok.
78 stop(_HostType) ->
79 71 ok.
80
81 %% ------------------------ SQL -------------------------------------------
82
83 prepare_queries() ->
84 71 prepare_cleaning_queries(),
85 71 prepare_room_queries(),
86 71 prepare_affiliation_queries(),
87 71 prepare_config_queries(),
88 71 prepare_blocking_queries(),
89 71 prepare_domain_removal_queries(),
90 71 ok.
91
92 prepare_cleaning_queries() ->
93 71 mongoose_rdbms:prepare(muc_light_config_delete_all, muc_light_config, [],
94 <<"DELETE FROM muc_light_config">>),
95 71 mongoose_rdbms:prepare(muc_light_occupants_delete_all, muc_light_occupants, [],
96 <<"DELETE FROM muc_light_occupants">>),
97 71 mongoose_rdbms:prepare(muc_light_rooms_delete_all, muc_light_rooms, [],
98 <<"DELETE FROM muc_light_rooms">>),
99 71 mongoose_rdbms:prepare(muc_light_blocking_delete_all, muc_light_blocking, [],
100 <<"DELETE FROM muc_light_blocking">>),
101 71 ok.
102
103 prepare_room_queries() ->
104 %% Returns maximum 1 record
105 71 mongoose_rdbms:prepare(muc_light_select_room_id, muc_light_rooms,
106 [luser, lserver],
107 <<"SELECT id FROM muc_light_rooms "
108 "WHERE luser = ? AND lserver = ?">>),
109 71 mongoose_rdbms:prepare(muc_light_select_room_id_and_version, muc_light_rooms,
110 [luser, lserver],
111 <<"SELECT id, version FROM muc_light_rooms "
112 "WHERE luser = ? AND lserver = ?">>),
113 71 mongoose_rdbms:prepare(muc_light_insert_room, muc_light_rooms,
114 [luser, lserver, version],
115 <<"INSERT INTO muc_light_rooms (luser, lserver, version)"
116 " VALUES (?, ?, ?)">>),
117 71 mongoose_rdbms:prepare(muc_light_update_room_version, muc_light_rooms,
118 [luser, lserver, version],
119 <<"UPDATE muc_light_rooms SET version = ? "
120 " WHERE luser = ? AND lserver = ?">>),
121 71 mongoose_rdbms:prepare(muc_light_delete_room, muc_light_rooms,
122 [luser, lserver],
123 <<"DELETE FROM muc_light_rooms"
124 " WHERE luser = ? AND lserver = ?">>),
125 %% These queries use multiple tables
126 71 mongoose_rdbms:prepare(muc_light_select_user_rooms, muc_light_occupants,
127 [luser, lserver],
128 <<"SELECT r.luser, r.lserver "
129 " FROM muc_light_occupants AS o "
130 " INNER JOIN muc_light_rooms AS r ON o.room_id = r.id"
131 " WHERE o.luser = ? AND o.lserver = ?">>),
132 71 mongoose_rdbms:prepare(muc_light_select_user_rooms_count, muc_light_occupants,
133 [luser, lserver],
134 <<"SELECT count(*) "
135 " FROM muc_light_occupants AS o "
136 " INNER JOIN muc_light_rooms AS r ON o.room_id = r.id"
137 " WHERE o.luser = ? AND o.lserver = ?">>),
138 71 ok.
139
140 prepare_affiliation_queries() ->
141 %% This query uses multiple tables
142 %% Also returns a room version
143 71 mongoose_rdbms:prepare(muc_light_select_affs_by_us, muc_light_rooms,
144 [luser, lserver],
145 <<"SELECT version, o.luser, o.lserver, aff"
146 " FROM muc_light_rooms AS r "
147 " LEFT OUTER JOIN muc_light_occupants AS o ON r.id = o.room_id"
148 " WHERE r.luser = ? AND r.lserver = ?">>),
149 71 mongoose_rdbms:prepare(muc_light_select_affs_by_room_id, muc_light_occupants,
150 [room_id],
151 <<"SELECT luser, lserver, aff "
152 "FROM muc_light_occupants WHERE room_id = ?">>),
153 71 mongoose_rdbms:prepare(muc_light_insert_aff, muc_light_occupants,
154 [room_id, luser, lserver, aff],
155 <<"INSERT INTO muc_light_occupants"
156 " (room_id, luser, lserver, aff)"
157 " VALUES(?, ?, ?, ?)">>),
158 71 mongoose_rdbms:prepare(muc_light_update_aff, muc_light_occupants,
159 [aff, room_id, luser, lserver],
160 <<"UPDATE muc_light_occupants SET aff = ? "
161 "WHERE room_id = ? AND luser = ? AND lserver = ?">>),
162 71 mongoose_rdbms:prepare(muc_light_delete_affs, muc_light_occupants,
163 [room_id],
164 <<"DELETE FROM muc_light_occupants WHERE room_id = ?">>),
165 71 mongoose_rdbms:prepare(muc_light_delete_aff, muc_light_occupants,
166 [room_id, luser, lserver],
167 <<"DELETE FROM muc_light_occupants "
168 "WHERE room_id = ? AND luser = ? AND lserver = ?">>),
169 71 ok.
170
171 prepare_config_queries() ->
172 71 mongoose_rdbms:prepare(muc_light_select_config_by_room_id, muc_light_config,
173 [room_id],
174 <<"SELECT opt, val FROM muc_light_config WHERE room_id = ?">>),
175 %% This query uses multiple tables
176 71 mongoose_rdbms:prepare(muc_light_select_config_by_us, muc_light_rooms,
177 [luser, lserver],
178 <<"SELECT version, opt, val "
179 "FROM muc_light_rooms AS r "
180 "LEFT OUTER JOIN muc_light_config AS c ON r.id = c.room_id "
181 "WHERE r.luser = ? AND r.lserver = ?">>),
182 71 mongoose_rdbms:prepare(muc_light_insert_config, muc_light_config,
183 [room_id, opt, val],
184 <<"INSERT INTO muc_light_config (room_id, opt, val)"
185 " VALUES(?, ?, ?)">>),
186 71 mongoose_rdbms:prepare(muc_light_update_config, muc_light_config,
187 [val, room_id, opt],
188 <<"UPDATE muc_light_config SET val = ? "
189 "WHERE room_id = ? AND opt = ?">>),
190 71 mongoose_rdbms:prepare(muc_light_delete_config, muc_light_config,
191 [room_id],
192 <<"DELETE FROM muc_light_config WHERE room_id = ?">>),
193 71 ok.
194
195 prepare_blocking_queries() ->
196 71 mongoose_rdbms:prepare(muc_light_select_blocking, muc_light_blocking,
197 [luser, lserver],
198 <<"SELECT what, who FROM muc_light_blocking "
199 "WHERE luser = ? AND lserver = ?">>),
200 71 mongoose_rdbms:prepare(muc_light_select_blocking_cnt, muc_light_blocking,
201 [luser, lserver, what, who],
202 <<"SELECT COUNT(*) FROM muc_light_blocking "
203 "WHERE luser = ? AND lserver = ? AND "
204 "what = ? AND who = ?">>),
205 71 mongoose_rdbms:prepare(muc_light_select_blocking_cnt2, muc_light_blocking,
206 [luser, lserver, what, who, what, who],
207 <<"SELECT COUNT(*) FROM muc_light_blocking "
208 "WHERE luser = ? AND lserver = ? AND "
209 "((what = ? AND who = ?) OR (what = ? AND who = ?))">>),
210 71 mongoose_rdbms:prepare(muc_light_insert_blocking, muc_light_blocking,
211 [luser, lserver, what, who],
212 <<"INSERT INTO muc_light_blocking"
213 " (luser, lserver, what, who)"
214 " VALUES (?, ?, ?, ?)">>),
215 71 mongoose_rdbms:prepare(muc_light_delete_blocking1, muc_light_blocking,
216 [luser, lserver, what, who],
217 <<"DELETE FROM muc_light_blocking "
218 "WHERE luser = ? AND lserver = ? AND what = ? AND who = ?">>),
219 71 mongoose_rdbms:prepare(muc_light_delete_blocking, muc_light_blocking,
220 [luser, lserver],
221 <<"DELETE FROM muc_light_blocking"
222 " WHERE luser = ? AND lserver = ?">>),
223 71 ok.
224
225 prepare_domain_removal_queries() ->
226 %% RoomS argument
227 71 mongoose_rdbms:prepare(muc_light_occupants_remove_room_domain, muc_light_occupants,
228 ['muc_light_rooms.lserver'],
229 <<"DELETE FROM muc_light_occupants WHERE room_id IN "
230 "(SELECT id FROM muc_light_rooms WHERE lserver = ?)">>),
231 %% User's LServer argument
232 71 mongoose_rdbms:prepare(muc_light_occupants_remove_user_domain, muc_light_occupants,
233 [lserver],
234 <<"DELETE FROM muc_light_occupants WHERE lserver = ?">>),
235 %% RoomS argument
236 71 mongoose_rdbms:prepare(muc_light_config_remove_room_domain, muc_light_config,
237 ['muc_light_rooms.lserver'],
238 <<"DELETE FROM muc_light_config WHERE room_id IN "
239 "(SELECT id FROM muc_light_rooms WHERE lserver = ?)">>),
240 %% User's LServer argument
241 71 mongoose_rdbms:prepare(muc_light_blocking_remove_user_domain, muc_light_blocking,
242 [lserver],
243 <<"DELETE FROM muc_light_blocking WHERE lserver = ?">>),
244 %% RoomS argument
245 71 mongoose_rdbms:prepare(muc_light_rooms_remove_room_domain, muc_light_rooms,
246 [lserver],
247 <<"DELETE FROM muc_light_rooms WHERE lserver = ?">>),
248 71 ok.
249
250 %% ------------------------ Room SQL functions ------------------------
251
252 select_room_id(HostType, RoomU, RoomS) ->
253 1148 mongoose_rdbms:execute_successfully(
254 HostType, muc_light_select_room_id, [RoomU, RoomS]).
255
256 select_room_id_and_version(HostType, RoomU, RoomS) ->
257 445 mongoose_rdbms:execute_successfully(
258 HostType, muc_light_select_room_id_and_version, [RoomU, RoomS]).
259
260 select_user_rooms(HostType, LUser, LServer) ->
261 995 mongoose_rdbms:execute_successfully(
262 HostType, muc_light_select_user_rooms, [LUser, LServer]).
263
264 select_user_rooms_count(HostType, LUser, LServer) ->
265 3 mongoose_rdbms:execute_successfully(
266 HostType, muc_light_select_user_rooms_count, [LUser, LServer]).
267
268 insert_room(HostType, RoomU, RoomS, Version) ->
269 479 mongoose_rdbms:execute_successfully(
270 HostType, muc_light_insert_room, [RoomU, RoomS, Version]).
271
272 update_room_version(HostType, RoomU, RoomS, Version) ->
273 390 mongoose_rdbms:execute_successfully(
274 HostType, muc_light_update_room_version, [Version, RoomU, RoomS]).
275
276 delete_room(HostType, RoomU, RoomS) ->
277 181 mongoose_rdbms:execute_successfully(
278 HostType, muc_light_delete_room, [RoomU, RoomS]).
279
280 %% ------------------------ Affiliation SQL functions ------------------------
281
282 %% Returns affiliations with a version
283 select_affs_by_us(HostType, RoomU, RoomS) ->
284 769 mongoose_rdbms:execute_successfully(
285 HostType, muc_light_select_affs_by_us, [RoomU, RoomS]).
286
287 %% Returns affiliations without a version
288 select_affs_by_room_id(HostType, RoomID) ->
289 406 mongoose_rdbms:execute_successfully(
290 HostType, muc_light_select_affs_by_room_id, [RoomID]).
291
292 insert_aff(HostType, RoomID, UserU, UserS, Aff) ->
293 795 DbAff = aff_atom2db(Aff),
294 795 mongoose_rdbms:execute_successfully(
295 HostType, muc_light_insert_aff, [RoomID, UserU, UserS, DbAff]).
296
297 update_aff(HostType, RoomID, UserU, UserS, Aff) ->
298 59 DbAff = aff_atom2db(Aff),
299 59 mongoose_rdbms:execute_successfully(
300 HostType, muc_light_update_aff, [DbAff, RoomID, UserU, UserS]).
301
302 delete_affs(HostType, RoomID) ->
303 181 mongoose_rdbms:execute_successfully(
304 HostType, muc_light_delete_affs, [RoomID]).
305
306 delete_aff(HostType, RoomID, UserU, UserS) ->
307 313 mongoose_rdbms:execute_successfully(
308 HostType, muc_light_delete_aff, [RoomID, UserU, UserS]).
309
310 %% ------------------------ Config SQL functions ---------------------------
311 select_config_by_room_id(HostType, RoomID) ->
312 43 mongoose_rdbms:execute_successfully(
313 HostType, muc_light_select_config_by_room_id, [RoomID]).
314
315 select_config_by_us(HostType, RoomU, RoomS) ->
316 48 mongoose_rdbms:execute_successfully(
317 HostType, muc_light_select_config_by_us, [RoomU, RoomS]).
318
319 insert_config(HostType, RoomID, Key, Val) ->
320 964 mongoose_rdbms:execute_successfully(
321 HostType, muc_light_insert_config, [RoomID, Key, Val]).
322
323 update_config(HostType, RoomID, Key, Val) ->
324 45 mongoose_rdbms:execute_successfully(
325 HostType, muc_light_update_config, [Val, RoomID, Key]).
326
327 delete_config(HostType, RoomID) ->
328 181 mongoose_rdbms:execute_successfully(
329 HostType, muc_light_delete_config, [RoomID]).
330
331 %% ------------------------ Blocking SQL functions -------------------------
332
333 select_blocking(HostType, LUser, LServer) ->
334 26 mongoose_rdbms:execute_successfully(
335 HostType, muc_light_select_blocking, [LUser, LServer]).
336
337 select_blocking_cnt(HostType, LUser, LServer, [{What, Who}]) ->
338 10 DbWhat = what_atom2db(What),
339 10 DbWho = jid:to_binary(Who),
340 10 mongoose_rdbms:execute_successfully(
341 HostType, muc_light_select_blocking_cnt,
342 [LUser, LServer, DbWhat, DbWho]);
343 select_blocking_cnt(HostType, LUser, LServer, [{What1, Who1}, {What2, Who2}]) ->
344 240 DbWhat1 = what_atom2db(What1),
345 240 DbWhat2 = what_atom2db(What2),
346 240 DbWho1 = jid:to_binary(Who1),
347 240 DbWho2 = jid:to_binary(Who2),
348 240 mongoose_rdbms:execute_successfully(
349 HostType, muc_light_select_blocking_cnt2,
350 [LUser, LServer, DbWhat1, DbWho1, DbWhat2, DbWho2]).
351
352 insert_blocking(HostType, LUser, LServer, What, Who) ->
353 16 DbWhat = what_atom2db(What),
354 16 DbWho = jid:to_binary(Who),
355 16 mongoose_rdbms:execute_successfully(
356 HostType, muc_light_insert_blocking,
357 [LUser, LServer, DbWhat, DbWho]).
358
359 delete_blocking1(HostType, LUser, LServer, What, Who) ->
360 10 DbWhat = what_atom2db(What),
361 10 DbWho = jid:to_binary(Who),
362 10 mongoose_rdbms:execute_successfully(
363 HostType, muc_light_delete_blocking1,
364 [LUser, LServer, DbWhat, DbWho]).
365
366 delete_blocking(HostType, UserU, UserS) ->
367 951 mongoose_rdbms:execute_successfully(
368 HostType, muc_light_delete_blocking, [UserU, UserS]).
369
370 %% ------------------------ General room management ------------------------
371
372 -spec create_room(HostType :: mongooseim:host_type(),
373 RoomUS :: jid:simple_bare_jid(), Config :: mod_muc_light_room_config:kv(),
374 AffUsers :: aff_users(), Version :: binary()) ->
375 {ok, FinalRoomUS :: jid:simple_bare_jid()} | {error, exists}.
376 create_room(HostType, {<<>>, RoomS}, Config, AffUsers, Version) ->
377 127 create_room_with_random_name(HostType, RoomS, Config, AffUsers, Version, 10);
378 create_room(HostType, RoomUS, Config, AffUsers, Version) ->
379 261 create_room_with_specified_name(HostType, RoomUS, Config, AffUsers, Version).
380
381 create_room_with_random_name(_HostType, RoomS, _Config, _AffUsers, _Version, 0) ->
382
:-(
?LOG_ERROR(#{what => muc_create_room_with_random_name_failed,
383
:-(
sub_host => RoomS}),
384
:-(
error(create_room_with_random_name_failed);
385 create_room_with_random_name(HostType, RoomS, Config, AffUsers, Version, Retries)
386 when Retries > 0 ->
387 128 RoomU = mongoose_bin:gen_from_timestamp(),
388 128 RoomUS = {RoomU, RoomS},
389 128 F = fun() -> create_room_transaction(HostType, RoomUS, Config, AffUsers, Version) end,
390 128 case mongoose_rdbms:sql_transaction(HostType, F) of
391 {atomic, ok} ->
392 127 {ok, RoomUS};
393 Other ->
394 1 ?LOG_ERROR(#{what => muc_create_room_with_random_name_retry,
395
:-(
candidate_room => RoomU, sub_host => RoomS, reason => Other}),
396 1 create_room_with_random_name(HostType, RoomS, Config, AffUsers, Version, Retries-1)
397 end.
398
399 create_room_with_specified_name(HostType, RoomUS, Config, AffUsers, Version) ->
400 261 F = fun() -> create_room_transaction(HostType, RoomUS, Config, AffUsers, Version) end,
401 261 case mongoose_rdbms:sql_transaction(HostType, F) of
402 {atomic, ok} ->
403 253 {ok, RoomUS};
404 Other ->
405 8 case room_exists(HostType, RoomUS) of
406 true ->
407 8 {error, exists};
408 false -> %% Some unknown error condition
409
:-(
{RoomU, RoomS} = RoomUS,
410
:-(
?LOG_ERROR(#{what => muc_create_room_with_specified_name_failed,
411
:-(
room => RoomU, sub_host => RoomS, reason => Other}),
412
:-(
error(create_room_with_specified_name_failed)
413 end
414 end.
415
416 -spec destroy_room(HostType :: mongooseim:host_type(),
417 RoomUS :: jid:simple_bare_jid()) ->
418 ok | {error, not_exists}.
419 destroy_room(HostType, RoomUS) ->
420 192 F = fun() -> destroy_room_transaction(HostType, RoomUS) end,
421 192 {atomic, Res} = mongoose_rdbms:sql_transaction(HostType, F),
422 192 Res.
423
424 -spec room_exists(HostType :: mongooseim:host_type(),
425 RoomUS :: jid:simple_bare_jid()) -> boolean().
426 room_exists(HostType, {RoomU, RoomS}) ->
427 575 {selected, Res} = select_room_id(HostType, RoomU, RoomS),
428 575 Res /= [].
429
430 -spec get_user_rooms(HostType :: mongooseim:host_type(),
431 UserUS :: jid:simple_bare_jid(),
432 MUCServer :: jid:lserver() | undefined) ->
433 [RoomUS :: jid:simple_bare_jid()].
434 get_user_rooms(HostType, {LUser, LServer}, undefined) ->
435 %% Only one hosttype is handled here
436 %% It is used to be map over MYHOSTS
437 967 {selected, Rooms} = select_user_rooms(HostType, LUser, LServer),
438 967 lists:usort(Rooms);
439 get_user_rooms(HostType, {LUser, LServer}, _MUCServer) ->
440 28 {selected, Rooms} = select_user_rooms(HostType, LUser, LServer),
441 28 Rooms.
442
443 -spec get_user_rooms_count(HostType :: mongooseim:host_type(),
444 UserUS :: jid:simple_bare_jid()) ->
445 non_neg_integer().
446 get_user_rooms_count(HostType, {LUser, LServer}) ->
447 3 {selected, [{Cnt}]} = select_user_rooms_count(HostType, LUser, LServer),
448 3 mongoose_rdbms:result_to_integer(Cnt).
449
450 -spec remove_user(HostType :: mongooseim:host_type(),
451 UserUS :: jid:simple_bare_jid(),
452 Version :: binary()) ->
453 mod_muc_light_db_backend:remove_user_return() | {error, term()}.
454 remove_user(HostType, {_, UserS} = UserUS, Version) ->
455 951 F = fun() -> remove_user_transaction(HostType, UserUS, Version) end,
456 951 {atomic, Res} = mongoose_rdbms:sql_transaction(UserS, F),
457 951 Res.
458
459 -spec remove_domain(mongooseim:host_type(), jid:lserver(), jid:lserver()) -> ok.
460 remove_domain(HostType, RoomS, LServer) ->
461 7 F = fun() ->
462 7 mongoose_rdbms:execute_successfully(
463 HostType, muc_light_occupants_remove_room_domain, [RoomS]),
464 7 mongoose_rdbms:execute_successfully(
465 HostType, muc_light_occupants_remove_user_domain, [LServer]),
466 7 mongoose_rdbms:execute_successfully(
467 HostType, muc_light_config_remove_room_domain, [RoomS]),
468 7 mongoose_rdbms:execute_successfully(
469 HostType, muc_light_blocking_remove_user_domain, [LServer]),
470 7 mongoose_rdbms:execute_successfully(
471 HostType, muc_light_rooms_remove_room_domain, [RoomS]),
472 7 ok
473 end,
474 7 {atomic, ok} = mongoose_rdbms:sql_transaction(HostType, F),
475 7 ok.
476
477 %% ------------------------ Configuration manipulation ------------------------
478
479 -spec get_config(HostType :: mongooseim:host_type(),
480 RoomUS :: jid:simple_bare_jid()) ->
481 {ok, mod_muc_light_room_config:kv(), Version :: binary()} | {error, not_exists}.
482 get_config(HostType, {RoomU, RoomS}) ->
483 48 {selected, Result} = select_config_by_us(HostType, RoomU, RoomS),
484 48 case Result of
485 [] ->
486
:-(
{error, not_exists};
487 [{Version, null, null}] ->
488
:-(
{ok, [], Version};
489 [{Version, _, _} | _] ->
490 48 RawConfig = [{Key, Val} || {_, Key, Val} <- Result],
491 48 {ok, Config} = mod_muc_light_room_config:from_binary_kv(
492 RawConfig, mod_muc_light:config_schema(RoomS)),
493 48 {ok, Config, Version}
494 end.
495
496 -spec set_config(HostType :: mongooseim:host_type(),
497 RoomUS :: jid:simple_bare_jid(), Config :: mod_muc_light_room_config:kv(),
498 Version :: binary()) ->
499 {ok, PrevVersion :: binary()} | {error, not_exists}.
500 set_config(HostType, RoomUS, ConfigChanges, Version) ->
501 29 {atomic, Res}
502 = mongoose_rdbms:sql_transaction(
503 29 HostType, fun() -> set_config_transaction(RoomUS, ConfigChanges, Version) end),
504 29 Res.
505
506 %% ------------------------ Blocking manipulation ------------------------
507
508 -spec get_blocking(HostType :: mongooseim:host_type(),
509 UserUS :: jid:simple_bare_jid(), MUCServer :: jid:lserver()) ->
510 [blocking_item()].
511 get_blocking(HostType, {LUser, LServer}, _MUCServer) ->
512 26 {selected, WhatWhos} = select_blocking(HostType, LUser, LServer),
513 26 decode_blocking(WhatWhos).
514
515 -spec get_blocking(HostType :: mongooseim:host_type(),
516 UserUS :: jid:simple_bare_jid(),
517 MUCServer :: jid:lserver(),
518 WhatWhos :: [{blocking_what(), jid:simple_bare_jid()}]) ->
519 blocking_action().
520 get_blocking(HostType, {LUser, LServer}, _MUCServer, WhatWhos) ->
521 250 {selected, [{Count}]} = select_blocking_cnt(HostType, LUser, LServer, WhatWhos),
522 250 case mongoose_rdbms:result_to_integer(Count) of
523 246 0 -> allow;
524 4 _ -> deny
525 end.
526
527 -spec set_blocking(HostType :: mongooseim:host_type(),
528 UserUS :: jid:simple_bare_jid(),
529 MUCServer :: jid:lserver(),
530 BlockingItems :: [blocking_item()]) -> ok.
531 set_blocking(HostType, UserUS, MUCServer, BlockingItems) ->
532 21 set_blocking_loop(HostType, UserUS, MUCServer, BlockingItems).
533
534 set_blocking_loop(_HostType, _UserUS, _MUCServer, []) ->
535 21 ok;
536 set_blocking_loop(HostType, {LUser, LServer} = UserUS, MUCServer,
537 [{What, deny, Who} | RBlockingItems]) ->
538 16 {updated, _} = insert_blocking(HostType, LUser, LServer, What, Who),
539 16 set_blocking_loop(HostType, UserUS, MUCServer, RBlockingItems);
540 set_blocking_loop(HostType, {LUser, LServer} = UserUS, MUCServer,
541 [{What, allow, Who} | RBlockingItems]) ->
542 10 {updated, _} = delete_blocking1(HostType, LUser, LServer, What, Who),
543 10 set_blocking_loop(HostType, UserUS, MUCServer, RBlockingItems).
544
545 %% ------------------------ Affiliations manipulation ------------------------
546
547 -spec get_aff_users(HostType :: mongooseim:host_type(),
548 RoomUS :: jid:simple_bare_jid()) ->
549 {ok, aff_users(), Version :: binary()} | {error, not_exists}.
550 get_aff_users(HostType, {RoomU, RoomS}) ->
551 769 case select_affs_by_us(HostType, RoomU, RoomS) of
552 {selected, []} ->
553 19 {error, not_exists};
554 {selected, [{Version, null, null, null}]} ->
555
:-(
{ok, [], Version};
556 {selected, [{Version, _, _, _} | _] = Res} ->
557 750 AffUsers = decode_affs_with_versions(Res),
558 750 {ok, AffUsers, Version}
559 end.
560
561 -spec modify_aff_users(HostType :: mongooseim:host_type(),
562 RoomUS :: jid:simple_bare_jid(),
563 AffUsersChanges :: aff_users(),
564 ExternalCheck :: external_check_fun(),
565 Version :: binary()) ->
566 mod_muc_light_db_backend:modify_aff_users_return().
567 modify_aff_users(HostType, RoomUS, AffUsersChanges, ExternalCheck, Version) ->
568 142 F = fun() -> modify_aff_users_transaction(HostType, RoomUS, AffUsersChanges,
569 ExternalCheck, Version) end,
570 142 {atomic, Res} = mongoose_rdbms:sql_transaction(HostType, F),
571 142 Res.
572
573 %% ------------------------ Misc ------------------------
574
575 -spec get_info(HostType :: mongooseim:host_type(),
576 RoomUS :: jid:simple_bare_jid()) ->
577 {ok, mod_muc_light_room_config:kv(), aff_users(), Version :: binary()}
578 | {error, not_exists}.
579 get_info(HostType, {RoomU, RoomS}) ->
580 53 case select_room_id_and_version(HostType, RoomU, RoomS) of
581 {selected, [{DbRoomID, Version}]} ->
582 41 RoomID = mongoose_rdbms:result_to_integer(DbRoomID),
583 41 {selected, AffUsersDB} = select_affs_by_room_id(HostType, RoomID),
584 41 AffUsers = decode_affs(AffUsersDB),
585 41 {selected, ConfigDB} = select_config_by_room_id(HostType, RoomID),
586 41 {ok, Config} = mod_muc_light_room_config:from_binary_kv(
587 ConfigDB, mod_muc_light:config_schema(RoomS)),
588
589 41 {ok, Config, AffUsers, Version};
590 {selected, []} ->
591 12 {error, not_exists}
592 end.
593
594 %% ------------------------ Conversions ------------------------
595
596 decode_affs(AffUsersDB) ->
597 404 US2Aff = [{{UserU, UserS}, aff_db2atom(Aff)}
598 404 || {UserU, UserS, Aff} <- AffUsersDB],
599 404 lists:sort(US2Aff).
600
601 decode_affs_with_versions(AffUsersDB) ->
602 750 US2Aff = [{{UserU, UserS}, aff_db2atom(Aff)}
603 750 || {_Version, UserU, UserS, Aff} <- AffUsersDB],
604 750 lists:sort(US2Aff).
605
606 decode_blocking(WhatWhos) ->
607 26 [ {what_db2atom(What), deny, jid:to_lus(jid:from_binary(Who))}
608 26 || {What, Who} <- WhatWhos ].
609
610 -spec what_db2atom(binary() | pos_integer()) -> blocking_what().
611 3 what_db2atom(1) -> room;
612 9 what_db2atom(2) -> user;
613
:-(
what_db2atom(Bin) -> what_db2atom(mongoose_rdbms:result_to_integer(Bin)).
614
615 -spec what_atom2db(blocking_what()) -> non_neg_integer().
616 247 what_atom2db(room) -> 1;
617 269 what_atom2db(user) -> 2.
618
619 -spec aff_atom2db(aff()) -> non_neg_integer().
620 435 aff_atom2db(owner) -> 1;
621 419 aff_atom2db(member) -> 2.
622
623 -spec aff_db2atom(binary() | pos_integer()) -> aff().
624 1154 aff_db2atom(1) -> owner;
625 1109 aff_db2atom(2) -> member;
626
:-(
aff_db2atom(Bin) -> aff_db2atom(mongoose_rdbms:result_to_integer(Bin)).
627
628 %%====================================================================
629 %% API for tests
630 %%====================================================================
631
632 force_clear_statements() ->
633 312 [muc_light_config_delete_all,
634 muc_light_occupants_delete_all,
635 muc_light_rooms_delete_all,
636 muc_light_blocking_delete_all].
637
638 -spec force_clear() -> ok.
639 force_clear() ->
640 104 [mongoose_rdbms:execute_successfully(Host, Statement, [])
641 104 || Host <- ?MYHOSTS, Statement <- force_clear_statements()],
642 104 ok.
643
644 %%====================================================================
645 %% Internal functions
646 %%====================================================================
647
648 %% ------------------------ General room management ------------------------
649
650 %% Expects config to have unique fields!
651 -spec create_room_transaction(HostType :: mongooseim:host_type(),
652 RoomUS :: jid:simple_bare_jid(),
653 Config :: mod_muc_light_room_config:kv(),
654 AffUsers :: aff_users(),
655 Version :: binary()) -> ok.
656 create_room_transaction(HostType, {RoomU, RoomS}, Config, AffUsers, Version) ->
657 479 insert_room(HostType, RoomU, RoomS, Version),
658 380 RoomID = mongoose_rdbms:selected_to_integer(select_room_id(HostType, RoomU, RoomS)),
659 380 Schema = mod_muc_light:config_schema(RoomS),
660 380 {ok, ConfigFields} = mod_muc_light_room_config:to_binary_kv(Config, Schema),
661 380 [insert_aff_tuple(HostType, RoomID, AffUser) || AffUser <- AffUsers],
662 380 [insert_config_kv(HostType, RoomID, KV) || KV <- ConfigFields],
663 380 ok.
664
665 insert_aff_tuple(HostType, RoomID, {{UserU, UserS}, Aff}) ->
666 739 insert_aff(HostType, RoomID, UserU, UserS, Aff).
667
668 insert_config_kv(HostType, RoomID, {Key, Val}) ->
669 964 insert_config(HostType, RoomID, Key, Val).
670
671 -spec destroy_room_transaction(HostType :: mongooseim:host_type(),
672 RoomUS :: jid:simple_bare_jid()) ->
673 ok | {error, not_exists}.
674 destroy_room_transaction(HostType, {RoomU, RoomS}) ->
675 192 case select_room_id(HostType, RoomU, RoomS) of
676 {selected, [{DbRoomID}]} ->
677 181 RoomID = mongoose_rdbms:result_to_integer(DbRoomID),
678 181 {updated, _} = delete_affs(HostType, RoomID),
679 181 {updated, _} = delete_config(HostType, RoomID),
680 181 {updated, _} = delete_room(HostType, RoomU, RoomS),
681 181 ok;
682 {selected, []} ->
683 11 {error, not_exists}
684 end.
685
686 -spec remove_user_transaction(HostType :: mongooseim:host_type(),
687 UserUS :: jid:simple_bare_jid(),
688 Version :: binary()) ->
689 mod_muc_light_db_backend:remove_user_return().
690 remove_user_transaction(HostType, {UserU, UserS} = UserUS, Version) ->
691 951 Rooms = get_user_rooms(HostType, UserUS, undefined),
692 951 {updated, _} = delete_blocking(HostType, UserU, UserS),
693 951 lists:map(
694 fun(RoomUS) ->
695 221 {RoomUS, modify_aff_users_transaction(HostType,
696 221 RoomUS, [{UserUS, none}], fun(_, _) -> ok end, Version)}
697 end, Rooms).
698
699 %% ------------------------ Configuration manipulation ------------------------
700
701 -spec set_config_transaction(RoomUS :: jid:simple_bare_jid(),
702 ConfigChanges :: mod_muc_light_room_config:kv(),
703 Version :: binary()) ->
704 {ok, PrevVersion :: binary()} | {error, not_exists}.
705 set_config_transaction({RoomU, RoomS} = RoomUS, ConfigChanges, Version) ->
706 29 HostType = room_us_to_host_type(RoomUS),
707 29 case select_room_id_and_version(HostType, RoomU, RoomS) of
708 {selected, [{DbRoomID, PrevVersion}]} ->
709 29 RoomID = mongoose_rdbms:result_to_integer(DbRoomID),
710 29 {updated, _} = update_room_version(HostType, RoomU, RoomS, Version),
711 29 {ok, Config} = mod_muc_light_room_config:to_binary_kv_diff(
712 ConfigChanges, mod_muc_light:config_schema(RoomS)),
713 29 lists:foreach(
714 fun({Key, Val}) ->
715 45 {updated, _} = update_config(HostType, RoomID, Key, Val)
716 end, Config),
717 29 {ok, PrevVersion};
718 {selected, []} ->
719
:-(
{error, not_exists}
720 end.
721
722 %% ------------------------ Blocking manipulation ------------------------
723
724 %% ------------------------ Affiliations manipulation ------------------------
725
726 -spec modify_aff_users_transaction(HostType :: mongooseim:host_type(),
727 RoomUS :: jid:simple_bare_jid(),
728 AffUsersChanges :: aff_users(),
729 CheckFun :: external_check_fun(),
730 Version :: binary()) ->
731 mod_muc_light_db_backend:modify_aff_users_return().
732 modify_aff_users_transaction(HostType, {RoomU, RoomS} = RoomUS,
733 AffUsersChanges, CheckFun, Version) ->
734 363 case select_room_id_and_version(HostType, RoomU, RoomS) of
735 {selected, [{DbRoomID, PrevVersion}]} ->
736 363 RoomID = mongoose_rdbms:result_to_integer(DbRoomID),
737 363 modify_aff_users_transaction(HostType,
738 RoomUS, RoomID, AffUsersChanges, CheckFun, PrevVersion, Version);
739 {selected, []} ->
740
:-(
{error, not_exists}
741 end.
742
743 -spec modify_aff_users_transaction(HostType :: mongooseim:host_type(),
744 RoomUS :: jid:simple_bare_jid(),
745 RoomID :: room_id(),
746 AffUsersChanges :: aff_users(),
747 CheckFun :: external_check_fun(),
748 PrevVersion :: binary(),
749 Version :: binary()) ->
750 mod_muc_light_db_backend:modify_aff_users_return().
751 modify_aff_users_transaction(HostType, RoomUS, RoomID, AffUsersChanges,
752 CheckFun, PrevVersion, Version) ->
753 363 {selected, AffUsersDB} = select_affs_by_room_id(HostType, RoomID),
754 363 AffUsers = decode_affs(AffUsersDB),
755 363 case mod_muc_light_utils:change_aff_users(AffUsers, AffUsersChanges) of
756 {ok, NewAffUsers, AffUsersChanged, JoiningUsers, _LeavingUsers} ->
757 362 case CheckFun(RoomUS, NewAffUsers) of
758 ok ->
759 361 apply_aff_users_transaction(HostType, RoomID, AffUsersChanged, JoiningUsers),
760 361 {RoomU, RoomS} = RoomUS,
761 361 {updated, _} = update_room_version(HostType, RoomU, RoomS, Version),
762 361 {ok, AffUsers, NewAffUsers, AffUsersChanged, PrevVersion};
763 Error ->
764 1 Error
765 end;
766 Error ->
767 1 Error
768 end.
769
770 -spec apply_aff_users_transaction(HostType :: mongooseim:host_type(),
771 RoomID :: room_id(),
772 AffUsersChanges :: aff_users(),
773 JoiningUsers :: [jid:simple_bare_jid()]) -> ok.
774 apply_aff_users_transaction(HostType, RoomID, AffUsersChanged, JoiningUsers) ->
775 361 lists:foreach(
776 fun({{UserU, UserS}, none}) ->
777 313 {updated, _} = delete_aff(HostType, RoomID, UserU, UserS);
778 ({{UserU, UserS} = UserUS, Aff}) ->
779 115 case lists:member(UserUS, JoiningUsers) of
780 true ->
781 56 {updated, _} = insert_aff(HostType, RoomID, UserU, UserS, Aff);
782 false ->
783 59 {updated, _} = update_aff(HostType, RoomID, UserU, UserS, Aff)
784 end
785 end, AffUsersChanged).
786
787 %% ------------------------ Common ------------------------
788
789 -spec room_us_to_host_type(jid:simple_bare_jid()) -> mongooseim:host_type().
790 room_us_to_host_type({_, RoomS}) ->
791 29 muc_server_to_host_type(RoomS).
792
793 -spec muc_server_to_host_type(jid:lserver()) -> mongooseim:host_type().
794 muc_server_to_host_type(MUCServer) ->
795 29 mod_muc_light_utils:muc_host_to_host_type(MUCServer).
Line Hits Source