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}}. |