mnesia_schema.erl
来自「OTP是开放电信平台的简称」· ERL 代码 · 共 2,011 行 · 第 1/5 页
ERL
2,011 行
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %% $Id$%%%% In this module we provide a number of explicit functions%% to maninpulate the schema. All these functions are called%% within a special schema transaction.%%%% We also have an init/1 function defined here, this func is%% used by mnesia:start() to initialize the entire schema.-module(mnesia_schema).-export([ add_snmp/2, add_table_copy/3, add_table_index/2, arrange_restore/3, attr_tab_to_pos/2, attr_to_pos/2, change_table_copy_type/3, change_table_access_mode/2, change_table_load_order/2, change_table_frag/2, clear_table/1, create_table/1, cs2list/1, del_snmp/1, del_table_copy/2, del_table_index/2, delete_cstruct/2, delete_schema/1, delete_schema2/0, delete_table/1, delete_table_property/2, dump_tables/1, ensure_no_schema/1, get_create_list/1, get_initial_schema/2, get_table_properties/1, info/0, info/1, init/1, insert_cstruct/3, is_remote_member/1, list2cs/1, lock_schema/0, merge_schema/0, move_table/3, opt_create_dir/2, prepare_commit/3, purge_dir/2, purge_tmp_files/0, ram_delete_table/2,% ram_delete_table/3, read_cstructs_from_disc/0, read_nodes/0, remote_read_schema/0, restore/1, restore/2, restore/3, schema_coordinator/3, set_where_to_read/3, transform_table/4, undo_prepare_commit/2, unlock_schema/0, version/0, write_table_property/2 ]).%% Exports for mnesia_frag-export([ get_tid_ts_and_lock/2, make_create_table/1, ensure_active/1, pick/4, verify/3, incr_version/1, check_keys/3, check_duplicates/2, make_delete_table/2 ]).%% Needed outside to be able to use/set table_properties%% from user (not supported) -export([schema_transaction/1, insert_schema_ops/2, do_create_table/1, do_delete_table/1, do_read_table_property/2, do_delete_table_property/2, do_write_table_property/2]).-include("mnesia.hrl").-include_lib("kernel/include/file.hrl").-import(mnesia_lib, [set/2, del/2, verbose/2, dbg_out/2]).%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Here comes the init function which also resides in%% this module, it is called upon by the trans server%% at startup of the system%% %% We have a meta table which looks like%% {table, schema, %% {type, set},%% {disc_copies, all},%% {arity, 2}%% {attributes, [key, val]}%%%% This means that we have a series of {schema, Name, Cs} tuples%% in a table called schema !!init(IgnoreFallback) -> Res = read_schema(true, IgnoreFallback), {ok, Source, _CreateList} = exit_on_error(Res), verbose("Schema initiated from: ~p~n", [Source]), set({schema, tables}, []), set({schema, local_tables}, []), Tabs = set_schema(?ets_first(schema)), lists:foreach(fun(Tab) -> clear_whereabouts(Tab) end, Tabs), set({schema, where_to_read}, node()), set({schema, load_node}, node()), set({schema, load_reason}, initial), mnesia_controller:add_active_replica(schema, node()).exit_on_error({error, Reason}) -> exit(Reason);exit_on_error(GoodRes) -> GoodRes.val(Var) -> case ?catch_val(Var) of {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); Value -> Value end.%% This function traverses all cstructs in the schema and%% sets all values in mnesia_gvar accordingly for each table/cstructset_schema('$end_of_table') -> [];set_schema(Tab) -> do_set_schema(Tab), [Tab | set_schema(?ets_next(schema, Tab))].get_create_list(Tab) -> ?ets_lookup_element(schema, Tab, 3).do_set_schema(Tab) -> List = get_create_list(Tab), Cs = list2cs(List), do_set_schema(Tab, Cs).do_set_schema(Tab, Cs) -> Type = Cs#cstruct.type, set({Tab, setorbag}, Type), set({Tab, local_content}, Cs#cstruct.local_content), set({Tab, ram_copies}, Cs#cstruct.ram_copies), set({Tab, disc_copies}, Cs#cstruct.disc_copies), set({Tab, disc_only_copies}, Cs#cstruct.disc_only_copies), set({Tab, load_order}, Cs#cstruct.load_order), set({Tab, access_mode}, Cs#cstruct.access_mode), set({Tab, snmp}, Cs#cstruct.snmp), set({Tab, user_properties}, Cs#cstruct.user_properties), [set({Tab, user_property, element(1, P)}, P) || P <- Cs#cstruct.user_properties], set({Tab, frag_properties}, Cs#cstruct.frag_properties), mnesia_frag:set_frag_hash(Tab, Cs#cstruct.frag_properties), set({Tab, attributes}, Cs#cstruct.attributes), Arity = length(Cs#cstruct.attributes) + 1, set({Tab, arity}, Arity), RecName = Cs#cstruct.record_name, set({Tab, record_name}, RecName), set({Tab, record_validation}, {RecName, Arity, Type}), set({Tab, wild_pattern}, wild(RecName, Arity)), set({Tab, index}, Cs#cstruct.index), %% create actual index tabs later set({Tab, cookie}, Cs#cstruct.cookie), set({Tab, version}, Cs#cstruct.version), set({Tab, cstruct}, Cs), Storage = mnesia_lib:schema_cs_to_storage_type(node(), Cs), set({Tab, storage_type}, Storage), mnesia_lib:add({schema, tables}, Tab), Ns = mnesia_lib:cs_to_nodes(Cs), case lists:member(node(), Ns) of true -> mnesia_lib:add({schema, local_tables}, Tab); false when Tab == schema -> mnesia_lib:add({schema, local_tables}, Tab); false -> ignore end.wild(RecName, Arity) -> Wp0 = list_to_tuple(lists:duplicate(Arity, '_')), setelement(1, Wp0, RecName).%% Temporarily read the local schema and return a list%% of all nodes mentioned in the schema.DAT fileread_nodes() -> %% Ensure that we access the intended Mnesia %% directory. This function may not be called %% during startup since it will cause the %% application_controller to get into deadlock case mnesia_lib:ensure_loaded(?APPLICATION) of ok -> case read_schema(false) of {ok, _Source, CreateList} -> Cs = list2cs(CreateList), {ok, Cs#cstruct.disc_copies ++ Cs#cstruct.ram_copies}; {error, Reason} -> {error, Reason} end; {error, Reason} -> {error, Reason} end.%% Returns Version from the tuple {Version,MasterNodes}version() -> case read_schema(false) of {ok, Source, CreateList} when Source /= default -> Cs = list2cs(CreateList), {Version, _Details} = Cs#cstruct.version, Version; _ -> case dir_exists(mnesia_lib:dir()) of true -> {1,0}; false -> {0,0} end end.%% Calculate next table version from old cstructincr_version(Cs) -> {{Major, Minor}, _} = Cs#cstruct.version, Nodes = mnesia_lib:intersect(val({schema, disc_copies}), mnesia_lib:cs_to_nodes(Cs)), V = case Nodes -- val({Cs#cstruct.name, active_replicas}) of [] -> {Major + 1, 0}; % All replicas are active _ -> {Major, Minor + 1} % Some replicas are inactive end, Cs#cstruct{version = {V, {node(), now()}}}.%% Returns table nameinsert_cstruct(Tid, Cs, KeepWhereabouts) -> Tab = Cs#cstruct.name, TabDef = cs2list(Cs), Val = {schema, Tab, TabDef}, mnesia_checkpoint:tm_retain(Tid, schema, Tab, write), mnesia_subscr:report_table_event(schema, Tid, Val, write), Active = val({Tab, active_replicas}), case KeepWhereabouts of true -> ignore; false when Active == [] -> clear_whereabouts(Tab); false -> %% Someone else has initiated table ignore end, set({Tab, cstruct}, Cs), ?ets_insert(schema, Val), do_set_schema(Tab, Cs), Val.clear_whereabouts(Tab) -> set({Tab, checkpoints}, []), set({Tab, subscribers}, []), set({Tab, where_to_read}, nowhere), set({Tab, active_replicas}, []), set({Tab, commit_work}, []), set({Tab, where_to_write}, []), set({Tab, where_to_commit}, []), set({Tab, load_by_force}, false), set({Tab, load_node}, unknown), set({Tab, load_reason}, unknown).%% Returns table namedelete_cstruct(Tid, Cs) -> Tab = Cs#cstruct.name, TabDef = cs2list(Cs), Val = {schema, Tab, TabDef}, mnesia_checkpoint:tm_retain(Tid, schema, Tab, delete), mnesia_subscr:report_table_event(schema, Tid, Val, delete), mnesia_controller:update( fun() -> ?ets_match_delete(mnesia_gvar, {{Tab, '_'}, '_'}), ?ets_match_delete(mnesia_gvar, {{Tab, '_', '_'}, '_'}), del({schema, local_tables}, Tab), del({schema, tables}, Tab), ?ets_delete(schema, Tab) end), Val.%% Delete the Mnesia directory on all given nodes%% Requires that Mnesia is not running anywhere%% Returns ok | {error,Reason}delete_schema(Ns) when list(Ns), Ns /= [] -> RunningNs = mnesia_lib:running_nodes(Ns), Reason = "Cannot delete schema on all nodes", if RunningNs == [] -> case rpc:multicall(Ns, ?MODULE, delete_schema2, []) of {Replies, []} -> case [R || R <- Replies, R /= ok] of [] -> ok; BadReplies -> verbose("~s: ~p~n", [Reason, BadReplies]), {error, {"All nodes not running", BadReplies}} end; {_Replies, BadNs} -> verbose("~s: ~p~n", [Reason, BadNs]), {error, {"All nodes not running", BadNs}} end; true -> verbose("~s: ~p~n", [Reason, RunningNs]), {error, {"Mnesia is not stopped everywhere", RunningNs}} end;delete_schema(Ns) -> {error, {badarg, Ns}}.delete_schema2() -> %% Ensure that we access the intended Mnesia %% directory. This function may not be called %% during startup since it will cause the %% application_controller to get into deadlock case mnesia_lib:ensure_loaded(?APPLICATION) of ok -> case mnesia_lib:is_running() of no -> Dir = mnesia_lib:dir(), purge_dir(Dir, []), ok; _ -> {error, {"Mnesia still running", node()}} end; {error, Reason} -> {error, Reason} end. ensure_no_schema([H|T]) when atom(H) -> case rpc:call(H, ?MODULE, remote_read_schema, []) of {badrpc, Reason} -> {H, {"All nodes not running", H, Reason}}; {ok,Source, _} when Source /= default -> {H, {already_exists, H}}; _ -> ensure_no_schema(T) end;ensure_no_schema([H|_]) -> {error,{badarg, H}};ensure_no_schema([]) -> ok.remote_read_schema() -> %% Ensure that we access the intended Mnesia %% directory. This function may not be called %% during startup since it will cause the %% application_controller to get into deadlock case mnesia_lib:ensure_loaded(?APPLICATION) of ok -> case mnesia_monitor:get_env(schema_location) of opt_disc -> read_schema(false); _ -> read_schema(false) end; {error, Reason} -> {error, Reason} end.dir_exists(Dir) -> dir_exists(Dir, mnesia_monitor:use_dir()).dir_exists(Dir, true) -> case file:read_file_info(Dir) of {ok, _} -> true; _ -> false end;dir_exists(_Dir, false) -> false.opt_create_dir(UseDir, Dir) when UseDir == true-> case dir_exists(Dir, UseDir) of true -> check_can_write(Dir); false -> case file:make_dir(Dir) of ok ->
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?