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