1
%% ``The contents of this file are subject to the Erlang Public License,
2
%% Version 1.1, (the "License"); you may not use this file except in
3
%% compliance with the License. You should have received a copy of the
4
%% Erlang Public License along with this software. If not, it can be
5
%% retrieved via the world wide web at http://www.erlang.org/.
7
%% Software distributed under the License is distributed on an "AS IS"
8
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
9
%% the License for the specific language governing rights and limitations
12
%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
13
%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
14
%% AB. All Rights Reserved.''
16
%% $Id: mnesia_schema.erl,v 1.2 2010/03/04 13:54:20 maria Exp $
18
%% In this module we provide a number of explicit functions
19
%% to maninpulate the schema. All these functions are called
20
%% within a special schema transaction.
22
%% We also have an init/1 function defined here, this func is
23
%% used by mnesia:start() to initialize the entire schema.
25
-module(mnesia_schema).
34
change_table_copy_type/3,
35
change_table_access_mode/2,
36
change_table_load_order/2,
48
delete_table_property/2,
53
get_table_properties/1,
61
lock_del_table/4, % Spawned
70
read_cstructs_from_disc/0,
79
undo_prepare_commit/2,
82
write_table_property/2
85
%% Exports for mnesia_frag
87
get_tid_ts_and_lock/2,
98
%% Needed outside to be able to use/set table_properties
99
%% from user (not supported)
100
-export([schema_transaction/1,
104
do_delete_table_property/2,
105
do_write_table_property/2]).
107
-include("mnesia.hrl").
108
-include_lib("kernel/include/file.hrl").
110
-import(mnesia_lib, [set/2, del/2, verbose/2, dbg_out/2]).
112
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
113
%% Here comes the init function which also resides in
114
%% this module, it is called upon by the trans server
115
%% at startup of the system
117
%% We have a meta table which looks like
120
%% {disc_copies, all},
122
%% {attributes, [key, val]}
124
%% This means that we have a series of {schema, Name, Cs} tuples
125
%% in a table called schema !!
127
init(IgnoreFallback) ->
128
Res = read_schema(true, false, IgnoreFallback),
129
{ok, Source, _CreateList} = exit_on_error(Res),
130
verbose("Schema initiated from: ~p~n", [Source]),
131
set({schema, tables}, []),
132
set({schema, local_tables}, []),
133
Tabs = set_schema(?ets_first(schema)),
134
lists:foreach(fun(Tab) -> clear_whereabouts(Tab) end, Tabs),
135
set({schema, where_to_read}, node()),
136
set({schema, load_node}, node()),
137
set({schema, load_reason}, initial),
138
mnesia_controller:add_active_replica(schema, node()).
140
exit_on_error({error, Reason}) ->
142
exit_on_error(GoodRes) ->
146
case ?catch_val(Var) of
147
{'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason);
151
%% This function traverses all cstructs in the schema and
152
%% sets all values in mnesia_gvar accordingly for each table/cstruct
154
set_schema('$end_of_table') ->
158
[Tab | set_schema(?ets_next(schema, Tab))].
160
get_create_list(Tab) ->
161
?ets_lookup_element(schema, Tab, 3).
163
do_set_schema(Tab) ->
164
List = get_create_list(Tab),
166
do_set_schema(Tab, Cs).
168
do_set_schema(Tab, Cs) ->
169
Type = Cs#cstruct.type,
170
set({Tab, setorbag}, Type),
171
set({Tab, local_content}, Cs#cstruct.local_content),
172
set({Tab, ram_copies}, Cs#cstruct.ram_copies),
173
set({Tab, disc_copies}, Cs#cstruct.disc_copies),
174
set({Tab, disc_only_copies}, Cs#cstruct.disc_only_copies),
175
set({Tab, load_order}, Cs#cstruct.load_order),
176
set({Tab, access_mode}, Cs#cstruct.access_mode),
177
set({Tab, snmp}, Cs#cstruct.snmp),
178
set({Tab, user_properties}, Cs#cstruct.user_properties),
179
[set({Tab, user_property, element(1, P)}, P) || P <- Cs#cstruct.user_properties],
180
set({Tab, frag_properties}, Cs#cstruct.frag_properties),
181
mnesia_frag:set_frag_hash(Tab, Cs#cstruct.frag_properties),
182
set({Tab, attributes}, Cs#cstruct.attributes),
183
Arity = length(Cs#cstruct.attributes) + 1,
184
set({Tab, arity}, Arity),
185
RecName = Cs#cstruct.record_name,
186
set({Tab, record_name}, RecName),
187
set({Tab, record_validation}, {RecName, Arity, Type}),
188
set({Tab, wild_pattern}, wild(RecName, Arity)),
189
set({Tab, index}, Cs#cstruct.index),
190
%% create actual index tabs later
191
set({Tab, cookie}, Cs#cstruct.cookie),
192
set({Tab, version}, Cs#cstruct.version),
193
set({Tab, cstruct}, Cs),
194
Storage = mnesia_lib:schema_cs_to_storage_type(node(), Cs),
195
set({Tab, storage_type}, Storage),
196
mnesia_lib:add({schema, tables}, Tab),
197
Ns = mnesia_lib:cs_to_nodes(Cs),
198
case lists:member(node(), Ns) of
200
mnesia_lib:add({schema, local_tables}, Tab);
201
false when Tab == schema ->
202
mnesia_lib:add({schema, local_tables}, Tab);
207
wild(RecName, Arity) ->
208
Wp0 = list_to_tuple(lists:duplicate(Arity, '_')),
209
setelement(1, Wp0, RecName).
211
%% Temporarily read the local schema and return a list
212
%% of all nodes mentioned in the schema.DAT file
214
%% Ensure that we access the intended Mnesia
215
%% directory. This function may not be called
216
%% during startup since it will cause the
217
%% application_controller to get into deadlock
218
case mnesia_lib:ensure_loaded(?APPLICATION) of
220
case read_schema(false, false) of
221
{ok, _Source, CreateList} ->
222
Cs = list2cs(CreateList),
223
{ok, Cs#cstruct.disc_copies ++ Cs#cstruct.ram_copies};
231
%% Returns Version from the tuple {Version,MasterNodes}
233
case read_schema(false, false) of
234
{ok, Source, CreateList} when Source /= default ->
235
Cs = list2cs(CreateList),
236
{Version, _Details} = Cs#cstruct.version,
239
case dir_exists(mnesia_lib:dir()) of
245
%% Calculate next table version from old cstruct
247
{{Major, Minor}, _} = Cs#cstruct.version,
248
Nodes = mnesia_lib:intersect(val({schema, disc_copies}),
249
mnesia_lib:cs_to_nodes(Cs)),
251
case Nodes -- val({Cs#cstruct.name, active_replicas}) of
252
[] -> {Major + 1, 0}; % All replicas are active
253
_ -> {Major, Minor + 1} % Some replicas are inactive
255
Cs#cstruct{version = {V, {node(), now()}}}.
257
%% Returns table name
258
insert_cstruct(Tid, Cs, KeepWhereabouts) ->
259
Tab = Cs#cstruct.name,
260
TabDef = cs2list(Cs),
261
Val = {schema, Tab, TabDef},
262
mnesia_checkpoint:tm_retain(Tid, schema, Tab, write),
263
mnesia_subscr:report_table_event(schema, Tid, Val, write),
264
Active = val({Tab, active_replicas}),
266
case KeepWhereabouts of
269
false when Active == [] ->
270
clear_whereabouts(Tab);
272
%% Someone else has initiated table
275
set({Tab, cstruct}, Cs),
276
?ets_insert(schema, Val),
277
do_set_schema(Tab, Cs),
280
clear_whereabouts(Tab) ->
281
set({Tab, checkpoints}, []),
282
set({Tab, subscribers}, []),
283
set({Tab, where_to_read}, nowhere),
284
set({Tab, active_replicas}, []),
285
set({Tab, commit_work}, []),
286
set({Tab, where_to_write}, []),
287
set({Tab, where_to_commit}, []),
288
set({Tab, load_by_force}, false),
289
set({Tab, load_node}, unknown),
290
set({Tab, load_reason}, unknown).
292
%% Returns table name
293
delete_cstruct(Tid, Cs) ->
294
Tab = Cs#cstruct.name,
295
TabDef = cs2list(Cs),
296
Val = {schema, Tab, TabDef},
297
mnesia_checkpoint:tm_retain(Tid, schema, Tab, delete),
298
mnesia_subscr:report_table_event(schema, Tid, Val, delete),
299
?ets_match_delete(mnesia_gvar, {{Tab, '_'}, '_'}),
300
?ets_match_delete(mnesia_gvar, {{Tab, '_', '_'}, '_'}),
301
del({schema, local_tables}, Tab),
302
del({schema, tables}, Tab),
303
?ets_delete(schema, Tab),
306
%% Delete the Mnesia directory on all given nodes
307
%% Requires that Mnesia is not running anywhere
308
%% Returns ok | {error,Reason}
309
delete_schema(Ns) when list(Ns), Ns /= [] ->
310
RunningNs = mnesia_lib:running_nodes(Ns),
311
Reason = "Cannot delete schema on all nodes",
314
case rpc:multicall(Ns, ?MODULE, delete_schema2, []) of
316
case [R || R <- Replies, R /= ok] of
320
verbose("~s: ~p~n", [Reason, BadReplies]),
321
{error, {"All nodes not running", BadReplies}}
324
verbose("~s: ~p~n", [Reason, BadNs]),
325
{error, {"All nodes not running", BadNs}}
328
verbose("~s: ~p~n", [Reason, RunningNs]),
329
{error, {"Mnesia is not stopped everywhere", RunningNs}}
332
{error, {badarg, Ns}}.
335
%% Ensure that we access the intended Mnesia
336
%% directory. This function may not be called
337
%% during startup since it will cause the
338
%% application_controller to get into deadlock
339
case mnesia_lib:ensure_loaded(?APPLICATION) of
341
case mnesia_lib:is_running() of
343
Dir = mnesia_lib:dir(),
347
{error, {"Mnesia still running", node()}}
353
ensure_no_schema([H|T]) when atom(H) ->
354
case rpc:call(H, ?MODULE, remote_read_schema, []) of
356
{H, {"All nodes not running", H, Reason}};
357
{ok,Source, _} when Source /= default ->
358
{H, {already_exists, H}};
362
ensure_no_schema([H|_]) ->
364
ensure_no_schema([]) ->
367
remote_read_schema() ->
368
%% Ensure that we access the intended Mnesia
369
%% directory. This function may not be called
370
%% during startup since it will cause the
371
%% application_controller to get into deadlock
372
case mnesia_lib:ensure_loaded(?APPLICATION) of
374
case mnesia_monitor:get_env(schema_location) of
376
read_schema(false, true);
378
read_schema(false, false)
385
dir_exists(Dir, mnesia_monitor:use_dir()).
386
dir_exists(Dir, true) ->
387
case file:read_file_info(Dir) of
391
dir_exists(_Dir, false) ->
394
opt_create_dir(UseDir, Dir) when UseDir == true->
395
case dir_exists(Dir, UseDir) of
397
check_can_write(Dir);
399
case file:make_dir(Dir) of
401
verbose("Create Directory ~p~n", [Dir]),
404
verbose("Cannot create mnesia dir ~p~n", [Reason]),
405
{error, {"Cannot create Mnesia dir", Dir, Reason}}
408
opt_create_dir(false, _) ->
409
{error, {has_no_disc, node()}}.
411
check_can_write(Dir) ->
412
case file:read_file_info(Dir) of
413
{ok, FI} when FI#file_info.type == directory,
414
FI#file_info.access == read_write ->
417
{error, "Not allowed to write in Mnesia dir", Dir};
419
{error, "Non existent Mnesia dir", Dir}
423
mnesia_lib:lock_table(schema).
426
mnesia_lib:unlock_table(schema).
428
read_schema(Keep, _UseDirAnyway) ->
429
read_schema(Keep, false, false).
431
%% The schema may be read for several reasons.
432
%% If Mnesia is not already started the read intention
433
%% we normally do not want the ets table named schema
435
%% If Keep == true, the ets table schema is kept
436
%% If Keep == false, the ets table schema is removed
438
%% Returns {ok, Source, SchemaCstruct} or {error, Reason}
439
%% Source may be: default | ram | disc | fallback
441
read_schema(Keep, UseDirAnyway, IgnoreFallback) ->
444
case mnesia:system_info(is_running) of
446
{ok, ram, get_create_list(schema)};
448
case mnesia_monitor:use_dir() of
450
read_disc_schema(Keep, IgnoreFallback);
451
false when UseDirAnyway == true ->
452
read_disc_schema(Keep, IgnoreFallback);
453
false when Keep == true ->
454
Args = [{keypos, 2}, public, named_table, set],
455
mnesia_monitor:mktab(schema, Args),
456
CreateList = get_initial_schema(ram_copies, []),
457
?ets_insert(schema,{schema, schema, CreateList}),
458
{ok, default, CreateList};
459
false when Keep == false ->
460
CreateList = get_initial_schema(ram_copies, []),
461
{ok, default, CreateList}
467
read_disc_schema(Keep, IgnoreFallback) ->
468
Running = mnesia:system_info(is_running),
469
case mnesia_bup:fallback_exists() of
470
true when IgnoreFallback == false, Running /= yes ->
471
mnesia_bup:fallback_to_schema();
473
%% If we're running, we read the schema file even
474
%% if fallback exists
475
Dat = mnesia_lib:tab2dat(schema),
476
case mnesia_lib:exists(Dat) of
478
do_read_disc_schema(Dat, Keep);
480
Dmp = mnesia_lib:tab2dmp(schema),
481
case mnesia_lib:exists(Dmp) of
483
%% May only happen when toggling of
484
%% schema storage type has been
486
do_read_disc_schema(Dmp, Keep);
488
{error, "No schema file exists"}
493
do_read_disc_schema(Fname, Keep) ->
497
Args = [{keypos, 2}, public, set],
498
?ets_new_table(schema, Args);
500
Args = [{keypos, 2}, public, named_table, set],
501
mnesia_monitor:mktab(schema, Args)
503
Repair = mnesia_monitor:get_env(auto_repair),
504
Res = % BUGBUG Fixa till dcl!
505
case mnesia_lib:dets_to_ets(schema, T, Fname, set, Repair, no) of
506
loaded -> {ok, disc, ?ets_lookup_element(T, schema, 3)};
507
Other -> {error, {"Cannot read schema", Fname, Other}}
511
false -> ?ets_delete_table(T)
515
get_initial_schema(SchemaStorage, Nodes) ->
516
Cs = #cstruct{name = schema,
517
record_name = schema,
518
attributes = [table, cstruct]},
520
case SchemaStorage of
521
ram_copies -> Cs#cstruct{ram_copies = Nodes};
522
disc_copies -> Cs#cstruct{disc_copies = Nodes}
526
read_cstructs_from_disc() ->
528
%% - local schema lock in global
530
%% - Mnesia is not running
533
Fname = mnesia_lib:tab2dat(schema),
534
case mnesia_lib:exists(Fname) of
536
Args = [{file, Fname},
538
{repair, mnesia_monitor:get_env(auto_repair)},
540
case dets:open_file(make_ref(), Args) of
542
Fun = fun({_, _, List}) ->
543
{continue, list2cs(List)}
545
Cstructs = dets:traverse(Tab, Fun),
552
{error, "No schema file exists"}
555
%% We run a very special type of transactions when we
556
%% we want to manipulate the schema.
558
get_tid_ts_and_lock(Tab, Intent) ->
559
TidTs = get(mnesia_activity_state),
561
{_Mod, Tid, Ts} when record(Ts, tidstore)->
562
Store = Ts#tidstore.store,
564
read -> mnesia_locker:rlock_table(Tid, Store, Tab);
565
write -> mnesia_locker:wlock_table(Tid, Store, Tab);
570
mnesia:abort(no_transaction)
573
schema_transaction(Fun) ->
574
case get(mnesia_activity_state) of
576
Args = [self(), Fun, whereis(mnesia_controller)],
577
Pid = spawn_link(?MODULE, schema_coordinator, Args),
579
{transaction_done, Res, Pid} -> Res;
580
{'EXIT', Pid, R} -> {aborted, {transaction_crashed, R}}
583
{aborted, nested_transaction}
586
%% This process may dump the transaction log, and should
587
%% therefore not be run in an application process
589
schema_coordinator(Client, _Fun, undefined) ->
590
Res = {aborted, {node_not_running, node()}},
591
Client ! {transaction_done, Res, self()},
594
schema_coordinator(Client, Fun, Controller) when pid(Controller) ->
595
%% Do not trap exit in order to automatically die
596
%% when the controller dies
601
%% Fulfull the transaction even if the client dies
602
Res = mnesia:transaction(Fun),
603
Client ! {transaction_done, Res, self()},
604
unlink(Controller), % Avoids spurious exit message
605
unlink(whereis(mnesia_tm)), % Avoids spurious exit message
608
%% The make* rotines return a list of ops, this function
609
%% inserts em all in the Store and maintains the local order
612
insert_schema_ops({_Mod, _Tid, Ts}, SchemaIOps) ->
613
do_insert_schema_ops(Ts#tidstore.store, SchemaIOps).
615
do_insert_schema_ops(Store, [Head | Tail]) ->
616
?ets_insert(Store, Head),
617
do_insert_schema_ops(Store, Tail);
618
do_insert_schema_ops(_Store, []) ->
621
cs2list(Cs) when record(Cs, cstruct) ->
622
Tags = record_info(fields, cstruct),
623
rec2list(Tags, 2, Cs);
624
cs2list(CreateList) when list(CreateList) ->
627
rec2list([Tag | Tags], Pos, Rec) ->
628
Val = element(Pos, Rec),
629
[{Tag, Val} | rec2list(Tags, Pos + 1, Rec)];
630
rec2list([], _Pos, _Rec) ->
633
list2cs(List) when list(List) ->
634
Name = pick(unknown, name, List, must),
635
Type = pick(Name, type, List, set),
636
Rc0 = pick(Name, ram_copies, List, []),
637
Dc = pick(Name, disc_copies, List, []),
638
Doc = pick(Name, disc_only_copies, List, []),
639
Rc = case {Rc0, Dc, Doc} of
640
{[], [], []} -> [node()];
643
LC = pick(Name, local_content, List, false),
644
RecName = pick(Name, record_name, List, Name),
645
Attrs = pick(Name, attributes, List, [key, val]),
646
Snmp = pick(Name, snmp, List, []),
647
LoadOrder = pick(Name, load_order, List, 0),
648
AccessMode = pick(Name, access_mode, List, read_write),
649
UserProps = pick(Name, user_properties, List, []),
650
verify({alt, [nil, list]}, mnesia_lib:etype(UserProps),
651
{bad_type, Name, {user_properties, UserProps}}),
652
Cookie = pick(Name, cookie, List, ?unique_cookie),
653
Version = pick(Name, version, List, {{2, 0}, []}),
654
Ix = pick(Name, index, List, []),
655
verify({alt, [nil, list]}, mnesia_lib:etype(Ix),
656
{bad_type, Name, {index, [Ix]}}),
657
Ix2 = [attr_to_pos(I, Attrs) || I <- Ix],
659
Frag = pick(Name, frag_properties, List, []),
660
verify({alt, [nil, list]}, mnesia_lib:etype(Frag),
661
{badarg, Name, {frag_properties, Frag}}),
663
Keys = check_keys(Name, List, record_info(fields, cstruct)),
664
check_duplicates(Name, Keys),
665
#cstruct{name = Name,
668
disc_only_copies = Doc,
672
load_order = LoadOrder,
673
access_mode = AccessMode,
675
record_name = RecName,
677
user_properties = lists:sort(UserProps),
678
frag_properties = lists:sort(Frag),
682
mnesia:abort({badarg, Other}).
684
pick(Tab, Key, List, Default) ->
685
case lists:keysearch(Key, 1, List) of
686
false when Default == must ->
687
mnesia:abort({badarg, Tab, "Missing key", Key, List});
690
{value, {Key, Value}} ->
693
mnesia:abort({bad_type, Tab, BadArg})
696
%% Convert attribute name to integer if neccessary
697
attr_tab_to_pos(_Tab, Pos) when integer(Pos) ->
699
attr_tab_to_pos(Tab, Attr) ->
700
attr_to_pos(Attr, val({Tab, attributes})).
702
%% Convert attribute name to integer if neccessary
703
attr_to_pos(Pos, _Attrs) when integer(Pos) ->
705
attr_to_pos(Attr, Attrs) when atom(Attr) ->
706
attr_to_pos(Attr, Attrs, 2);
707
attr_to_pos(Attr, _) ->
708
mnesia:abort({bad_type, Attr}).
710
attr_to_pos(Attr, [Attr | _Attrs], Pos) ->
712
attr_to_pos(Attr, [_ | Attrs], Pos) ->
713
attr_to_pos(Attr, Attrs, Pos + 1);
714
attr_to_pos(Attr, _, _) ->
715
mnesia:abort({bad_type, Attr}).
717
check_keys(Tab, [{Key, _Val} | Tail], Items) ->
718
case lists:member(Key, Items) of
719
true -> [Key | check_keys(Tab, Tail, Items)];
720
false -> mnesia:abort({badarg, Tab, Key})
722
check_keys(_, [], _) ->
724
check_keys(Tab, Arg, _) ->
725
mnesia:abort({badarg, Tab, Arg}).
727
check_duplicates(Tab, Keys) ->
728
case has_duplicates(Keys) of
730
true -> mnesia:abort({badarg, Tab, "Duplicate keys", Keys})
733
has_duplicates([H | T]) ->
734
case lists:member(H, T) of
736
false -> has_duplicates(T)
738
has_duplicates([]) ->
741
%% This is the only place where we check the validity of data
742
verify_cstruct(Cs) when record(Cs, cstruct) ->
745
Tab = Cs#cstruct.name,
746
verify(atom, mnesia_lib:etype(Tab), {bad_type, Tab}),
747
Type = Cs#cstruct.type,
748
verify(true, lists:member(Type, [set, bag, ordered_set]),
749
{bad_type, Tab, {type, Type}}),
751
%% Currently ordered_set is not supported for disk_only_copies.
753
Type == ordered_set, Cs#cstruct.disc_only_copies /= [] ->
754
mnesia:abort({bad_type, Tab, {not_supported, Type, disc_only_copies}});
759
RecName = Cs#cstruct.record_name,
760
verify(atom, mnesia_lib:etype(RecName),
761
{bad_type, Tab, {record_name, RecName}}),
763
Attrs = Cs#cstruct.attributes,
764
verify(list, mnesia_lib:etype(Attrs),
765
{bad_type, Tab, {attributes, Attrs}}),
767
Arity = length(Attrs) + 1,
768
verify(true, Arity > 2, {bad_type, Tab, {attributes, Attrs}}),
770
lists:foldl(fun(Attr,_Other) when Attr == snmp ->
771
mnesia:abort({bad_type, Tab, {attributes, [Attr]}});
773
verify(atom, mnesia_lib:etype(Attr),
774
{bad_type, Tab, {attributes, [Attr]}}),
775
verify(false, lists:member(Attr, Other),
776
{combine_error, Tab, {attributes, [Attr | Other]}}),
782
Index = Cs#cstruct.index,
783
verify({alt, [nil, list]}, mnesia_lib:etype(Index),
784
{bad_type, Tab, {index, Index}}),
788
verify(true, fun() ->
797
{bad_type, Tab, {index, [Pos]}})
799
lists:foreach(IxFun, Index),
801
LC = Cs#cstruct.local_content,
802
verify({alt, [true, false]}, LC,
803
{bad_type, Tab, {local_content, LC}}),
804
Access = Cs#cstruct.access_mode,
805
verify({alt, [read_write, read_only]}, Access,
806
{bad_type, Tab, {access_mode, Access}}),
808
Snmp = Cs#cstruct.snmp,
809
verify(true, mnesia_snmp_hook:check_ustruct(Snmp),
810
{badarg, Tab, {snmp, Snmp}}),
812
CheckProp = fun(Prop) when tuple(Prop), size(Prop) >= 1 -> ok;
813
(Prop) -> mnesia:abort({bad_type, Tab, {user_properties, [Prop]}})
815
lists:foreach(CheckProp, Cs#cstruct.user_properties),
817
case Cs#cstruct.cookie of
818
{{MegaSecs, Secs, MicroSecs}, _Node}
819
when integer(MegaSecs), integer(Secs),
820
integer(MicroSecs), atom(node) ->
823
mnesia:abort({bad_type, Tab, {cookie, Cookie}})
825
case Cs#cstruct.version of
826
{{Major, Minor}, _Detail}
827
when integer(Major), integer(Minor) ->
830
mnesia:abort({bad_type, Tab, {version, Version}})
834
Tab = Cs#cstruct.name,
835
Ram = Cs#cstruct.ram_copies,
836
Disc = Cs#cstruct.disc_copies,
837
DiscOnly = Cs#cstruct.disc_only_copies,
838
LoadOrder = Cs#cstruct.load_order,
840
verify({alt, [nil, list]}, mnesia_lib:etype(Ram),
841
{bad_type, Tab, {ram_copies, Ram}}),
842
verify({alt, [nil, list]}, mnesia_lib:etype(Disc),
843
{bad_type, Tab, {disc_copies, Disc}}),
846
verify([], DiscOnly, {bad_type, Tab, {disc_only_copies, DiscOnly}});
848
verify({alt, [nil, list]},
849
mnesia_lib:etype(DiscOnly),
850
{bad_type, Tab, {disc_only_copies, DiscOnly}})
852
verify(integer, mnesia_lib:etype(LoadOrder),
853
{bad_type, Tab, {load_order, LoadOrder}}),
855
Nodes = Ram ++ Disc ++ DiscOnly,
856
verify(list, mnesia_lib:etype(Nodes),
858
[{ram_copies, []}, {disc_copies, []}, {disc_only_copies, []}]}),
859
verify(false, has_duplicates(Nodes), {combine_error, Tab, Nodes}),
860
AtomCheck = fun(N) -> verify(atom, mnesia_lib:etype(N), {bad_type, Tab, N}) end,
861
lists:foreach(AtomCheck, Nodes).
863
verify(Expected, Fun, Error) when function(Fun) ->
864
do_verify(Expected, catch Fun(), Error);
865
verify(Expected, Actual, Error) ->
866
do_verify(Expected, Actual, Error).
868
do_verify({alt, Values}, Value, Error) ->
869
case lists:member(Value, Values) of
871
false -> mnesia:abort(Error)
873
do_verify(Value, Value, _) ->
875
do_verify(_Value, _, Error) ->
878
ensure_writable(Tab) ->
879
case val({Tab, where_to_write}) of
880
[] -> mnesia:abort({read_only, Tab});
884
%% Ensure that all replicas on disk full nodes are active
886
ensure_active(Cs, active_replicas).
888
ensure_active(Cs, What) ->
889
Tab = Cs#cstruct.name,
890
case val({Tab, What}) of
891
[] -> mnesia:abort({no_exists, Tab});
894
Nodes = mnesia_lib:intersect(val({schema, disc_copies}),
895
mnesia_lib:cs_to_nodes(Cs)),
897
case Nodes -- val(W) of
901
Expl = "All replicas on diskfull nodes are not active yet",
902
case val({Tab, local_content}) of
904
case rpc:multicall(Ns, ?MODULE, is_remote_member, [W]) of
906
check_active(Replies, Expl, Tab);
908
mnesia:abort({not_active, Expl, Tab, BadNs})
911
mnesia:abort({not_active, Expl, Tab, Ns})
915
ensure_not_active(schema, Node) ->
916
case lists:member(Node, val({schema, active_replicas})) of
920
Expl = "Mnesia is running",
921
mnesia:abort({active, Expl, Node})
924
is_remote_member(Key) ->
925
IsActive = lists:member(node(), val(Key)),
928
check_active([{true, _Node} | Replies], Expl, Tab) ->
929
check_active(Replies, Expl, Tab);
930
check_active([{false, Node} | _Replies], Expl, Tab) ->
931
mnesia:abort({not_active, Expl, Tab, [Node]});
932
check_active([{badrpc, Reason} | _Replies], Expl, Tab) ->
933
mnesia:abort({not_active, Expl, Tab, Reason});
934
check_active([], _Expl, _Tab) ->
937
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
938
%% Here's the real interface function to create a table
940
create_table(TabDef) ->
941
schema_transaction(fun() -> do_multi_create_table(TabDef) end).
943
%% And the corresponding do routines ....
945
do_multi_create_table(TabDef) ->
946
get_tid_ts_and_lock(schema, write),
947
ensure_writable(schema),
948
Cs = list2cs(TabDef),
949
case Cs#cstruct.frag_properties of
953
CsList = mnesia_frag:expand_cstruct(Cs),
954
lists:foreach(fun do_create_table/1, CsList)
958
do_create_table(Cs) ->
959
{_Mod, _Tid, Ts} = get_tid_ts_and_lock(schema, none),
960
Store = Ts#tidstore.store,
961
do_insert_schema_ops(Store, make_create_table(Cs)).
963
make_create_table(Cs) ->
964
Tab = Cs#cstruct.name,
965
verify('EXIT', element(1, ?catch_val({Tab, cstruct})),
966
{already_exists, Tab}),
967
unsafe_make_create_table(Cs).
969
% unsafe_do_create_table(Cs) ->
970
% {_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, none),
971
% Store = Ts#tidstore.store,
972
% do_insert_schema_ops(Store, unsafe_make_create_table(Cs)).
974
unsafe_make_create_table(Cs) ->
975
{_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, none),
977
Tab = Cs#cstruct.name,
979
%% Check that we have all disc replica nodes running
980
DiscNodes = Cs#cstruct.disc_copies ++ Cs#cstruct.disc_only_copies,
981
RunningNodes = val({current, db_nodes}),
982
CheckDisc = fun(N) ->
983
verify(true, lists:member(N, RunningNodes),
984
{not_active, Tab, N})
986
lists:foreach(CheckDisc, DiscNodes),
988
Nodes = mnesia_lib:intersect(mnesia_lib:cs_to_nodes(Cs), RunningNodes),
989
Store = Ts#tidstore.store,
990
mnesia_locker:wlock_no_exist(Tid, Store, Tab, Nodes),
991
[{op, create_table, cs2list(Cs)}].
993
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
994
%% Delete a table entirely on all nodes.
997
schema_transaction(fun() -> do_delete_table(Tab) end).
999
do_delete_table(schema) ->
1000
mnesia:abort({bad_type, schema});
1001
do_delete_table(Tab) ->
1002
TidTs = get_tid_ts_and_lock(schema, write),
1003
ensure_writable(schema),
1004
insert_schema_ops(TidTs, make_delete_table(Tab, whole_table)).
1006
make_delete_table(Tab, Mode) ->
1009
case val({Tab, frag_properties}) of
1011
[make_delete_table2(Tab)];
1013
%% Check if it is a base table
1014
mnesia_frag:lookup_frag_hash(Tab),
1016
%% Check for foreigners
1017
F = mnesia_frag:lookup_foreigners(Tab),
1018
verify([], F, {combine_error, Tab, "Too many foreigners", F}),
1019
[make_delete_table2(T) || T <- mnesia_frag:frag_names(Tab)]
1022
[make_delete_table2(Tab)]
1025
make_delete_table2(Tab) ->
1026
get_tid_ts_and_lock(Tab, write),
1027
Cs = val({Tab, cstruct}),
1029
ensure_writable(Tab),
1030
{op, delete_table, cs2list(Cs)}.
1032
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1033
%% Change fragmentation of a table
1035
change_table_frag(Tab, Change) ->
1036
schema_transaction(fun() -> do_change_table_frag(Tab, Change) end).
1038
do_change_table_frag(Tab, Change) when atom(Tab), Tab /= schema ->
1039
TidTs = get_tid_ts_and_lock(schema, write),
1040
Ops = mnesia_frag:change_table_frag(Tab, Change),
1041
[insert_schema_ops(TidTs, Op) || Op <- Ops],
1043
do_change_table_frag(Tab, _Change) ->
1044
mnesia:abort({bad_type, Tab}).
1046
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1050
schema_transaction(fun() -> do_clear_table(Tab) end).
1052
do_clear_table(schema) ->
1053
mnesia:abort({bad_type, schema});
1054
do_clear_table(Tab) ->
1055
TidTs = get_tid_ts_and_lock(schema, write),
1056
get_tid_ts_and_lock(Tab, write),
1057
insert_schema_ops(TidTs, make_clear_table(Tab)).
1059
make_clear_table(Tab) ->
1060
ensure_writable(schema),
1061
Cs = val({Tab, cstruct}),
1063
ensure_writable(Tab),
1064
[{op, clear_table, cs2list(Cs)}].
1066
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1068
add_table_copy(Tab, Node, Storage) ->
1069
schema_transaction(fun() -> do_add_table_copy(Tab, Node, Storage) end).
1071
do_add_table_copy(Tab, Node, Storage) when atom(Tab), atom(Node) ->
1072
TidTs = get_tid_ts_and_lock(schema, write),
1073
insert_schema_ops(TidTs, make_add_table_copy(Tab, Node, Storage));
1074
do_add_table_copy(Tab,Node,_) ->
1075
mnesia:abort({badarg, Tab, Node}).
1077
make_add_table_copy(Tab, Node, Storage) ->
1078
ensure_writable(schema),
1079
Cs = incr_version(val({Tab, cstruct})),
1080
Ns = mnesia_lib:cs_to_nodes(Cs),
1081
verify(false, lists:member(Node, Ns), {already_exists, Tab, Node}),
1082
Cs2 = new_cs(Cs, Node, Storage, add),
1083
verify_cstruct(Cs2),
1085
%% Check storage and if node is running
1086
IsRunning = lists:member(Node, val({current, db_nodes})),
1088
Storage == unknown ->
1089
mnesia:abort({badarg, Tab, Storage});
1092
Storage /= ram_copies ->
1093
mnesia:abort({badarg, Tab, Storage});
1094
IsRunning == true ->
1095
mnesia:abort({already_exists, Tab, Node});
1099
Storage == ram_copies ->
1101
IsRunning == true ->
1103
IsRunning == false ->
1104
mnesia:abort({not_active, schema, Node})
1106
[{op, add_table_copy, Storage, Node, cs2list(Cs2)}].
1108
del_table_copy(Tab, Node) ->
1109
schema_transaction(fun() -> do_del_table_copy(Tab, Node) end).
1111
do_del_table_copy(Tab, Node) when atom(Node) ->
1112
TidTs = get_tid_ts_and_lock(schema, write),
1113
%% get_tid_ts_and_lock(Tab, write),
1114
insert_schema_ops(TidTs, make_del_table_copy(Tab, Node));
1115
do_del_table_copy(Tab, Node) ->
1116
mnesia:abort({badarg, Tab, Node}).
1118
make_del_table_copy(Tab, Node) ->
1119
ensure_writable(schema),
1120
Cs = incr_version(val({Tab, cstruct})),
1121
Storage = mnesia_lib:schema_cs_to_storage_type(Node, Cs),
1122
Cs2 = new_cs(Cs, Node, Storage, del),
1123
case mnesia_lib:cs_to_nodes(Cs2) of
1124
[] when Tab == schema ->
1125
mnesia:abort({combine_error, Tab, "Last replica"});
1128
dbg_out("Last replica deleted in table ~p~n", [Tab]),
1129
make_delete_table(Tab, whole_table);
1130
_ when Tab == schema ->
1132
ensure_not_active(Tab, Node),
1133
verify_cstruct(Cs2),
1134
Ops = remove_node_from_tabs(val({schema, tables}), Node),
1135
[{op, del_table_copy, ram_copies, Node, cs2list(Cs2)} | Ops];
1138
verify_cstruct(Cs2),
1139
[{op, del_table_copy, Storage, Node, cs2list(Cs2)}]
1142
remove_node_from_tabs([], _Node) ->
1144
remove_node_from_tabs([schema|Rest], Node) ->
1145
remove_node_from_tabs(Rest, Node);
1146
remove_node_from_tabs([Tab|Rest], Node) ->
1147
{Cs, IsFragModified} =
1148
mnesia_frag:remove_node(Node, incr_version(val({Tab, cstruct}))),
1149
case mnesia_lib:schema_cs_to_storage_type(Node, Cs) of
1151
case IsFragModified of
1153
[{op, change_table_frag, {del_node, Node}, cs2list(Cs)} |
1154
remove_node_from_tabs(Rest, Node)];
1156
remove_node_from_tabs(Rest, Node)
1159
Cs2 = new_cs(Cs, Node, Storage, del),
1160
case mnesia_lib:cs_to_nodes(Cs2) of
1162
[{op, delete_table, cs2list(Cs)} |
1163
remove_node_from_tabs(Rest, Node)];
1165
verify_cstruct(Cs2),
1166
[{op, del_table_copy, ram_copies, Node, cs2list(Cs2)}|
1167
remove_node_from_tabs(Rest, Node)]
1171
new_cs(Cs, Node, ram_copies, add) ->
1172
Cs#cstruct{ram_copies = opt_add(Node, Cs#cstruct.ram_copies)};
1173
new_cs(Cs, Node, disc_copies, add) ->
1174
Cs#cstruct{disc_copies = opt_add(Node, Cs#cstruct.disc_copies)};
1175
new_cs(Cs, Node, disc_only_copies, add) ->
1176
Cs#cstruct{disc_only_copies = opt_add(Node, Cs#cstruct.disc_only_copies)};
1177
new_cs(Cs, Node, ram_copies, del) ->
1178
Cs#cstruct{ram_copies = lists:delete(Node , Cs#cstruct.ram_copies)};
1179
new_cs(Cs, Node, disc_copies, del) ->
1180
Cs#cstruct{disc_copies = lists:delete(Node , Cs#cstruct.disc_copies)};
1181
new_cs(Cs, Node, disc_only_copies, del) ->
1182
Cs#cstruct{disc_only_copies =
1183
lists:delete(Node , Cs#cstruct.disc_only_copies)};
1184
new_cs(Cs, _Node, Storage, _Op) ->
1185
mnesia:abort({badarg, Cs#cstruct.name, Storage}).
1188
opt_add(N, L) -> [N | lists:delete(N, L)].
1190
move_table(Tab, FromNode, ToNode) ->
1191
schema_transaction(fun() -> do_move_table(Tab, FromNode, ToNode) end).
1193
do_move_table(schema, _FromNode, _ToNode) ->
1194
mnesia:abort({bad_type, schema});
1195
do_move_table(Tab, FromNode, ToNode) when atom(FromNode), atom(ToNode) ->
1196
TidTs = get_tid_ts_and_lock(schema, write),
1197
insert_schema_ops(TidTs, make_move_table(Tab, FromNode, ToNode));
1198
do_move_table(Tab, FromNode, ToNode) ->
1199
mnesia:abort({badarg, Tab, FromNode, ToNode}).
1201
make_move_table(Tab, FromNode, ToNode) ->
1202
ensure_writable(schema),
1203
Cs = incr_version(val({Tab, cstruct})),
1204
Ns = mnesia_lib:cs_to_nodes(Cs),
1205
verify(false, lists:member(ToNode, Ns), {already_exists, Tab, ToNode}),
1206
verify(true, lists:member(FromNode, val({Tab, where_to_write})),
1207
{not_active, Tab, FromNode}),
1208
verify(false, val({Tab,local_content}),
1209
{"Cannot move table with local content", Tab}),
1211
Running = val({current, db_nodes}),
1212
Storage = mnesia_lib:schema_cs_to_storage_type(FromNode, Cs),
1213
verify(true, lists:member(ToNode, Running), {not_active, schema, ToNode}),
1215
Cs2 = new_cs(Cs, ToNode, Storage, add),
1216
Cs3 = new_cs(Cs2, FromNode, Storage, del),
1217
verify_cstruct(Cs3),
1218
[{op, add_table_copy, Storage, ToNode, cs2list(Cs2)},
1220
{op, del_table_copy, Storage, FromNode, cs2list(Cs3)}].
1222
%% end of functions to add and delete nodes to tables
1223
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1226
change_table_copy_type(Tab, Node, ToS) ->
1227
schema_transaction(fun() -> do_change_table_copy_type(Tab, Node, ToS) end).
1229
do_change_table_copy_type(Tab, Node, ToS) when atom(Node) ->
1230
TidTs = get_tid_ts_and_lock(schema, write),
1231
get_tid_ts_and_lock(Tab, write), % ensure global sync
1232
%% get_tid_ts_and_lock(Tab, read),
1233
insert_schema_ops(TidTs, make_change_table_copy_type(Tab, Node, ToS));
1234
do_change_table_copy_type(Tab, Node, _ToS) ->
1235
mnesia:abort({badarg, Tab, Node}).
1237
make_change_table_copy_type(Tab, Node, unknown) ->
1238
make_del_table_copy(Tab, Node);
1239
make_change_table_copy_type(Tab, Node, ToS) ->
1240
ensure_writable(schema),
1241
Cs = incr_version(val({Tab, cstruct})),
1242
FromS = mnesia_lib:storage_type_at_node(Node, Tab),
1244
case compare_storage_type(false, FromS, ToS) of
1246
mnesia:abort({already_exists, Tab, Node, ToS});
1253
Cs2 = new_cs(Cs, Node, FromS, del),
1254
Cs3 = new_cs(Cs2, Node, ToS, add),
1255
verify_cstruct(Cs3),
1259
make_add_table_copy(Tab, Node, ToS);
1264
[{op, change_table_copy_type, Node, FromS, ToS, cs2list(Cs3)}].
1266
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1267
%% change index functions ....
1268
%% Pos is allready added by 1 in both of these functions
1270
add_table_index(Tab, Pos) ->
1271
schema_transaction(fun() -> do_add_table_index(Tab, Pos) end).
1273
do_add_table_index(schema, _Attr) ->
1274
mnesia:abort({bad_type, schema});
1275
do_add_table_index(Tab, Attr) ->
1276
TidTs = get_tid_ts_and_lock(schema, write),
1277
get_tid_ts_and_lock(Tab, read),
1278
Pos = attr_tab_to_pos(Tab, Attr),
1279
insert_schema_ops(TidTs, make_add_table_index(Tab, Pos)).
1281
make_add_table_index(Tab, Pos) ->
1282
ensure_writable(schema),
1283
Cs = incr_version(val({Tab, cstruct})),
1285
Ix = Cs#cstruct.index,
1286
verify(false, lists:member(Pos, Ix), {already_exists, Tab, Pos}),
1287
Ix2 = lists:sort([Pos | Ix]),
1288
Cs2 = Cs#cstruct{index = Ix2},
1289
verify_cstruct(Cs2),
1290
[{op, add_index, Pos, cs2list(Cs2)}].
1292
del_table_index(Tab, Pos) ->
1293
schema_transaction(fun() -> do_del_table_index(Tab, Pos) end).
1295
do_del_table_index(schema, _Attr) ->
1296
mnesia:abort({bad_type, schema});
1297
do_del_table_index(Tab, Attr) ->
1298
TidTs = get_tid_ts_and_lock(schema, write),
1299
get_tid_ts_and_lock(Tab, read),
1300
Pos = attr_tab_to_pos(Tab, Attr),
1301
insert_schema_ops(TidTs, make_del_table_index(Tab, Pos)).
1303
make_del_table_index(Tab, Pos) ->
1304
ensure_writable(schema),
1305
Cs = incr_version(val({Tab, cstruct})),
1307
Ix = Cs#cstruct.index,
1308
verify(true, lists:member(Pos, Ix), {no_exists, Tab, Pos}),
1309
Cs2 = Cs#cstruct{index = lists:delete(Pos, Ix)},
1310
verify_cstruct(Cs2),
1311
[{op, del_index, Pos, cs2list(Cs2)}].
1313
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1315
add_snmp(Tab, Ustruct) ->
1316
schema_transaction(fun() -> do_add_snmp(Tab, Ustruct) end).
1318
do_add_snmp(schema, _Ustruct) ->
1319
mnesia:abort({bad_type, schema});
1320
do_add_snmp(Tab, Ustruct) ->
1321
TidTs = get_tid_ts_and_lock(schema, write),
1322
get_tid_ts_and_lock(Tab, read),
1323
insert_schema_ops(TidTs, make_add_snmp(Tab, Ustruct)).
1325
make_add_snmp(Tab, Ustruct) ->
1326
ensure_writable(schema),
1327
Cs = incr_version(val({Tab, cstruct})),
1329
verify([], Cs#cstruct.snmp, {already_exists, Tab, snmp}),
1330
Error = {badarg, Tab, snmp, Ustruct},
1331
verify(true, mnesia_snmp_hook:check_ustruct(Ustruct), Error),
1332
Cs2 = Cs#cstruct{snmp = Ustruct},
1333
verify_cstruct(Cs2),
1334
[{op, add_snmp, Ustruct, cs2list(Cs2)}].
1337
schema_transaction(fun() -> do_del_snmp(Tab) end).
1339
do_del_snmp(schema) ->
1340
mnesia:abort({bad_type, schema});
1342
TidTs = get_tid_ts_and_lock(schema, write),
1343
get_tid_ts_and_lock(Tab, read),
1344
insert_schema_ops(TidTs, make_del_snmp(Tab)).
1346
make_del_snmp(Tab) ->
1347
ensure_writable(schema),
1348
Cs = incr_version(val({Tab, cstruct})),
1350
Cs2 = Cs#cstruct{snmp = []},
1351
verify_cstruct(Cs2),
1352
[{op, del_snmp, cs2list(Cs2)}].
1354
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1357
transform_table(Tab, Fun, NewAttrs, NewRecName)
1358
when function(Fun), list(NewAttrs), atom(NewRecName) ->
1359
schema_transaction(fun() -> do_transform_table(Tab, Fun, NewAttrs, NewRecName) end);
1361
transform_table(Tab, ignore, NewAttrs, NewRecName)
1362
when list(NewAttrs), atom(NewRecName) ->
1363
schema_transaction(fun() -> do_transform_table(Tab, ignore, NewAttrs, NewRecName) end);
1365
transform_table(Tab, Fun, NewAttrs, NewRecName) ->
1366
{aborted,{bad_type, Tab, Fun, NewAttrs, NewRecName}}.
1368
do_transform_table(schema, _Fun, _NewAttrs, _NewRecName) ->
1369
mnesia:abort({bad_type, schema});
1370
do_transform_table(Tab, Fun, NewAttrs, NewRecName) ->
1371
TidTs = get_tid_ts_and_lock(schema, write),
1372
get_tid_ts_and_lock(Tab, write),
1373
insert_schema_ops(TidTs, make_transform(Tab, Fun, NewAttrs, NewRecName)).
1375
make_transform(Tab, Fun, NewAttrs, NewRecName) ->
1376
ensure_writable(schema),
1377
Cs = incr_version(val({Tab, cstruct})),
1379
ensure_writable(Tab),
1380
case mnesia_lib:val({Tab, index}) of
1382
Cs2 = Cs#cstruct{attributes = NewAttrs, record_name = NewRecName},
1383
verify_cstruct(Cs2),
1384
[{op, transform, Fun, cs2list(Cs2)}];
1386
DelIdx = fun(Pos, Ncs) ->
1387
Ix = Ncs#cstruct.index,
1388
Ncs1 = Ncs#cstruct{index = lists:delete(Pos, Ix)},
1389
Op = {op, del_index, Pos, cs2list(Ncs1)},
1392
AddIdx = fun(Pos, Ncs) ->
1393
Ix = Ncs#cstruct.index,
1394
Ix2 = lists:sort([Pos | Ix]),
1395
Ncs1 = Ncs#cstruct{index = Ix2},
1396
Op = {op, add_index, Pos, cs2list(Ncs1)},
1399
{DelOps, Cs1} = lists:mapfoldl(DelIdx, Cs, PosList),
1400
Cs2 = Cs1#cstruct{attributes = NewAttrs, record_name = NewRecName},
1401
{AddOps, Cs3} = lists:mapfoldl(AddIdx, Cs2, PosList),
1402
verify_cstruct(Cs3),
1403
lists:flatten([DelOps, {op, transform, Fun, cs2list(Cs2)}, AddOps])
1406
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1409
change_table_access_mode(Tab, Mode) ->
1410
schema_transaction(fun() -> do_change_table_access_mode(Tab, Mode) end).
1412
do_change_table_access_mode(Tab, Mode) ->
1413
{_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, write),
1414
Store = Ts#tidstore.store,
1415
mnesia_locker:wlock_no_exist(Tid, Store, schema, val({schema, active_replicas})),
1416
mnesia_locker:wlock_no_exist(Tid, Store, Tab, val({Tab, active_replicas})),
1417
do_insert_schema_ops(Store, make_change_table_access_mode(Tab, Mode)).
1419
make_change_table_access_mode(Tab, Mode) ->
1420
ensure_writable(schema),
1421
Cs = incr_version(val({Tab, cstruct})),
1423
OldMode = Cs#cstruct.access_mode,
1424
verify(false, OldMode == Mode, {already_exists, Tab, Mode}),
1425
Cs2 = Cs#cstruct{access_mode = Mode},
1426
verify_cstruct(Cs2),
1427
[{op, change_table_access_mode, cs2list(Cs2), OldMode, Mode}].
1429
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1431
change_table_load_order(Tab, LoadOrder) ->
1432
schema_transaction(fun() -> do_change_table_load_order(Tab, LoadOrder) end).
1434
do_change_table_load_order(schema, _LoadOrder) ->
1435
mnesia:abort({bad_type, schema});
1436
do_change_table_load_order(Tab, LoadOrder) ->
1437
TidTs = get_tid_ts_and_lock(schema, write),
1438
get_tid_ts_and_lock(Tab, none),
1439
insert_schema_ops(TidTs, make_change_table_load_order(Tab, LoadOrder)).
1441
make_change_table_load_order(Tab, LoadOrder) ->
1442
ensure_writable(schema),
1443
Cs = incr_version(val({Tab, cstruct})),
1445
OldLoadOrder = Cs#cstruct.load_order,
1446
Cs2 = Cs#cstruct{load_order = LoadOrder},
1447
verify_cstruct(Cs2),
1448
[{op, change_table_load_order, cs2list(Cs2), OldLoadOrder, LoadOrder}].
1450
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1452
write_table_property(Tab, Prop) when tuple(Prop), size(Prop) >= 1 ->
1453
schema_transaction(fun() -> do_write_table_property(Tab, Prop) end);
1454
write_table_property(Tab, Prop) ->
1455
{aborted, {bad_type, Tab, Prop}}.
1456
do_write_table_property(Tab, Prop) ->
1457
TidTs = get_tid_ts_and_lock(schema, write),
1459
Store = Ts#tidstore.store,
1460
case change_prop_in_existing_op(Tab, Prop, write_property, Store) of
1462
dbg_out("change_prop_in_existing_op"
1463
"(~p,~p,write_property,Store) -> true~n",
1465
%% we have merged the table prop into the create_table op
1468
dbg_out("change_prop_in_existing_op"
1469
"(~p,~p,write_property,Store) -> false~n",
1471
%% this must be an existing table
1472
get_tid_ts_and_lock(Tab, none),
1473
insert_schema_ops(TidTs, make_write_table_properties(Tab, [Prop]))
1476
make_write_table_properties(Tab, Props) ->
1477
ensure_writable(schema),
1478
Cs = incr_version(val({Tab, cstruct})),
1480
make_write_table_properties(Tab, Props, Cs).
1482
make_write_table_properties(Tab, [Prop | Props], Cs) ->
1483
OldProps = Cs#cstruct.user_properties,
1484
PropKey = element(1, Prop),
1485
DelProps = lists:keydelete(PropKey, 1, OldProps),
1486
MergedProps = lists:merge(DelProps, [Prop]),
1487
Cs2 = Cs#cstruct{user_properties = MergedProps},
1488
verify_cstruct(Cs2),
1489
[{op, write_property, cs2list(Cs2), Prop} |
1490
make_write_table_properties(Tab, Props, Cs2)];
1491
make_write_table_properties(_Tab, [], _Cs) ->
1494
change_prop_in_existing_op(Tab, Prop, How, Store) ->
1495
Ops = ets:match_object(Store, '_'),
1496
case update_existing_op(Ops, Tab, Prop, How, []) of
1498
ets:match_delete(Store, '_'),
1499
[ets:insert(Store, Op) || Op <- Ops1],
1505
update_existing_op([{op, Op, L = [{name,Tab}|_], _OldProp}|Ops],
1506
Tab, Prop, How, Acc) when Op == write_property;
1507
Op == delete_property ->
1508
%% Apparently, mnesia_dumper doesn't care about OldProp here -- just L,
1509
%% so we will throw away OldProp (not that it matters...) and insert Prop.
1511
L1 = insert_prop(Prop, L, How),
1512
NewOp = {op, How, L1, Prop},
1513
{true, lists:reverse(Acc) ++ [NewOp|Ops]};
1514
update_existing_op([Op = {op, create_table, L}|Ops], Tab, Prop, How, Acc) ->
1515
case lists:keysearch(name, 1, L) of
1516
{value, {_, Tab}} ->
1517
%% Tab is being created here -- insert Prop into L
1518
L1 = insert_prop(Prop, L, How),
1519
{true, lists:reverse(Acc) ++ [{op, create_table, L1}|Ops]};
1521
update_existing_op(Ops, Tab, Prop, How, [Op|Acc])
1523
update_existing_op([Op|Ops], Tab, Prop, How, Acc) ->
1524
update_existing_op(Ops, Tab, Prop, How, [Op|Acc]);
1525
update_existing_op([], _, _, _, _) ->
1528
%% perhaps a misnomer. How could also be delete_property... never mind.
1529
%% Returns the modified L.
1530
insert_prop(Prop, L, How) ->
1531
Prev = find_props(L),
1532
MergedProps = merge_with_previous(How, Prop, Prev),
1533
replace_props(L, MergedProps).
1536
find_props([{user_properties, P}|_]) -> P;
1537
find_props([_H|T]) -> find_props(T).
1538
%% we shouldn't reach []
1540
replace_props([{user_properties, _}|T], P) -> [{user_properties, P}|T];
1541
replace_props([H|T], P) -> [H|replace_props(T, P)].
1542
%% again, we shouldn't reach []
1544
merge_with_previous(write_property, Prop, Prev) ->
1545
Key = element(1, Prop),
1546
Prev1 = lists:keydelete(Key, 1, Prev),
1547
lists:sort([Prop|Prev1]);
1548
merge_with_previous(delete_property, PropKey, Prev) ->
1549
lists:keydelete(PropKey, 1, Prev).
1551
delete_table_property(Tab, PropKey) ->
1552
schema_transaction(fun() -> do_delete_table_property(Tab, PropKey) end).
1554
do_delete_table_property(Tab, PropKey) ->
1555
TidTs = get_tid_ts_and_lock(schema, write),
1557
Store = Ts#tidstore.store,
1558
case change_prop_in_existing_op(Tab, PropKey, delete_property, Store) of
1560
dbg_out("change_prop_in_existing_op"
1561
"(~p,~p,delete_property,Store) -> true~n",
1563
%% we have merged the table prop into the create_table op
1566
dbg_out("change_prop_in_existing_op"
1567
"(~p,~p,delete_property,Store) -> false~n",
1569
%% this must be an existing table
1570
get_tid_ts_and_lock(Tab, none),
1571
insert_schema_ops(TidTs,
1572
make_delete_table_properties(Tab, [PropKey]))
1575
make_delete_table_properties(Tab, PropKeys) ->
1576
ensure_writable(schema),
1577
Cs = incr_version(val({Tab, cstruct})),
1579
make_delete_table_properties(Tab, PropKeys, Cs).
1581
make_delete_table_properties(Tab, [PropKey | PropKeys], Cs) ->
1582
OldProps = Cs#cstruct.user_properties,
1583
Props = lists:keydelete(PropKey, 1, OldProps),
1584
Cs2 = Cs#cstruct{user_properties = Props},
1585
verify_cstruct(Cs2),
1586
[{op, delete_property, cs2list(Cs2), PropKey} |
1587
make_delete_table_properties(Tab, PropKeys, Cs2)];
1588
make_delete_table_properties(_Tab, [], _Cs) ->
1591
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1593
%% Ensure that the transaction can be committed even
1594
%% if the node crashes and Mnesia is restarted
1595
prepare_commit(Tid, Commit, WaitFor) ->
1596
case Commit#commit.schema_ops of
1598
{false, Commit, optional};
1600
{Modified, Ops, DumperMode} =
1601
prepare_ops(Tid, OrigOps, WaitFor, false, [], optional),
1602
InitBy = schema_prepare,
1603
GoodRes = {Modified,
1604
Commit#commit{schema_ops = lists:reverse(Ops)},
1608
dbg_out("Transaction log dump skipped (~p): ~w~n",
1609
[DumperMode, InitBy]);
1611
case mnesia_controller:sync_dump_log(InitBy) of
1615
mnesia:abort(Reason)
1622
%% We need to grab a dumper lock here, the log may not
1623
%% be dumped by others, during the schema commit phase.
1624
mnesia_controller:wait_for_schema_commit_lock()
1629
prepare_ops(Tid, [Op | Ops], WaitFor, Changed, Acc, DumperMode) ->
1630
case prepare_op(Tid, Op, WaitFor) of
1631
{true, mandatory} ->
1632
prepare_ops(Tid, Ops, WaitFor, Changed, [Op | Acc], mandatory);
1634
prepare_ops(Tid, Ops, WaitFor, Changed, [Op | Acc], DumperMode);
1635
{true, Ops2, mandatory} ->
1636
prepare_ops(Tid, Ops, WaitFor, true, Ops2 ++ Acc, mandatory);
1637
{true, Ops2, optional} ->
1638
prepare_ops(Tid, Ops, WaitFor, true, Ops2 ++ Acc, DumperMode);
1639
{false, mandatory} ->
1640
prepare_ops(Tid, Ops, WaitFor, true, Acc, mandatory);
1641
{false, optional} ->
1642
prepare_ops(Tid, Ops, WaitFor, true, Acc, DumperMode)
1644
prepare_ops(_Tid, [], _WaitFor, Changed, Acc, DumperMode) ->
1645
{Changed, Acc, DumperMode}.
1647
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1648
%% Prepare for commit
1649
%% returns true if Op should be included, i.e. unmodified
1650
%% {true, Operation} if NewRecs should be included, i.e. modified
1651
%% false if Op should NOT be included, i.e. modified
1653
prepare_op(_Tid, {op, rec, unknown, Rec}, _WaitFor) ->
1654
{{Tab, Key}, Items, _Op} = Rec,
1655
case val({Tab, storage_type}) of
1659
mnesia_tm:prepare_snmp(Tab, Key, Items), % May exit
1660
{true, [{op, rec, Storage, Rec}], optional}
1663
prepare_op(_Tid, {op, announce_im_running, _Node, SchemaDef, Running, RemoteRunning}, _WaitFor) ->
1664
SchemaCs = list2cs(SchemaDef),
1665
case lists:member(node(), Running) of
1667
announce_im_running(RemoteRunning -- Running, SchemaCs);
1669
announce_im_running(Running -- RemoteRunning, SchemaCs)
1673
prepare_op(_Tid, {op, sync_trans}, {part, CoordPid}) ->
1674
CoordPid ! {sync_trans, self()},
1676
{sync_trans, CoordPid} ->
1679
mnesia_lib:verbose("sync_op terminated due to ~p~n", [Else]),
1683
prepare_op(_Tid, {op, sync_trans}, {coord, Nodes}) ->
1684
case receive_sync(Nodes, []) of
1686
mnesia_lib:verbose("sync_op terminated due to ~p~n", [Reason]),
1687
mnesia:abort(Reason);
1689
[Pid ! {sync_trans, self()} || Pid <- Pids],
1692
prepare_op(Tid, {op, create_table, TabDef}, _WaitFor) ->
1693
Cs = list2cs(TabDef),
1694
Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
1695
UseDir = mnesia_monitor:use_dir(),
1696
Tab = Cs#cstruct.name,
1698
disc_copies when UseDir == false ->
1699
UseDirReason = {bad_type, Tab, Storage, node()},
1700
mnesia:abort(UseDirReason);
1701
disc_only_copies when UseDir == false ->
1702
UseDirReason = {bad_type, Tab, Storage, node()},
1703
mnesia:abort(UseDirReason);
1705
create_ram_table(Tab, Cs#cstruct.type),
1706
insert_cstruct(Tid, Cs, false),
1709
create_ram_table(Tab, Cs#cstruct.type),
1710
create_disc_table(Tab),
1711
insert_cstruct(Tid, Cs, false),
1714
create_disc_only_table(Tab,Cs#cstruct.type),
1715
insert_cstruct(Tid, Cs, false),
1717
unknown -> %% No replica on this node
1718
insert_cstruct(Tid, Cs, false),
1722
prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}, _WaitFor) ->
1723
Cs = list2cs(TabDef),
1724
Tab = Cs#cstruct.name,
1728
{true, optional}; % Nothing to prepare
1730
case mnesia_lib:val({schema, storage_type}) of
1731
ram_copies when Storage /= ram_copies ->
1732
Error = {combine_error, Tab, "has no disc", Node},
1733
mnesia:abort(Error);
1737
%% Tables are created by mnesia_loader get_network code
1738
insert_cstruct(Tid, Cs, true),
1739
case mnesia_controller:get_network_copy(Tab, Cs) of
1742
{not_loaded, ErrReason} ->
1743
Reason = {system_limit, Tab, {Node, ErrReason}},
1744
mnesia:abort(Reason)
1747
%% Verify that ram table not has been dumped to disc
1749
Storage /= ram_copies ->
1750
case mnesia_lib:schema_cs_to_storage_type(node(), Cs) of
1752
Dat = mnesia_lib:tab2dcd(Tab),
1753
case mnesia_lib:exists(Dat) of
1755
mnesia:abort({combine_error, Tab, Storage,
1756
"Table dumped to disc", node()});
1766
insert_cstruct(Tid, Cs, true),
1770
prepare_op(Tid, {op, del_table_copy, _Storage, Node, TabDef}, _WaitFor) ->
1771
Cs = list2cs(TabDef),
1772
Tab = Cs#cstruct.name,
1775
%% Schema table lock is always required to run a schema op.
1776
%% No need to look it.
1777
node(Tid#tid.pid) == node(), Tab /= schema ->
1778
Pid = spawn_link(?MODULE, lock_del_table, [Tab, Node, Cs, self()]),
1782
{Pid, FailReason} ->
1783
mnesia:abort(FailReason);
1784
{'EXIT', Pid, Reason} ->
1785
mnesia:abort(Reason)
1791
prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}, _WaitFor)
1793
Cs = list2cs(TabDef),
1794
Tab = Cs#cstruct.name,
1796
NotActive = mnesia_lib:not_active_here(Tab),
1799
NotActive == true ->
1800
mnesia:abort({not_active, Tab, node()});
1803
case {FromS, ToS} of
1804
{ram_copies, disc_copies} ->
1805
case mnesia:system_info(schema_location) of
1809
mnesia:abort({combine_error, Tab, node(),
1810
"schema_location must be opt_disc"})
1812
Dir = mnesia_lib:dir(),
1813
case opt_create_dir(true, Dir) of
1816
mnesia_log:purge_all_logs(),
1819
Ns = val({current, db_nodes}), %mnesia_lib:running_nodes(),
1820
F = fun(U) -> mnesia_recover:log_mnesia_up(U) end,
1821
lists:foreach(F, Ns),
1823
mnesia_dumper:raw_named_dump_table(Tab, dmp),
1824
mnesia_checkpoint:tm_change_table_copy_type(Tab, FromS, ToS);
1826
mnesia:abort(Reason)
1828
{disc_copies, ram_copies} ->
1829
Ltabs = val({schema, local_tables}) -- [schema],
1830
Dtabs = [L || L <- Ltabs,
1831
val({L, storage_type}) /= ram_copies],
1832
verify([], Dtabs, {"Disc resident tables", Dtabs, N});
1834
mnesia:abort({combine_error, Tab, ToS})
1837
FromS == ram_copies ->
1838
case mnesia_monitor:use_dir() of
1840
Dat = mnesia_lib:tab2dcd(Tab),
1841
case mnesia_lib:exists(Dat) of
1843
mnesia:abort({combine_error, Tab, node(),
1844
"Table dump exists"});
1848
mnesia_log:ets2dcd(Tab, dmp);
1850
mnesia_dumper:raw_named_dump_table(Tab, dmp)
1852
mnesia_checkpoint:tm_change_table_copy_type(Tab, FromS, ToS)
1855
mnesia:abort({has_no_disc, node()})
1858
FromS == disc_copies, ToS == disc_only_copies ->
1859
mnesia_dumper:raw_named_dump_table(Tab, dmp);
1860
FromS == disc_only_copies ->
1861
Type = Cs#cstruct.type,
1862
create_ram_table(Tab, Type),
1863
Datname = mnesia_lib:tab2dat(Tab),
1864
Repair = mnesia_monitor:get_env(auto_repair),
1865
case mnesia_lib:dets_to_ets(Tab, Tab, Datname, Type, Repair, no) of
1868
Err = "Failed to copy disc data to ram",
1869
mnesia:abort({system_limit, Tab, {Err,Reason}})
1876
prepare_op(_Tid, {op, change_table_copy_type, N, _FromS, _ToS, _TabDef}, _WaitFor)
1880
prepare_op(_Tid, {op, delete_table, _TabDef}, _WaitFor) ->
1883
prepare_op(_Tid, {op, dump_table, unknown, TabDef}, _WaitFor) ->
1884
Cs = list2cs(TabDef),
1885
Tab = Cs#cstruct.name,
1886
case lists:member(node(), Cs#cstruct.ram_copies) of
1888
case mnesia_monitor:use_dir() of
1890
mnesia_log:ets2dcd(Tab, dmp),
1891
Size = mnesia:table_info(Tab, size),
1892
{true, [{op, dump_table, Size, TabDef}], optional};
1894
mnesia:abort({has_no_disc, node()})
1900
prepare_op(_Tid, {op, add_snmp, Ustruct, TabDef}, _WaitFor) ->
1901
Cs = list2cs(TabDef),
1902
case mnesia_lib:cs_to_storage_type(node(), Cs) of
1906
Tab = Cs#cstruct.name,
1907
Stab = mnesia_snmp_hook:create_table(Ustruct, Tab, Storage),
1908
mnesia_lib:set({Tab, {index, snmp}}, Stab),
1912
prepare_op(_Tid, {op, transform, ignore, _TabDef}, _WaitFor) ->
1913
{true, mandatory}; %% Apply schema changes only.
1914
prepare_op(_Tid, {op, transform, Fun, TabDef}, _WaitFor) ->
1915
Cs = list2cs(TabDef),
1916
case mnesia_lib:cs_to_storage_type(node(), Cs) of
1920
Tab = Cs#cstruct.name,
1921
RecName = Cs#cstruct.record_name,
1922
Type = Cs#cstruct.type,
1923
NewArity = length(Cs#cstruct.attributes) + 1,
1924
mnesia_lib:db_fixtable(Storage, Tab, true),
1925
Key = mnesia_lib:db_first(Tab),
1926
Op = {op, transform, Fun, TabDef},
1927
case catch transform_objs(Fun, Tab, RecName,
1928
Key, NewArity, Storage, Type, [Op]) of
1930
mnesia_lib:db_fixtable(Storage, Tab, false),
1931
exit({"Bad transform function", Tab, Fun, node(), Reason});
1933
mnesia_lib:db_fixtable(Storage, Tab, false),
1934
{true, Objs, mandatory}
1938
prepare_op(_Tid, _Op, _WaitFor) ->
1942
create_ram_table(Tab, Type) ->
1943
Args = [{keypos, 2}, public, named_table, Type],
1944
case mnesia_monitor:unsafe_mktab(Tab, Args) of
1948
Err = "Failed to create ets table",
1949
mnesia:abort({system_limit, Tab, {Err,Reason}})
1951
create_disc_table(Tab) ->
1952
File = mnesia_lib:tab2dcd(Tab),
1954
FArg = [{file, File}, {name, {mnesia,create}},
1955
{repair, false}, {mode, read_write}],
1956
case mnesia_monitor:open_log(FArg) of
1958
mnesia_monitor:unsafe_close_log(Log),
1961
Err = "Failed to create disc table",
1962
mnesia:abort({system_limit, Tab, {Err,Reason}})
1964
create_disc_only_table(Tab,Type) ->
1965
File = mnesia_lib:tab2dat(Tab),
1967
Args = [{file, mnesia_lib:tab2dat(Tab)},
1968
{type, mnesia_lib:disk_type(Tab, Type)},
1970
{repair, mnesia_monitor:get_env(auto_repair)}],
1971
case mnesia_monitor:unsafe_open_dets(Tab, Args) of
1975
Err = "Failed to create disc table",
1976
mnesia:abort({system_limit, Tab, {Err,Reason}})
1980
receive_sync([], Pids) ->
1982
receive_sync(Nodes, Pids) ->
1984
{sync_trans, Pid} ->
1986
receive_sync(lists:delete(Node, Nodes), [Pid | Pids]);
1991
lock_del_table(Tab, Node, Cs, Father) ->
1992
Ns = val({schema, active_replicas}),
1994
mnesia:write_lock_table(Tab),
1995
{Res, []} = rpc:multicall(Ns, ?MODULE, set_where_to_read, [Tab, Node, Cs]),
1998
({badrpc, {'EXIT', {undef, _}}}) ->
1999
%% This will be the case we talks with elder nodes
2000
%% than 3.8.2, they will set where_to_read without
2006
[] = lists:filter(Filter, Res),
2009
case mnesia:transaction(Lock) of
2011
Father ! {self(), updated};
2013
Father ! {self(), R}
2018
set_where_to_read(Tab, Node, Cs) ->
2019
case mnesia_lib:val({Tab, where_to_read}) of
2021
case Cs#cstruct.local_content of
2025
mnesia_lib:set_remote_where_to_read(Tab, [Node]),
2032
%% Build up the list in reverse order.
2033
transform_objs(_Fun, _Tab, _RT, '$end_of_table', _NewArity, _Storage, _Type, Acc) ->
2035
transform_objs(Fun, Tab, RecName, Key, A, Storage, Type, Acc) ->
2036
Objs = mnesia_lib:db_get(Tab, Key),
2037
NextKey = mnesia_lib:db_next_key(Tab, Key),
2039
NewObjs = {Ws, Ds} = transform_obj(Tab, RecName, Key, Fun, Objs, A, Type, [], []),
2041
NewObjs == {[], []} ->
2042
transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type, Acc);
2044
transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
2045
[{op, rec, Storage, {Oid, Ws, write}},
2046
{op, rec, Storage, {Oid, [Oid], delete}} | Acc]);
2048
%% Type is set or ordered_set, no need to delete the record first
2049
transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
2050
[{op, rec, Storage, {Oid, Ws, write}} | Acc]);
2052
transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
2053
[{op, rec, Storage, {Oid, Ds, write}} | Acc]);
2055
transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
2056
[{op, rec, Storage, {Oid, Ws, write}},
2057
{op, rec, Storage, {Oid, Ds, delete}} | Acc])
2060
transform_obj(Tab, RecName, Key, Fun, [Obj|Rest], NewArity, Type, Ws, Ds) ->
2063
size(NewObj) /= NewArity ->
2064
exit({"Bad arity", Obj, NewObj});
2066
transform_obj(Tab, RecName, Key, Fun, Rest, NewArity, Type, Ws, Ds);
2067
RecName == element(1, NewObj), Key == element(2, NewObj) ->
2068
transform_obj(Tab, RecName, Key, Fun, Rest, NewArity,
2069
Type, [NewObj | Ws], Ds);
2072
bag -> %% Just don't write that object
2073
transform_obj(Tab, RecName, Key, Fun, Rest,
2074
NewArity, Type, Ws, Ds);
2076
transform_obj(Tab, RecName, Key, Fun, Rest, NewArity,
2077
Type, Ws, [NewObj | Ds])
2080
exit({"Bad key or Record Name", Obj, NewObj})
2082
transform_obj(_Tab, _RecName, _Key, _Fun, [], _NewArity, _Type, Ws, Ds) ->
2083
{lists:reverse(Ws), lists:reverse(Ds)}.
2085
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2086
%% Undo prepare of commit
2087
undo_prepare_commit(Tid, Commit) ->
2088
case Commit#commit.schema_ops of
2092
%% Catch to allow failure mnesia_controller may not be started
2093
catch mnesia_controller:release_schema_commit_lock(),
2094
undo_prepare_ops(Tid, Ops)
2098
%% Undo in reverse order
2099
undo_prepare_ops(Tid, [Op | Ops]) ->
2100
case element(1, Op) of
2101
TheOp when TheOp /= op, TheOp /= restore_op ->
2102
undo_prepare_ops(Tid, Ops);
2104
undo_prepare_ops(Tid, Ops),
2105
undo_prepare_op(Tid, Op)
2107
undo_prepare_ops(_Tid, []) ->
2110
undo_prepare_op(_Tid, {op, announce_im_running, _, _, Running, RemoteRunning}) ->
2111
case lists:member(node(), Running) of
2113
unannounce_im_running(RemoteRunning -- Running);
2115
unannounce_im_running(Running -- RemoteRunning)
2118
undo_prepare_op(_Tid, {op, sync_trans}) ->
2121
undo_prepare_op(Tid, {op, create_table, TabDef}) ->
2122
Cs = list2cs(TabDef),
2123
Tab = Cs#cstruct.name,
2124
mnesia_lib:unset({Tab, create_table}),
2125
delete_cstruct(Tid, Cs),
2126
case mnesia_lib:cs_to_storage_type(node(), Cs) of
2130
ram_delete_table(Tab, ram_copies);
2132
ram_delete_table(Tab, disc_copies),
2133
DcdFile = mnesia_lib:tab2dcd(Tab),
2134
%% disc_delete_table(Tab, Storage),
2135
file:delete(DcdFile);
2137
mnesia_monitor:unsafe_close_dets(Tab),
2138
Dat = mnesia_lib:tab2dat(Tab),
2139
%% disc_delete_table(Tab, Storage),
2143
undo_prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}) ->
2144
Cs = list2cs(TabDef),
2145
Tab = Cs#cstruct.name,
2148
true; % Nothing to prepare
2150
mnesia_checkpoint:tm_del_copy(Tab, Node),
2151
mnesia_controller:unannounce_add_table_copy(Tab, Node),
2153
Storage == disc_only_copies; Tab == schema ->
2154
mnesia_monitor:close_dets(Tab),
2155
file:delete(mnesia_lib:tab2dat(Tab));
2157
file:delete(mnesia_lib:tab2dcd(Tab))
2159
ram_delete_table(Tab, Storage),
2160
Cs2 = new_cs(Cs, Node, Storage, del),
2161
insert_cstruct(Tid, Cs2, true); % Don't care about the version
2163
mnesia_controller:unannounce_add_table_copy(Tab, Node),
2164
Cs2 = new_cs(Cs, Node, Storage, del),
2165
insert_cstruct(Tid, Cs2, true) % Don't care about the version
2168
undo_prepare_op(_Tid, {op, del_table_copy, _, Node, TabDef})
2169
when Node == node() ->
2170
Cs = list2cs(TabDef),
2171
Tab = Cs#cstruct.name,
2172
mnesia_lib:set({Tab, where_to_read}, Node);
2175
undo_prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef})
2177
Cs = list2cs(TabDef),
2178
Tab = Cs#cstruct.name,
2179
mnesia_checkpoint:tm_change_table_copy_type(Tab, ToS, FromS),
2180
Dmp = mnesia_lib:tab2dmp(Tab),
2182
case {FromS, ToS} of
2183
{ram_copies, disc_copies} when Tab == schema ->
2185
mnesia_log:purge_some_logs(),
2186
set(use_dir, false);
2187
{ram_copies, disc_copies} ->
2189
{ram_copies, disc_only_copies} ->
2191
{disc_only_copies, _} ->
2192
ram_delete_table(Tab, ram_copies);
2197
undo_prepare_op(_Tid, {op, dump_table, _Size, TabDef}) ->
2198
Cs = list2cs(TabDef),
2199
case lists:member(node(), Cs#cstruct.ram_copies) of
2201
Tab = Cs#cstruct.name,
2202
Dmp = mnesia_lib:tab2dmp(Tab),
2208
undo_prepare_op(_Tid, {op, add_snmp, _Ustruct, TabDef}) ->
2209
Cs = list2cs(TabDef),
2210
case mnesia_lib:cs_to_storage_type(node(), Cs) of
2214
Tab = Cs#cstruct.name,
2215
case ?catch_val({Tab, {index, snmp}}) of
2219
mnesia_snmp_hook:delete_table(Tab, Stab),
2220
mnesia_lib:unset({Tab, {index, snmp}})
2224
undo_prepare_op(_Tid, _Op) ->
2227
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2229
ram_delete_table(Tab, Storage) ->
2236
%% delete possible index files and data .....
2237
%% Got to catch this since if no info has been set in the
2238
%% mnesia_gvar it will crash
2239
catch mnesia_index:del_transient(Tab, Storage),
2240
case ?catch_val({Tab, {index, snmp}}) of
2244
catch mnesia_snmp_hook:delete_table(Tab, Etab)
2246
catch ?ets_delete_table(Tab)
2249
purge_dir(Dir, KeepFiles) ->
2250
Suffixes = known_suffixes(),
2251
purge_dir(Dir, KeepFiles, Suffixes).
2253
purge_dir(Dir, KeepFiles, Suffixes) ->
2254
case dir_exists(Dir) of
2256
{ok, AllFiles} = file:list_dir(Dir),
2257
purge_known_files(AllFiles, KeepFiles, Dir, Suffixes);
2262
purge_tmp_files() ->
2263
case mnesia_monitor:use_dir() of
2265
Dir = mnesia_lib:dir(),
2267
Exists = mnesia_lib:exists(mnesia_lib:tab2dat(schema)),
2270
Suffixes = tmp_suffixes(),
2271
purge_dir(Dir, KeepFiles, Suffixes);
2273
%% Interrupted change of storage type
2275
Suffixes = known_suffixes(),
2276
purge_dir(Dir, KeepFiles, Suffixes),
2277
mnesia_lib:set(use_dir, false)
2284
purge_known_files([File | Tail], KeepFiles, Dir, Suffixes) ->
2285
case lists:member(File, KeepFiles) of
2289
case has_known_suffix(File, Suffixes, false) of
2293
AbsFile = filename:join([Dir, File]),
2294
file:delete(AbsFile)
2297
purge_known_files(Tail, KeepFiles, Dir, Suffixes);
2298
purge_known_files([], _KeepFiles, _Dir, _Suffixes) ->
2301
has_known_suffix(_File, _Suffixes, true) ->
2303
has_known_suffix(File, [Suffix | Tail], false) ->
2304
has_known_suffix(File, Tail, lists:suffix(Suffix, File));
2305
has_known_suffix(_File, [], Bool) ->
2308
known_suffixes() -> real_suffixes() ++ tmp_suffixes().
2310
real_suffixes() -> [".DAT", ".LOG", ".BUP", ".DCL", ".DCD"].
2312
tmp_suffixes() -> [".TMP", ".BUPTMP", ".RET", ".DMP"].
2315
Tabs = lists:sort(val({schema, tables})),
2316
lists:foreach(fun(T) -> info(T) end, Tabs),
2320
Props = get_table_properties(Tab),
2321
io:format("-- Properties for ~w table --- ~n",[Tab]),
2323
info2(Tab, [{cstruct, _V} | Tail]) -> % Ignore cstruct
2325
info2(Tab, [{frag_hash, _V} | Tail]) -> % Ignore frag_hash
2327
info2(Tab, [{P, V} | Tail]) ->
2328
io:format("~-20w -> ~p~n",[P,V]),
2331
io:format("~n", []).
2333
get_table_properties(Tab) ->
2334
case catch mnesia_lib:db_match_object(ram_copies,
2335
mnesia_gvar, {{Tab, '_'}, '_'}) of
2337
mnesia:abort({no_exists, Tab, all});
2339
case [{Item, Val} || {{_Tab, Item}, Val} <- RawGvar] of
2343
Size = {size, mnesia:table_info(Tab, size)},
2344
Memory = {memory, mnesia:table_info(Tab, memory)},
2345
Master = {master_nodes, mnesia:table_info(Tab, master_nodes)},
2346
lists:sort([Size, Memory, Master | Gvar])
2350
%%%%%%%%%%% RESTORE %%%%%%%%%%%
2352
-record(r, {iter = schema,
2355
default_op = clear_tables,
2358
insert_op = error_fun,
2363
restore(Opaque, [], mnesia_monitor:get_env(backup_module)).
2364
restore(Opaque, Args) when list(Args) ->
2365
restore(Opaque, Args, mnesia_monitor:get_env(backup_module));
2366
restore(_Opaque, BadArg) ->
2367
{aborted, {badarg, BadArg}}.
2368
restore(Opaque, Args, Module) when list(Args), atom(Module) ->
2369
InitR = #r{opaque = Opaque, module = Module},
2370
case catch lists:foldl(fun check_restore_arg/2, InitR, Args) of
2371
R when record(R, r) ->
2372
case mnesia_bup:read_schema(Module, Opaque) of
2376
schema_transaction(fun() -> do_restore(R, BupSchema) end)
2381
restore(_Opaque, Args, Module) ->
2382
{aborted, {badarg, Args, Module}}.
2384
check_restore_arg({module, Mod}, R) when atom(Mod) ->
2387
check_restore_arg({clear_tables, List}, R) when list(List) ->
2388
case lists:member(schema, List) of
2390
TableList = [{Tab, clear_tables} || Tab <- List],
2391
R#r{table_options = R#r.table_options ++ TableList};
2393
exit({badarg, {clear_tables, schema}})
2395
check_restore_arg({recreate_tables, List}, R) when list(List) ->
2396
case lists:member(schema, List) of
2398
TableList = [{Tab, recreate_tables} || Tab <- List],
2399
R#r{table_options = R#r.table_options ++ TableList};
2401
exit({badarg, {recreate_tables, schema}})
2403
check_restore_arg({keep_tables, List}, R) when list(List) ->
2404
TableList = [{Tab, keep_tables} || Tab <- List],
2405
R#r{table_options = R#r.table_options ++ TableList};
2406
check_restore_arg({skip_tables, List}, R) when list(List) ->
2407
TableList = [{Tab, skip_tables} || Tab <- List],
2408
R#r{table_options = R#r.table_options ++ TableList};
2409
check_restore_arg({default_op, Op}, R) ->
2412
recreate_tables -> ok;
2416
exit({badarg, {bad_default_op, Else}})
2418
R#r{default_op = Op};
2420
check_restore_arg(BadArg,_) ->
2421
exit({badarg, BadArg}).
2423
do_restore(R, BupSchema) ->
2424
TidTs = get_tid_ts_and_lock(schema, write),
2425
R2 = restore_schema(BupSchema, R),
2426
insert_schema_ops(TidTs, [{restore_op, R2}]),
2427
[element(1, TabStruct) || TabStruct <- R2#r.tables].
2429
arrange_restore(R, Fun, Recs) ->
2430
R2 = R#r{insert_op = Fun, recs = Recs},
2431
case mnesia_bup:iterate(R#r.module, fun restore_items/4, R#r.opaque, R2) of
2432
{ok, R3} -> R3#r.recs;
2433
{error, Reason} -> mnesia:abort(Reason);
2434
Reason -> mnesia:abort(Reason)
2437
restore_items([Rec | Recs], Header, Schema, R) ->
2438
Tab = element(1, Rec),
2439
case lists:keysearch(Tab, 1, R#r.tables) of
2440
{value, {Tab, Where, Snmp, RecName}} ->
2442
restore_tab_items([Rec | Recs], Tab, RecName, Where, Snmp,
2443
R#r.recs, R#r.insert_op),
2444
restore_items(Rest, Header, Schema, R#r{recs = NRecs});
2446
Rest = skip_tab_items(Recs, Tab),
2447
restore_items(Rest, Header, Schema, R)
2450
restore_items([], _Header, _Schema, R) ->
2453
restore_func(Tab, R) ->
2454
case lists:keysearch(Tab, 1, R#r.table_options) of
2455
{value, {Tab, OP}} ->
2461
where_to_commit(Tab, CsList) ->
2462
Ram = [{N, ram_copies} || N <- pick(Tab, ram_copies, CsList, [])],
2463
Disc = [{N, disc_copies} || N <- pick(Tab, disc_copies, CsList, [])],
2464
DiscO = [{N, disc_only_copies} || N <- pick(Tab, disc_only_copies, CsList, [])],
2465
Ram ++ Disc ++ DiscO.
2467
%% Changes of the Meta info of schema itself is not allowed
2468
restore_schema([{schema, schema, _List} | Schema], R) ->
2469
restore_schema(Schema, R);
2470
restore_schema([{schema, Tab, List} | Schema], R) ->
2471
case restore_func(Tab, R) of
2473
do_clear_table(Tab),
2474
Where = val({Tab, where_to_commit}),
2475
Snmp = val({Tab, snmp}),
2476
RecName = val({Tab, record_name}),
2477
R2 = R#r{tables = [{Tab, Where, Snmp, RecName} | R#r.tables]},
2478
restore_schema(Schema, R2);
2480
TidTs = get_tid_ts_and_lock(Tab, write),
2481
NC = {cookie, ?unique_cookie},
2482
List2 = lists:keyreplace(cookie, 1, List, NC),
2483
Where = where_to_commit(Tab, List2),
2484
Snmp = pick(Tab, snmp, List2, []),
2485
RecName = pick(Tab, record_name, List2, Tab),
2486
% case ?catch_val({Tab, cstruct}) of
2489
% OldCs when record(OldCs, cstruct) ->
2490
% do_delete_table(Tab)
2492
% unsafe_do_create_table(list2cs(List2)),
2493
insert_schema_ops(TidTs, [{op, restore_recreate, List2}]),
2494
R2 = R#r{tables = [{Tab, Where, Snmp, RecName} | R#r.tables]},
2495
restore_schema(Schema, R2);
2497
get_tid_ts_and_lock(Tab, write),
2498
Where = val({Tab, where_to_commit}),
2499
Snmp = val({Tab, snmp}),
2500
RecName = val({Tab, record_name}),
2501
R2 = R#r{tables = [{Tab, Where, Snmp, RecName} | R#r.tables]},
2502
restore_schema(Schema, R2);
2504
restore_schema(Schema, R)
2507
restore_schema([{schema, Tab} | Schema], R) ->
2508
do_delete_table(Tab),
2509
Tabs = lists:delete(Tab,R#r.tables),
2510
restore_schema(Schema, R#r{tables = Tabs});
2511
restore_schema([], R) ->
2514
restore_tab_items([Rec | Rest], Tab, RecName, Where, Snmp, Recs, Op)
2515
when element(1, Rec) == Tab ->
2516
NewRecs = Op(Rec, Recs, RecName, Where, Snmp),
2517
restore_tab_items(Rest, Tab, RecName, Where, Snmp, NewRecs, Op);
2519
restore_tab_items(Rest, _Tab, _RecName, _Where, _Snmp, Recs, _Op) ->
2522
skip_tab_items([Rec| Rest], Tab)
2523
when element(1, Rec) == Tab ->
2524
skip_tab_items(Rest, Tab);
2525
skip_tab_items(Recs, _) ->
2528
%%%%%%%%% Dump tables %%%%%%%%%%%%%
2529
dump_tables(Tabs) when list(Tabs) ->
2530
schema_transaction(fun() -> do_dump_tables(Tabs) end);
2531
dump_tables(Tabs) ->
2532
{aborted, {bad_type, Tabs}}.
2534
do_dump_tables(Tabs) ->
2535
TidTs = get_tid_ts_and_lock(schema, write),
2536
insert_schema_ops(TidTs, make_dump_tables(Tabs)).
2538
make_dump_tables([schema | _Tabs]) ->
2539
mnesia:abort({bad_type, schema});
2540
make_dump_tables([Tab | Tabs]) ->
2541
get_tid_ts_and_lock(Tab, read),
2542
TabDef = get_create_list(Tab),
2543
DiscResident = val({Tab, disc_copies}) ++ val({Tab, disc_only_copies}),
2544
verify([], DiscResident,
2545
{"Only allowed on ram_copies", Tab, DiscResident}),
2546
[{op, dump_table, unknown, TabDef} | make_dump_tables(Tabs)];
2547
make_dump_tables([]) ->
2550
%% Merge the local schema with the schema on other nodes
2552
schema_transaction(fun() -> do_merge_schema() end).
2554
do_merge_schema() ->
2555
{_Mod, Tid, Ts} = get_tid_ts_and_lock(schema, write),
2556
Connected = val(recover_nodes),
2557
Running = val({current, db_nodes}),
2558
Store = Ts#tidstore.store,
2559
case Connected -- Running of
2561
%% Time for a schema merging party!
2562
mnesia_locker:wlock_no_exist(Tid, Store, schema, [Node]),
2564
case rpc:call(Node, mnesia_controller, get_cstructs, []) of
2565
{cstructs, Cstructs, RemoteRunning1} ->
2566
LockedAlready = Running ++ [Node],
2567
{New, Old} = mnesia_recover:connect_nodes(RemoteRunning1),
2568
RemoteRunning = mnesia_lib:intersect(New ++ Old, RemoteRunning1),
2570
RemoteRunning /= RemoteRunning1 ->
2571
mnesia_lib:error("Mnesia on ~p could not connect to node(s) ~p~n",
2572
[node(), RemoteRunning1 -- RemoteRunning]);
2575
NeedsLock = RemoteRunning -- LockedAlready,
2576
mnesia_locker:wlock_no_exist(Tid, Store, schema, NeedsLock),
2579
lists:keysearch(schema, #cstruct.name, Cstructs),
2581
%% Announce that Node is running
2582
A = [{op, announce_im_running, node(),
2583
cs2list(SchemaCs), Running, RemoteRunning}],
2584
do_insert_schema_ops(Store, A),
2586
%% Introduce remote tables to local node
2587
do_insert_schema_ops(Store, make_merge_schema(Node, Cstructs)),
2589
%% Introduce local tables to remote nodes
2590
Tabs = val({schema, tables}),
2591
Ops = [{op, merge_schema, get_create_list(T)}
2593
not lists:keymember(T, #cstruct.name, Cstructs)],
2594
do_insert_schema_ops(Store, Ops),
2596
%% Ensure that the txn will be committed on all nodes
2597
announce_im_running(RemoteRunning, SchemaCs),
2598
{merged, Running, RemoteRunning};
2600
{"Cannot get cstructs", Node, Reason};
2602
{"Cannot get cstructs", Node, {badrpc, Reason}}
2605
%% No more nodes to merge schema with
2609
make_merge_schema(Node, [Cs | Cstructs]) ->
2610
Ops = do_make_merge_schema(Node, Cs),
2611
Ops ++ make_merge_schema(Node, Cstructs);
2612
make_merge_schema(_Node, []) ->
2615
%% Merge definitions of schema table
2616
do_make_merge_schema(Node, RemoteCs)
2617
when RemoteCs#cstruct.name == schema ->
2618
Cs = val({schema, cstruct}),
2619
Masters = mnesia_recover:get_master_nodes(schema),
2620
HasRemoteMaster = lists:member(Node, Masters),
2621
HasLocalMaster = lists:member(node(), Masters),
2622
Force = HasLocalMaster or HasRemoteMaster,
2623
%% What is the storage types opinions?
2624
StCsLocal = mnesia_lib:cs_to_storage_type(node(), Cs),
2625
StRcsLocal = mnesia_lib:cs_to_storage_type(node(), RemoteCs),
2626
StCsRemote = mnesia_lib:cs_to_storage_type(Node, Cs),
2627
StRcsRemote = mnesia_lib:cs_to_storage_type(Node, RemoteCs),
2630
Cs#cstruct.cookie == RemoteCs#cstruct.cookie,
2631
Cs#cstruct.version == RemoteCs#cstruct.version ->
2632
%% Great, we have the same cookie and version
2633
%% and do not need to merge cstructs
2636
Cs#cstruct.cookie /= RemoteCs#cstruct.cookie,
2637
Cs#cstruct.disc_copies /= [],
2638
RemoteCs#cstruct.disc_copies /= [] ->
2639
%% Both cstructs involves disc nodes
2640
%% and we cannot merge them
2642
HasLocalMaster == true,
2643
HasRemoteMaster == false ->
2644
%% Choose local cstruct,
2645
%% since it's the master
2646
[{op, merge_schema, cs2list(Cs)}];
2648
HasRemoteMaster == true,
2649
HasLocalMaster == false ->
2650
%% Choose remote cstruct,
2651
%% since it's the master
2652
[{op, merge_schema, cs2list(RemoteCs)}];
2655
Str = io_lib:format("Incompatible schema cookies. "
2656
"Please, restart from old backup."
2657
"~w = ~w, ~w = ~w~n",
2658
[Node, cs2list(RemoteCs), node(), cs2list(Cs)]),
2662
StCsLocal /= StRcsLocal, StRcsLocal /= unknown ->
2663
Str = io_lib:format("Incompatible schema storage types. "
2664
"on ~w storage ~w, on ~w storage ~w~n",
2665
[node(), StCsLocal, Node, StRcsLocal]),
2667
StCsRemote /= StRcsRemote, StCsRemote /= unknown ->
2668
Str = io_lib:format("Incompatible schema storage types. "
2669
"on ~w storage ~w, on ~w storage ~w~n",
2670
[node(), StCsRemote, Node, StRcsRemote]),
2673
Cs#cstruct.disc_copies /= [] ->
2674
%% Choose local cstruct,
2675
%% since it involves disc nodes
2676
MergedCs = merge_cstructs(Cs, RemoteCs, Force),
2677
[{op, merge_schema, cs2list(MergedCs)}];
2679
RemoteCs#cstruct.disc_copies /= [] ->
2680
%% Choose remote cstruct,
2681
%% since it involves disc nodes
2682
MergedCs = merge_cstructs(RemoteCs, Cs, Force),
2683
[{op, merge_schema, cs2list(MergedCs)}];
2686
%% Choose remote cstruct
2687
MergedCs = merge_cstructs(RemoteCs, Cs, Force),
2688
[{op, merge_schema, cs2list(MergedCs)}];
2691
%% Choose local cstruct
2692
MergedCs = merge_cstructs(Cs, RemoteCs, Force),
2693
[{op, merge_schema, cs2list(MergedCs)}]
2696
%% Merge definitions of normal table
2697
do_make_merge_schema(Node, RemoteCs) ->
2698
Tab = RemoteCs#cstruct.name,
2699
Masters = mnesia_recover:get_master_nodes(schema),
2700
HasRemoteMaster = lists:member(Node, Masters),
2701
HasLocalMaster = lists:member(node(), Masters),
2702
Force = HasLocalMaster or HasRemoteMaster,
2703
case ?catch_val({Tab, cstruct}) of
2705
%% A completely new table, created while Node was down
2706
[{op, merge_schema, cs2list(RemoteCs)}];
2707
Cs when Cs#cstruct.cookie == RemoteCs#cstruct.cookie ->
2709
Cs#cstruct.version == RemoteCs#cstruct.version ->
2710
%% We have exactly the same version of the
2714
Cs#cstruct.version > RemoteCs#cstruct.version ->
2715
%% Oops, we have different versions
2716
%% of the table def, lets merge them.
2717
%% The only changes that may have occurred
2718
%% is that new replicas may have been added.
2719
MergedCs = merge_cstructs(Cs, RemoteCs, Force),
2720
[{op, merge_schema, cs2list(MergedCs)}];
2722
Cs#cstruct.version < RemoteCs#cstruct.version ->
2723
%% Oops, we have different versions
2724
%% of the table def, lets merge them
2725
MergedCs = merge_cstructs(RemoteCs, Cs, Force),
2726
[{op, merge_schema, cs2list(MergedCs)}]
2729
%% Different cookies, not possible to merge
2731
HasLocalMaster == true,
2732
HasRemoteMaster == false ->
2733
%% Choose local cstruct,
2734
%% since it's the master
2735
[{op, merge_schema, cs2list(Cs)}];
2737
HasRemoteMaster == true,
2738
HasLocalMaster == false ->
2739
%% Choose remote cstruct,
2740
%% since it's the master
2741
[{op, merge_schema, cs2list(RemoteCs)}];
2744
Str = io_lib:format("Bad cookie in table definition"
2745
" ~w: ~w = ~w, ~w = ~w~n",
2746
[Tab, node(), Cs, Node, RemoteCs]),
2751
%% Change of table definitions (cstructs) requires all replicas
2752
%% of the table to be active. New replicas, db_nodes and tables
2753
%% may however be added even if some replica is inactive. These
2754
%% invariants must be enforced in order to allow merge of cstructs.
2756
%% Returns a new cstruct or issues a fatal error
2757
merge_cstructs(Cs, RemoteCs, Force) ->
2759
case catch do_merge_cstructs(Cs, RemoteCs, Force) of
2760
{'EXIT', {aborted, _Reason}} when Force == true ->
2764
MergedCs when record(MergedCs, cstruct) ->
2770
do_merge_cstructs(Cs, RemoteCs, Force) ->
2771
verify_cstruct(RemoteCs),
2772
Ns = mnesia_lib:uniq(mnesia_lib:cs_to_nodes(Cs) ++
2773
mnesia_lib:cs_to_nodes(RemoteCs)),
2774
{AnythingNew, MergedCs} =
2775
merge_storage_type(Ns, false, Cs, RemoteCs, Force),
2776
MergedCs2 = merge_versions(AnythingNew, MergedCs, RemoteCs, Force),
2777
verify_cstruct(MergedCs2),
2780
merge_storage_type([N | Ns], AnythingNew, Cs, RemoteCs, Force) ->
2781
Local = mnesia_lib:cs_to_storage_type(N, Cs),
2782
Remote = mnesia_lib:cs_to_storage_type(N, RemoteCs),
2783
case compare_storage_type(true, Local, Remote) of
2785
merge_storage_type(Ns, AnythingNew, Cs, RemoteCs, Force);
2787
Cs2 = change_storage_type(N, Storage, Cs),
2788
merge_storage_type(Ns, true, Cs2, RemoteCs, Force);
2789
incompatible when Force == true ->
2790
merge_storage_type(Ns, AnythingNew, Cs, RemoteCs, Force);
2792
Str = io_lib:format("Cannot merge storage type for node ~w "
2793
"in cstruct ~w with remote cstruct ~w (~w)~n",
2794
[N, Cs, RemoteCs, Other]),
2797
merge_storage_type([], AnythingNew, MergedCs, _RemoteCs, _Force) ->
2798
{AnythingNew, MergedCs}.
2800
compare_storage_type(_Retry, Any, Any) ->
2802
compare_storage_type(_Retry, unknown, Any) ->
2804
compare_storage_type(_Retry, ram_copies, disc_copies) ->
2805
{diff, disc_copies};
2806
compare_storage_type(_Retry, disc_copies, disc_only_copies) ->
2807
{diff, disc_only_copies};
2808
compare_storage_type(true, One, Another) ->
2809
compare_storage_type(false, Another, One);
2810
compare_storage_type(false, _One, _Another) ->
2813
change_storage_type(N, ram_copies, Cs) ->
2814
Nodes = [N | Cs#cstruct.ram_copies],
2815
Cs#cstruct{ram_copies = mnesia_lib:uniq(Nodes)};
2816
change_storage_type(N, disc_copies, Cs) ->
2817
Nodes = [N | Cs#cstruct.disc_copies],
2818
Cs#cstruct{disc_copies = mnesia_lib:uniq(Nodes)};
2819
change_storage_type(N, disc_only_copies, Cs) ->
2820
Nodes = [N | Cs#cstruct.disc_only_copies],
2821
Cs#cstruct{disc_only_copies = mnesia_lib:uniq(Nodes)}.
2823
%% BUGBUG: Verify match of frag info; equalit demanded for all but add_node
2825
merge_versions(AnythingNew, Cs, RemoteCs, Force) ->
2827
Cs#cstruct.name == schema ->
2829
Cs#cstruct.name /= schema,
2830
Cs#cstruct.cookie == RemoteCs#cstruct.cookie ->
2835
Str = io_lib:format("Bad cookies. Cannot merge definitions of "
2836
"table ~w. Local = ~w, Remote = ~w~n",
2837
[Cs#cstruct.name, Cs, RemoteCs]),
2841
Cs#cstruct.name == RemoteCs#cstruct.name,
2842
Cs#cstruct.type == RemoteCs#cstruct.type,
2843
Cs#cstruct.local_content == RemoteCs#cstruct.local_content,
2844
Cs#cstruct.attributes == RemoteCs#cstruct.attributes,
2845
Cs#cstruct.index == RemoteCs#cstruct.index,
2846
Cs#cstruct.snmp == RemoteCs#cstruct.snmp,
2847
Cs#cstruct.access_mode == RemoteCs#cstruct.access_mode,
2848
Cs#cstruct.load_order == RemoteCs#cstruct.load_order,
2849
Cs#cstruct.user_properties == RemoteCs#cstruct.user_properties ->
2850
do_merge_versions(AnythingNew, Cs, RemoteCs);
2852
do_merge_versions(AnythingNew, Cs, RemoteCs);
2854
Str1 = io_lib:format("Cannot merge definitions of "
2855
"table ~w. Local = ~w, Remote = ~w~n",
2856
[Cs#cstruct.name, Cs, RemoteCs]),
2860
do_merge_versions(AnythingNew, MergedCs, RemoteCs) ->
2861
{{Major1, Minor1}, _Detail1} = MergedCs#cstruct.version,
2862
{{Major2, Minor2}, _Detail2} = RemoteCs#cstruct.version,
2864
MergedCs#cstruct.version == RemoteCs#cstruct.version ->
2866
AnythingNew == false ->
2869
Minor = lists:max([Minor1, Minor2]),
2870
V = {{Major1, Minor}, dummy},
2871
incr_version(MergedCs#cstruct{version = V});
2873
Major = lists:max([Major1, Major2]),
2874
V = {{Major, 0}, dummy},
2875
incr_version(MergedCs#cstruct{version = V})
2878
announce_im_running([N | Ns], SchemaCs) ->
2879
{L1, L2} = mnesia_recover:connect_nodes([N]),
2880
case lists:member(N, L1) or lists:member(N, L2) of
2882
%% dbg_out("Adding ~p to {current db_nodes} ~n", [N]), %% qqqq
2883
mnesia_lib:add({current, db_nodes}, N),
2884
mnesia_controller:add_active_replica(schema, N, SchemaCs);
2888
announce_im_running(Ns, SchemaCs);
2889
announce_im_running([], _) ->
2892
unannounce_im_running([N | Ns]) ->
2893
mnesia_lib:del({current, db_nodes}, N),
2894
mnesia_controller:del_active_replica(schema, N),
2895
mnesia_recover:disconnect(N),
2896
unannounce_im_running(Ns);
2897
unannounce_im_running([]) ->