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