./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 66 prepare_queries(),
75 66 ok.
76
77 -spec stop(HostType :: mongooseim:host_type()) -> ok.
78 stop(_HostType) ->
79 66 ok.
80
81 %% ------------------------ SQL -------------------------------------------
82
83 prepare_queries() ->
84 66 prepare_cleaning_queries(),
85 66 prepare_room_queries(),
86 66 prepare_affiliation_queries(),
87 66 prepare_config_queries(),
88 66 prepare_blocking_queries(),
89 66 prepare_domain_removal_queries(),
90 66 ok.
91
92 prepare_cleaning_queries() ->
93 66 mongoose_rdbms:prepare(muc_light_config_delete_all, muc_light_config, [],
94 <<"DELETE FROM muc_light_config">>),
95 66 mongoose_rdbms:prepare(muc_light_occupants_delete_all, muc_light_occupants, [],
96 <<"DELETE FROM muc_light_occupants">>),
97 66 mongoose_rdbms:prepare(muc_light_rooms_delete_all, muc_light_rooms, [],
98 <<"DELETE FROM muc_light_rooms">>),
99 66 mongoose_rdbms:prepare(muc_light_blocking_delete_all, muc_light_blocking, [],
100 <<"DELETE FROM muc_light_blocking">>),
101 66 ok.
102
103 prepare_room_queries() ->
104 %% Returns maximum 1 record
105 66 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 66 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 66 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 66 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 66 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 66 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 66 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 66 ok.
139
140 prepare_affiliation_queries() ->
141 %% This query uses multiple tables
142 %% Also returns a room version
143 66 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 66 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 66 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 66 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 66 mongoose_rdbms:prepare(muc_light_delete_affs, muc_light_occupants,
163 [room_id],
164 <<"DELETE FROM muc_light_occupants WHERE room_id = ?">>),
165 66 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 66 ok.
170
171 prepare_config_queries() ->
172 66 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 66 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 66 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 66 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 66 mongoose_rdbms:prepare(muc_light_delete_config, muc_light_config,
191 [room_id],
192 <<"DELETE FROM muc_light_config WHERE room_id = ?">>),
193 66 ok.
194
195 prepare_blocking_queries() ->
196 66 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 66 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 66 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 66 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 66 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 66 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 66 ok.
224
225 prepare_domain_removal_queries() ->
226 %% RoomS argument
227 66 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 66 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 66 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 66 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 66 mongoose_rdbms:prepare(muc_light_rooms_remove_room_domain, muc_light_rooms,
246 [lserver],
247 <<"DELETE FROM muc_light_rooms WHERE lserver = ?">>),
248 66 ok.
249
250 %% ------------------------ Room SQL functions ------------------------
251
252 select_room_id(HostType, RoomU, RoomS) ->
253 806 mongoose_rdbms:execute_successfully(
254 HostType, muc_light_select_room_id, [RoomU, RoomS]).
255
256 select_room_id_and_version(HostType, RoomU, RoomS) ->
257 263 mongoose_rdbms:execute_successfully(
258 HostType, muc_light_select_room_id_and_version, [RoomU, RoomS]).
259
260 select_user_rooms(HostType, LUser, LServer) ->
261 850 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 297 mongoose_rdbms:execute_successfully(
270 HostType, muc_light_insert_room, [RoomU, RoomS, Version]).
271
272 update_room_version(HostType, RoomU, RoomS, Version) ->
273 238 mongoose_rdbms:execute_successfully(
274 HostType, muc_light_update_room_version, [Version, RoomU, RoomS]).
275
276 delete_room(HostType, RoomU, RoomS) ->
277 78 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 546 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 241 mongoose_rdbms:execute_successfully(
290 HostType, muc_light_select_affs_by_room_id, [RoomID]).
291
292 insert_aff(HostType, RoomID, UserU, UserS, Aff) ->
293 635 DbAff = aff_atom2db(Aff),
294 635 mongoose_rdbms:execute_successfully(
295 HostType, muc_light_insert_aff, [RoomID, UserU, UserS, DbAff]).
296
297 update_aff(HostType, RoomID, UserU, UserS, Aff) ->
298 38 DbAff = aff_atom2db(Aff),
299 38 mongoose_rdbms:execute_successfully(
300 HostType, muc_light_update_aff, [DbAff, RoomID, UserU, UserS]).
301
302 delete_affs(HostType, RoomID) ->
303 78 mongoose_rdbms:execute_successfully(
304 HostType, muc_light_delete_affs, [RoomID]).
305
306 delete_aff(HostType, RoomID, UserU, UserS) ->
307 190 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 22 mongoose_rdbms:execute_successfully(
313 HostType, muc_light_select_config_by_room_id, [RoomID]).
314
315 select_config_by_us(HostType, RoomU, RoomS) ->
316 47 mongoose_rdbms:execute_successfully(
317 HostType, muc_light_select_config_by_us, [RoomU, RoomS]).
318
319 insert_config(HostType, RoomID, Key, Val) ->
320 506 mongoose_rdbms:execute_successfully(
321 HostType, muc_light_insert_config, [RoomID, Key, Val]).
322
323 update_config(HostType, RoomID, Key, Val) ->
324 40 mongoose_rdbms:execute_successfully(
325 HostType, muc_light_update_config, [Val, RoomID, Key]).
326
327 delete_config(HostType, RoomID) ->
328 78 mongoose_rdbms:execute_successfully(
329 HostType, muc_light_delete_config, [RoomID]).
330
331 %% ------------------------ Blocking SQL functions -------------------------
332
333 select_blocking(HostType, LUser, LServer) ->
334 8 mongoose_rdbms:execute_successfully(
335 HostType, muc_light_select_blocking, [LUser, LServer]).
336
337 select_blocking_cnt(HostType, LUser, LServer, [{What, Who}]) ->
338 4 DbWhat = what_atom2db(What),
339 4 DbWho = jid:to_binary(Who),
340 4 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 193 DbWhat1 = what_atom2db(What1),
345 193 DbWhat2 = what_atom2db(What2),
346 193 DbWho1 = jid:to_binary(Who1),
347 193 DbWho2 = jid:to_binary(Who2),
348 193 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 9 DbWhat = what_atom2db(What),
354 9 DbWho = jid:to_binary(Who),
355 9 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 4 DbWhat = what_atom2db(What),
361 4 DbWho = jid:to_binary(Who),
362 4 mongoose_rdbms:execute_successfully(
363 HostType, muc_light_delete_blocking1,
364 [LUser, LServer, DbWhat, DbWho]).
365
366 delete_blocking(HostType, UserU, UserS) ->
367 810 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 30 create_room_with_random_name(HostType, RoomS, Config, AffUsers, Version, 10);
378 create_room(HostType, RoomUS, Config, AffUsers, Version) ->
379 227 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 30 RoomU = mongoose_bin:gen_from_timestamp(),
388 30 RoomUS = {RoomU, RoomS},
389 30 F = fun() -> create_room_transaction(HostType, RoomUS, Config, AffUsers, Version) end,
390 30 case mongoose_rdbms:sql_transaction(HostType, F) of
391 {atomic, ok} ->
392 30 {ok, RoomUS};
393 Other ->
394
:-(
?LOG_ERROR(#{what => muc_create_room_with_random_name_retry,
395
:-(
candidate_room => RoomU, sub_host => RoomS, reason => Other}),
396
:-(
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 227 F = fun() -> create_room_transaction(HostType, RoomUS, Config, AffUsers, Version) end,
401 227 case mongoose_rdbms:sql_transaction(HostType, F) of
402 {atomic, ok} ->
403 223 {ok, RoomUS};
404 Other ->
405 4 case room_exists(HostType, RoomUS) of
406 true ->
407 4 {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 86 F = fun() -> destroy_room_transaction(HostType, RoomUS) end,
421 86 {atomic, Res} = mongoose_rdbms:sql_transaction(HostType, F),
422 86 Res.
423
424 -spec room_exists(HostType :: mongooseim:host_type(),
425 RoomUS :: jid:simple_bare_jid()) -> boolean().
426 room_exists(HostType, {RoomU, RoomS}) ->
427 466 {selected, Res} = select_room_id(HostType, RoomU, RoomS),
428 466 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 826 {selected, Rooms} = select_user_rooms(HostType, LUser, LServer),
438 826 lists:usort(Rooms);
439 get_user_rooms(HostType, {LUser, LServer}, _MUCServer) ->
440 24 {selected, Rooms} = select_user_rooms(HostType, LUser, LServer),
441 24 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 810 F = fun() -> remove_user_transaction(HostType, UserUS, Version) end,
456 810 {atomic, Res} = mongoose_rdbms:sql_transaction(UserS, F),
457 810 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 47 {selected, Result} = select_config_by_us(HostType, RoomU, RoomS),
484 47 case Result of
485 [] ->
486
:-(
{error, not_exists};
487 [{Version, null, null}] ->
488
:-(
{ok, [], Version};
489 [{Version, _, _} | _] ->
490 47 RawConfig = [{Key, Val} || {_, Key, Val} <- Result],
491 47 {ok, Config} = mod_muc_light_room_config:from_binary_kv(
492 RawConfig, mod_muc_light:config_schema(RoomS)),
493 47 {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 20 {atomic, Res}
502 = mongoose_rdbms:sql_transaction(
503 20 HostType, fun() -> set_config_transaction(RoomUS, ConfigChanges, Version) end),
504 20 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 8 {selected, WhatWhos} = select_blocking(HostType, LUser, LServer),
513 8 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 197 {selected, [{Count}]} = select_blocking_cnt(HostType, LUser, LServer, WhatWhos),
522 197 case mongoose_rdbms:result_to_integer(Count) of
523 193 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 9 set_blocking_loop(HostType, UserUS, MUCServer, BlockingItems).
533
534 set_blocking_loop(_HostType, _UserUS, _MUCServer, []) ->
535 9 ok;
536 set_blocking_loop(HostType, {LUser, LServer} = UserUS, MUCServer,
537 [{What, deny, Who} | RBlockingItems]) ->
538 9 {updated, _} = insert_blocking(HostType, LUser, LServer, What, Who),
539 9 set_blocking_loop(HostType, UserUS, MUCServer, RBlockingItems);
540 set_blocking_loop(HostType, {LUser, LServer} = UserUS, MUCServer,
541 [{What, allow, Who} | RBlockingItems]) ->
542 4 {updated, _} = delete_blocking1(HostType, LUser, LServer, What, Who),
543 4 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 546 case select_affs_by_us(HostType, RoomU, RoomS) of
552 {selected, []} ->
553 6 {error, not_exists};
554 {selected, [{Version, null, null, null}]} ->
555
:-(
{ok, [], Version};
556 {selected, [{Version, _, _, _} | _] = Res} ->
557 540 AffUsers = decode_affs_with_versions(Res),
558 540 {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 116 F = fun() -> modify_aff_users_transaction(HostType, RoomUS, AffUsersChanges,
569 ExternalCheck, Version) end,
570 116 {atomic, Res} = mongoose_rdbms:sql_transaction(HostType, F),
571 116 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 24 case select_room_id_and_version(HostType, RoomU, RoomS) of
581 {selected, [{DbRoomID, Version}]} ->
582 20 RoomID = mongoose_rdbms:result_to_integer(DbRoomID),
583 20 {selected, AffUsersDB} = select_affs_by_room_id(HostType, RoomID),
584 20 AffUsers = decode_affs(AffUsersDB),
585 20 {selected, ConfigDB} = select_config_by_room_id(HostType, RoomID),
586 20 {ok, Config} = mod_muc_light_room_config:from_binary_kv(
587 ConfigDB, mod_muc_light:config_schema(RoomS)),
588
589 20 {ok, Config, AffUsers, Version};
590 {selected, []} ->
591 4 {error, not_exists}
592 end.
593
594 %% ------------------------ Conversions ------------------------
595
596 decode_affs(AffUsersDB) ->
597 239 US2Aff = [{{UserU, UserS}, aff_db2atom(Aff)}
598 239 || {UserU, UserS, Aff} <- AffUsersDB],
599 239 lists:sort(US2Aff).
600
601 decode_affs_with_versions(AffUsersDB) ->
602 540 US2Aff = [{{UserU, UserS}, aff_db2atom(Aff)}
603 540 || {_Version, UserU, UserS, Aff} <- AffUsersDB],
604 540 lists:sort(US2Aff).
605
606 decode_blocking(WhatWhos) ->
607 8 [ {what_db2atom(What), deny, jid:to_lus(jid:from_binary(Who))}
608 8 || {What, Who} <- WhatWhos ].
609
610 -spec what_db2atom(binary() | pos_integer()) -> blocking_what().
611 2 what_db2atom(1) -> room;
612 3 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 199 what_atom2db(room) -> 1;
617 204 what_atom2db(user) -> 2.
618
619 -spec aff_atom2db(aff()) -> non_neg_integer().
620 287 aff_atom2db(owner) -> 1;
621 386 aff_atom2db(member) -> 2.
622
623 -spec aff_db2atom(binary() | pos_integer()) -> aff().
624 779 aff_db2atom(1) -> owner;
625 990 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 291 [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 97 [mongoose_rdbms:execute_successfully(Host, Statement, [])
641 97 || Host <- ?MYHOSTS, Statement <- force_clear_statements()],
642 97 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 297 insert_room(HostType, RoomU, RoomS, Version),
658 253 RoomID = mongoose_rdbms:selected_to_integer(select_room_id(HostType, RoomU, RoomS)),
659 253 Schema = mod_muc_light:config_schema(RoomS),
660 253 ConfigFields = mod_muc_light_room_config:to_binary_kv(Config, Schema),
661 253 [insert_aff_tuple(HostType, RoomID, AffUser) || AffUser <- AffUsers],
662 253 [insert_config_kv(HostType, RoomID, KV) || KV <- ConfigFields],
663 253 ok.
664
665 insert_aff_tuple(HostType, RoomID, {{UserU, UserS}, Aff}) ->
666 599 insert_aff(HostType, RoomID, UserU, UserS, Aff).
667
668 insert_config_kv(HostType, RoomID, {Key, Val}) ->
669 506 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 86 case select_room_id(HostType, RoomU, RoomS) of
676 {selected, [{DbRoomID}]} ->
677 78 RoomID = mongoose_rdbms:result_to_integer(DbRoomID),
678 78 {updated, _} = delete_affs(HostType, RoomID),
679 78 {updated, _} = delete_config(HostType, RoomID),
680 78 {updated, _} = delete_room(HostType, RoomU, RoomS),
681 78 ok;
682 {selected, []} ->
683 8 {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 810 Rooms = get_user_rooms(HostType, UserUS, undefined),
692 810 {updated, _} = delete_blocking(HostType, UserU, UserS),
693 810 lists:map(
694 fun(RoomUS) ->
695 103 {RoomUS, modify_aff_users_transaction(HostType,
696 103 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 20 HostType = room_us_to_host_type(RoomUS),
707 20 case select_room_id_and_version(HostType, RoomU, RoomS) of
708 {selected, [{DbRoomID, PrevVersion}]} ->
709 20 RoomID = mongoose_rdbms:result_to_integer(DbRoomID),
710 20 {updated, _} = update_room_version(HostType, RoomU, RoomS, Version),
711 20 lists:foreach(
712 fun({Key, Val}) ->
713 40 {updated, _} = update_config(HostType, RoomID, Key, Val)
714 end, mod_muc_light_room_config:to_binary_kv(
715 ConfigChanges, mod_muc_light:config_schema(RoomS))),
716 20 {ok, PrevVersion};
717 {selected, []} ->
718
:-(
{error, not_exists}
719 end.
720
721 %% ------------------------ Blocking manipulation ------------------------
722
723 %% ------------------------ Affiliations manipulation ------------------------
724
725 -spec modify_aff_users_transaction(HostType :: mongooseim:host_type(),
726 RoomUS :: jid:simple_bare_jid(),
727 AffUsersChanges :: aff_users(),
728 CheckFun :: external_check_fun(),
729 Version :: binary()) ->
730 mod_muc_light_db_backend:modify_aff_users_return().
731 modify_aff_users_transaction(HostType, {RoomU, RoomS} = RoomUS,
732 AffUsersChanges, CheckFun, Version) ->
733 219 case select_room_id_and_version(HostType, RoomU, RoomS) of
734 {selected, [{DbRoomID, PrevVersion}]} ->
735 219 RoomID = mongoose_rdbms:result_to_integer(DbRoomID),
736 219 modify_aff_users_transaction(HostType,
737 RoomUS, RoomID, AffUsersChanges, CheckFun, PrevVersion, Version);
738 {selected, []} ->
739
:-(
{error, not_exists}
740 end.
741
742 -spec modify_aff_users_transaction(HostType :: mongooseim:host_type(),
743 RoomUS :: jid:simple_bare_jid(),
744 RoomID :: room_id(),
745 AffUsersChanges :: aff_users(),
746 CheckFun :: external_check_fun(),
747 PrevVersion :: binary(),
748 Version :: binary()) ->
749 mod_muc_light_db_backend:modify_aff_users_return().
750 modify_aff_users_transaction(HostType, RoomUS, RoomID, AffUsersChanges,
751 CheckFun, PrevVersion, Version) ->
752 219 {selected, AffUsersDB} = select_affs_by_room_id(HostType, RoomID),
753 219 AffUsers = decode_affs(AffUsersDB),
754 219 case mod_muc_light_utils:change_aff_users(AffUsers, AffUsersChanges) of
755 {ok, NewAffUsers, AffUsersChanged, JoiningUsers, _LeavingUsers} ->
756 219 case CheckFun(RoomUS, NewAffUsers) of
757 ok ->
758 218 apply_aff_users_transaction(HostType, RoomID, AffUsersChanged, JoiningUsers),
759 218 {RoomU, RoomS} = RoomUS,
760 218 {updated, _} = update_room_version(HostType, RoomU, RoomS, Version),
761 218 {ok, AffUsers, NewAffUsers, AffUsersChanged, PrevVersion};
762 Error ->
763 1 Error
764 end;
765 Error ->
766
:-(
Error
767 end.
768
769 -spec apply_aff_users_transaction(HostType :: mongooseim:host_type(),
770 RoomID :: room_id(),
771 AffUsersChanges :: aff_users(),
772 JoiningUsers :: [jid:simple_bare_jid()]) -> ok.
773 apply_aff_users_transaction(HostType, RoomID, AffUsersChanged, JoiningUsers) ->
774 218 lists:foreach(
775 fun({{UserU, UserS}, none}) ->
776 190 {updated, _} = delete_aff(HostType, RoomID, UserU, UserS);
777 ({{UserU, UserS} = UserUS, Aff}) ->
778 74 case lists:member(UserUS, JoiningUsers) of
779 true ->
780 36 {updated, _} = insert_aff(HostType, RoomID, UserU, UserS, Aff);
781 false ->
782 38 {updated, _} = update_aff(HostType, RoomID, UserU, UserS, Aff)
783 end
784 end, AffUsersChanged).
785
786 %% ------------------------ Common ------------------------
787
788 -spec room_us_to_host_type(jid:simple_bare_jid()) -> mongooseim:host_type().
789 room_us_to_host_type({_, RoomS}) ->
790 20 muc_server_to_host_type(RoomS).
791
792 -spec muc_server_to_host_type(jid:lserver()) -> mongooseim:host_type().
793 muc_server_to_host_type(MUCServer) ->
794 20 mod_muc_light_utils:muc_host_to_host_type(MUCServer).
Line Hits Source