PageRenderTime 62ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/lib/rdbms/mnesia_patches/src/mnesia_lib.erl

http://github.com/gebi/jungerl
Erlang | 1377 lines | 1085 code | 182 blank | 110 comment | 17 complexity | d4c5ea24674b36201950c8c9da542b1f MD5 | raw file
Possible License(s): AGPL-1.0, JSON, LGPL-2.1, BSD-3-Clause
  1. %% ``The contents of this file are subject to the Erlang Public License,
  2. %% Version 1.1, (the "License"); you may not use this file except in
  3. %% compliance with the License. You should have received a copy of the
  4. %% Erlang Public License along with this software. If not, it can be
  5. %% retrieved via the world wide web at http://www.erlang.org/.
  6. %%
  7. %% Software distributed under the License is distributed on an "AS IS"
  8. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  9. %% the License for the specific language governing rights and limitations
  10. %% under the License.
  11. %%
  12. %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
  13. %% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
  14. %% AB. All Rights Reserved.''
  15. %%
  16. %% $Id$
  17. %%
  18. %% This module contains all sorts of various which doesn't fit
  19. %% anywhere else. Basically everything is exported.
  20. -module(mnesia_lib).
  21. -include("mnesia.hrl").
  22. -include_lib("kernel/include/file.hrl").
  23. -export([core_file/0]).
  24. -export([
  25. active_tables/0,
  26. add/2,
  27. add_list/2,
  28. all_nodes/0,
  29. %% catch_val/1,
  30. cleanup_tmp_files/1,
  31. copy_file/2,
  32. copy_holders/1,
  33. coredump/0,
  34. coredump/1,
  35. create_counter/1,
  36. cs_to_nodes/1,
  37. cs_to_storage_type/2,
  38. dets_to_ets/6, dets_to_ets/7,
  39. db_chunk/2,
  40. db_init_chunk/1,
  41. db_init_chunk/2,
  42. db_init_chunk/3,
  43. db_erase/2,
  44. db_erase/3,
  45. db_erase_tab/1,
  46. db_erase_tab/2,
  47. db_first/1,
  48. db_first/2,
  49. db_last/1,
  50. db_last/2,
  51. db_fixtable/3,
  52. db_get/2,
  53. db_get/3,
  54. db_match_erase/2,
  55. db_match_erase/3,
  56. db_match_object/2,
  57. db_match_object/3,
  58. db_next_key/2,
  59. db_next_key/3,
  60. db_prev_key/2,
  61. db_prev_key/3,
  62. db_put/2,
  63. db_put/3,
  64. db_select/2,
  65. db_select/3,
  66. db_select_init/4,
  67. db_select_cont/3,
  68. db_slot/2,
  69. db_slot/3,
  70. db_update_counter/3,
  71. db_update_counter/4,
  72. dbg_out/2,
  73. del/2,
  74. dets_sync_close/1,
  75. dets_sync_open/2,
  76. dets_sync_open/3,
  77. dir/0,
  78. dir/1,
  79. dir_info/0,
  80. dirty_rpc_error_tag/1,
  81. dist_coredump/0,
  82. disk_type/1,
  83. disk_type/2,
  84. elems/2,
  85. ensure_loaded/1,
  86. error/2,
  87. error_desc/1,
  88. etype/1,
  89. exists/1,
  90. fatal/2,
  91. get_node_number/0,
  92. fix_error/1,
  93. important/2,
  94. incr_counter/1,
  95. incr_counter/2,
  96. intersect/2,
  97. is_running/0,
  98. is_running/1,
  99. is_running_remote/0,
  100. is_string/1,
  101. key_search_delete/3,
  102. key_search_all/3,
  103. last_error/0,
  104. local_active_tables/0,
  105. lock_table/1,
  106. mkcore/1,
  107. not_active_here/1,
  108. other_val/2,
  109. pad_name/3,
  110. random_time/2,
  111. read_counter/1,
  112. readable_indecies/1,
  113. remote_copy_holders/1,
  114. report_fatal/2,
  115. report_system_event/1,
  116. running_nodes/0,
  117. running_nodes/1,
  118. schema_cs_to_storage_type/2,
  119. search_delete/2,
  120. set/2,
  121. set_counter/2,
  122. set_local_content_whereabouts/1,
  123. set_remote_where_to_read/1,
  124. set_remote_where_to_read/2,
  125. show/1,
  126. show/2,
  127. sort_commit/1,
  128. storage_type_at_node/2,
  129. swap_tmp_files/1,
  130. tab2dat/1,
  131. tab2dmp/1,
  132. tab2tmp/1,
  133. tab2dcd/1,
  134. tab2dcl/1,
  135. to_list/1,
  136. union/2,
  137. uniq/1,
  138. unlock_table/1,
  139. unset/1,
  140. update_counter/2,
  141. val/1,
  142. vcore/0,
  143. vcore/1,
  144. verbose/2,
  145. view/0,
  146. view/1,
  147. view/2,
  148. warning/2,
  149. is_debug_compiled/0,
  150. activate_debug_fun/5,
  151. deactivate_debug_fun/3,
  152. eval_debug_fun/4,
  153. scratch_debug_fun/0
  154. ]).
  155. search_delete(Obj, List) ->
  156. search_delete(Obj, List, [], none).
  157. search_delete(Obj, [Obj|Tail], Ack, _Res) ->
  158. search_delete(Obj, Tail, Ack, Obj);
  159. search_delete(Obj, [H|T], Ack, Res) ->
  160. search_delete(Obj, T, [H|Ack], Res);
  161. search_delete(_, [], Ack, Res) ->
  162. {Res, Ack}.
  163. key_search_delete(Key, Pos, TupleList) ->
  164. key_search_delete(Key, Pos, TupleList, none, []).
  165. key_search_delete(Key, Pos, [H|T], _Obj, Ack) when element(Pos, H) == Key ->
  166. key_search_delete(Key, Pos, T, H, Ack);
  167. key_search_delete(Key, Pos, [H|T], Obj, Ack) ->
  168. key_search_delete(Key, Pos, T, Obj, [H|Ack]);
  169. key_search_delete(_, _, [], Obj, Ack) ->
  170. {Obj, Ack}.
  171. key_search_all(Key, Pos, TupleList) ->
  172. key_search_all(Key, Pos, TupleList, []).
  173. key_search_all(Key, N, [H|T], Ack) when element(N, H) == Key ->
  174. key_search_all(Key, N, T, [H|Ack]);
  175. key_search_all(Key, N, [_|T], Ack) ->
  176. key_search_all(Key, N, T, Ack);
  177. key_search_all(_, _, [], Ack) -> Ack.
  178. intersect(L1, L2) ->
  179. L2 -- (L2 -- L1).
  180. elems(I, [H|T]) ->
  181. [element(I, H) | elems(I, T)];
  182. elems(_, []) ->
  183. [].
  184. %% sort_commit see to that checkpoint info is always first in
  185. %% commit_work structure the other info don't need to be sorted.
  186. sort_commit(List) ->
  187. sort_commit2(List, []).
  188. sort_commit2([{checkpoints, ChkpL}| Rest], Acc) ->
  189. [{checkpoints, ChkpL}| Rest] ++ Acc;
  190. sort_commit2([H | R], Acc) ->
  191. sort_commit2(R, [H | Acc]);
  192. sort_commit2([], Acc) -> Acc.
  193. is_string([H|T]) ->
  194. if
  195. 0 =< H, H < 256, integer(H) -> is_string(T);
  196. true -> false
  197. end;
  198. is_string([]) -> true.
  199. %%%
  200. union([H|L1], L2) ->
  201. case lists:member(H, L2) of
  202. true -> union(L1, L2);
  203. false -> [H | union(L1, L2)]
  204. end;
  205. union([], L2) -> L2.
  206. uniq([]) ->
  207. [];
  208. uniq(List) ->
  209. [H|T] = lists:sort(List),
  210. uniq1(H, T, []).
  211. uniq1(H, [H|R], Ack) ->
  212. uniq1(H, R, Ack);
  213. uniq1(Old, [H|R], Ack) ->
  214. uniq1(H, R, [Old|Ack]);
  215. uniq1(Old, [], Ack) ->
  216. [Old| Ack].
  217. to_list(X) when list(X) -> X;
  218. to_list(X) -> atom_to_list(X).
  219. all_nodes() ->
  220. Ns = mnesia:system_info(db_nodes) ++
  221. mnesia:system_info(extra_db_nodes),
  222. mnesia_lib:uniq(Ns).
  223. running_nodes() ->
  224. running_nodes(all_nodes()).
  225. running_nodes(Ns) ->
  226. {Replies, _BadNs} = rpc:multicall(Ns, ?MODULE, is_running_remote, []),
  227. [N || {GoodState, N} <- Replies, GoodState == true].
  228. is_running_remote() ->
  229. IsRunning = is_running(),
  230. {IsRunning == yes, node()}.
  231. is_running(Node) when atom(Node) ->
  232. case rpc:call(Node, ?MODULE, is_running, []) of
  233. {badrpc, _} -> no;
  234. X -> X
  235. end.
  236. is_running() ->
  237. case ?catch_val(mnesia_status) of
  238. {'EXIT', _} -> no;
  239. running -> yes;
  240. starting -> starting;
  241. stopping -> stopping
  242. end.
  243. show(X) ->
  244. show(X, []).
  245. show(F, A) ->
  246. io:format(user, F, A).
  247. pad_name([Char | Chars], Len, Tail) ->
  248. [Char | pad_name(Chars, Len - 1, Tail)];
  249. pad_name([], Len, Tail) when Len =< 0 ->
  250. Tail;
  251. pad_name([], Len, Tail) ->
  252. [$ | pad_name([], Len - 1, Tail)].
  253. %% Some utility functions .....
  254. active_here(Tab) ->
  255. case val({Tab, where_to_read}) of
  256. Node when Node == node() -> true;
  257. _ -> false
  258. end.
  259. not_active_here(Tab) ->
  260. not active_here(Tab).
  261. exists(Fname) ->
  262. case file:rawopen(Fname, read) of
  263. {ok, F} ->file:close(F), true;
  264. _ -> false
  265. end.
  266. dir() -> mnesia_monitor:get_env(dir).
  267. dir(Fname) ->
  268. filename:join([dir(), to_list(Fname)]).
  269. tab2dat(Tab) -> %% DETS files
  270. dir(lists:concat([Tab, ".DAT"])).
  271. tab2tmp(Tab) ->
  272. dir(lists:concat([Tab, ".TMP"])).
  273. tab2dmp(Tab) -> %% Dumped ets tables
  274. dir(lists:concat([Tab, ".DMP"])).
  275. tab2dcd(Tab) -> %% Disc copies data
  276. dir(lists:concat([Tab, ".DCD"])).
  277. tab2dcl(Tab) -> %% Disc copies log
  278. dir(lists:concat([Tab, ".DCL"])).
  279. storage_type_at_node(Node, Tab) ->
  280. search_key(Node, [{disc_copies, val({Tab, disc_copies})},
  281. {ram_copies, val({Tab, ram_copies})},
  282. {disc_only_copies, val({Tab, disc_only_copies})}|
  283. [{{external_copies, Mod}, Ns} ||
  284. {Mod,Ns} <- val({Tab, external_copies})]]).
  285. cs_to_storage_type(Node, Cs) ->
  286. search_key(Node, [{disc_copies, Cs#cstruct.disc_copies},
  287. {ram_copies, Cs#cstruct.ram_copies},
  288. {disc_only_copies, Cs#cstruct.disc_only_copies} |
  289. [{{external_copies, Mod}, Ns} ||
  290. {Mod,Ns} <- Cs#cstruct.external_copies]]).
  291. schema_cs_to_storage_type(Node, Cs) ->
  292. case cs_to_storage_type(Node, Cs) of
  293. unknown when Cs#cstruct.name == schema -> ram_copies;
  294. Other -> Other
  295. end.
  296. search_key(Key, [{Val, List} | Tail]) ->
  297. case lists:member(Key, List) of
  298. true -> Val;
  299. false -> search_key(Key, Tail)
  300. end;
  301. search_key(_Key, []) ->
  302. unknown.
  303. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  304. %% ops, we've got some global variables here :-)
  305. %% They are
  306. %%
  307. %% {Tab, setorbag}, -> set | bag
  308. %% {Tab, storage_type} -> disc_copies |ram_copies | unknown (**)
  309. %% {Tab, disc_copies} -> node list (from schema)
  310. %% {Tab, ram_copies}, -> node list (from schema)
  311. %% {Tab, arity}, -> number
  312. %% {Tab, attributes}, -> atom list
  313. %% {Tab, wild_pattern}, -> record tuple with '_'s
  314. %% {Tab, {index, Pos}} -> ets table
  315. %% {Tab, index} -> integer list
  316. %% {Tab, cstruct} -> cstruct structure
  317. %%
  318. %% The following fields are dynamic according to the
  319. %% the current node/table situation
  320. %% {Tab, where_to_write} -> node list
  321. %% {Tab, where_to_read} -> node | nowhere
  322. %%
  323. %% {schema, tables} -> tab list
  324. %% {schema, local_tables} -> tab list (**)
  325. %%
  326. %% {current, db_nodes} -> node list
  327. %%
  328. %% dir -> directory path (**)
  329. %% mnesia_status -> status | running | stopping (**)
  330. %% (**) == (Different on all nodes)
  331. %%
  332. val(Var) ->
  333. case ?catch_val(Var) of
  334. {'EXIT', _ReASoN_} -> mnesia_lib:other_val(Var, _ReASoN_);
  335. _VaLuE_ -> _VaLuE_
  336. end.
  337. set(Var, Val) ->
  338. ?ets_insert(mnesia_gvar, {Var, Val}).
  339. unset(Var) ->
  340. ?ets_delete(mnesia_gvar, Var).
  341. other_val(Var, Other) ->
  342. case Var of
  343. {_, where_to_read} -> nowhere;
  344. {_, where_to_write} -> [];
  345. {_, active_replicas} -> [];
  346. _ ->
  347. pr_other(Var, Other)
  348. end.
  349. pr_other(Var, Other) ->
  350. Why =
  351. case is_running() of
  352. no -> {node_not_running, node()};
  353. _ -> {no_exists, Var}
  354. end,
  355. verbose("~p (~p) val(mnesia_gvar, ~w) -> ~p ~p ~n",
  356. [self(), process_info(self(), registered_name),
  357. Var, Other, Why]),
  358. case Other of
  359. {badarg, [{ets, lookup_element, _}|_]} ->
  360. exit(Why);
  361. _ ->
  362. erlang:fault(Why)
  363. end.
  364. %% Some functions for list valued variables
  365. add(Var, Val) ->
  366. L = val(Var),
  367. set(Var, [Val | lists:delete(Val, L)]).
  368. add_list(Var, List) ->
  369. L = val(Var),
  370. set(Var, union(L, List)).
  371. del(Var, Val) ->
  372. L = val(Var),
  373. set(Var, lists:delete(Val, L)).
  374. %% This function is needed due to the fact
  375. %% that the application_controller enters
  376. %% a deadlock now and then. ac is implemented
  377. %% as a rather naive server.
  378. ensure_loaded(Appl) ->
  379. case application_controller:get_loaded(Appl) of
  380. {true, _} ->
  381. ok;
  382. false ->
  383. case application:load(Appl) of
  384. ok ->
  385. ok;
  386. {error, {already_loaded, Appl}} ->
  387. ok;
  388. {error, Reason} ->
  389. {error, {application_load_error, Reason}}
  390. end
  391. end.
  392. local_active_tables() ->
  393. Tabs = val({schema, local_tables}),
  394. lists:zf(fun(Tab) -> active_here(Tab) end, Tabs).
  395. active_tables() ->
  396. Tabs = val({schema, tables}),
  397. F = fun(Tab) ->
  398. case val({Tab, where_to_read}) of
  399. nowhere -> false;
  400. _ -> {true, Tab}
  401. end
  402. end,
  403. lists:zf(F, Tabs).
  404. etype(X) when integer(X) -> integer;
  405. etype([]) -> nil;
  406. etype(X) when list(X) -> list;
  407. etype(X) when tuple(X) -> tuple;
  408. etype(X) when atom(X) -> atom;
  409. etype(_) -> othertype.
  410. remote_copy_holders(Cs) ->
  411. copy_holders(Cs) -- [node()].
  412. copy_holders(Cs) when Cs#cstruct.local_content == false ->
  413. cs_to_nodes(Cs);
  414. copy_holders(Cs) when Cs#cstruct.local_content == true ->
  415. case lists:member(node(), cs_to_nodes(Cs)) of
  416. true -> [node()];
  417. false -> []
  418. end.
  419. set_remote_where_to_read(Tab) ->
  420. set_remote_where_to_read(Tab, []).
  421. set_remote_where_to_read(Tab, Ignore) ->
  422. Active = val({Tab, active_replicas}),
  423. Valid =
  424. case mnesia_recover:get_master_nodes(Tab) of
  425. [] -> Active;
  426. Masters -> mnesia_lib:intersect(Masters, Active)
  427. end,
  428. Available = mnesia_lib:intersect(val({current, db_nodes}), Valid -- Ignore),
  429. DiscOnlyC = val({Tab, disc_only_copies}),
  430. Prefered = Available -- DiscOnlyC,
  431. if
  432. Prefered /= [] ->
  433. set({Tab, where_to_read}, hd(Prefered));
  434. Available /= [] ->
  435. set({Tab, where_to_read}, hd(Available));
  436. true ->
  437. set({Tab, where_to_read}, nowhere)
  438. end.
  439. %%% Local only
  440. set_local_content_whereabouts(Tab) ->
  441. add({schema, local_tables}, Tab),
  442. add({Tab, active_replicas}, node()),
  443. set({Tab, where_to_write}, [node()]),
  444. set({Tab, where_to_read}, node()).
  445. %%% counter routines
  446. create_counter(Name) ->
  447. set_counter(Name, 0).
  448. set_counter(Name, Val) ->
  449. ?ets_insert(mnesia_gvar, {Name, Val}).
  450. incr_counter(Name) ->
  451. ?ets_update_counter(mnesia_gvar, Name, 1).
  452. incr_counter(Name, I) ->
  453. ?ets_update_counter(mnesia_gvar, Name, I).
  454. update_counter(Name, Val) ->
  455. ?ets_update_counter(mnesia_gvar, Name, Val).
  456. read_counter(Name) ->
  457. ?ets_lookup_element(mnesia_gvar, Name, 2).
  458. cs_to_nodes(Cs) ->
  459. Cs#cstruct.disc_only_copies ++
  460. Cs#cstruct.disc_copies ++
  461. Cs#cstruct.ram_copies ++
  462. ext_copy_nodes(Cs#cstruct.external_copies).
  463. ext_copy_nodes(Ext) ->
  464. ordsets:from_list(lists:concat([Ns || {_, Ns} <- Ext])).
  465. dist_coredump() ->
  466. dist_coredump(all_nodes()).
  467. dist_coredump(Ns) ->
  468. {Replies, _} = rpc:multicall(Ns, ?MODULE, coredump, []),
  469. Replies.
  470. coredump() ->
  471. coredump({crashinfo, {"user initiated~n", []}}).
  472. coredump(CrashInfo) ->
  473. Core = mkcore(CrashInfo),
  474. Out = core_file(),
  475. important("Writing Mnesia core to file: ~p...~p~n", [Out, CrashInfo]),
  476. file:write_file(Out, Core),
  477. Out.
  478. core_file() ->
  479. Integers = tuple_to_list(date()) ++ tuple_to_list(time()),
  480. Fun = fun(I) when I < 10 -> ["_0", I];
  481. (I) -> ["_", I]
  482. end,
  483. List = lists:append([Fun(I) || I <- Integers]),
  484. case mnesia_monitor:get_env(core_dir) of
  485. Dir when list(Dir) ->
  486. filename:absname(lists:concat(["MnesiaCore.", node()] ++ List), Dir);
  487. _ ->
  488. filename:absname(lists:concat(["MnesiaCore.", node()] ++ List))
  489. end.
  490. mkcore(CrashInfo) ->
  491. % dbg_out("Making a Mnesia core dump...~p~n", [CrashInfo]),
  492. Nodes = [node() |nodes()],
  493. TidLocks = (catch ets:tab2list(mnesia_tid_locks)),
  494. Core = [
  495. CrashInfo,
  496. {time, {date(), time()}},
  497. {self, catch process_info(self())},
  498. {nodes, catch rpc:multicall(Nodes, ?MODULE, get_node_number, [])},
  499. {applications, catch lists:sort(application:loaded_applications())},
  500. {flags, catch init:get_arguments()},
  501. {code_path, catch code:get_path()},
  502. {code_loaded, catch lists:sort(code:all_loaded())},
  503. {etsinfo, catch ets_info(ets:all())},
  504. {version, catch mnesia:system_info(version)},
  505. {schema, catch ets:tab2list(schema)},
  506. {gvar, catch ets:tab2list(mnesia_gvar)},
  507. {master_nodes, catch mnesia_recover:get_master_node_info()},
  508. {processes, catch procs()},
  509. {relatives, catch relatives()},
  510. {workers, catch workers(mnesia_controller:get_workers(2000))},
  511. {locking_procs, catch locking_procs(TidLocks)},
  512. {held_locks, catch mnesia:system_info(held_locks)},
  513. {tid_locks, TidLocks},
  514. {lock_queue, catch mnesia:system_info(lock_queue)},
  515. {load_info, catch mnesia_controller:get_info(2000)},
  516. {trans_info, catch mnesia_tm:get_info(2000)},
  517. {schema_file, catch file:read_file(tab2dat(schema))},
  518. {dir_info, catch dir_info()},
  519. {logfile, catch {ok, read_log_files()}}
  520. ],
  521. term_to_binary(Core).
  522. procs() ->
  523. Fun = fun(P) -> {P, (catch lists:zf(fun proc_info/1, process_info(P)))} end,
  524. lists:map(Fun, processes()).
  525. proc_info({registered_name, Val}) -> {true, Val};
  526. proc_info({message_queue_len, Val}) -> {true, Val};
  527. proc_info({status, Val}) -> {true, Val};
  528. proc_info({current_function, Val}) -> {true, Val};
  529. proc_info(_) -> false.
  530. get_node_number() ->
  531. {node(), self()}.
  532. read_log_files() ->
  533. [{F, catch file:read_file(F)} || F <- mnesia_log:log_files()].
  534. dir_info() ->
  535. {ok, Cwd} = file:get_cwd(),
  536. Dir = dir(),
  537. [{cwd, Cwd, file:read_file_info(Cwd)},
  538. {mnesia_dir, Dir, file:read_file_info(Dir)}] ++
  539. case file:list_dir(Dir) of
  540. {ok, Files} ->
  541. [{mnesia_file, F, catch file:read_file_info(dir(F))} || F <- Files];
  542. Other ->
  543. [Other]
  544. end.
  545. ets_info([H|T]) ->
  546. [{table, H, mk_info_tuple(ets:info(H))} | ets_info(T)];
  547. ets_info([]) -> [].
  548. mk_info_tuple(T) when is_list(T) ->
  549. list_to_tuple(T);
  550. mk_info_tuple(T) -> T.
  551. relatives() ->
  552. Info = fun(Name) ->
  553. case whereis(Name) of
  554. undefined -> false;
  555. Pid -> {true, {Name, Pid, catch process_info(Pid)}}
  556. end
  557. end,
  558. lists:zf(Info, mnesia:ms()).
  559. workers({workers, Loader, Senders, Dumper}) ->
  560. Info = fun({Pid, {send_table, Tab, _Receiver, _St}}) ->
  561. case Pid of
  562. undefined -> false;
  563. Pid -> {true, {Pid, Tab, catch process_info(Pid)}}
  564. end;
  565. ({Name, Pid}) ->
  566. case Pid of
  567. undefined -> false;
  568. Pid -> {true, {Name, Pid, catch process_info(Pid)}}
  569. end
  570. end,
  571. SInfo = lists:zf(Info, Senders),
  572. [{senders, SInfo} | lists:zf(Info, [{loader, Loader}, {dumper, Dumper}])].
  573. locking_procs(LockList) when list(LockList) ->
  574. Tids = [element(1, Lock) || Lock <- LockList],
  575. UT = uniq(Tids),
  576. Info = fun(Tid) ->
  577. Pid = Tid#tid.pid,
  578. case node(Pid) == node() of
  579. true ->
  580. {true, {Pid, catch process_info(Pid)}};
  581. _ ->
  582. false
  583. end
  584. end,
  585. lists:zf(Info, UT).
  586. view() ->
  587. Bin = mkcore({crashinfo, {"view only~n", []}}),
  588. vcore(Bin).
  589. %% Displays a Mnesia file on the tty. The file may be repaired.
  590. view(File) ->
  591. case suffix([".DAT", ".RET", ".DMP", ".TMP"], File) of
  592. true ->
  593. view(File, dat);
  594. false ->
  595. case suffix([".LOG", ".BUP", ".ETS"], File) of
  596. true ->
  597. view(File, log);
  598. false ->
  599. case lists:prefix("MnesiaCore.", File) of
  600. true ->
  601. view(File, core);
  602. false ->
  603. {error, "Unknown file name"}
  604. end
  605. end
  606. end.
  607. view(File, dat) ->
  608. dets:view(File);
  609. view(File, log) ->
  610. mnesia_log:view(File);
  611. view(File, core) ->
  612. vcore(File).
  613. suffix(Suffixes, File) ->
  614. Fun = fun(S) -> lists:suffix(S, File) end,
  615. lists:any(Fun, Suffixes).
  616. %% View a core file
  617. vcore() ->
  618. Prefix = lists:concat(["MnesiaCore.", node()]),
  619. Filter = fun(F) -> lists:prefix(Prefix, F) end,
  620. {ok, Cwd} = file:get_cwd(),
  621. case file:list_dir(Cwd) of
  622. {ok, Files}->
  623. CoreFiles = lists:sort(lists:zf(Filter, Files)),
  624. show("Mnesia core files: ~p~n", [CoreFiles]),
  625. vcore(lists:last(CoreFiles));
  626. Error ->
  627. Error
  628. end.
  629. vcore(Bin) when binary(Bin) ->
  630. Core = binary_to_term(Bin),
  631. Fun = fun({Item, Info}) ->
  632. show("***** ~p *****~n", [Item]),
  633. case catch vcore_elem({Item, Info}) of
  634. {'EXIT', Reason} ->
  635. show("{'EXIT', ~p}~n", [Reason]);
  636. _ -> ok
  637. end
  638. end,
  639. lists:foreach(Fun, Core);
  640. vcore(File) ->
  641. show("~n***** Mnesia core: ~p *****~n", [File]),
  642. case file:read_file(File) of
  643. {ok, Bin} ->
  644. vcore(Bin);
  645. _ ->
  646. nocore
  647. end.
  648. vcore_elem({schema_file, {ok, B}}) ->
  649. Fname = "/tmp/schema.DAT",
  650. file:write_file(Fname, B),
  651. dets:view(Fname),
  652. file:delete(Fname);
  653. vcore_elem({logfile, {ok, BinList}}) ->
  654. Fun = fun({F, Info}) ->
  655. show("----- logfile: ~p -----~n", [F]),
  656. case Info of
  657. {ok, B} ->
  658. Fname = "/tmp/mnesia_vcore_elem.TMP",
  659. file:write_file(Fname, B),
  660. mnesia_log:view(Fname),
  661. file:delete(Fname);
  662. _ ->
  663. show("~p~n", [Info])
  664. end
  665. end,
  666. lists:foreach(Fun, BinList);
  667. vcore_elem({crashinfo, {Format, Args}}) ->
  668. show(Format, Args);
  669. vcore_elem({gvar, L}) ->
  670. show("~p~n", [lists:sort(L)]);
  671. vcore_elem({transactions, Info}) ->
  672. mnesia_tm:display_info(user, Info);
  673. vcore_elem({_Item, Info}) ->
  674. show("~p~n", [Info]).
  675. fix_error(X) ->
  676. set(last_error, X), %% for debugabililty
  677. case X of
  678. {aborted, Reason} -> Reason;
  679. {abort, Reason} -> Reason;
  680. Y when atom(Y) -> Y;
  681. {'EXIT', {_Reason, {Mod, _, _}}} when atom(Mod) ->
  682. save(X),
  683. case atom_to_list(Mod) of
  684. [$m, $n, $e|_] -> badarg;
  685. _ -> X
  686. end;
  687. _ -> X
  688. end.
  689. last_error() ->
  690. val(last_error).
  691. %% The following is a list of possible mnesia errors and what they
  692. %% actually mean
  693. error_desc(nested_transaction) -> "Nested transactions are not allowed";
  694. error_desc(badarg) -> "Bad or invalid argument, possibly bad type";
  695. error_desc(no_transaction) -> "Operation not allowed outside transactions";
  696. error_desc(combine_error) -> "Table options were ilegally combined";
  697. error_desc(bad_index) -> "Index already exists or was out of bounds";
  698. error_desc(already_exists) -> "Some schema option we try to set is already on";
  699. error_desc(index_exists)-> "Some ops can not be performed on tabs with index";
  700. error_desc(no_exists)-> "Tried to perform op on non-existing (non alive) item";
  701. error_desc(system_limit) -> "Some system_limit was exhausted";
  702. error_desc(mnesia_down) -> "A transaction involving objects at some remote "
  703. "node which died while transaction was executing"
  704. "*and* object(s) are no longer available elsewhere"
  705. "in the network";
  706. error_desc(not_a_db_node) -> "A node which is non existant in "
  707. "the schema was mentioned";
  708. error_desc(bad_type) -> "Bad type on some provided arguments";
  709. error_desc(node_not_running) -> "Node not running";
  710. error_desc(truncated_binary_file) -> "Truncated binary in file";
  711. error_desc(active) -> "Some delete ops require that "
  712. "all active objects are removed";
  713. error_desc(illegal) -> "Operation not supported on object";
  714. error_desc({'EXIT', Reason}) ->
  715. error_desc(Reason);
  716. error_desc({error, Reason}) ->
  717. error_desc(Reason);
  718. error_desc({aborted, Reason}) ->
  719. error_desc(Reason);
  720. error_desc(Reason) when tuple(Reason), size(Reason) > 0 ->
  721. setelement(1, Reason, error_desc(element(1, Reason)));
  722. error_desc(Reason) ->
  723. Reason.
  724. dirty_rpc_error_tag(Reason) ->
  725. case Reason of
  726. {'EXIT', _} -> badarg;
  727. no_variable -> badarg;
  728. _ -> no_exists
  729. end.
  730. fatal(Format, Args) ->
  731. catch set(mnesia_status, stopping),
  732. Core = mkcore({crashinfo, {Format, Args}}),
  733. report_fatal(Format, Args, Core),
  734. timer:sleep(10000), % Enough to write the core dump to disc?
  735. mnesia:lkill(),
  736. exit(fatal).
  737. report_fatal(Format, Args) ->
  738. report_fatal(Format, Args, nocore).
  739. report_fatal(Format, Args, Core) ->
  740. report_system_event({mnesia_fatal, Format, Args, Core}),
  741. catch exit(whereis(mnesia_monitor), fatal).
  742. %% We sleep longer and longer the more we try
  743. %% Made some testing and came up with the following constants
  744. random_time(Retries, _Counter0) ->
  745. % UpperLimit = 2000,
  746. % MaxIntv = trunc(UpperLimit * (1-(4/((Retries*Retries)+4)))),
  747. UpperLimit = 500,
  748. Dup = Retries * Retries,
  749. MaxIntv = trunc(UpperLimit * (1-(50/((Dup)+50)))),
  750. case get(random_seed) of
  751. undefined ->
  752. {X, Y, Z} = erlang:now(), %% time()
  753. random:seed(X, Y, Z),
  754. Time = Dup + random:uniform(MaxIntv),
  755. %% dbg_out("---random_test rs ~w max ~w val ~w---~n", [Retries, MaxIntv, Time]),
  756. Time;
  757. _ ->
  758. Time = Dup + random:uniform(MaxIntv),
  759. %% dbg_out("---random_test rs ~w max ~w val ~w---~n", [Retries, MaxIntv, Time]),
  760. Time
  761. end.
  762. report_system_event(Event0) ->
  763. Event = {mnesia_system_event, Event0},
  764. report_system_event(catch_notify(Event), Event),
  765. case ?catch_val(subscribers) of
  766. {'EXIT', _} -> ignore;
  767. Pids -> lists:foreach(fun(Pid) -> Pid ! Event end, Pids)
  768. end,
  769. ok.
  770. catch_notify(Event) ->
  771. case whereis(mnesia_event) of
  772. undefined ->
  773. {'EXIT', {badarg, {mnesia_event, Event}}};
  774. Pid ->
  775. gen_event:notify(Pid, Event)
  776. end.
  777. report_system_event({'EXIT', Reason}, Event) ->
  778. Mod = mnesia_monitor:get_env(event_module),
  779. case mnesia_sup:start_event() of
  780. {ok, Pid} ->
  781. link(Pid),
  782. gen_event:call(mnesia_event, Mod, Event, infinity),
  783. unlink(Pid),
  784. %% We get an exit signal if server dies
  785. receive
  786. {'EXIT', Pid, _Reason} ->
  787. {error, {node_not_running, node()}}
  788. after 0 ->
  789. gen_event:stop(mnesia_event),
  790. ok
  791. end;
  792. Error ->
  793. Msg = "Mnesia(~p): Cannot report event ~p: ~p (~p)~n",
  794. error_logger:format(Msg, [node(), Event, Reason, Error])
  795. end;
  796. report_system_event(_Res, _Event) ->
  797. ignore.
  798. %% important messages are reported regardless of debug level
  799. important(Format, Args) ->
  800. save({Format, Args}),
  801. report_system_event({mnesia_info, Format, Args}).
  802. %% Warning messages are reported regardless of debug level
  803. warning(Format, Args) ->
  804. save({Format, Args}),
  805. report_system_event({mnesia_warning, Format, Args}).
  806. %% error messages are reported regardless of debug level
  807. error(Format, Args) ->
  808. save({Format, Args}),
  809. report_system_event({mnesia_error, Format, Args}).
  810. %% verbose messages are reported if debug level == debug or verbose
  811. verbose(Format, Args) ->
  812. case mnesia_monitor:get_env(debug) of
  813. none -> save({Format, Args});
  814. verbose -> important(Format, Args);
  815. debug -> important(Format, Args);
  816. trace -> important(Format, Args)
  817. end.
  818. %% debug message are display if debug level == 2
  819. dbg_out(Format, Args) ->
  820. case mnesia_monitor:get_env(debug) of
  821. none -> ignore;
  822. verbose -> save({Format, Args});
  823. _ -> report_system_event({mnesia_info, Format, Args})
  824. end.
  825. %% Keep the last 10 debug print outs
  826. save(DbgInfo) ->
  827. catch save2(DbgInfo).
  828. save2(DbgInfo) ->
  829. Key = {'$$$_report', current_pos},
  830. P =
  831. case ?ets_lookup_element(mnesia_gvar, Key, 2) of
  832. 30 -> -1;
  833. I -> I
  834. end,
  835. set({'$$$_report', current_pos}, P+1),
  836. set({'$$$_report', P+1}, {date(), time(), DbgInfo}).
  837. copy_file(From, To) ->
  838. case file:rawopen(From, {binary, read}) of
  839. {ok, F} ->
  840. case file:rawopen(To, {binary, write}) of
  841. {ok, T} ->
  842. Res = copy_file_loop(F, T, 8000),
  843. file:close(F),
  844. file:close(T),
  845. Res;
  846. {error, Reason} ->
  847. {error, Reason}
  848. end;
  849. {error, Reason} ->
  850. {error, Reason}
  851. end.
  852. copy_file_loop(F, T, ChunkSize) ->
  853. case file:read(F, ChunkSize) of
  854. {ok, {0, _}} ->
  855. ok;
  856. {ok, {_, Bin}} ->
  857. file:write(T, Bin),
  858. copy_file_loop(F, T, ChunkSize);
  859. {ok, Bin} ->
  860. file:write(T, Bin),
  861. copy_file_loop(F, T, ChunkSize);
  862. eof ->
  863. ok;
  864. {error, Reason} ->
  865. {error, Reason}
  866. end.
  867. %%%%%%%%%%%%
  868. %% versions of all the lowlevel db funcs that determine whether we
  869. %% shall go to disc or ram to do the actual operation.
  870. db_get(Tab, Key) ->
  871. db_get(val({Tab, storage_type}), Tab, Key).
  872. db_get(ram_copies, Tab, Key) -> ?ets_lookup(Tab, Key);
  873. db_get(disc_copies, Tab, Key) -> ?ets_lookup(Tab, Key);
  874. db_get(disc_only_copies, Tab, Key) -> dets:lookup(Tab, Key);
  875. db_get({external_copies, Mod}, Tab, Key) -> Mod:db_get(Tab, Key).
  876. db_init_chunk(Tab) ->
  877. db_init_chunk(val({Tab, storage_type}), Tab, 1000).
  878. db_init_chunk(Tab, N) ->
  879. db_init_chunk(val({Tab, storage_type}), Tab, N).
  880. db_init_chunk(disc_only_copies, Tab, N) ->
  881. dets:select(Tab, [{'_', [], ['$_']}], N);
  882. db_init_chunk({external_copies, Mod}, Tab, N) ->
  883. Mod:db_init_chunk(Tab, N);
  884. db_init_chunk(_, Tab, N) ->
  885. ets:select(Tab, [{'_', [], ['$_']}], N).
  886. db_chunk(disc_only_copies, State) ->
  887. dets:select(State);
  888. db_chunk({external_copies, Mod}, State) ->
  889. Mod:db_chunk(State);
  890. db_chunk(_, State) ->
  891. ets:select(State).
  892. db_put(Tab, Val) ->
  893. db_put(val({Tab, storage_type}), Tab, Val).
  894. db_put(ram_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;
  895. db_put(disc_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;
  896. db_put(disc_only_copies, Tab, Val) -> dets:insert(Tab, Val);
  897. db_put({external_copies, Mod}, Tab, Val) ->
  898. Mod:db_put(Tab, Val).
  899. db_match_object(Tab, Pat) ->
  900. db_match_object(val({Tab, storage_type}), Tab, Pat).
  901. db_match_object(Storage, Tab, Pat) ->
  902. db_fixtable(Storage, Tab, true),
  903. Res = catch_match_object(Storage, Tab, Pat),
  904. db_fixtable(Storage, Tab, false),
  905. case Res of
  906. {'EXIT', Reason} -> exit(Reason);
  907. _ -> Res
  908. end.
  909. catch_match_object(disc_only_copies, Tab, Pat) ->
  910. catch dets:match_object(Tab, Pat);
  911. catch_match_object({external_copies, Mod}, Tab, Pat) ->
  912. catch Mod:match_object(Tab, Pat);
  913. catch_match_object(_, Tab, Pat) ->
  914. catch ets:match_object(Tab, Pat).
  915. db_select(Tab, Pat) ->
  916. db_select(val({Tab, storage_type}), Tab, Pat).
  917. db_select(Storage, Tab, Pat) ->
  918. db_fixtable(Storage, Tab, true),
  919. Res = catch_select(Storage, Tab, Pat),
  920. db_fixtable(Storage, Tab, false),
  921. case Res of
  922. {'EXIT', Reason} -> exit(Reason);
  923. _ -> Res
  924. end.
  925. catch_select(disc_only_copies, Tab, Pat) ->
  926. catch dets:select(Tab, Pat);
  927. catch_select({external_copies, Mod}, Tab, Pat) ->
  928. catch Mod:select(Tab, Pat);
  929. catch_select(_, Tab, Pat) ->
  930. catch ets:select(Tab, Pat).
  931. db_select_init(disc_only_copies, Tab, Pat, Limit) ->
  932. dets:select(Tab, Pat, Limit);
  933. db_select_init({external_copies, Mod}, Tab, Pat, Limit) ->
  934. Mod:select_init(Tab, Pat, Limit);
  935. db_select_init(_, Tab, Pat, Limit) ->
  936. ets:select(Tab, Pat, Limit).
  937. db_select_cont(disc_only_copies, Cont0, Ms) ->
  938. Cont = dets:repair_continuation(Cont0, Ms),
  939. dets:select(Cont);
  940. db_select_cont({external_copies, Mod}, Cont0, Ms) ->
  941. Cont = Mod:repair_continuation(Cont0, Ms),
  942. Mod:select(Cont);
  943. db_select_cont(_, Cont0, Ms) ->
  944. Cont = ets:repair_continuation(Cont0, Ms),
  945. ets:select(Cont).
  946. db_fixtable(ets, Tab, Bool) ->
  947. ets:safe_fixtable(Tab, Bool);
  948. db_fixtable(ram_copies, Tab, Bool) ->
  949. ets:safe_fixtable(Tab, Bool);
  950. db_fixtable(disc_copies, Tab, Bool) ->
  951. ets:safe_fixtable(Tab, Bool);
  952. db_fixtable(dets, Tab, Bool) ->
  953. dets:safe_fixtable(Tab, Bool);
  954. db_fixtable(disc_only_copies, Tab, Bool) ->
  955. dets:safe_fixtable(Tab, Bool);
  956. db_fixtable({external_copies, Mod}, Tab, Bool) ->
  957. Mod:safe_fixtable(Tab, Bool).
  958. db_erase(Tab, Key) ->
  959. db_erase(val({Tab, storage_type}), Tab, Key).
  960. db_erase(ram_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;
  961. db_erase(disc_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;
  962. db_erase(disc_only_copies, Tab, Key) -> dets:delete(Tab, Key);
  963. db_erase({external_copies, Mod}, Tab, Key) ->
  964. Mod:db_erase(Tab, Key).
  965. db_match_erase(Tab, Pat) ->
  966. db_match_erase(val({Tab, storage_type}), Tab, Pat).
  967. db_match_erase(ram_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;
  968. db_match_erase(disc_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;
  969. db_match_erase(disc_only_copies, Tab, Pat) -> dets:match_delete(Tab, Pat);
  970. db_match_erase({external_copies, Mod}, Tab, Pat) ->
  971. Mod:match_erase(Tab, Pat).
  972. db_first(Tab) ->
  973. db_first(val({Tab, storage_type}), Tab).
  974. db_first(ram_copies, Tab) -> ?ets_first(Tab);
  975. db_first(disc_copies, Tab) -> ?ets_first(Tab);
  976. db_first(disc_only_copies, Tab) -> dets:first(Tab);
  977. db_first({external_copies, Mod}, Tab) -> Mod:db_first(Tab).
  978. db_next_key(Tab, Key) ->
  979. db_next_key(val({Tab, storage_type}), Tab, Key).
  980. db_next_key(ram_copies, Tab, Key) -> ?ets_next(Tab, Key);
  981. db_next_key(disc_copies, Tab, Key) -> ?ets_next(Tab, Key);
  982. db_next_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key);
  983. db_next_key({external_copies, Mod}, Tab, Key) ->
  984. Mod:db_next_key(Tab, Key).
  985. db_last(Tab) ->
  986. db_last(val({Tab, storage_type}), Tab).
  987. db_last(ram_copies, Tab) -> ?ets_last(Tab);
  988. db_last(disc_copies, Tab) -> ?ets_last(Tab);
  989. db_last(disc_only_copies, Tab) -> dets:first(Tab); %% Dets don't have order
  990. db_last({external_copies, Mod}, Tab) ->
  991. Mod:db_last(Tab).
  992. db_prev_key(Tab, Key) ->
  993. db_prev_key(val({Tab, storage_type}), Tab, Key).
  994. db_prev_key(ram_copies, Tab, Key) -> ?ets_prev(Tab, Key);
  995. db_prev_key(disc_copies, Tab, Key) -> ?ets_prev(Tab, Key);
  996. db_prev_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key); %% Dets don't have order
  997. db_prev_key({external_copies, Mod}, Tab, Key) ->
  998. Mod:db_prev_key(Tab, Key).
  999. db_slot(Tab, Pos) ->
  1000. db_slot(val({Tab, storage_type}), Tab, Pos).
  1001. db_slot(ram_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);
  1002. db_slot(disc_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);
  1003. db_slot(disc_only_copies, Tab, Pos) -> dets:slot(Tab, Pos);
  1004. db_slot({external_copies, Mod}, Tab, Pos) ->
  1005. Mod:db_slot(Tab, Pos).
  1006. db_update_counter(Tab, C, Val) ->
  1007. db_update_counter(val({Tab, storage_type}), Tab, C, Val).
  1008. db_update_counter(ram_copies, Tab, C, Val) ->
  1009. ?ets_update_counter(Tab, C, Val);
  1010. db_update_counter(disc_copies, Tab, C, Val) ->
  1011. ?ets_update_counter(Tab, C, Val);
  1012. db_update_counter(disc_only_copies, Tab, C, Val) ->
  1013. dets:update_counter(Tab, C, Val);
  1014. db_update_counter({external_copies, Mod}, Tab, C, Val) ->
  1015. Mod:db_update_counter(Tab, C, Val).
  1016. db_erase_tab(Tab) ->
  1017. db_erase_tab(val({Tab, storage_type}), Tab).
  1018. db_erase_tab(ram_copies, Tab) -> ?ets_delete_table(Tab);
  1019. db_erase_tab(disc_copies, Tab) -> ?ets_delete_table(Tab);
  1020. db_erase_tab(disc_only_copies, _Tab) -> ignore;
  1021. db_erase_tab({external_copies, Mod}, Tab) ->
  1022. Mod:db_erase_tab(Tab).
  1023. %% assuming that Tab is a valid ets-table
  1024. dets_to_ets(Tabname, Tab, File, Type, Rep, Lock) ->
  1025. {Open, Close} = mkfuns(Lock),
  1026. case Open(Tabname, [{file, File}, {type, disk_type(Tab, Type)},
  1027. {keypos, 2}, {repair, Rep}]) of
  1028. {ok, Tabname} ->
  1029. Res = dets:to_ets(Tabname, Tab),
  1030. Close(Tabname),
  1031. trav_ret(Res, Tab);
  1032. Other ->
  1033. Other
  1034. end.
  1035. %% assuming that Tab is a valid ets-table
  1036. dets_to_ets(Tabname, Tab, File, Type, Rep, Lock, OnLoadFun) ->
  1037. {Open, Close} = mkfuns(Lock),
  1038. case Open(Tabname, [{file, File}, {type, disk_type(Tab, Type)},
  1039. {keypos, 2}, {repair, Rep}]) of
  1040. {ok, Tabname} ->
  1041. %%% Res = dets:to_ets(Tabname, Tab),
  1042. Res = chunk_dets_to_ets(dets:bchunk(Tabname, start), Tabname,
  1043. Tab, OnLoadFun),
  1044. Close(Tabname),
  1045. trav_ret(Res, Tab);
  1046. Other ->
  1047. Other
  1048. end.
  1049. chunk_dets_to_ets('$end_of_table', _Tabname, Tab, _OnLoadFun) ->
  1050. Tab;
  1051. chunk_dets_to_ets({error, Reason}, Tabname, _Tab, _OnLoadFun) ->
  1052. {error, {Reason, Tabname}};
  1053. chunk_dets_to_ets({Cont, Objs}, Tabname, Tab, OnLoadFun) ->
  1054. ets:insert(Tab, Objs),
  1055. OnLoadFun([{write,O} || O <- Objs]),
  1056. chunk_dets_to_ets(dets:bchunk(Tabname, Cont), Tabname, Tab, OnLoadFun).
  1057. trav_ret(Tabname, Tabname) -> loaded;
  1058. trav_ret(Other, _Tabname) -> Other.
  1059. mkfuns(yes) ->
  1060. {fun(Tab, Args) -> dets_sync_open(Tab, Args) end,
  1061. fun(Tab) -> dets_sync_close(Tab) end};
  1062. mkfuns(no) ->
  1063. {fun(Tab, Args) -> dets:open_file(Tab, Args) end,
  1064. fun(Tab) -> dets:close(Tab) end}.
  1065. disk_type(Tab) ->
  1066. disk_type(Tab, val({Tab, setorbag})).
  1067. disk_type(_Tab, ordered_set) ->
  1068. set;
  1069. disk_type(_, Type) ->
  1070. Type.
  1071. dets_sync_open(Tab, Ref, File) ->
  1072. Args = [{file, File},
  1073. {keypos, 2},
  1074. {repair, mnesia_monitor:get_env(auto_repair)},
  1075. {type, disk_type(Tab)}],
  1076. dets_sync_open(Ref, Args).
  1077. lock_table(Tab) ->
  1078. global:set_lock({{mnesia_table_lock, Tab}, self()}, [node()], infinity).
  1079. % dbg_out("dets_sync_open: ~p ~p~n", [T, self()]),
  1080. unlock_table(Tab) ->
  1081. global:del_lock({{mnesia_table_lock, Tab}, self()}, [node()]).
  1082. % dbg_out("unlock_table: ~p ~p~n", [T, self()]),
  1083. dets_sync_open(Tab, Args) ->
  1084. lock_table(Tab),
  1085. case dets:open_file(Tab, Args) of
  1086. {ok, Tab} ->
  1087. {ok, Tab};
  1088. Other ->
  1089. dets_sync_close(Tab),
  1090. Other
  1091. end.
  1092. dets_sync_close(Tab) ->
  1093. catch dets:close(Tab),
  1094. unlock_table(Tab),
  1095. ok.
  1096. cleanup_tmp_files([Tab | Tabs]) ->
  1097. dets_sync_close(Tab),
  1098. file:delete(tab2tmp(Tab)),
  1099. cleanup_tmp_files(Tabs);
  1100. cleanup_tmp_files([]) ->
  1101. ok.
  1102. %% Returns a list of bad tables
  1103. swap_tmp_files([Tab | Tabs]) ->
  1104. dets_sync_close(Tab),
  1105. Tmp = tab2tmp(Tab),
  1106. Dat = tab2dat(Tab),
  1107. case file:rename(Tmp, Dat) of
  1108. ok ->
  1109. swap_tmp_files(Tabs);
  1110. _ ->
  1111. file:delete(Tmp),
  1112. [Tab | swap_tmp_files(Tabs)]
  1113. end;
  1114. swap_tmp_files([]) ->
  1115. [].
  1116. readable_indecies(Tab) ->
  1117. val({Tab, index}).
  1118. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  1119. %% Managing conditional debug functions
  1120. %%
  1121. %% The main idea with the debug_fun's is to allow test programs
  1122. %% to control the internal behaviour of Mnesia. This is needed
  1123. %% to make the test programs independent of system load, swapping
  1124. %% and other circumstances that may affect the behaviour of Mnesia.
  1125. %%
  1126. %% First should calls to ?eval_debug_fun be inserted at well
  1127. %% defined places in Mnesia's code. E.g. in critical situations
  1128. %% of startup, transaction commit, backups etc.
  1129. %%
  1130. %% Then compile Mnesia with the compiler option 'debug'.
  1131. %%
  1132. %% In test programs ?activate_debug_fun should be called
  1133. %% in order to bind a fun to the debug identifier stated
  1134. %% in the call to ?eval_debug_fun.
  1135. %%
  1136. %% If eval_debug_fun finds that the fun is activated it
  1137. %% invokes the fun as NewContext = Fun(PreviousContext, EvalContext)
  1138. %% and replaces the PreviousContext with the NewContext.
  1139. %% The initial context of a debug_fun is given as argument to
  1140. %% activate_debug_fun.
  1141. -define(DEBUG_TAB, mnesia_debug).
  1142. -record(debug_info, {id, function, context, file, line}).
  1143. scratch_debug_fun() ->
  1144. dbg_out("scratch_debug_fun(): ~p~n", [?DEBUG_TAB]),
  1145. (catch ?ets_delete_table(?DEBUG_TAB)),
  1146. ?ets_new_table(?DEBUG_TAB, [set, public, named_table, {keypos, 2}]).
  1147. activate_debug_fun(FunId, Fun, InitialContext, File, Line) ->
  1148. Info = #debug_info{id = FunId,
  1149. function = Fun,
  1150. context = InitialContext,
  1151. file = File,
  1152. line = Line
  1153. },
  1154. update_debug_info(Info).
  1155. update_debug_info(Info) ->
  1156. case catch ?ets_insert(?DEBUG_TAB, Info) of
  1157. {'EXIT', _} ->
  1158. scratch_debug_fun(),
  1159. ?ets_insert(?DEBUG_TAB, Info);
  1160. _ ->
  1161. ok
  1162. end,
  1163. dbg_out("update_debug_info(~p)~n", [Info]),
  1164. ok.
  1165. deactivate_debug_fun(FunId, _File, _Line) ->
  1166. catch ?ets_delete(?DEBUG_TAB, FunId),
  1167. ok.
  1168. eval_debug_fun(FunId, EvalContext, EvalFile, EvalLine) ->
  1169. case catch ?ets_lookup(?DEBUG_TAB, FunId) of
  1170. [] ->
  1171. ok;
  1172. [Info] ->
  1173. OldContext = Info#debug_info.context,
  1174. dbg_out("~s(~p): ~w "
  1175. "activated in ~s(~p)~n "
  1176. "eval_debug_fun(~w, ~w)~n",
  1177. [filename:basename(EvalFile), EvalLine, Info#debug_info.id,
  1178. filename:basename(Info#debug_info.file), Info#debug_info.line,
  1179. OldContext, EvalContext]),
  1180. Fun = Info#debug_info.function,
  1181. NewContext = Fun(OldContext, EvalContext),
  1182. case catch ?ets_lookup(?DEBUG_TAB, FunId) of
  1183. [Info] when NewContext /= OldContext ->
  1184. NewInfo = Info#debug_info{context = NewContext},
  1185. update_debug_info(NewInfo);
  1186. _ ->
  1187. ok
  1188. end;
  1189. {'EXIT', _} -> ok
  1190. end.
  1191. -ifdef(debug).
  1192. is_debug_compiled() -> true.
  1193. -else.
  1194. is_debug_compiled() -> false.
  1195. -endif.