mnesia_schema.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 2,011 行 · 第 1/5 页

ERL
2,011
字号
                    verbose("Create Directory ~p~n", [Dir]),                    ok;                {error, Reason} ->                    verbose("Cannot create mnesia dir ~p~n", [Reason]),                    {error, {"Cannot create Mnesia dir", Dir, Reason}}            end    end;opt_create_dir(false, _) ->    {error, {has_no_disc, node()}}.    check_can_write(Dir) ->    case file:read_file_info(Dir) of        {ok, FI} when FI#file_info.type == directory,		      FI#file_info.access == read_write ->            ok;        {ok, _} ->            {error, "Not allowed to write in Mnesia dir", Dir};        _ ->            {error, "Non existent Mnesia dir", Dir}    end.lock_schema() ->    mnesia_lib:lock_table(schema).unlock_schema() ->    mnesia_lib:unlock_table(schema).read_schema(Keep) ->    read_schema(Keep, false).%% The schema may be read for several reasons.%% If Mnesia is not already started the read intention%% we normally do not want the ets table named schema%% be left around.%% If Keep == true, the ets table schema is kept%% If Keep == false, the ets table schema is removed%%%% Returns {ok, Source, SchemaCstruct} or {error, Reason}%% Source may be: default | ram | disc | fallbackread_schema(Keep, IgnoreFallback) ->    lock_schema(),    Res =         case mnesia:system_info(is_running) of            yes ->                {ok, ram, get_create_list(schema)};            _IsRunning ->                    case mnesia_monitor:use_dir() of                        true ->                            read_disc_schema(Keep, IgnoreFallback);                        false when Keep == true ->                            Args = [{keypos, 2}, public, named_table, set],                            mnesia_monitor:mktab(schema, Args),                            CreateList = get_initial_schema(ram_copies, []),                            ?ets_insert(schema,{schema, schema, CreateList}),                            {ok, default, CreateList};                        false when Keep == false ->			    CreateList = get_initial_schema(ram_copies, []),                            {ok, default, CreateList}                    end        end,    unlock_schema(),    Res.read_disc_schema(Keep, IgnoreFallback) ->    Running = mnesia:system_info(is_running),    case mnesia_bup:fallback_exists() of        true when IgnoreFallback == false, Running /= yes ->             mnesia_bup:fallback_to_schema();        _ ->              %% If we're running, we read the schema file even            %% if fallback exists            Dat = mnesia_lib:tab2dat(schema),            case mnesia_lib:exists(Dat) of                true ->                    do_read_disc_schema(Dat, Keep);                false ->		    Dmp = mnesia_lib:tab2dmp(schema),		    case mnesia_lib:exists(Dmp) of			true ->			    %% May only happen when toggling of			    %% schema storage type has been			    %% interrupted			    do_read_disc_schema(Dmp, Keep);			false ->			    {error, "No schema file exists"}		    end            end    end.do_read_disc_schema(Fname, Keep) ->    T =         case Keep of            false ->                Args = [{keypos, 2}, public, set],                ?ets_new_table(schema, Args);            true ->                Args = [{keypos, 2}, public, named_table, set],                mnesia_monitor:mktab(schema, Args)        end,    Repair = mnesia_monitor:get_env(auto_repair),    Res =  % BUGBUG Fixa till dcl!        case mnesia_lib:dets_to_ets(schema, T, Fname, set, Repair, no) of            loaded -> {ok, disc, ?ets_lookup_element(T, schema, 3)};            Other -> {error, {"Cannot read schema", Fname, Other}}        end,    case Keep of        true -> ignore;        false -> ?ets_delete_table(T)    end,    Res.get_initial_schema(SchemaStorage, Nodes) ->    Cs = #cstruct{name = schema,		  record_name = schema,		  attributes = [table, cstruct]},        Cs2 =	case SchemaStorage of        ram_copies -> Cs#cstruct{ram_copies = Nodes};        disc_copies -> Cs#cstruct{disc_copies = Nodes}    end,    cs2list(Cs2).read_cstructs_from_disc() ->    %% Assumptions:     %% - local schema lock in global    %% - use_dir is true    %% - Mnesia is not running    %% - Ignore fallback    Fname = mnesia_lib:tab2dat(schema),    case mnesia_lib:exists(Fname) of	true ->	    Args = [{file, Fname},		    {keypos, 2},		    {repair, mnesia_monitor:get_env(auto_repair)},		    {type, set}],	    case dets:open_file(make_ref(), Args) of		{ok, Tab} ->		    Fun = fun({_, _, List}) ->				  {continue, list2cs(List)}			  end,		    Cstructs = dets:traverse(Tab, Fun),		    dets:close(Tab),		    {ok, Cstructs};  		{error, Reason} ->		    {error, Reason}	    end;	false ->	    {error, "No schema file exists"}    end.    %% We run a very special type of transactions when we%% we want to manipulate the schema.get_tid_ts_and_lock(Tab, Intent) ->    TidTs = get(mnesia_activity_state),    case TidTs of	{_Mod, Tid, Ts} when record(Ts, tidstore)->	    Store = Ts#tidstore.store,	    case Intent of		read -> mnesia_locker:rlock_table(Tid, Store, Tab);		write -> mnesia_locker:wlock_table(Tid, Store, Tab);		none -> ignore	    end,	    TidTs;	_ ->	    mnesia:abort(no_transaction)    end.schema_transaction(Fun) ->    case get(mnesia_activity_state) of	undefined ->	    Args = [self(), Fun, whereis(mnesia_controller)],	    Pid = spawn_link(?MODULE, schema_coordinator, Args),	    receive		{transaction_done, Res, Pid} -> Res;		{'EXIT', Pid, R} -> {aborted, {transaction_crashed, R}}	    end;	_ ->            {aborted, nested_transaction}    end.%% This process may dump the transaction log, and should%% therefore not be run in an application process%% schema_coordinator(Client, _Fun, undefined) ->    Res = {aborted, {node_not_running, node()}},    Client ! {transaction_done, Res, self()},    unlink(Client);    schema_coordinator(Client, Fun, Controller) when pid(Controller) ->    %% Do not trap exit in order to automatically die    %% when the controller dies    link(Controller),    unlink(Client),        %% Fulfull the transaction even if the client dies         Res = mnesia:transaction(Fun),    Client ! {transaction_done, Res, self()},    unlink(Controller),         % Avoids spurious exit message    unlink(whereis(mnesia_tm)), % Avoids spurious exit message    exit(normal).%% The make* rotines return a list of ops, this function%% inserts em all in the Store and maintains the local order%% of ops.insert_schema_ops({_Mod, _Tid, Ts}, SchemaIOps) ->    do_insert_schema_ops(Ts#tidstore.store, SchemaIOps).    do_insert_schema_ops(Store, [Head | Tail]) ->    ?ets_insert(Store, Head),    do_insert_schema_ops(Store, Tail);do_insert_schema_ops(_Store, []) ->    ok.cs2list(Cs) when record(Cs, cstruct) ->    Tags = record_info(fields, cstruct),    rec2list(Tags, 2, Cs);cs2list(CreateList) when list(CreateList) ->    CreateList.rec2list([Tag | Tags], Pos, Rec) ->    Val = element(Pos, Rec),    [{Tag, Val} | rec2list(Tags, Pos + 1, Rec)];rec2list([], _Pos, _Rec) ->    [].list2cs(List) when list(List) ->    Name = pick(unknown, name, List, must),    Type = pick(Name, type, List, set),    Rc0 = pick(Name, ram_copies, List, []),    Dc = pick(Name, disc_copies, List, []),    Doc = pick(Name, disc_only_copies, List, []),    Rc = case {Rc0, Dc, Doc} of             {[], [], []} -> [node()];             _ -> Rc0         end,    LC = pick(Name, local_content, List, false),    RecName = pick(Name, record_name, List, Name),    Attrs = pick(Name, attributes, List, [key, val]),    Snmp = pick(Name, snmp, List, []),    LoadOrder = pick(Name, load_order, List, 0),    AccessMode = pick(Name, access_mode, List, read_write),    UserProps = pick(Name, user_properties, List, []),    verify({alt, [nil, list]}, mnesia_lib:etype(UserProps),	   {bad_type, Name, {user_properties, UserProps}}),    Cookie = pick(Name, cookie, List, ?unique_cookie),    Version = pick(Name, version, List, {{2, 0}, []}),    Ix = pick(Name, index, List, []),    verify({alt, [nil, list]}, mnesia_lib:etype(Ix),	   {bad_type, Name, {index, [Ix]}}),    Ix2 = [attr_to_pos(I, Attrs) || I <- Ix],    Frag = pick(Name, frag_properties, List, []),    verify({alt, [nil, list]}, mnesia_lib:etype(Frag),	   {badarg, Name, {frag_properties, Frag}}),             Keys = check_keys(Name, List, record_info(fields, cstruct)),    check_duplicates(Name, Keys),    #cstruct{name = Name,             ram_copies = Rc,             disc_copies = Dc,             disc_only_copies = Doc,             type = Type,             index = Ix2,             snmp = Snmp,             load_order = LoadOrder,             access_mode = AccessMode,             local_content = LC,	     record_name = RecName,             attributes = Attrs,             user_properties = lists:sort(UserProps),	     frag_properties = lists:sort(Frag),             cookie = Cookie,             version = Version};list2cs(Other) ->    mnesia:abort({badarg, Other}).pick(Tab, Key, List, Default) ->    case lists:keysearch(Key, 1, List) of        false  when Default == must ->            mnesia:abort({badarg, Tab, "Missing key", Key, List});        false ->            Default;        {value, {Key, Value}} ->            Value;	{value, BadArg} ->	    mnesia:abort({bad_type, Tab, BadArg})    end.%% Convert attribute name to integer if neccessaryattr_tab_to_pos(_Tab, Pos) when integer(Pos) ->    Pos;attr_tab_to_pos(Tab, Attr) ->    attr_to_pos(Attr, val({Tab, attributes})).    %% Convert attribute name to integer if neccessaryattr_to_pos(Pos, _Attrs) when integer(Pos) ->    Pos;attr_to_pos(Attr, Attrs) when atom(Attr) ->    attr_to_pos(Attr, Attrs, 2);attr_to_pos(Attr, _) ->    mnesia:abort({bad_type, Attr}).attr_to_pos(Attr, [Attr | _Attrs], Pos) ->    Pos;attr_to_pos(Attr, [_ | Attrs], Pos) ->    attr_to_pos(Attr, Attrs, Pos + 1);attr_to_pos(Attr, _, _) ->    mnesia:abort({bad_type, Attr}).    check_keys(Tab, [{Key, _Val} | Tail], Items) ->    case lists:member(Key, Items) of        true ->  [Key | check_keys(Tab, Tail, Items)];        false -> mnesia:abort({badarg, Tab, Key})    end;check_keys(_, [], _) ->    [];check_keys(Tab, Arg, _) ->    mnesia:abort({badarg, Tab, Arg}).check_duplicates(Tab, Keys) ->    case has_duplicates(Keys) of        false -> ok;        true -> mnesia:abort({badarg, Tab, "Duplicate keys", Keys})    end.has_duplicates([H | T]) ->    case lists:member(H, T) of        true -> true;        false -> has_duplicates(T)    end;has_duplicates([]) ->    false.%% This is the only place where we check the validity of dataverify_cstruct(Cs) when record(Cs, cstruct) ->    verify_nodes(Cs),    Tab = Cs#cstruct.name,    verify(atom, mnesia_lib:etype(Tab), {bad_type, Tab}),    Type = Cs#cstruct.type,    verify(true, lists:member(Type, [set, bag, ordered_set]),	   {bad_type, Tab, {type, Type}}),    %% Currently ordered_set is not supported for disk_only_copies.    if  	Type == ordered_set, Cs#cstruct.disc_only_copies /= [] ->	    mnesia:abort({bad_type, Tab, {not_supported, Type, disc_only_copies}});	true ->	    ok    end,    RecName = Cs#cstruct.record_name,    verify(atom, mnesia_lib:etype(RecName),	   {bad_type, Tab, {record_name, RecName}}),    Attrs = Cs#cstruct.attributes,    verify(list, mnesia_lib:etype(Attrs),	   {bad_type, Tab, {attributes, Attrs}}),    Arity = length(Attrs) + 1,    verify(true, Arity > 2, {bad_type, Tab, {attributes, Attrs}}),        lists:foldl(fun(Attr,_Other) when Attr == snmp ->                        mnesia:abort({bad_type, Tab, {attributes, [Attr]}});                   (Attr,Other) ->                         verify(atom, mnesia_lib:etype(Attr),                               {bad_type, Tab, {attributes, [Attr]}}),                        verify(false, lists:member(Attr, Other),                               {combine_error, Tab, {attributes, [Attr | Other]}}),                        [Attr | Other]                end,                [],                Attrs),    Index = Cs#cstruct.index,    verify({alt, [nil, list]}, mnesia_lib:etype(Index),	   {bad_type, Tab, {index, Index}}),        IxFun =        fun(Pos) ->                verify(true, fun() ->                                     if					 integer(Pos),                                         Pos > 2,                                         Pos =< Arity ->                                             true;                                         true -> false                                     end                             end,                       {bad_type, Tab, {index, [Pos]}})        end,    lists:foreach(IxFun, Index),        LC = Cs#cstruct.local_content,    verify({alt, [true, false]}, LC,	   {bad_type, Tab, {local_content, LC}}),    Access = Cs#cstruct.access_mode,    verify({alt, [read_write, read_only]}, Access,

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?