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 + -
显示快捷键?