./ct_report/coverage/mod_inbox_rdbms.COVER.html

1 %%%-------------------------------------------------------------------
2 %%% @copyright (C) 2018, Erlang Solutions Ltd.
3 %%% @doc
4 %%%
5 %%% @end
6 %%% Created : 30. Jan 2018 16:59
7 %%%-------------------------------------------------------------------
8 -module(mod_inbox_rdbms).
9
10 -include("mod_inbox.hrl").
11
12 -behaviour(mod_inbox_backend).
13
14 %% API
15 -export([get_inbox/4,
16 init/2,
17 set_inbox/6,
18 set_inbox_incr_unread/5,
19 reset_unread/4,
20 empty_user_bin/4,
21 empty_global_bin/2,
22 remove_inbox_row/2,
23 remove_domain/2,
24 clear_inbox/3,
25 get_inbox_unread/2,
26 get_full_entry/2,
27 get_entry_properties/2,
28 set_entry_properties/3]).
29 -export([check_result/1]).
30
31 %% ----------------------------------------------------------------------
32 %% API
33 %% ----------------------------------------------------------------------
34
35 %% TODO pools aren't multitenancy-ready yet
36 init(HostType, _Options) ->
37 45 RowCond = <<"WHERE luser = ? AND lserver = ? AND remote_bare_jid = ?">>,
38 45 mongoose_rdbms:prepare(inbox_select_entry, inbox,
39 [luser, lserver, remote_bare_jid],
40 <<"SELECT", (query_row())/binary, "FROM inbox ", RowCond/binary>>),
41 45 mongoose_rdbms:prepare(inbox_select_unread_count, inbox,
42 [luser, lserver, remote_bare_jid],
43 <<"SELECT unread_count FROM inbox ", RowCond/binary>>),
44 45 mongoose_rdbms:prepare(inbox_select_properties, inbox,
45 [luser, lserver, remote_bare_jid],
46 <<"SELECT box, muted_until, unread_count FROM inbox ", RowCond/binary>>),
47 45 mongoose_rdbms:prepare(inbox_reset_unread, inbox,
48 [luser, lserver, remote_bare_jid, timestamp],
49 <<"UPDATE inbox SET unread_count = 0 ",
50 RowCond/binary, " AND timestamp <= ?">>),
51 45 mongoose_rdbms:prepare(inbox_reset_unread_msg, inbox,
52 [luser, lserver, remote_bare_jid, msg_id, timestamp],
53 <<"UPDATE inbox SET unread_count = 0 ", RowCond/binary,
54 " AND msg_id = ? AND timestamp <= ?">>),
55 % removals
56 45 mongoose_rdbms:prepare(inbox_clean_global_bin, inbox, [timestamp],
57 <<"DELETE FROM inbox WHERE box='bin' AND timestamp < ?">>),
58 45 mongoose_rdbms:prepare(inbox_clean_user_bin, inbox, [lserver, luser, timestamp],
59 <<"DELETE FROM inbox WHERE",
60 " lserver = ? AND luser = ? AND box='bin' AND timestamp < ?">>),
61 45 mongoose_rdbms:prepare(inbox_delete_row, inbox,
62 [luser, lserver, remote_bare_jid],
63 <<"DELETE FROM inbox ", RowCond/binary>>),
64 45 mongoose_rdbms:prepare(inbox_delete, inbox,
65 [luser, lserver],
66 <<"DELETE FROM inbox WHERE luser = ? AND lserver = ?">>),
67 45 mongoose_rdbms:prepare(inbox_delete_domain, inbox,
68 [lserver], <<"DELETE FROM inbox WHERE lserver = ?">>),
69 % upserts
70 45 BoxQuery = <<"CASE WHEN ?='bin' THEN 'bin'",
71 " WHEN inbox.box='archive' THEN 'inbox'",
72 " ELSE inbox.box END">>,
73 45 UniqueKeyFields = [<<"luser">>, <<"lserver">>, <<"remote_bare_jid">>],
74 45 InsertFields = UniqueKeyFields ++ [<<"msg_id">>, <<"box">>, <<"content">>, <<"unread_count">>, <<"timestamp">>],
75 45 rdbms_queries:prepare_upsert(HostType, inbox_upsert, inbox,
76 InsertFields,
77 [<<"msg_id">>,
78 {expression, <<"box">>, BoxQuery},
79 <<"content">>,
80 <<"unread_count">>,
81 <<"timestamp">>],
82 UniqueKeyFields, <<"timestamp">>),
83 45 rdbms_queries:prepare_upsert(HostType, inbox_upsert_incr_unread, inbox,
84 InsertFields,
85 [<<"msg_id">>,
86 {expression, <<"box">>, BoxQuery},
87 <<"content">>,
88 {expression, <<"unread_count">>, <<"inbox.unread_count + ?">>},
89 <<"timestamp">>],
90 UniqueKeyFields, <<"timestamp">>),
91 45 ok.
92
93 -spec get_inbox(HostType :: mongooseim:host_type(),
94 LUser :: jid:luser(),
95 LServer :: jid:lserver(),
96 Params :: mod_inbox:get_inbox_params()) -> [mod_inbox:inbox_res()].
97 get_inbox(HostType, LUser, LServer, Params) ->
98 807 case get_inbox_rdbms(HostType, LUser, LServer, Params) of
99 {selected, []} ->
100 168 [];
101 {selected, Res} ->
102 639 [decode_row(HostType, R) || R <- Res]
103 end.
104
105 -spec get_inbox_unread(mongooseim:host_type(), mod_inbox:entry_key()) ->
106 {ok, integer()}.
107 get_inbox_unread(HostType, {LUser, LServer, RemBareJID}) ->
108
:-(
Res = execute_select_unread_count(HostType, LUser, LServer, RemBareJID),
109
:-(
{ok, Val} = check_result(Res),
110 %% We read unread_count value when the message is sent and is not yet in receiver inbox
111 %% so we have to add +1
112
:-(
{ok, Val + 1}.
113
114 -spec set_inbox(HostType, InboxEntryKey, Packet, Count, MsgId, Timestamp) ->
115 mod_inbox:write_res() when
116 HostType :: mongooseim:host_type(),
117 InboxEntryKey :: mod_inbox:entry_key(),
118 Packet :: exml:element(),
119 Count :: integer(),
120 MsgId :: binary(),
121 Timestamp :: integer().
122 set_inbox(HostType, {LUser, LServer, LToBareJid}, Packet, Count, MsgId, Timestamp) ->
123 134 Content = exml:to_binary(Packet),
124 134 Unique = [LUser, LServer, LToBareJid],
125 134 Update = [MsgId, <<"inbox">>, Content, Count, Timestamp],
126 134 Insert = [LUser, LServer, LToBareJid, MsgId, <<"inbox">>, Content, Count, Timestamp],
127 134 Res = rdbms_queries:execute_upsert(HostType, inbox_upsert, Insert, Update, Unique),
128 %% MySQL returns 1 when an upsert is an insert
129 %% and 2, when an upsert acts as update
130 134 check_result_is_expected(Res, [1, 2]).
131
132 -spec empty_user_bin(HostType :: mongooseim:host_type(),
133 LServer :: jid:lserver(),
134 LUser :: jid:luser(),
135 TS :: integer()) -> non_neg_integer().
136 empty_user_bin(HostType, LServer, LUser, TS) ->
137 2 {updated, BinN} = mongoose_rdbms:execute_successfully(
138 HostType, inbox_clean_user_bin, [LServer, LUser, TS]),
139 2 mongoose_rdbms:result_to_integer(BinN).
140
141 -spec empty_global_bin(HostType :: mongooseim:host_type(),
142 TS :: integer()) -> non_neg_integer().
143 empty_global_bin(HostType, TS) ->
144 111 {updated, BinN} = mongoose_rdbms:execute_successfully(
145 HostType, inbox_clean_global_bin, [TS]),
146 111 mongoose_rdbms:result_to_integer(BinN).
147
148 -spec remove_inbox_row(HostType :: mongooseim:host_type(),
149 InboxEntryKey :: mod_inbox:entry_key()) -> mod_inbox:write_res().
150 remove_inbox_row(HostType, {LUser, LServer, LToBareJid}) ->
151 5 Res = execute_delete(HostType, LUser, LServer, LToBareJid),
152 5 check_result(Res).
153
154 -spec remove_domain(HostType :: mongooseim:host_type(),
155 LServer :: jid:lserver()) -> ok.
156 remove_domain(HostType, LServer) ->
157 1 execute_delete_domain(HostType, LServer),
158 1 ok.
159
160 -spec set_inbox_incr_unread(
161 mongooseim:host_type(), mod_inbox:entry_key(), exml:element(), binary(), integer()) ->
162 mod_inbox:count_res().
163 set_inbox_incr_unread(HostType, Entry, Packet, MsgId, Timestamp) ->
164 207 set_inbox_incr_unread(HostType, Entry, Packet, MsgId, Timestamp, 1).
165
166 -spec set_inbox_incr_unread(HostType :: mongooseim:host_type(),
167 InboxEntryKey :: mod_inbox:entry_key(),
168 Packet :: exml:element(),
169 MsgId :: binary(),
170 Timestamp :: integer(),
171 Incrs :: pos_integer()) -> mod_inbox:count_res().
172 set_inbox_incr_unread(HostType, {LUser, LServer, LToBareJid}, Packet, MsgId, Timestamp, Incrs) ->
173 207 Content = exml:to_binary(Packet),
174 207 Unique = [LUser, LServer, LToBareJid],
175 207 Update = [MsgId, <<"inbox">>, Content, Incrs, Timestamp],
176 207 Insert = [LUser, LServer, LToBareJid, MsgId, <<"inbox">>, Content, Incrs, Timestamp],
177 207 Res = rdbms_queries:execute_upsert(HostType, inbox_upsert_incr_unread, Insert, Update, Unique),
178 207 check_result(Res).
179
180 -spec reset_unread(HostType :: mongooseim:host_type(),
181 InboxEntryKey :: mod_inbox:entry_key(),
182 MsgId :: binary() | undefined,
183 TS :: integer()) -> mod_inbox:write_res().
184 reset_unread(HostType, {LUser, LServer, LToBareJid}, MsgId, TS) ->
185 57 Res = execute_reset_unread(HostType, LUser, LServer, LToBareJid, MsgId, TS),
186 57 check_result(Res).
187
188 -spec clear_inbox(HostType :: mongooseim:host_type(),
189 LUser :: jid:luser(),
190 LServer :: jid:lserver()) -> mod_inbox:write_res().
191 clear_inbox(HostType, LUser, LServer) ->
192 610 Res = execute_delete(HostType, LUser, LServer),
193 610 check_result(Res).
194
195 -spec get_full_entry(HostType :: mongooseim:host_type(),
196 InboxEntryKey :: mod_inbox:entry_key()) ->
197 inbox_res() | nil().
198 get_full_entry(HostType, {LUser, LServer, RemBareJID}) ->
199 2 case execute_select_full_entry(HostType, LUser, LServer, RemBareJID) of
200 {selected, []} ->
201
:-(
[];
202 {selected, [Selected]} ->
203 2 decode_row(HostType, Selected)
204 end.
205
206 -spec get_entry_properties(HostType :: mongooseim:host_type(),
207 InboxEntryKey :: mod_inbox:entry_key()) ->
208 entry_properties() | nil().
209 get_entry_properties(HostType, {LUser, LServer, RemBareJID}) ->
210 2 case execute_select_properties(HostType, LUser, LServer, RemBareJID) of
211 {selected, []} ->
212
:-(
[];
213 {selected, [Selected]} ->
214 2 decode_properties(Selected)
215 end.
216
217 -spec set_entry_properties(HostType :: mongooseim:host_type(),
218 InboxEntryKey :: mod_inbox:entry_key(),
219 entry_properties()) ->
220 entry_properties() | {error, binary()}.
221 set_entry_properties(HostType, {LUser, LServer, RemBareJID}, Properties) ->
222 102 case set_entry_properties_rdbms(HostType, LUser, LServer, RemBareJID, Properties) of
223 {error, Msg} when is_list(Msg) ->
224
:-(
{error, list_to_binary(Msg)};
225 {error, Msg} ->
226
:-(
{error, Msg};
227 {updated, 0} ->
228 8 {error, <<"item-not-found">>};
229 {selected, [Result]} ->
230 94 decode_properties(Result)
231 end.
232
233 %% ----------------------------------------------------------------------
234 %% Internal functions
235 %% ----------------------------------------------------------------------
236
237 -spec get_inbox_rdbms(HostType :: mongooseim:host_type(),
238 LUser :: jid:luser(),
239 LServer :: jid:lserver(),
240 Params :: mod_inbox:get_inbox_params()) ->
241 mongoose_rdbms:query_result().
242 get_inbox_rdbms(HostType, LUser, LServer, Params) ->
243 807 QueryName = lookup_query_name(Params),
244 807 case mongoose_rdbms:prepared(QueryName) of
245 false ->
246 12 SQL = lookup_query(Params),
247 12 Columns = lookup_query_columns(Params),
248 12 mongoose_rdbms:prepare(QueryName, inbox, Columns, SQL);
249 true ->
250 795 ok
251 end,
252 807 Args = lookup_query_args(LServer, LUser, Params),
253 807 mongoose_rdbms:execute_successfully(HostType, QueryName, Args).
254
255 set_entry_properties_rdbms(HostType, LUser, LServer, RemBareJID, Properties) ->
256 102 QueryName = update_query_name(Properties),
257 102 case mongoose_rdbms:prepared(QueryName) of
258 false ->
259 6 SQL = update_properties_query(Properties),
260 6 Columns = update_query_columns(Properties),
261 6 mongoose_rdbms:prepare(QueryName, inbox, Columns, SQL);
262 true ->
263 96 ok
264 end,
265 102 {atomic, TransactionResult} =
266 mongoose_rdbms:sql_transaction(
267 LServer,
268 102 fun() -> set_entry_properties_t(HostType, QueryName, LUser, LServer, RemBareJID, Properties) end),
269 102 TransactionResult.
270
271 -spec set_entry_properties_t(mongooseim:host_type(), atom(), jid:luser(), jid:lserver(), jid:literal_jid(),
272 entry_properties()) ->
273 mongoose_rdbms:query_result().
274 set_entry_properties_t(HostType, QueryName, LUser, LServer, RemBareJID, Properties) ->
275 102 Args = update_query_args(LUser, LServer, RemBareJID, Properties),
276 102 case mongoose_rdbms:execute_successfully(HostType, QueryName, Args) of
277 {updated, 1} ->
278 94 execute_select_properties(HostType, LUser, LServer, RemBareJID);
279 Other ->
280 8 Other
281 end.
282
283 %% Inbox lookup
284
285 -spec lookup_query(mod_inbox:get_inbox_params()) -> iolist().
286 lookup_query(#{order := Order} = Params) ->
287 12 OrderSQL = order_to_sql(Order),
288 12 {LimitSQL, MSLimitSQL} = sql_and_where_limit(maps:get(limit, Params, undefined)),
289 12 Conditions = [lookup_sql_condition(Key, maps:get(Key, Params, undefined))
290 12 || Key <- [start, 'end', hidden_read, box]],
291 12 ["SELECT ", MSLimitSQL, query_row(),
292 " FROM inbox WHERE luser = ? AND lserver = ?", Conditions,
293 " ORDER BY timestamp ", OrderSQL, " ", LimitSQL].
294
295 -spec lookup_query_args(jid:lserver(), jid:luser(), mod_inbox:get_inbox_params()) -> list().
296 lookup_query_args(LServer, LUser, Params) ->
297 807 Args = [LUser, LServer | [maps:get(Key, Params) || Key <- lookup_arg_keys(Params)]],
298 807 case maps:get(limit, Params, undefined) of
299 800 undefined -> Args;
300 7 Limit -> rdbms_queries:add_limit_arg(Limit, Args)
301 end.
302
303 -spec lookup_query_columns(mod_inbox:get_inbox_params()) -> [atom()].
304 lookup_query_columns(Params) ->
305 12 Columns = [luser, lserver | lists:map(fun param_to_column/1, lookup_arg_keys(Params))],
306 12 case maps:get(limit, Params, undefined) of
307 10 undefined -> Columns;
308 2 _ -> rdbms_queries:add_limit_arg(limit, Columns)
309 end.
310
311 -spec lookup_arg_keys(mod_inbox:get_inbox_params()) -> [atom()].
312 lookup_arg_keys(Params) ->
313 819 lists:filter(
314 819 fun(box) -> maps:is_key(box, Params) andalso maps:get(box, Params, undefined) =/= <<"all">>;
315 1638 (Key) -> maps:is_key(Key, Params)
316 end, [start, 'end', box]).
317
318 -spec lookup_query_name(mod_inbox:get_inbox_params()) -> atom().
319 lookup_query_name(Params) ->
320 807 IDString = lists:flatmap(fun(Param) ->
321 4842 param_id(Param, maps:get(Param, Params, undefined))
322 end, lookup_param_keys()),
323 807 list_to_atom("inbox_lookup" ++ IDString).
324
325 -spec lookup_param_keys() -> [atom()].
326 lookup_param_keys() ->
327 807 [order, limit, start, 'end', hidden_read, box].
328
329 -spec param_to_column(atom()) -> atom().
330 3 param_to_column(start) -> timestamp;
331 4 param_to_column('end') -> timestamp;
332 3 param_to_column(box) -> box.
333
334 -spec param_id(Key :: atom(), Value :: any()) -> string().
335 679 param_id(box, undefined) -> "_no_bin";
336 5 param_id(box, <<"all">>) -> "";
337 123 param_id(box, _) -> "_box";
338 2344 param_id(_, undefined) -> "";
339 775 param_id(order, desc) -> "_desc";
340 32 param_id(order, asc) -> "_asc";
341 7 param_id(limit, _) -> "_lim";
342 34 param_id(start, _) -> "_start";
343 36 param_id('end', _) -> "_end";
344 13 param_id(hidden_read, true) -> "_hr";
345 794 param_id(hidden_read, false) -> "".
346
347 -spec order_to_sql(Order :: asc | desc) -> binary().
348 2 order_to_sql(asc) -> <<"ASC">>;
349 10 order_to_sql(desc) -> <<"DESC">>.
350
351 -spec sql_and_where_limit(non_neg_integer() | undefined) -> {iolist(), iolist()}.
352 sql_and_where_limit(undefined) ->
353 10 {"", ""};
354 sql_and_where_limit(_) ->
355 2 rdbms_queries:get_db_specific_limits().
356
357 -spec lookup_sql_condition(Key :: atom(), Value :: any()) -> string().
358 lookup_sql_condition(start, Timestamp) when is_integer(Timestamp) ->
359 3 " AND timestamp >= ?";
360 lookup_sql_condition('end', Timestamp) when is_integer(Timestamp) ->
361 4 " AND timestamp <= ?";
362 lookup_sql_condition(hidden_read, true) ->
363 1 " AND unread_count > 0";
364 lookup_sql_condition(box, undefined) ->
365 8 " AND box <> 'bin'";
366 lookup_sql_condition(box, <<"all">>) ->
367 1 "";
368 lookup_sql_condition(box, Val) when is_binary(Val) ->
369 3 " AND box = ?";
370 lookup_sql_condition(_, _) ->
371 28 "".
372
373 %% Property update
374
375 update_properties_query(Properties) ->
376 6 KVs = [{Key, maps:get(Key, Properties, undefined)} || Key <- property_keys()],
377 6 Parts = [update_sql_part(Key, Value) || {Key, Value} <- KVs, Value =/= undefined],
378 6 ["UPDATE inbox SET ", string:join(Parts, ", "),
379 " WHERE luser = ? AND lserver = ? AND remote_bare_jid = ?"].
380
381 update_query_args(LUser, LServer, RemBareJID, Properties) ->
382 102 [maps:get(Key, Properties) || Key <- update_arg_keys(Properties)] ++
383 [LUser, LServer, RemBareJID].
384
385 update_query_columns(Properties) ->
386 6 update_arg_keys(Properties) ++ [luser, lserver, remote_bare_jid].
387
388 update_arg_keys(Properties) ->
389 108 lists:filter(fun(Key) -> maps:is_key(Key, Properties) end, [box, muted_until]).
390
391 update_query_name(Properties) ->
392 102 IDString = lists:flatmap(fun(Prop) ->
393 306 property_id(Prop, maps:get(Prop, Properties, undefined))
394 end, property_keys()),
395 102 list_to_atom("inbox_update_properties" ++ IDString).
396
397 property_keys() ->
398 108 [unread_count, box, muted_until].
399
400 -spec property_id(Key :: atom(), Value :: any()) -> string().
401 194 property_id(_, undefined) -> "";
402 16 property_id(unread_count, 0) -> "_read";
403 8 property_id(unread_count, 1) -> "_unread";
404 68 property_id(box, _) -> "_box";
405 20 property_id(muted_until, _) -> "_muted".
406
407 -spec update_sql_part(Key :: atom(), Value :: any()) -> string().
408 update_sql_part(unread_count, 0) ->
409 3 "unread_count = 0";
410 update_sql_part(unread_count, 1) ->
411 1 "unread_count = CASE unread_count WHEN 0 THEN 1 ELSE unread_count END";
412 update_sql_part(box, Val) when is_binary(Val) ->
413 2 "box = ?";
414 update_sql_part(muted_until, Val) when is_integer(Val) ->
415 3 "muted_until = ?".
416
417 %% Query execution
418
419 -spec execute_select_unread_count(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid()) ->
420 mongoose_rdbms:query_result().
421 execute_select_unread_count(HostType, LUser, LServer, RemBareJID) ->
422
:-(
mongoose_rdbms:execute_successfully(HostType, inbox_select_unread_count,
423 [LUser, LServer, RemBareJID]).
424
425 -spec execute_select_full_entry(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid()) ->
426 mongoose_rdbms:query_result().
427 execute_select_full_entry(HostType, LUser, LServer, RemBareJID) ->
428 2 mongoose_rdbms:execute_successfully(HostType, inbox_select_entry,
429 [LUser, LServer, RemBareJID]).
430
431 -spec execute_select_properties(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid()) ->
432 mongoose_rdbms:query_result().
433 execute_select_properties(HostType, LUser, LServer, RemBareJID) ->
434 96 mongoose_rdbms:execute_successfully(HostType, inbox_select_properties,
435 [LUser, LServer, RemBareJID]).
436
437 -spec execute_reset_unread(mongooseim:host_type(), jid:luser(), jid:lserver(), jid:literal_jid(), binary() | undefined, integer()) ->
438 mongoose_rdbms:query_result().
439 execute_reset_unread(HostType, LUser, LServer, RemBareJID, undefined, TS) ->
440 3 mongoose_rdbms:execute_successfully(HostType, inbox_reset_unread,
441 [LUser, LServer, RemBareJID, TS]);
442 execute_reset_unread(HostType, LUser, LServer, RemBareJID, MsgId, TS) ->
443 54 mongoose_rdbms:execute_successfully(HostType, inbox_reset_unread_msg,
444 [LUser, LServer, RemBareJID, MsgId, TS]).
445
446 -spec execute_delete(mongooseim:host_type(),
447 jid:luser(), jid:lserver(), jid:literal_jid()) ->
448 mongoose_rdbms:query_result().
449 execute_delete(HostType, LUser, LServer, RemBareJID) ->
450 5 mongoose_rdbms:execute_successfully(HostType, inbox_delete_row, [LUser, LServer, RemBareJID]).
451
452 -spec execute_delete(mongooseim:host_type(), jid:lserver(), jid:luser()) -> mongoose_rdbms:query_result().
453 execute_delete(HostType, LUser, LServer) ->
454 610 mongoose_rdbms:execute_successfully(HostType, inbox_delete, [LUser, LServer]).
455
456 -spec execute_delete_domain(HostType :: mongooseim:host_type(),
457 LServer :: jid:lserver()) ->
458 mongoose_rdbms:query_result().
459 execute_delete_domain(HostType, LServer) ->
460 1 mongoose_rdbms:execute_successfully(HostType, inbox_delete_domain, [LServer]).
461
462 %% DB result processing
463 -type db_return() :: {RemBareJID :: jid:luser(),
464 MsgId :: id(),
465 MsgContents :: binary(),
466 Timestamp :: non_neg_integer() | binary(),
467 Count :: non_neg_integer() | binary(),
468 MutedUntil :: binary(),
469 Box :: binary()}.
470
471 query_row() ->
472 57 <<" remote_bare_jid, msg_id, box, content, timestamp, muted_until, unread_count ">>.
473
474 -spec decode_row(mongooseim:host_type(), db_return()) -> inbox_res().
475 decode_row(HostType, {RemBareJID, MsgId, Box, Content, Timestamp, MutedUntil, Count}) ->
476 695 {ok, Parsed} = exml:parse(mongoose_rdbms:unescape_binary(HostType, Content)),
477 695 BCount = mongoose_rdbms:result_to_integer(Count),
478 695 NumericTimestamp = mongoose_rdbms:result_to_integer(Timestamp),
479 695 NumericMutedUntil = mongoose_rdbms:result_to_integer(MutedUntil),
480 695 #{remote_jid => RemBareJID,
481 msg_id => MsgId,
482 box => Box,
483 msg => Parsed,
484 timestamp => NumericTimestamp,
485 muted_until => NumericMutedUntil,
486 unread_count => BCount}.
487
488 -spec decode_properties({_, _, _}) -> entry_properties().
489 decode_properties({Box, BMutedUntil, BCount}) ->
490 96 Count = mongoose_rdbms:result_to_integer(BCount),
491 96 MutedUntil = mongoose_rdbms:result_to_integer(BMutedUntil),
492 96 #{box => Box,
493 unread_count => Count,
494 muted_until => MutedUntil}.
495
496 -spec check_result_is_expected(_, list()) -> mod_inbox:write_res().
497 check_result_is_expected({updated, Val}, ValList) when is_list(ValList) ->
498 134 case lists:member(Val, ValList) of
499 134 true -> ok;
500
:-(
_ -> {error, {expected_does_not_match, Val, ValList}}
501 end;
502 check_result_is_expected(Result, _) ->
503
:-(
{error, {bad_result, Result}}.
504
505 -spec check_result(_) -> mod_inbox:count_res().
506 check_result({selected, []}) ->
507
:-(
{ok, 0};
508 check_result({selected, [{Val}]}) ->
509
:-(
parse_result(Val);
510 check_result({updated, _, [{Val}]}) ->
511
:-(
parse_result(Val);
512 check_result({updated, _}) ->
513 1272 ok;
514 check_result(Result) ->
515
:-(
{error, {bad_result, Result}}.
516
517 parse_result(Value) when is_integer(Value) ->
518
:-(
{ok, Value};
519 parse_result(Value) when is_binary(Value) ->
520
:-(
{ok, binary_to_integer(Value)};
521 parse_result(null) ->
522
:-(
{ok, 0};
523 parse_result(Value) ->
524
:-(
{error, {unknown_result_value_type, Value}}.
Line Hits Source