mnesia_lib.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,331 行 · 第 1/3 页
ERL
1,331 行
Time end.report_system_event(Event0) -> Event = {mnesia_system_event, Event0}, report_system_event(catch_notify(Event), Event), case ?catch_val(subscribers) of {'EXIT', _} -> ignore; Pids -> lists:foreach(fun(Pid) -> Pid ! Event end, Pids) end, ok.catch_notify(Event) -> case whereis(mnesia_event) of undefined -> {'EXIT', {badarg, {mnesia_event, Event}}}; Pid -> gen_event:notify(Pid, Event) end.report_system_event({'EXIT', Reason}, Event) -> Mod = mnesia_monitor:get_env(event_module), case mnesia_sup:start_event() of {ok, Pid} -> link(Pid), gen_event:call(mnesia_event, Mod, Event, infinity), unlink(Pid), %% We get an exit signal if server dies receive {'EXIT', Pid, _Reason} -> {error, {node_not_running, node()}} after 0 -> gen_event:stop(mnesia_event), ok end; Error -> Msg = "Mnesia(~p): Cannot report event ~p: ~p (~p)~n", error_logger:format(Msg, [node(), Event, Reason, Error]) end;report_system_event(_Res, _Event) -> ignore.%% important messages are reported regardless of debug levelimportant(Format, Args) -> save({Format, Args}), report_system_event({mnesia_info, Format, Args}).%% Warning messages are reported regardless of debug levelwarning(Format, Args) -> save({Format, Args}), report_system_event({mnesia_warning, Format, Args}).%% error messages are reported regardless of debug levelerror(Format, Args) -> save({Format, Args}), report_system_event({mnesia_error, Format, Args}).%% verbose messages are reported if debug level == debug or verboseverbose(Format, Args) -> case mnesia_monitor:get_env(debug) of none -> save({Format, Args}); verbose -> important(Format, Args); debug -> important(Format, Args); trace -> important(Format, Args) end.%% debug message are display if debug level == 2dbg_out(Format, Args) -> case mnesia_monitor:get_env(debug) of none -> ignore; verbose -> save({Format, Args}); _ -> report_system_event({mnesia_info, Format, Args}) end.%% Keep the last 10 debug print outssave(DbgInfo) -> catch save2(DbgInfo).save2(DbgInfo) -> Key = {'$$$_report', current_pos}, P = case ?ets_lookup_element(mnesia_gvar, Key, 2) of 30 -> -1; I -> I end, set({'$$$_report', current_pos}, P+1), set({'$$$_report', P+1}, {date(), time(), DbgInfo}).copy_file(From, To) -> case file:rawopen(From, {binary, read}) of {ok, F} -> case file:rawopen(To, {binary, write}) of {ok, T} -> Res = copy_file_loop(F, T, 8000), file:close(F), file:close(T), Res; {error, Reason} -> {error, Reason} end; {error, Reason} -> {error, Reason} end.copy_file_loop(F, T, ChunkSize) -> case file:read(F, ChunkSize) of {ok, {0, _}} -> ok; {ok, {_, Bin}} -> file:write(T, Bin), copy_file_loop(F, T, ChunkSize); {ok, Bin} -> file:write(T, Bin), copy_file_loop(F, T, ChunkSize); eof -> ok; {error, Reason} -> {error, Reason} end.%%%%%%%%%%%%%% versions of all the lowlevel db funcs that determine whether we%% shall go to disc or ram to do the actual operation.db_get(Tab, Key) -> db_get(val({Tab, storage_type}), Tab, Key).db_get(ram_copies, Tab, Key) -> ?ets_lookup(Tab, Key);db_get(disc_copies, Tab, Key) -> ?ets_lookup(Tab, Key);db_get(disc_only_copies, Tab, Key) -> dets:lookup(Tab, Key).db_init_chunk(Tab) -> db_init_chunk(val({Tab, storage_type}), Tab, 1000).db_init_chunk(Tab, N) -> db_init_chunk(val({Tab, storage_type}), Tab, N).db_init_chunk(disc_only_copies, Tab, N) -> dets:select(Tab, [{'_', [], ['$_']}], N);db_init_chunk(_, Tab, N) -> ets:select(Tab, [{'_', [], ['$_']}], N).db_chunk(disc_only_copies, State) -> dets:select(State);db_chunk(_, State) -> ets:select(State).db_put(Tab, Val) -> db_put(val({Tab, storage_type}), Tab, Val).db_put(ram_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;db_put(disc_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;db_put(disc_only_copies, Tab, Val) -> dets:insert(Tab, Val).db_match_object(Tab, Pat) -> db_match_object(val({Tab, storage_type}), Tab, Pat).db_match_object(Storage, Tab, Pat) -> db_fixtable(Storage, Tab, true), Res = catch_match_object(Storage, Tab, Pat), db_fixtable(Storage, Tab, false), case Res of {'EXIT', Reason} -> exit(Reason); _ -> Res end.catch_match_object(disc_only_copies, Tab, Pat) -> catch dets:match_object(Tab, Pat);catch_match_object(_, Tab, Pat) -> catch ets:match_object(Tab, Pat).db_select(Tab, Pat) -> db_select(val({Tab, storage_type}), Tab, Pat).db_select(Storage, Tab, Pat) -> db_fixtable(Storage, Tab, true), Res = catch_select(Storage, Tab, Pat), db_fixtable(Storage, Tab, false), case Res of {'EXIT', Reason} -> exit(Reason); _ -> Res end.catch_select(disc_only_copies, Tab, Pat) -> catch dets:select(Tab, Pat);catch_select(_, Tab, Pat) -> catch ets:select(Tab, Pat).db_select_init(disc_only_copies, Tab, Pat, Limit) -> dets:select(Tab, Pat, Limit);db_select_init(_, Tab, Pat, Limit) -> ets:select(Tab, Pat, Limit).db_select_cont(disc_only_copies, Cont0, Ms) -> Cont = dets:repair_continuation(Cont0, Ms), dets:select(Cont);db_select_cont(_, Cont0, Ms) -> Cont = ets:repair_continuation(Cont0, Ms), ets:select(Cont).db_fixtable(ets, Tab, Bool) -> ets:safe_fixtable(Tab, Bool);db_fixtable(ram_copies, Tab, Bool) -> ets:safe_fixtable(Tab, Bool);db_fixtable(disc_copies, Tab, Bool) -> ets:safe_fixtable(Tab, Bool);db_fixtable(dets, Tab, Bool) -> dets:safe_fixtable(Tab, Bool);db_fixtable(disc_only_copies, Tab, Bool) -> dets:safe_fixtable(Tab, Bool).db_erase(Tab, Key) -> db_erase(val({Tab, storage_type}), Tab, Key).db_erase(ram_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;db_erase(disc_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;db_erase(disc_only_copies, Tab, Key) -> dets:delete(Tab, Key).db_match_erase(Tab, Pat) -> db_match_erase(val({Tab, storage_type}), Tab, Pat).db_match_erase(ram_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;db_match_erase(disc_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;db_match_erase(disc_only_copies, Tab, Pat) -> dets:match_delete(Tab, Pat).db_first(Tab) -> db_first(val({Tab, storage_type}), Tab).db_first(ram_copies, Tab) -> ?ets_first(Tab);db_first(disc_copies, Tab) -> ?ets_first(Tab);db_first(disc_only_copies, Tab) -> dets:first(Tab).db_next_key(Tab, Key) -> db_next_key(val({Tab, storage_type}), Tab, Key).db_next_key(ram_copies, Tab, Key) -> ?ets_next(Tab, Key);db_next_key(disc_copies, Tab, Key) -> ?ets_next(Tab, Key);db_next_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key).db_last(Tab) -> db_last(val({Tab, storage_type}), Tab).db_last(ram_copies, Tab) -> ?ets_last(Tab);db_last(disc_copies, Tab) -> ?ets_last(Tab);db_last(disc_only_copies, Tab) -> dets:first(Tab). %% Dets don't have orderdb_prev_key(Tab, Key) -> db_prev_key(val({Tab, storage_type}), Tab, Key).db_prev_key(ram_copies, Tab, Key) -> ?ets_prev(Tab, Key);db_prev_key(disc_copies, Tab, Key) -> ?ets_prev(Tab, Key);db_prev_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key). %% Dets don't have orderdb_slot(Tab, Pos) -> db_slot(val({Tab, storage_type}), Tab, Pos).db_slot(ram_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);db_slot(disc_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);db_slot(disc_only_copies, Tab, Pos) -> dets:slot(Tab, Pos).db_update_counter(Tab, C, Val) -> db_update_counter(val({Tab, storage_type}), Tab, C, Val).db_update_counter(ram_copies, Tab, C, Val) -> ?ets_update_counter(Tab, C, Val);db_update_counter(disc_copies, Tab, C, Val) -> ?ets_update_counter(Tab, C, Val);db_update_counter(disc_only_copies, Tab, C, Val) -> dets:update_counter(Tab, C, Val).db_erase_tab(Tab) -> db_erase_tab(val({Tab, storage_type}), Tab).db_erase_tab(ram_copies, Tab) -> ?ets_delete_table(Tab);db_erase_tab(disc_copies, Tab) -> ?ets_delete_table(Tab);db_erase_tab(disc_only_copies, _Tab) -> ignore.%% assuming that Tab is a valid ets-tabledets_to_ets(Tabname, Tab, File, Type, Rep, Lock) -> {Open, Close} = mkfuns(Lock), case Open(Tabname, [{file, File}, {type, disk_type(Tab, Type)}, {keypos, 2}, {repair, Rep}]) of {ok, Tabname} -> Res = dets:to_ets(Tabname, Tab), Close(Tabname), trav_ret(Res, Tab); Other -> Other end.trav_ret(Tabname, Tabname) -> loaded;trav_ret(Other, _Tabname) -> Other.mkfuns(yes) -> {fun(Tab, Args) -> dets_sync_open(Tab, Args) end, fun(Tab) -> dets_sync_close(Tab) end};mkfuns(no) -> {fun(Tab, Args) -> dets:open_file(Tab, Args) end, fun(Tab) -> dets:close(Tab) end}.disk_type(Tab) -> disk_type(Tab, val({Tab, setorbag})).disk_type(_Tab, ordered_set) -> set;disk_type(_, Type) -> Type.dets_sync_open(Tab, Ref, File) -> Args = [{file, File}, {keypos, 2}, {repair, mnesia_monitor:get_env(auto_repair)}, {type, disk_type(Tab)}], dets_sync_open(Ref, Args).lock_table(Tab) -> global:set_lock({{mnesia_table_lock, Tab}, self()}, [node()], infinity).% dbg_out("dets_sync_open: ~p ~p~n", [T, self()]),unlock_table(Tab) -> global:del_lock({{mnesia_table_lock, Tab}, self()}, [node()]).% dbg_out("unlock_table: ~p ~p~n", [T, self()]),dets_sync_open(Tab, Args) -> lock_table(Tab), case dets:open_file(Tab, Args) of {ok, Tab} -> {ok, Tab}; Other -> dets_sync_close(Tab), Other end.dets_sync_close(Tab) -> catch dets:close(Tab), unlock_table(Tab), ok.cleanup_tmp_files([Tab | Tabs]) -> dets_sync_close(Tab), file:delete(tab2tmp(Tab)), cleanup_tmp_files(Tabs);cleanup_tmp_files([]) -> ok.%% Returns a list of bad tablesswap_tmp_files([Tab | Tabs]) -> dets_sync_close(Tab), Tmp = tab2tmp(Tab), Dat = tab2dat(Tab), case file:rename(Tmp, Dat) of ok -> swap_tmp_files(Tabs); _ -> file:delete(Tmp), [Tab | swap_tmp_files(Tabs)] end;swap_tmp_files([]) -> [].readable_indecies(Tab) -> val({Tab, index}).%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Managing conditional debug functions%%%% The main idea with the debug_fun's is to allow test programs%% to control the internal behaviour of Mnesia. This is needed%% to make the test programs independent of system load, swapping%% and other circumstances that may affect the behaviour of Mnesia.%%%% First should calls to ?eval_debug_fun be inserted at well%% defined places in Mnesia's code. E.g. in critical situations%% of startup, transaction commit, backups etc.%%%% Then compile Mnesia with the compiler option 'debug'.%%%% In test programs ?activate_debug_fun should be called%% in order to bind a fun to the debug identifier stated%% in the call to ?eval_debug_fun.%%%% If eval_debug_fun finds that the fun is activated it%% invokes the fun as NewContext = Fun(PreviousContext, EvalContext)%% and replaces the PreviousContext with the NewContext.%% The initial context of a debug_fun is given as argument to%% activate_debug_fun.-define(DEBUG_TAB, mnesia_debug).-record(debug_info, {id, function, context, file, line}).scratch_debug_fun() -> dbg_out("scratch_debug_fun(): ~p~n", [?DEBUG_TAB]), (catch ?ets_delete_table(?DEBUG_TAB)), ?ets_new_table(?DEBUG_TAB, [set, public, named_table, {keypos, 2}]).activate_debug_fun(FunId, Fun, InitialContext, File, Line) -> Info = #debug_info{id = FunId, function = Fun, context = InitialContext, file = File, line = Line }, update_debug_info(Info).update_debug_info(Info) -> case catch ?ets_insert(?DEBUG_TAB, Info) of {'EXIT', _} -> scratch_debug_fun(), ?ets_insert(?DEBUG_TAB, Info); _ -> ok end, dbg_out("update_debug_info(~p)~n", [Info]), ok.deactivate_debug_fun(FunId, _File, _Line) -> catch ?ets_delete(?DEBUG_TAB, FunId), ok.eval_debug_fun(FunId, EvalContext, EvalFile, EvalLine) -> case catch ?ets_lookup(?DEBUG_TAB, FunId) of [] -> ok; [Info] -> OldContext = Info#debug_info.context, dbg_out("~s(~p): ~w " "activated in ~s(~p)~n " "eval_debug_fun(~w, ~w)~n", [filename:basename(EvalFile), EvalLine, Info#debug_info.id, filename:basename(Info#debug_info.file), Info#debug_info.line, OldContext, EvalContext]), Fun = Info#debug_info.function, NewContext = Fun(OldContext, EvalContext), case catch ?ets_lookup(?DEBUG_TAB, FunId) of [Info] when NewContext /= OldContext -> NewInfo = Info#debug_info{context = NewContext}, update_debug_info(NewInfo); _ -> ok end; {'EXIT', _} -> ok end. -ifdef(debug). is_debug_compiled() -> true.-else. is_debug_compiled() -> false.-endif.
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?