1 -module(gen_storage).
2
3 -author('stephan@spaceboyz.net').
4
5 -export([behaviour_info/1]).
6
7 -export([all_table_hosts/1, table_info/3, create_table/4, delete_table/2,
8 add_table_copy/4, add_table_index/3, read/2, write/2, delete/2, delete_object/2,
9 read/4, write/4, delete/4, delete_object/4, select/2, select/3, select/4,
10 select/5, count_records/2, count_records/3, delete_where/3, dirty_read/2,
11 dirty_write/2, dirty_delete/2, dirty_delete_object/2, dirty_read/3,
12 dirty_write/3, dirty_delete/3, dirty_delete_object/3, dirty_select/3,
13 dirty_count_records/2, dirty_count_records/3, dirty_delete_where/3,
14 async_dirty/3, sync_dirty/3, transaction/3, write_lock_table/2]).
15
16 behaviour_info(callbacks) ->
17 [{table_info, 1}, {prepare_tabdef, 2}, {create_table, 1}, {delete_table, 1},
18 {add_table_copy, 3}, {add_table_index, 2}, {dirty_read, 2}, {read, 3},
19 {dirty_select, 2}, {select, 3}, {dirty_count_records, 2}, {count_records, 2},
20 {dirty_write, 2}, {write, 3}, {dirty_delete, 2}, {delete, 3},
21 {dirty_delete_object, 2}, {delete_object, 3}, {delete_where, 2},
22 {dirty_delete_where, 2}, {async_dirty, 2}, {sync_dirty, 2}, {transaction, 2}];
23 behaviour_info(_) -> undefined.
24
25 -type({storage_host, {type, 49, binary, []}, []}).
26
27 -type({storage_table, {type, 50, atom, []}, []}).
28
29 -type({lock_kind,
30 {type, 51, union, [{atom, 51, read}, {atom, 51, write}, {atom, 51, sticky_write}]},
31 []}).
32
33 -record(table, {host_name, backend, def}).
34
35 -record(mnesia_def, {table, tabdef}).
36
37 -include("ejabberd.hrl").
38
39
40 -'spec'({{all_table_hosts, 1},
41 [{type, 61, 'fun',
42 [{type, 61, product, [{type, 61, storage_table, []}]},
43 {type, 62, list, [{type, 62, storage_host, []}]}]}]}).
44
45 all_table_hosts(Tab) ->
46 TT = setelement(2, {table, {<<"hidding_from_dialyzer">>, '$2'}, '_', '_'},
47 {'$1', '$2'}),
48 Res = (catch mnesia:dirty_select(table,
49 [{TT, [{'=:=', '$2', {const, Tab}}], ['$1']}])),
50 case Res of
51 Res when is_list(Res) -> [HostB || HostB <- Res, is_binary(HostB)];
52 _ -> []
53 end.
54
55 -'spec'({{table_info, 3},
56 [{type, 75, 'fun',
57 [{type, 75, product,
58 [{type, 75, storage_host, []}, {type, 75, storage_table, []},
59 {type, 75, atom, []}]},
60 {type, 76, any, []}]}]}).
61
62 table_info(Host, Tab, InfoKey) ->
63 Info = case get_table(Host, Tab) of
64 #table{backend = mnesia, def = #mnesia_def{tabdef = Def}} ->
65 [{backend, mnesia} | Def];
66 #table{backend = Backend, def = Def} ->
67 Info1 = Backend:table_info(Def),
68 BackendName = case Backend of gen_storage_odbc -> odbc end,
69 [{backend, BackendName} | Info1]
70 end,
71 case InfoKey of
72 all -> Info;
73 _ ->
74 case lists:keysearch(InfoKey, 1, Info) of
75 {value, {_, Value}} -> Value;
76 false when InfoKey =:= record_name -> Tab
77 end
78 end.
79
80
92
93
97
98 -'spec'({{create_table, 4},
99 [{type, 118, 'fun',
100 [{type, 118, product,
101 [{type, 118, atom, []}, {type, 118, storage_host, []},
102 {type, 118, storage_table, []}, {type, 118, list, []}]},
103 {type, 119, tuple, any}]}]}).
104
105 create_table(mnesia, Host, Tab, Def) ->
106 MDef = filter_mnesia_tabdef(Def),
107 define_table(mnesia, Host, Tab, #mnesia_def{table = Tab, tabdef = MDef}),
108 mnesia:create_table(Tab, MDef);
109 create_table(odbc, Host, Tab, Def) ->
110 ODef = gen_storage_odbc:prepare_tabdef(Tab, Def),
111 define_table(gen_storage_odbc, Host, Tab, ODef),
112 gen_storage_odbc:create_table(ODef).
113
114 -'spec'({{define_table, 4},
115 [{type, 132, 'fun',
116 [{type, 132, product,
117 [{type, 132, atom, []}, {type, 132, storage_host, []},
118 {type, 132, storage_table, []},
119 {type, 132, union,
120 [{type, 132, record, [{atom, 132, mnesia_def}]},
121 {type, 132, tuple, any}]}]},
122 {atom, 133, ok}]}]}).
123
124 define_table(Backend, Host, Name, Def) ->
125 mnesia:create_table(table, [{attributes, record_info(fields, table)}]),
126 mnesia:dirty_write(#table{host_name = {Host, Name}, backend = Backend, def = Def}).
127
128
130
131 -'spec'({{filter_mnesia_tabdef, 1},
132 [{type, 142, 'fun',
133 [{type, 142, product, [{type, 142, list, []}]},
134 {type, 143, list, [{type, 143, any, []}]}]}]}).
135
136 filter_mnesia_tabdef(TabDef) -> lists:filter(fun filter_mnesia_tabdef_/1, TabDef).
137
138 filter_mnesia_tabdef_({access_mode, _}) -> true;
139 filter_mnesia_tabdef_({attributes, _}) -> true;
140 filter_mnesia_tabdef_({disc_copies, _}) -> true;
141 filter_mnesia_tabdef_({disc_only_copies, _}) -> true;
142 filter_mnesia_tabdef_({index, _}) -> true;
143 filter_mnesia_tabdef_({load_order, _}) -> true;
144 filter_mnesia_tabdef_({ram_copies, _}) -> true;
145 filter_mnesia_tabdef_({record_name, _}) -> true;
146 filter_mnesia_tabdef_({snmp, _}) -> true;
147 filter_mnesia_tabdef_({type, _}) -> true;
148 filter_mnesia_tabdef_({local_content, _}) -> true;
149 filter_mnesia_tabdef_(_) -> false.
150
151 -'spec'({{delete_table, 2},
152 [{type, 162, 'fun',
153 [{type, 162, product,
154 [{type, 162, storage_host, []}, {type, 162, storage_table, []}]},
155 {type, 163, tuple, [{atom, 163, atomic}, {atom, 163, ok}]}]}]}).
156
157 delete_table(Host, Tab) -> backend_apply(delete_table, Host, Tab).
158
159 -'spec'({{add_table_copy, 4},
160 [{type, 168, 'fun',
161 [{type, 168, product,
162 [{type, 168, storage_host, []}, {type, 168, storage_table, []},
163 {type, 168, node, []}, {type, 168, atom, []}]},
164 {type, 169, tuple, [{atom, 169, atomic}, {atom, 169, ok}]}]}]}).
165
166 add_table_copy(Host, Tab, Node, Type) ->
167 backend_apply(add_table_copy, Host, Tab, [Node, Type]).
168
169 -'spec'({{add_table_index, 3},
170 [{type, 173, 'fun',
171 [{type, 173, product,
172 [{type, 173, storage_host, []}, {type, 173, storage_table, []},
173 {type, 173, atom, []}]},
174 {type, 174, tuple, [{atom, 174, atomic}, {atom, 174, ok}]}]}]}).
175
176 add_table_index(Host, Tab, Attribute) ->
177 backend_apply(add_table_index, Host, Tab, [Attribute]).
178
179 -'spec'({{read, 2},
180 [{type, 179, 'fun',
181 [{type, 179, product,
182 [{type, 179, storage_host, []},
183 {type, 179, tuple,
184 [{type, 179, storage_table, []}, {type, 179, any, []}]}]},
185 {type, 180, list, [{type, 180, tuple, any}]}]}]}).
186
187 read(Host, {Tab, Key}) -> backend_apply(read, Host, Tab, [Key, read]).
188
189 -'spec'({{read, 4},
190 [{type, 184, 'fun',
191 [{type, 184, product,
192 [{type, 184, storage_host, []}, {type, 184, storage_table, []},
193 {type, 184, any, []}, {type, 184, lock_kind, []}]},
194 {type, 185, list, [{type, 185, tuple, any}]}]}]}).
195
196 read(Host, Tab, Key, LockKind) -> backend_apply(read, Host, Tab, [Key, LockKind]).
197
198 -'spec'({{dirty_read, 2},
199 [{type, 190, 'fun',
200 [{type, 190, product,
201 [{type, 190, storage_host, []},
202 {type, 190, tuple,
203 [{type, 190, storage_table, []}, {type, 190, any, []}]}]},
204 {type, 191, list, [{type, 191, tuple, any}]}]}]}).
205
206 dirty_read(Host, {Tab, Key}) -> backend_apply(dirty_read, Host, Tab, [Key]).
207
208 -'spec'({{dirty_read, 3},
209 [{type, 195, 'fun',
210 [{type, 195, product,
211 [{type, 195, storage_host, []}, {type, 195, storage_table, []},
212 {type, 195, any, []}]},
213 {type, 196, list, [{type, 196, tuple, any}]}]}]}).
214
215 dirty_read(Host, Tab, Key) -> backend_apply(dirty_read, Host, Tab, [Key]).
216
217
219
220
221 -type({matchvalue,
222 {type, 204, union,
223 [{atom, 204, '_'}, {type, 205, integer, []}, {type, 206, string, []},
224 {type, 207, tuple, any}]},
225 []}).
226
227
228 -type({matchrule,
229 {type, 209, union,
230 [{type, 209, tuple,
231 [{atom, 209, 'and'}, {type, 209, matchrule, []}, {type, 209, matchrule, []}]},
232 {type, 210, tuple,
233 [{atom, 210, 'andalso'}, {type, 210, matchrule, []},
234 {type, 210, matchrule, []}]},
235 {type, 211, tuple,
236 [{atom, 211, 'or'}, {type, 211, matchrule, []}, {type, 211, matchrule, []}]},
237 {type, 212, tuple,
238 [{atom, 212, 'orelse'}, {type, 212, matchrule, []},
239 {type, 212, matchrule, []}]},
240 {type, 213, tuple,
241 [{atom, 213, '='},
242 {ann_type, 213, [{var, 213, 'Attribute'}, {type, 213, atom, []}]},
243 {type, 213, matchvalue, []}]},
244 {type, 214, tuple,
245 [{atom, 214, '<'},
246 {ann_type, 214, [{var, 214, 'Attribute'}, {type, 214, atom, []}]},
247 {type, 214, matchvalue, []}]},
248 {type, 215, tuple,
249 [{atom, 215, '=/='},
250 {ann_type, 215, [{var, 215, 'Attribute'}, {type, 215, atom, []}]},
251 {type, 215, matchvalue, []}]},
252 {type, 216, tuple,
253 [{atom, 216, like},
254 {ann_type, 216, [{var, 216, 'Attribute'}, {type, 216, atom, []}]},
255 {type, 216, matchvalue, []}]}]},
256 []}).
257
258
259
260 -'spec'({{select, 3},
261 [{type, 220, 'fun',
262 [{type, 220, product,
263 [{type, 220, storage_host, []}, {type, 220, storage_table, []},
264 {type, 220, list, [{type, 220, matchrule, []}]}]},
265 {type, 221, list, [{type, 221, tuple, any}]}]}]}).
266
267 select(Host, Tab, MatchRules) -> select(Host, Tab, MatchRules, read).
268
269 -'spec'({{select, 4},
270 [{type, 225, 'fun',
271 [{type, 225, product,
272 [{type, 225, storage_host, []}, {type, 225, storage_table, []},
273 {type, 225, list, [{type, 225, matchrule, []}]},
274 {type, 225, lock_kind, []}]},
275 {type, 226, list, [{type, 226, tuple, any}]}]}]}).
276
277 select(Host, Tab, MatchRules, Lock) ->
278 case get_table(Host, Tab) of
279 #table{backend = mnesia} ->
280 MatchSpec = matchrules_to_mnesia_matchspec(Tab, MatchRules),
281 mnesia:select(Tab, MatchSpec, Lock);
282 #table{backend = Backend, def = Def} -> Backend:select(Def, MatchRules, undefined)
283 end.
284
285 -'spec'({{select, 5},
286 [{type, 237, 'fun',
287 [{type, 237, product,
288 [{type, 237, storage_host, []}, {type, 237, storage_table, []},
289 {type, 237, list, [{type, 237, matchrule, []}]}, {type, 237, integer, []},
290 {type, 237, lock_kind, []}]},
291 {type, 238, union,
292 [{type, 238, tuple,
293 [{type, 238, list, [{type, 238, tuple, any}]}, {type, 238, any, []}]},
294 {atom, 238, '$end_of_table'}]}]}]}).
295
296 select(Host, Tab, MatchRules, N, Lock) ->
297 case get_table(Host, Tab) of
298 #table{backend = mnesia} ->
299 MatchSpec = matchrules_to_mnesia_matchspec(Tab, MatchRules),
300 mnesia:select(Tab, MatchSpec, N, Lock);
301 #table{backend = Backend, def = Def} -> Backend:select(Def, MatchRules, N)
302 end.
303
304 -'spec'({{select, 2},
305 [{type, 249, 'fun',
306 [{type, 249, product,
307 [{type, 249, tuple,
308 [{type, 249, storage_host, []}, {type, 249, storage_table, []}]},
309 {type, 249, any, []}]},
310 {type, 250, union,
311 [{type, 250, tuple,
312 [{type, 250, list, [{type, 250, tuple, any}]}, {type, 250, any, []}]},
313 {atom, 250, '$end_of_table'}]}]}]}).
314
315 select({Host, Tab}, Cont) ->
316 case get_table(Host, Tab) of
317 #table{backend = mnesia} -> mnesia:select(Cont);
318 #table{backend = Backend} -> Backend:select(Cont)
319 end.
320
321 -'spec'({{dirty_select, 3},
322 [{type, 259, 'fun',
323 [{type, 259, product,
324 [{type, 259, storage_host, []}, {type, 259, storage_table, []},
325 {type, 259, list, [{type, 259, matchrule, []}]}]},
326 {type, 260, list, [{type, 260, tuple, any}]}]}]}).
327
328 dirty_select(Host, Tab, MatchRules) ->
329 case get_table(Host, Tab) of
330 #table{backend = mnesia} ->
331 MatchSpec = matchrules_to_mnesia_matchspec(Tab, MatchRules),
332 mnesia:dirty_select(Tab, MatchSpec);
333 #table{backend = Backend, def = Def} -> Backend:dirty_select(Def, MatchRules)
334 end.
335
336 matchrules_to_mnesia_matchspec(Tab, MatchRules) ->
337 RecordName = mnesia:table_info(Tab, record_name),
338 Attributes = mnesia:table_info(Tab, attributes),
339
340 MatchHead = list_to_tuple([RecordName | lists:reverse(lists:foldl(fun (_, L) ->
341 A =
342 list_to_atom([$$
343 | integer_to_list(length(L)
344 +
345 1)]),
346 [A | L]
347 end,
348 [], Attributes))]),
349
350 MatchConditions = [matchrules_transform_conditions(Attributes, Rule)
351 || Rule <- MatchRules],
352
353 MatchBody = ['$_'],
354 [{MatchHead, MatchConditions, MatchBody}].
355
356
357 matchrules_transform_conditions(Attributes, {Op, Attribute, Value})
358 when Op =:= '=';
359 Op =:= '==';
360 Op =:= '=:=';
361 Op =:= like;
362 Op =:= '=/=';
363 Op =:= '<';
364 Op =:= '>';
365 Op =:= '>=';
366 Op =:= '=<' ->
367 Var = case list_find(Attribute, Attributes) of
368 false -> exit(unknown_attribute);
369 N -> list_to_atom([$$ | integer_to_list(N)])
370 end,
371 if is_tuple(Value) ->
372 {Expr, _} = lists:foldl(fun ('_', {R, N}) -> {R, N + 1};
373 (V, {R, N}) ->
374 {[matchrules_transform_column_op(Op,
375 {element, N,
376 Var},
377 {const, V})
378 | R],
379 N + 1}
380 end,
381 {[], 1}, tuple_to_list(Value)),
382 case Expr of
383 [E] -> E;
384 _ -> list_to_tuple(['andalso' | Expr])
385 end;
386 true -> matchrules_transform_column_op(Op, Var, Value)
387 end;
388 matchrules_transform_conditions(Attributes, T) when is_tuple(T) ->
389 L = tl(tuple_to_list(T)),
390 L2 = [matchrules_transform_conditions(Attributes, E) || E <- L],
391 list_to_tuple([element(1, T) | L2]).
392
393 matchrules_transform_column_op(like, Expression, Pattern) ->
394 case lists:foldl(fun ('_', {R, E1}) -> {R, E1};
395 (P, {R, E1}) ->
396 Comparision = {'=:=', {hd, E1}, {const, P}},
397 {[Comparision | R], {tl, E1}}
398 end,
399 {[], Expression}, Pattern)
400 of
401 {[Comparision], _} -> Comparision;
402 {Comparisions, _} -> list_to_tuple(['andalso' | lists:reverse(Comparisions)])
403 end;
404 matchrules_transform_column_op(Op, Expression, Pattern) when Op =:= '='; Op =:= '=:=' ->
405 {'=:=', Expression, Pattern};
406 matchrules_transform_column_op(Op, Expression, Pattern) -> {Op, Expression, Pattern}.
407
408
409 list_find(E, L) -> list_find(E, L, 1).
410
411 list_find(_, [], _) -> false;
412 list_find(E, [E | _], N) -> N;
413 list_find(E, [_ | L], N) -> list_find(E, L, N + 1).
414
415 -'spec'({{dirty_count_records, 2},
416 [{type, 363, 'fun',
417 [{type, 363, product,
418 [{type, 363, storage_host, []}, {type, 363, storage_table, []}]},
419 {type, 364, integer, []}]}]}).
420
421 dirty_count_records(Host, Tab) -> dirty_count_records(Host, Tab, []).
422
423 -'spec'({{dirty_count_records, 3},
424 [{type, 368, 'fun',
425 [{type, 368, product,
426 [{type, 368, storage_host, []}, {type, 368, storage_table, []},
427 {type, 368, list, [{type, 368, matchrule, []}]}]},
428 {type, 369, integer, []}]}]}).
429
430 dirty_count_records(Host, Tab, MatchRules) ->
431 case get_table(Host, Tab) of
432 #table{backend = mnesia} ->
433 [{MatchHead, MatchConditions, _}] = matchrules_to_mnesia_matchspec(Tab,
434 MatchRules),
435 MatchSpec = [{MatchHead, MatchConditions, [[]]}],
436 length(mnesia:dirty_select(Tab, MatchSpec));
437 #table{backend = Backend, def = Def} -> Backend:dirty_count_records(Def, MatchRules)
438 end.
439
440 -define(COUNT_RECORDS_BATCHSIZE, 100).
441
442 -'spec'({{count_records, 2},
443 [{type, 383, 'fun',
444 [{type, 383, product,
445 [{type, 383, storage_host, []}, {type, 383, storage_table, []}]},
446 {type, 384, integer, []}]}]}).
447
448 count_records(Host, Tab) -> count_records(Host, Tab, []).
449
450 -'spec'({{count_records, 3},
451 [{type, 388, 'fun',
452 [{type, 388, product,
453 [{type, 388, storage_host, []}, {type, 388, storage_table, []},
454 {type, 388, list, [{type, 388, matchrule, []}]}]},
455 {type, 389, integer, []}]}]}).
456
457 count_records(Host, Tab, MatchRules) ->
458 case get_table(Host, Tab) of
459 #table{backend = mnesia} ->
460 [{MatchHead, MatchConditions, _}] = matchrules_to_mnesia_matchspec(Tab,
461 MatchRules),
462 MatchSpec = [{MatchHead, MatchConditions, [[]]}],
463 case mnesia:select(Tab, MatchSpec, ?COUNT_RECORDS_BATCHSIZE, read) of
464 {Result, Cont} ->
465 Count = length(Result), mnesia_count_records_cont(Cont, Count);
466 '$end_of_table' -> 0
467 end;
468 #table{backend = Backend, def = Def} -> Backend:count_records(Def, MatchRules)
469 end.
470
471 mnesia_count_records_cont(Cont, Count) ->
472 case mnesia:select(Cont) of
473 {Result, Cont} ->
474 NewCount = Count + length(Result), mnesia_count_records_cont(Cont, NewCount);
475 '$end_of_table' -> Count
476 end.
477
478 -'spec'({{write, 2},
479 [{type, 418, 'fun',
480 [{type, 418, product,
481 [{type, 418, storage_host, []}, {type, 418, tuple, any}]},
482 {atom, 419, ok}]}]}).
483
484 write(Host, Rec) -> Tab = element(1, Rec), backend_apply(write, Host, Tab, [Rec, write]).
485
486 -'spec'({{write, 4},
487 [{type, 424, 'fun',
488 [{type, 424, product,
489 [{type, 424, storage_host, []}, {type, 424, storage_table, []},
490 {type, 424, tuple, any}, {type, 424, lock_kind, []}]},
491 {atom, 425, ok}]}]}).
492
493 write(Host, Tab, Rec, LockKind) -> backend_apply(write, Host, Tab, [Rec, LockKind]).
494
495 -'spec'({{dirty_write, 2},
496 [{type, 430, 'fun',
497 [{type, 430, product,
498 [{type, 430, storage_host, []}, {type, 430, tuple, any}]},
499 {atom, 431, ok}]}]}).
500
501 dirty_write(Host, Rec) ->
502 Tab = element(1, Rec), backend_apply(dirty_write, Host, Tab, [Rec]).
503
504 -'spec'({{dirty_write, 3},
505 [{type, 436, 'fun',
506 [{type, 436, product,
507 [{type, 436, storage_host, []}, {type, 436, storage_table, []},
508 {type, 436, tuple, any}]},
509 {atom, 437, ok}]}]}).
510
511 dirty_write(Host, Tab, Rec) -> backend_apply(dirty_write, Host, Tab, [Rec]).
512
513 -'spec'({{delete, 2},
514 [{type, 442, 'fun',
515 [{type, 442, product,
516 [{type, 442, storage_host, []},
517 {type, 442, tuple,
518 [{type, 442, storage_table, []}, {type, 442, any, []}]}]},
519 {atom, 443, ok}]}]}).
520
521 delete(Host, {Tab, Key}) -> backend_apply(delete, Host, Tab, [Key, write]).
522
523 -'spec'({{delete, 4},
524 [{type, 447, 'fun',
525 [{type, 447, product,
526 [{type, 447, storage_host, []}, {type, 447, storage_table, []},
527 {type, 447, any, []}, {type, 447, lock_kind, []}]},
528 {atom, 448, ok}]}]}).
529
530 delete(Host, Tab, Key, LockKind) -> backend_apply(delete, Host, Tab, [Key, LockKind]).
531
532 -'spec'({{dirty_delete, 2},
533 [{type, 453, 'fun',
534 [{type, 453, product,
535 [{type, 453, storage_host, []},
536 {type, 453, tuple,
537 [{type, 453, storage_table, []}, {type, 453, any, []}]}]},
538 {atom, 454, ok}]}]}).
539
540 dirty_delete(Host, {Tab, Key}) -> backend_apply(dirty_delete, Host, Tab, [Key]).
541
542 -'spec'({{dirty_delete, 3},
543 [{type, 458, 'fun',
544 [{type, 458, product,
545 [{type, 458, storage_host, []}, {type, 458, storage_table, []},
546 {type, 458, any, []}]},
547 {atom, 459, ok}]}]}).
548
549 dirty_delete(Host, Tab, Key) -> backend_apply(dirty_delete, Host, Tab, [Key]).
550
551 -'spec'({{delete_object, 2},
552 [{type, 464, 'fun',
553 [{type, 464, product,
554 [{type, 464, storage_host, []}, {type, 464, tuple, any}]},
555 {atom, 465, ok}]}]}).
556
557 delete_object(Host, Rec) ->
558 Tab = element(1, Rec), backend_apply(delete_object, Host, Tab, [Rec, write]).
559
560 -'spec'({{delete_object, 4},
561 [{type, 470, 'fun',
562 [{type, 470, product,
563 [{type, 470, storage_host, []}, {type, 470, storage_table, []},
564 {type, 470, tuple, any}, {type, 470, lock_kind, []}]},
565 {atom, 471, ok}]}]}).
566
567 delete_object(Host, Tab, Rec, LockKind) ->
568 backend_apply(delete_object, Host, Tab, [Rec, LockKind]).
569
570 -'spec'({{dirty_delete_object, 2},
571 [{type, 476, 'fun',
572 [{type, 476, product,
573 [{type, 476, storage_host, []}, {type, 476, tuple, any}]},
574 {atom, 477, ok}]}]}).
575
576 dirty_delete_object(Host, Rec) ->
577 Tab = element(1, Rec), backend_apply(delete_object, Host, Tab, [Rec]).
578
579 -'spec'({{dirty_delete_object, 3},
580 [{type, 482, 'fun',
581 [{type, 482, product,
582 [{type, 482, storage_host, []}, {type, 482, storage_table, []},
583 {type, 482, tuple, any}]},
584 {atom, 483, ok}]}]}).
585
586 dirty_delete_object(Host, Tab, Rec) -> backend_apply(delete_object, Host, Tab, [Rec]).
587
588 -define(DELETE_WHERE_BATCH_SIZE, 100).
589
590 -'spec'({{delete_where, 3},
591 [{type, 490, 'fun',
592 [{type, 490, product,
593 [{type, 490, storage_host, []}, {type, 490, storage_table, []},
594 {type, 490, list, [{type, 490, matchrule, []}]}]},
595 {atom, 491, ok}]}]}).
596
597 delete_where(Host, Tab, MatchRules) ->
598 case get_table(Host, Tab) of
599 #table{backend = mnesia} ->
600 MatchSpec = matchrules_to_mnesia_matchspec(Tab, MatchRules),
601 mnesia:write_lock_table(Tab),
602 SR = mnesia:select(Tab, MatchSpec, ?DELETE_WHERE_BATCH_SIZE, write),
603 delete_where_mnesia1(SR);
604 #table{backend = Backend, def = Def} -> Backend:delete_where(Def, MatchRules)
605 end.
606
607 delete_where_mnesia1('$end_of_table') -> ok;
608 delete_where_mnesia1({Objects, Cont}) ->
609 lists:foreach(fun (Object) -> mnesia:delete_object(Object) end, Objects),
610 delete_where_mnesia1(mnesia:select(Cont)).
611
612 -'spec'({{dirty_delete_where, 3},
613 [{type, 513, 'fun',
614 [{type, 513, product,
615 [{type, 513, storage_host, []}, {type, 513, storage_table, []},
616 {type, 513, list, [{type, 513, matchrule, []}]}]},
617 {atom, 514, ok}]}]}).
618
619 dirty_delete_where(Host, Tab, MatchRules) ->
620 case get_table(Host, Tab) of
621 #table{backend = mnesia} ->
622 MatchSpec = matchrules_to_mnesia_matchspec(Tab, MatchRules),
623 F = fun () ->
624 mnesia:write_lock_table(Tab),
625 Objects = mnesia:select(Tab, MatchSpec, write),
626 lists:foreach(fun (Object) -> mnesia:delete_object(Object) end,
627 Objects)
628 end,
629 {atomic, _} = mnesia:transaction(F);
630 #table{backend = Backend, def = Def} -> Backend:dirty_delete_where(Def, MatchRules)
631 end.
632
633 -'spec'({{write_lock_table, 2},
634 [{type, 533, 'fun',
635 [{type, 533, product,
636 [{type, 533, storage_host, []}, {type, 533, storage_table, []}]},
637 {atom, 534, ok}]}]}).
638
639 write_lock_table(Host, Tab) ->
640 case get_table(Host, Tab) of
641 #table{backend = mnesia} -> mnesia:write_lock_table(Tab);
642 _ -> ignored
643 end.
644
645 -'spec'({{transaction, 3},
646 [{type, 544, 'fun',
647 [{type, 544, product,
648 [{type, 544, storage_host, []}, {type, 544, storage_table, []},
649 {type, 544, 'fun', []}]},
650 {type, 545, union,
651 [{type, 545, tuple, [{atom, 545, atomic}, {type, 545, any, []}]},
652 {type, 545, tuple, [{atom, 545, aborted}, {type, 545, string, []}]}]}]}]}).
653
654
655
656 transaction(Host, Tab, Fun) ->
657
658 case transaction2(Host, Tab, Fun) of
659 {atomic, _} = Good -> Good;
660 {aborted, Reason} = Bad ->
661 Stacktrace = erlang:get_stacktrace(),
662 ?ERROR_MSG("Transaction failed for host ~p in tab ~p with fun ~p:~nReason: ~p~nStacktrace: ~p",
663 [Host, Tab, Fun, Reason, Stacktrace]),
664 Bad
665 end.
666
667 transaction2(Host, Tab, Fun) ->
668 case get_table(Host, Tab) of
669 #table{backend = mnesia} -> mnesia:transaction(Fun);
670 #table{backend = Backend, def = Def} -> Backend:transaction(Def, Fun)
671 end.
672
673 -'spec'({{sync_dirty, 3},
674 [{type, 569, 'fun',
675 [{type, 569, product,
676 [{type, 569, storage_host, []}, {type, 569, storage_table, []},
677 {type, 569, 'fun', []}]},
678 {type, 570, tuple, [{atom, 570, atomic}, {type, 570, any, []}]}]}]}).
679
680
681
682 sync_dirty(Host, Tab, Fun) ->
683 case get_table(Host, Tab) of
684 #table{backend = mnesia} -> mnesia:sync_dirty(Fun);
685 #table{backend = Backend, def = Def} -> Backend:sync_dirty(Def, Fun)
686 end.
687
688 -'spec'({{async_dirty, 3},
689 [{type, 582, 'fun',
690 [{type, 582, product,
691 [{type, 582, storage_host, []}, {type, 582, storage_table, []},
692 {type, 582, 'fun', []}]},
693 {type, 583, tuple, [{atom, 583, atomic}, {type, 583, any, []}]}]}]}).
694
695
696
697 async_dirty(Host, Tab, Fun) ->
698 case get_table(Host, Tab) of
699 #table{backend = mnesia} -> mnesia:async_dirty(Fun);
700 #table{backend = Backend, def = Def} -> Backend:async_dirty(Def, Fun)
701 end.
702
703
704 get_table(Host, Tab) when is_list(Host) -> get_table(list_to_binary(Host), Tab);
705 get_table(Host, Tab) ->
706 case {mnesia:dirty_read(table, {Host, Tab}), Host} of
707 {[T], _} -> T;
708 {_, Host} when is_binary(Host) ->
709 Prefix = lists:nth(1, string:tokens(binary_to_list(Host), ".")),
710 get_table({global, Prefix}, Tab);
711 {_, {global, _Prefix}} -> get_table(global, Tab);
712 {_, global} ->
713 catch throw(error123),
714 Stacktrace = erlang:get_stacktrace(),
715 error_logger:error_msg("gen_storage: Table ~p not found on ~p~nStacktrace: ~p",
716 [Tab, Host, Stacktrace]),
717 exit(table_not_found)
718 end.
719
720 backend_apply(F, Host, Tab) -> backend_apply(F, Host, Tab, []).
721
722 backend_apply(F, Host, Tab, A) ->
723 #table{backend = Backend, def = Def} = get_table(Host, Tab),
724 case Def of
725 #mnesia_def{table = Tab} -> apply(Backend, F, [Tab | A]);
726 _ -> apply(Backend, F, [Def | A])
727 end.