~ubuntu-branches/ubuntu/trusty/erlang/trusty

« back to all changes in this revision

Viewing changes to lib/dialyzer/test/r9c_tests_SUITE_data/src/mnesia/mnesia_schema.erl

  • Committer: Bazaar Package Importer
  • Author(s): Clint Byrum
  • Date: 2011-05-05 15:48:43 UTC
  • mfrom: (3.5.13 sid)
  • Revision ID: james.westby@ubuntu.com-20110505154843-0om6ekzg6m7ugj27
Tags: 1:14.b.2-dfsg-3ubuntu1
* Merge from debian unstable.  Remaining changes:
  - Drop libwxgtk2.8-dev build dependency. Wx isn't in main, and not
    supposed to.
  - Drop erlang-wx binary.
  - Drop erlang-wx dependency from -megaco, -common-test, and -reltool, they
    do not really need wx. Also drop it from -debugger; the GUI needs wx,
    but it apparently has CLI bits as well, and is also needed by -megaco,
    so let's keep the package for now.
  - debian/patches/series: Do what I meant, and enable build-options.patch
    instead.
* Additional changes:
  - Drop erlang-wx from -et
* Dropped Changes:
  - patches/pcre-crash.patch: CVE-2008-2371: outer level option with
    alternatives caused crash. (Applied Upstream)
  - fix for ssl certificate verification in newSSL: 
    ssl_cacertfile_fix.patch (Applied Upstream)
  - debian/patches/series: Enable native.patch again, to get stripped beam
    files and reduce the package size again. (build-options is what
    actually accomplished this)
  - Remove build-options.patch on advice from upstream and because it caused
    odd build failures.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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/.
 
6
%% 
 
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
 
10
%% under the License.
 
11
%% 
 
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.''
 
15
%% 
 
16
%%     $Id: mnesia_schema.erl,v 1.2 2010/03/04 13:54:20 maria Exp $
 
17
%%
 
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.
 
21
%%
 
22
%% We also have an init/1 function defined here, this func is
 
23
%% used by mnesia:start() to initialize the entire schema.
 
24
 
 
25
-module(mnesia_schema).
 
26
 
 
27
-export([
 
28
         add_snmp/2,
 
29
         add_table_copy/3,
 
30
         add_table_index/2,
 
31
         arrange_restore/3,
 
32
         attr_tab_to_pos/2,
 
33
         attr_to_pos/2,
 
34
         change_table_copy_type/3,
 
35
         change_table_access_mode/2,
 
36
         change_table_load_order/2,
 
37
         change_table_frag/2,
 
38
         clear_table/1,
 
39
         create_table/1,
 
40
         cs2list/1,
 
41
         del_snmp/1,
 
42
         del_table_copy/2,
 
43
         del_table_index/2,
 
44
         delete_cstruct/2,
 
45
         delete_schema/1,
 
46
         delete_schema2/0,
 
47
         delete_table/1,
 
48
         delete_table_property/2,
 
49
         dump_tables/1,
 
50
         ensure_no_schema/1,
 
51
         get_create_list/1,
 
52
         get_initial_schema/2,
 
53
         get_table_properties/1,
 
54
         info/0,
 
55
         info/1,
 
56
         init/1,
 
57
         insert_cstruct/3,
 
58
         is_remote_member/1,
 
59
         list2cs/1,
 
60
         lock_schema/0,
 
61
         lock_del_table/4, % Spawned
 
62
         merge_schema/0,
 
63
         move_table/3,
 
64
         opt_create_dir/2,
 
65
         prepare_commit/3,
 
66
         purge_dir/2,
 
67
         purge_tmp_files/0,
 
68
         ram_delete_table/2,
 
69
%         ram_delete_table/3,
 
70
         read_cstructs_from_disc/0,
 
71
         read_nodes/0,
 
72
         remote_read_schema/0,
 
73
         restore/1,
 
74
         restore/2,
 
75
         restore/3,
 
76
         schema_coordinator/3,
 
77
         set_where_to_read/3,
 
78
         transform_table/4,
 
79
         undo_prepare_commit/2,
 
80
         unlock_schema/0,
 
81
         version/0,
 
82
         write_table_property/2
 
83
        ]).
 
84
 
 
85
%% Exports for mnesia_frag
 
86
-export([
 
87
         get_tid_ts_and_lock/2,
 
88
         make_create_table/1,
 
89
         ensure_active/1,
 
90
         pick/4,
 
91
         verify/3,
 
92
         incr_version/1,
 
93
         check_keys/3,
 
94
         check_duplicates/2,
 
95
         make_delete_table/2
 
96
        ]).
 
97
 
 
98
%% Needed outside to be able to use/set table_properties
 
99
%% from user (not supported) 
 
100
-export([schema_transaction/1,
 
101
         insert_schema_ops/2,
 
102
         do_create_table/1,
 
103
         do_delete_table/1,
 
104
         do_delete_table_property/2,
 
105
         do_write_table_property/2]).
 
106
 
 
107
-include("mnesia.hrl").
 
108
-include_lib("kernel/include/file.hrl").
 
109
 
 
110
-import(mnesia_lib, [set/2, del/2, verbose/2, dbg_out/2]).
 
111
 
 
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
 
116
%% 
 
117
%% We have a meta table which looks like
 
118
%% {table, schema, 
 
119
%%    {type, set},
 
120
%%    {disc_copies, all},
 
121
%%    {arity, 2}
 
122
%%    {attributes, [key, val]}
 
123
%%
 
124
%% This means that we have a series of {schema, Name, Cs} tuples
 
125
%% in a table called schema !!
 
126
 
 
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()).
 
139
 
 
140
exit_on_error({error, Reason}) ->
 
141
    exit(Reason);
 
142
exit_on_error(GoodRes) ->
 
143
    GoodRes.
 
144
 
 
145
val(Var) ->
 
146
    case ?catch_val(Var) of
 
147
        {'EXIT', Reason} -> mnesia_lib:other_val(Var, Reason); 
 
148
        Value -> Value 
 
149
    end.
 
150
 
 
151
%% This function traverses all cstructs in the schema and
 
152
%% sets all values in mnesia_gvar accordingly for each table/cstruct
 
153
 
 
154
set_schema('$end_of_table') -> 
 
155
    [];
 
156
set_schema(Tab) ->
 
157
    do_set_schema(Tab),
 
158
    [Tab | set_schema(?ets_next(schema, Tab))].
 
159
 
 
160
get_create_list(Tab) ->
 
161
    ?ets_lookup_element(schema, Tab, 3).
 
162
 
 
163
do_set_schema(Tab) ->
 
164
    List = get_create_list(Tab),
 
165
    Cs = list2cs(List),
 
166
    do_set_schema(Tab, Cs).
 
167
 
 
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
 
199
        true ->
 
200
            mnesia_lib:add({schema, local_tables}, Tab);
 
201
        false when Tab == schema ->
 
202
            mnesia_lib:add({schema, local_tables}, Tab);
 
203
        false ->
 
204
            ignore
 
205
    end.
 
206
 
 
207
wild(RecName, Arity) ->
 
208
    Wp0 = list_to_tuple(lists:duplicate(Arity, '_')),
 
209
    setelement(1, Wp0, RecName).
 
210
 
 
211
%% Temporarily read the local schema and return a list
 
212
%% of all nodes mentioned in the schema.DAT file
 
213
read_nodes() ->
 
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
 
219
        ok ->
 
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};
 
224
                {error, Reason} ->
 
225
                    {error, Reason}
 
226
            end;
 
227
        {error, Reason} ->
 
228
            {error, Reason}
 
229
    end.
 
230
 
 
231
%% Returns Version from the tuple {Version,MasterNodes}
 
232
version() ->
 
233
    case read_schema(false, false) of
 
234
        {ok, Source, CreateList} when Source /= default ->
 
235
            Cs = list2cs(CreateList),
 
236
            {Version, _Details} = Cs#cstruct.version,
 
237
            Version;
 
238
        _ ->
 
239
            case dir_exists(mnesia_lib:dir()) of
 
240
                true -> {1,0};
 
241
                false -> {0,0}
 
242
            end
 
243
    end.
 
244
 
 
245
%% Calculate next table version from old cstruct
 
246
incr_version(Cs) ->
 
247
    {{Major, Minor}, _} = Cs#cstruct.version,
 
248
    Nodes = mnesia_lib:intersect(val({schema, disc_copies}),
 
249
                                 mnesia_lib:cs_to_nodes(Cs)), 
 
250
    V = 
 
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
 
254
        end,
 
255
    Cs#cstruct{version = {V, {node(), now()}}}.
 
256
 
 
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}),
 
265
 
 
266
    case KeepWhereabouts of
 
267
        true ->
 
268
            ignore;
 
269
        false when Active == [] ->
 
270
            clear_whereabouts(Tab);
 
271
        false ->
 
272
            %% Someone else has initiated table
 
273
            ignore
 
274
    end,
 
275
    set({Tab, cstruct}, Cs),
 
276
    ?ets_insert(schema, Val),
 
277
    do_set_schema(Tab, Cs),
 
278
    Val.
 
279
 
 
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).
 
291
 
 
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),
 
304
    Val.
 
305
 
 
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",
 
312
    if
 
313
        RunningNs == [] ->
 
314
            case rpc:multicall(Ns, ?MODULE, delete_schema2, []) of
 
315
                {Replies, []} ->
 
316
                    case [R || R <- Replies, R /= ok]  of
 
317
                        [] ->
 
318
                            ok;
 
319
                        BadReplies ->
 
320
                            verbose("~s: ~p~n", [Reason, BadReplies]),
 
321
                            {error, {"All nodes not running", BadReplies}}
 
322
                    end;
 
323
                {_Replies, BadNs} ->
 
324
                    verbose("~s: ~p~n", [Reason, BadNs]),
 
325
                    {error, {"All nodes not running", BadNs}}
 
326
            end;
 
327
        true ->
 
328
            verbose("~s: ~p~n", [Reason, RunningNs]),
 
329
            {error, {"Mnesia is not stopped everywhere", RunningNs}}
 
330
    end;
 
331
delete_schema(Ns) ->
 
332
    {error, {badarg, Ns}}.
 
333
 
 
334
delete_schema2() ->
 
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
 
340
        ok ->
 
341
            case mnesia_lib:is_running() of
 
342
                no ->
 
343
                    Dir = mnesia_lib:dir(),
 
344
                    purge_dir(Dir, []),
 
345
                    ok;
 
346
                _ ->
 
347
                    {error, {"Mnesia still running", node()}}
 
348
            end;
 
349
        {error, Reason} ->
 
350
            {error, Reason}
 
351
    end.
 
352
    
 
353
ensure_no_schema([H|T]) when atom(H) ->
 
354
    case rpc:call(H, ?MODULE, remote_read_schema, []) of
 
355
        {badrpc, Reason} ->
 
356
            {H, {"All nodes not running", H, Reason}};
 
357
        {ok,Source, _} when Source /= default ->
 
358
            {H, {already_exists, H}};
 
359
        _ ->
 
360
            ensure_no_schema(T)
 
361
    end;
 
362
ensure_no_schema([H|_]) ->
 
363
    {error,{badarg, H}};
 
364
ensure_no_schema([]) ->
 
365
    ok.
 
366
 
 
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
 
373
        ok ->
 
374
            case mnesia_monitor:get_env(schema_location) of
 
375
                opt_disc ->
 
376
                    read_schema(false, true);
 
377
                _ ->
 
378
                    read_schema(false, false)
 
379
            end;
 
380
        {error, Reason} ->
 
381
            {error, Reason}
 
382
    end.
 
383
 
 
384
dir_exists(Dir) ->
 
385
    dir_exists(Dir, mnesia_monitor:use_dir()).
 
386
dir_exists(Dir, true) ->
 
387
    case file:read_file_info(Dir) of
 
388
        {ok, _} -> true;
 
389
        _ -> false
 
390
    end;
 
391
dir_exists(_Dir, false) ->
 
392
    false.
 
393
 
 
394
opt_create_dir(UseDir, Dir) when UseDir == true->
 
395
    case dir_exists(Dir, UseDir) of
 
396
        true ->
 
397
            check_can_write(Dir);
 
398
        false ->
 
399
            case file:make_dir(Dir) of
 
400
                ok -> 
 
401
                    verbose("Create Directory ~p~n", [Dir]),
 
402
                    ok;
 
403
                {error, Reason} ->
 
404
                    verbose("Cannot create mnesia dir ~p~n", [Reason]),
 
405
                    {error, {"Cannot create Mnesia dir", Dir, Reason}}
 
406
            end
 
407
    end;
 
408
opt_create_dir(false, _) ->
 
409
    {error, {has_no_disc, node()}}.
 
410
    
 
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 ->
 
415
            ok;
 
416
        {ok, _} ->
 
417
            {error, "Not allowed to write in Mnesia dir", Dir};
 
418
        _ ->
 
419
            {error, "Non existent Mnesia dir", Dir}
 
420
    end.
 
421
 
 
422
lock_schema() ->
 
423
    mnesia_lib:lock_table(schema).
 
424
 
 
425
unlock_schema() ->
 
426
    mnesia_lib:unlock_table(schema).
 
427
 
 
428
read_schema(Keep, _UseDirAnyway) ->
 
429
    read_schema(Keep, false, false).
 
430
 
 
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
 
434
%% be left around.
 
435
%% If Keep == true, the ets table schema is kept
 
436
%% If Keep == false, the ets table schema is removed
 
437
%%
 
438
%% Returns {ok, Source, SchemaCstruct} or {error, Reason}
 
439
%% Source may be: default | ram | disc | fallback
 
440
 
 
441
read_schema(Keep, UseDirAnyway, IgnoreFallback) ->
 
442
    lock_schema(),
 
443
    Res = 
 
444
        case mnesia:system_info(is_running) of
 
445
            yes ->
 
446
                {ok, ram, get_create_list(schema)};
 
447
            _IsRunning ->
 
448
                    case mnesia_monitor:use_dir() of
 
449
                        true ->
 
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}
 
462
                    end
 
463
        end,
 
464
    unlock_schema(),
 
465
    Res.
 
466
 
 
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();
 
472
        _ ->  
 
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
 
477
                true ->
 
478
                    do_read_disc_schema(Dat, Keep);
 
479
                false ->
 
480
                    Dmp = mnesia_lib:tab2dmp(schema),
 
481
                    case mnesia_lib:exists(Dmp) of
 
482
                        true ->
 
483
                            %% May only happen when toggling of
 
484
                            %% schema storage type has been
 
485
                            %% interrupted
 
486
                            do_read_disc_schema(Dmp, Keep);
 
487
                        false ->
 
488
                            {error, "No schema file exists"}
 
489
                    end
 
490
            end
 
491
    end.
 
492
 
 
493
do_read_disc_schema(Fname, Keep) ->
 
494
    T = 
 
495
        case Keep of
 
496
            false ->
 
497
                Args = [{keypos, 2}, public, set],
 
498
                ?ets_new_table(schema, Args);
 
499
            true ->
 
500
                Args = [{keypos, 2}, public, named_table, set],
 
501
                mnesia_monitor:mktab(schema, Args)
 
502
        end,
 
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}}
 
508
        end,
 
509
    case Keep of
 
510
        true -> ignore;
 
511
        false -> ?ets_delete_table(T)
 
512
    end,
 
513
    Res.
 
514
 
 
515
get_initial_schema(SchemaStorage, Nodes) ->
 
516
    Cs = #cstruct{name = schema,
 
517
                  record_name = schema,
 
518
                  attributes = [table, cstruct]},    
 
519
    Cs2 =
 
520
        case SchemaStorage of
 
521
        ram_copies -> Cs#cstruct{ram_copies = Nodes};
 
522
        disc_copies -> Cs#cstruct{disc_copies = Nodes}
 
523
    end,
 
524
    cs2list(Cs2).
 
525
 
 
526
read_cstructs_from_disc() ->
 
527
    %% Assumptions: 
 
528
    %% - local schema lock in global
 
529
    %% - use_dir is true
 
530
    %% - Mnesia is not running
 
531
    %% - Ignore fallback
 
532
 
 
533
    Fname = mnesia_lib:tab2dat(schema),
 
534
    case mnesia_lib:exists(Fname) of
 
535
        true ->
 
536
            Args = [{file, Fname},
 
537
                    {keypos, 2},
 
538
                    {repair, mnesia_monitor:get_env(auto_repair)},
 
539
                    {type, set}],
 
540
            case dets:open_file(make_ref(), Args) of
 
541
                {ok, Tab} ->
 
542
                    Fun = fun({_, _, List}) ->
 
543
                                  {continue, list2cs(List)}
 
544
                          end,
 
545
                    Cstructs = dets:traverse(Tab, Fun),
 
546
                    dets:close(Tab),
 
547
                    {ok, Cstructs};  
 
548
                {error, Reason} ->
 
549
                    {error, Reason}
 
550
            end;
 
551
        false ->
 
552
            {error, "No schema file exists"}
 
553
    end.
 
554
    
 
555
%% We run a very special type of transactions when we
 
556
%% we want to manipulate the schema.
 
557
 
 
558
get_tid_ts_and_lock(Tab, Intent) ->
 
559
    TidTs = get(mnesia_activity_state),
 
560
    case TidTs of
 
561
        {_Mod, Tid, Ts} when record(Ts, tidstore)->
 
562
            Store = Ts#tidstore.store,
 
563
            case Intent of
 
564
                read -> mnesia_locker:rlock_table(Tid, Store, Tab);
 
565
                write -> mnesia_locker:wlock_table(Tid, Store, Tab);
 
566
                none -> ignore
 
567
            end,
 
568
            TidTs;
 
569
        _ ->
 
570
            mnesia:abort(no_transaction)
 
571
    end.
 
572
 
 
573
schema_transaction(Fun) ->
 
574
    case get(mnesia_activity_state) of
 
575
        undefined ->
 
576
            Args = [self(), Fun, whereis(mnesia_controller)],
 
577
            Pid = spawn_link(?MODULE, schema_coordinator, Args),
 
578
            receive
 
579
                {transaction_done, Res, Pid} -> Res;
 
580
                {'EXIT', Pid, R} -> {aborted, {transaction_crashed, R}}
 
581
            end;
 
582
        _ ->
 
583
            {aborted, nested_transaction}
 
584
    end.
 
585
 
 
586
%% This process may dump the transaction log, and should
 
587
%% therefore not be run in an application process
 
588
%% 
 
589
schema_coordinator(Client, _Fun, undefined) ->
 
590
    Res = {aborted, {node_not_running, node()}},
 
591
    Client ! {transaction_done, Res, self()},
 
592
    unlink(Client);
 
593
    
 
594
schema_coordinator(Client, Fun, Controller) when pid(Controller) ->
 
595
    %% Do not trap exit in order to automatically die
 
596
    %% when the controller dies
 
597
 
 
598
    link(Controller),
 
599
    unlink(Client),
 
600
    
 
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
 
606
    exit(normal).
 
607
 
 
608
%% The make* rotines return a list of ops, this function
 
609
%% inserts em all in the Store and maintains the local order
 
610
%% of ops.
 
611
 
 
612
insert_schema_ops({_Mod, _Tid, Ts}, SchemaIOps) ->
 
613
    do_insert_schema_ops(Ts#tidstore.store, SchemaIOps).
 
614
    
 
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, []) ->
 
619
    ok.
 
620
 
 
621
cs2list(Cs) when record(Cs, cstruct) ->
 
622
    Tags = record_info(fields, cstruct),
 
623
    rec2list(Tags, 2, Cs);
 
624
cs2list(CreateList) when list(CreateList) ->
 
625
    CreateList.
 
626
 
 
627
rec2list([Tag | Tags], Pos, Rec) ->
 
628
    Val = element(Pos, Rec),
 
629
    [{Tag, Val} | rec2list(Tags, Pos + 1, Rec)];
 
630
rec2list([], _Pos, _Rec) ->
 
631
    [].
 
632
 
 
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()];
 
641
             _ -> Rc0
 
642
         end,
 
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],
 
658
 
 
659
    Frag = pick(Name, frag_properties, List, []),
 
660
    verify({alt, [nil, list]}, mnesia_lib:etype(Frag),
 
661
           {badarg, Name, {frag_properties, Frag}}), 
 
662
        
 
663
    Keys = check_keys(Name, List, record_info(fields, cstruct)),
 
664
    check_duplicates(Name, Keys),
 
665
    #cstruct{name = Name,
 
666
             ram_copies = Rc,
 
667
             disc_copies = Dc,
 
668
             disc_only_copies = Doc,
 
669
             type = Type,
 
670
             index = Ix2,
 
671
             snmp = Snmp,
 
672
             load_order = LoadOrder,
 
673
             access_mode = AccessMode,
 
674
             local_content = LC,
 
675
             record_name = RecName,
 
676
             attributes = Attrs,
 
677
             user_properties = lists:sort(UserProps),
 
678
             frag_properties = lists:sort(Frag),
 
679
             cookie = Cookie,
 
680
             version = Version};
 
681
list2cs(Other) ->
 
682
    mnesia:abort({badarg, Other}).
 
683
 
 
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});
 
688
        false ->
 
689
            Default;
 
690
        {value, {Key, Value}} ->
 
691
            Value;
 
692
        {value, BadArg} ->
 
693
            mnesia:abort({bad_type, Tab, BadArg})
 
694
    end.
 
695
 
 
696
%% Convert attribute name to integer if neccessary
 
697
attr_tab_to_pos(_Tab, Pos) when integer(Pos) ->
 
698
    Pos;
 
699
attr_tab_to_pos(Tab, Attr) ->
 
700
    attr_to_pos(Attr, val({Tab, attributes})).
 
701
    
 
702
%% Convert attribute name to integer if neccessary
 
703
attr_to_pos(Pos, _Attrs) when integer(Pos) ->
 
704
    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}).
 
709
 
 
710
attr_to_pos(Attr, [Attr | _Attrs], Pos) ->
 
711
    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}).
 
716
    
 
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})
 
721
    end;
 
722
check_keys(_, [], _) ->
 
723
    [];
 
724
check_keys(Tab, Arg, _) ->
 
725
    mnesia:abort({badarg, Tab, Arg}).
 
726
 
 
727
check_duplicates(Tab, Keys) ->
 
728
    case has_duplicates(Keys) of
 
729
        false -> ok;
 
730
        true -> mnesia:abort({badarg, Tab, "Duplicate keys", Keys})
 
731
    end.
 
732
 
 
733
has_duplicates([H | T]) ->
 
734
    case lists:member(H, T) of
 
735
        true -> true;
 
736
        false -> has_duplicates(T)
 
737
    end;
 
738
has_duplicates([]) ->
 
739
    false.
 
740
 
 
741
%% This is the only place where we check the validity of data
 
742
verify_cstruct(Cs) when record(Cs, cstruct) ->
 
743
    verify_nodes(Cs),
 
744
 
 
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}}),
 
750
 
 
751
    %% Currently ordered_set is not supported for disk_only_copies.
 
752
    if 
 
753
        Type == ordered_set, Cs#cstruct.disc_only_copies /= [] ->
 
754
            mnesia:abort({bad_type, Tab, {not_supported, Type, disc_only_copies}});
 
755
        true ->
 
756
            ok
 
757
    end,
 
758
 
 
759
    RecName = Cs#cstruct.record_name,
 
760
    verify(atom, mnesia_lib:etype(RecName),
 
761
           {bad_type, Tab, {record_name, RecName}}),
 
762
 
 
763
    Attrs = Cs#cstruct.attributes,
 
764
    verify(list, mnesia_lib:etype(Attrs),
 
765
           {bad_type, Tab, {attributes, Attrs}}),
 
766
 
 
767
    Arity = length(Attrs) + 1,
 
768
    verify(true, Arity > 2, {bad_type, Tab, {attributes, Attrs}}),
 
769
    
 
770
    lists:foldl(fun(Attr,_Other) when Attr == snmp ->
 
771
                        mnesia:abort({bad_type, Tab, {attributes, [Attr]}});
 
772
                   (Attr,Other) -> 
 
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]}}),
 
777
                        [Attr | Other]
 
778
                end,
 
779
                [],
 
780
                Attrs),
 
781
 
 
782
    Index = Cs#cstruct.index,
 
783
    verify({alt, [nil, list]}, mnesia_lib:etype(Index),
 
784
           {bad_type, Tab, {index, Index}}),
 
785
    
 
786
    IxFun =
 
787
        fun(Pos) ->
 
788
                verify(true, fun() ->
 
789
                                     if
 
790
                                         integer(Pos),
 
791
                                         Pos > 2,
 
792
                                         Pos =< Arity ->
 
793
                                             true;
 
794
                                         true -> false
 
795
                                     end
 
796
                             end,
 
797
                       {bad_type, Tab, {index, [Pos]}})
 
798
        end,
 
799
    lists:foreach(IxFun, Index),
 
800
    
 
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}}),
 
807
 
 
808
    Snmp = Cs#cstruct.snmp,
 
809
    verify(true, mnesia_snmp_hook:check_ustruct(Snmp),
 
810
           {badarg, Tab, {snmp, Snmp}}),
 
811
 
 
812
    CheckProp = fun(Prop) when tuple(Prop), size(Prop) >= 1 -> ok;
 
813
                   (Prop) -> mnesia:abort({bad_type, Tab, {user_properties, [Prop]}})
 
814
                end,
 
815
    lists:foreach(CheckProp, Cs#cstruct.user_properties),
 
816
 
 
817
    case Cs#cstruct.cookie of
 
818
        {{MegaSecs, Secs, MicroSecs}, _Node} 
 
819
        when integer(MegaSecs), integer(Secs),
 
820
             integer(MicroSecs), atom(node) ->
 
821
            ok;
 
822
        Cookie ->
 
823
            mnesia:abort({bad_type, Tab, {cookie, Cookie}})
 
824
    end,
 
825
    case Cs#cstruct.version of
 
826
        {{Major, Minor}, _Detail}
 
827
                when integer(Major), integer(Minor) ->
 
828
            ok;
 
829
        Version ->
 
830
            mnesia:abort({bad_type, Tab, {version, Version}})
 
831
    end.
 
832
 
 
833
verify_nodes(Cs) ->
 
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,
 
839
 
 
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}}),
 
844
    case Tab of
 
845
        schema ->
 
846
            verify([], DiscOnly, {bad_type, Tab, {disc_only_copies, DiscOnly}});
 
847
        _ ->
 
848
            verify({alt, [nil, list]},
 
849
                   mnesia_lib:etype(DiscOnly),
 
850
                   {bad_type, Tab, {disc_only_copies, DiscOnly}})
 
851
    end,
 
852
    verify(integer, mnesia_lib:etype(LoadOrder),
 
853
           {bad_type, Tab, {load_order, LoadOrder}}),
 
854
    
 
855
    Nodes = Ram ++ Disc ++ DiscOnly,
 
856
    verify(list, mnesia_lib:etype(Nodes),
 
857
           {combine_error, Tab,
 
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).
 
862
    
 
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).
 
867
 
 
868
do_verify({alt, Values}, Value, Error) ->
 
869
    case lists:member(Value, Values) of
 
870
        true -> ok;
 
871
        false -> mnesia:abort(Error)
 
872
    end;
 
873
do_verify(Value, Value, _) ->
 
874
    ok;
 
875
do_verify(_Value, _, Error) ->
 
876
     mnesia:abort(Error).
 
877
 
 
878
ensure_writable(Tab) ->
 
879
    case val({Tab, where_to_write}) of
 
880
        [] -> mnesia:abort({read_only, Tab});
 
881
        _ -> ok
 
882
    end.
 
883
 
 
884
%% Ensure that all replicas on disk full nodes are active
 
885
ensure_active(Cs) ->
 
886
    ensure_active(Cs, active_replicas).
 
887
 
 
888
ensure_active(Cs, What) ->
 
889
    Tab = Cs#cstruct.name,
 
890
    case val({Tab, What}) of
 
891
        [] -> mnesia:abort({no_exists, Tab});
 
892
        _ -> ok
 
893
    end,
 
894
    Nodes = mnesia_lib:intersect(val({schema, disc_copies}),
 
895
                                 mnesia_lib:cs_to_nodes(Cs)), 
 
896
    W = {Tab, What},
 
897
    case Nodes -- val(W) of
 
898
        [] ->
 
899
            ok;
 
900
        Ns ->
 
901
            Expl = "All replicas on diskfull nodes are not active yet",
 
902
            case val({Tab, local_content}) of
 
903
                true ->
 
904
                    case rpc:multicall(Ns, ?MODULE, is_remote_member, [W]) of
 
905
                        {Replies, []} ->
 
906
                            check_active(Replies, Expl, Tab);
 
907
                        {_Replies, BadNs} ->
 
908
                            mnesia:abort({not_active, Expl, Tab, BadNs})
 
909
                    end;
 
910
                false ->
 
911
                    mnesia:abort({not_active, Expl, Tab, Ns})
 
912
            end
 
913
    end.
 
914
 
 
915
ensure_not_active(schema, Node) ->
 
916
    case lists:member(Node, val({schema, active_replicas})) of 
 
917
        false ->
 
918
            ok;
 
919
        true ->
 
920
            Expl = "Mnesia is running",
 
921
            mnesia:abort({active, Expl, Node})
 
922
    end.
 
923
 
 
924
is_remote_member(Key) ->
 
925
    IsActive = lists:member(node(), val(Key)),
 
926
    {IsActive, node()}.
 
927
 
 
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) ->
 
935
    ok.
 
936
 
 
937
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
938
%% Here's the real interface function to create a table
 
939
 
 
940
create_table(TabDef) ->
 
941
    schema_transaction(fun() -> do_multi_create_table(TabDef) end).
 
942
 
 
943
%% And the corresponding do routines ....
 
944
 
 
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
 
950
        [] ->
 
951
            do_create_table(Cs);
 
952
        _Props ->
 
953
            CsList = mnesia_frag:expand_cstruct(Cs),
 
954
            lists:foreach(fun do_create_table/1, CsList)
 
955
    end,
 
956
    ok.
 
957
 
 
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)).
 
962
 
 
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).
 
968
 
 
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)).
 
973
 
 
974
unsafe_make_create_table(Cs) ->
 
975
    {_Mod, Tid, Ts} =  get_tid_ts_and_lock(schema, none),
 
976
    verify_cstruct(Cs),
 
977
    Tab = Cs#cstruct.name,
 
978
        
 
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})
 
985
                end,
 
986
    lists:foreach(CheckDisc, DiscNodes),
 
987
 
 
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)}].
 
992
 
 
993
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
994
%% Delete a table entirely on all nodes.
 
995
 
 
996
delete_table(Tab) ->
 
997
    schema_transaction(fun() -> do_delete_table(Tab) end).
 
998
 
 
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)).
 
1005
 
 
1006
make_delete_table(Tab, Mode) ->
 
1007
    case Mode of
 
1008
        whole_table ->
 
1009
            case val({Tab, frag_properties}) of
 
1010
                [] ->
 
1011
                    [make_delete_table2(Tab)];
 
1012
                _Props ->
 
1013
                    %% Check if it is a base table 
 
1014
                    mnesia_frag:lookup_frag_hash(Tab),
 
1015
 
 
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)]             
 
1020
            end;
 
1021
        single_frag ->
 
1022
            [make_delete_table2(Tab)]
 
1023
    end.
 
1024
 
 
1025
make_delete_table2(Tab) ->
 
1026
    get_tid_ts_and_lock(Tab, write),
 
1027
    Cs = val({Tab, cstruct}),
 
1028
    ensure_active(Cs),
 
1029
    ensure_writable(Tab),
 
1030
    {op, delete_table, cs2list(Cs)}.
 
1031
 
 
1032
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1033
%% Change fragmentation of a table
 
1034
    
 
1035
change_table_frag(Tab, Change) ->
 
1036
    schema_transaction(fun() -> do_change_table_frag(Tab, Change) end).
 
1037
 
 
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],
 
1042
    ok;
 
1043
do_change_table_frag(Tab, _Change) ->
 
1044
    mnesia:abort({bad_type, Tab}).
 
1045
    
 
1046
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1047
%% Clear a table
 
1048
 
 
1049
clear_table(Tab) ->
 
1050
    schema_transaction(fun() -> do_clear_table(Tab) end).
 
1051
 
 
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)).
 
1058
 
 
1059
make_clear_table(Tab) ->
 
1060
    ensure_writable(schema),
 
1061
    Cs = val({Tab, cstruct}),
 
1062
    ensure_active(Cs),
 
1063
    ensure_writable(Tab),
 
1064
    [{op, clear_table, cs2list(Cs)}].
 
1065
 
 
1066
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1067
 
 
1068
add_table_copy(Tab, Node, Storage) ->
 
1069
    schema_transaction(fun() -> do_add_table_copy(Tab, Node, Storage) end).
 
1070
 
 
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}).
 
1076
 
 
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),
 
1084
    
 
1085
    %% Check storage and if node is running
 
1086
    IsRunning = lists:member(Node, val({current, db_nodes})),
 
1087
    if
 
1088
        Storage == unknown ->
 
1089
            mnesia:abort({badarg, Tab, Storage});
 
1090
        Tab == schema ->
 
1091
            if
 
1092
                Storage /= ram_copies ->
 
1093
                    mnesia:abort({badarg, Tab, Storage});
 
1094
                IsRunning == true ->
 
1095
                    mnesia:abort({already_exists, Tab, Node});
 
1096
                true ->
 
1097
                    ignore
 
1098
            end;
 
1099
        Storage == ram_copies ->
 
1100
            ignore;
 
1101
        IsRunning == true ->
 
1102
            ignore;
 
1103
        IsRunning == false ->
 
1104
            mnesia:abort({not_active, schema, Node})
 
1105
    end,
 
1106
    [{op, add_table_copy, Storage, Node, cs2list(Cs2)}].
 
1107
 
 
1108
del_table_copy(Tab, Node) ->
 
1109
    schema_transaction(fun() -> do_del_table_copy(Tab, Node) end).
 
1110
 
 
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}).
 
1117
   
 
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"});
 
1126
        [] ->
 
1127
            ensure_active(Cs),      
 
1128
            dbg_out("Last replica deleted in table ~p~n",  [Tab]),
 
1129
            make_delete_table(Tab,  whole_table);
 
1130
        _ when Tab == schema ->
 
1131
            ensure_active(Cs2),
 
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];
 
1136
        _ ->
 
1137
            ensure_active(Cs),
 
1138
            verify_cstruct(Cs2),
 
1139
            [{op, del_table_copy, Storage, Node, cs2list(Cs2)}]
 
1140
    end.
 
1141
 
 
1142
remove_node_from_tabs([], _Node) ->
 
1143
    [];
 
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
 
1150
        unknown ->
 
1151
            case IsFragModified of
 
1152
                true ->
 
1153
                    [{op, change_table_frag, {del_node, Node}, cs2list(Cs)} | 
 
1154
                     remove_node_from_tabs(Rest, Node)];
 
1155
                false ->
 
1156
                    remove_node_from_tabs(Rest, Node)
 
1157
            end;
 
1158
        Storage ->
 
1159
            Cs2 = new_cs(Cs, Node, Storage, del),
 
1160
            case mnesia_lib:cs_to_nodes(Cs2) of
 
1161
                [] ->
 
1162
                    [{op, delete_table, cs2list(Cs)} |
 
1163
                     remove_node_from_tabs(Rest, Node)];
 
1164
                _Ns ->
 
1165
                    verify_cstruct(Cs2),
 
1166
                    [{op, del_table_copy, ram_copies, Node, cs2list(Cs2)}|
 
1167
                     remove_node_from_tabs(Rest, Node)]
 
1168
            end
 
1169
    end.
 
1170
 
 
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}).
 
1186
 
 
1187
 
 
1188
opt_add(N, L) -> [N | lists:delete(N, L)].
 
1189
 
 
1190
move_table(Tab, FromNode, ToNode) ->
 
1191
    schema_transaction(fun() -> do_move_table(Tab, FromNode, ToNode) end).
 
1192
 
 
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}).
 
1200
 
 
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}),
 
1210
    ensure_active(Cs),
 
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}),
 
1214
    
 
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)},
 
1219
     {op, sync_trans},
 
1220
     {op, del_table_copy, Storage, FromNode, cs2list(Cs3)}].
 
1221
 
 
1222
%% end of functions to add and delete nodes to tables
 
1223
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1224
%%
 
1225
 
 
1226
change_table_copy_type(Tab, Node, ToS) ->
 
1227
    schema_transaction(fun() -> do_change_table_copy_type(Tab, Node, ToS) end).
 
1228
 
 
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}).
 
1236
 
 
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), 
 
1243
 
 
1244
    case compare_storage_type(false, FromS, ToS) of
 
1245
        {same, _} ->
 
1246
            mnesia:abort({already_exists, Tab, Node, ToS});
 
1247
        {diff, _} ->
 
1248
            ignore;
 
1249
        incompatible ->
 
1250
            ensure_active(Cs)
 
1251
    end,
 
1252
 
 
1253
    Cs2 = new_cs(Cs, Node, FromS, del),
 
1254
    Cs3 = new_cs(Cs2, Node, ToS, add),
 
1255
    verify_cstruct(Cs3),
 
1256
    
 
1257
    if
 
1258
        FromS == unknown ->
 
1259
            make_add_table_copy(Tab, Node, ToS);
 
1260
        true ->
 
1261
            ignore
 
1262
    end,
 
1263
 
 
1264
    [{op, change_table_copy_type, Node, FromS, ToS, cs2list(Cs3)}].
 
1265
 
 
1266
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1267
%% change index functions ....
 
1268
%% Pos is allready added by 1 in both of these functions 
 
1269
 
 
1270
add_table_index(Tab, Pos) ->
 
1271
    schema_transaction(fun() -> do_add_table_index(Tab, Pos) end).
 
1272
 
 
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)).
 
1280
 
 
1281
make_add_table_index(Tab, Pos) ->
 
1282
    ensure_writable(schema),
 
1283
    Cs = incr_version(val({Tab, cstruct})),
 
1284
    ensure_active(Cs),
 
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)}].
 
1291
 
 
1292
del_table_index(Tab, Pos) ->
 
1293
    schema_transaction(fun() -> do_del_table_index(Tab, Pos) end).
 
1294
 
 
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)).
 
1302
 
 
1303
make_del_table_index(Tab, Pos) ->
 
1304
    ensure_writable(schema),
 
1305
    Cs = incr_version(val({Tab, cstruct})),
 
1306
    ensure_active(Cs),
 
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)}].
 
1312
 
 
1313
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1314
 
 
1315
add_snmp(Tab, Ustruct) ->
 
1316
    schema_transaction(fun() -> do_add_snmp(Tab, Ustruct) end).
 
1317
 
 
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)).
 
1324
 
 
1325
make_add_snmp(Tab, Ustruct) ->
 
1326
    ensure_writable(schema),
 
1327
    Cs = incr_version(val({Tab, cstruct})),
 
1328
    ensure_active(Cs),
 
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)}].
 
1335
 
 
1336
del_snmp(Tab) ->
 
1337
    schema_transaction(fun() -> do_del_snmp(Tab) end).
 
1338
 
 
1339
do_del_snmp(schema) ->
 
1340
    mnesia:abort({bad_type, schema});
 
1341
do_del_snmp(Tab) ->
 
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)).
 
1345
 
 
1346
make_del_snmp(Tab) ->
 
1347
    ensure_writable(schema),
 
1348
    Cs = incr_version(val({Tab, cstruct})),
 
1349
    ensure_active(Cs),
 
1350
    Cs2 = Cs#cstruct{snmp = []},
 
1351
    verify_cstruct(Cs2),
 
1352
    [{op, del_snmp, cs2list(Cs2)}].
 
1353
 
 
1354
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1355
%%  
 
1356
 
 
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);
 
1360
 
 
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);
 
1364
 
 
1365
transform_table(Tab, Fun, NewAttrs, NewRecName) ->
 
1366
    {aborted,{bad_type, Tab, Fun, NewAttrs, NewRecName}}.
 
1367
 
 
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)).
 
1374
 
 
1375
make_transform(Tab, Fun, NewAttrs, NewRecName) ->
 
1376
    ensure_writable(schema),
 
1377
    Cs = incr_version(val({Tab, cstruct})),
 
1378
    ensure_active(Cs),
 
1379
    ensure_writable(Tab),
 
1380
    case mnesia_lib:val({Tab, index}) of
 
1381
        [] -> 
 
1382
            Cs2 = Cs#cstruct{attributes = NewAttrs, record_name = NewRecName},
 
1383
            verify_cstruct(Cs2),
 
1384
            [{op, transform, Fun, cs2list(Cs2)}];
 
1385
        PosList ->
 
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)},
 
1390
                             {Op, Ncs1}
 
1391
                     end,
 
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)},
 
1397
                             {Op, Ncs1}
 
1398
                     end,
 
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])
 
1404
    end.
 
1405
 
 
1406
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1407
%%  
 
1408
 
 
1409
change_table_access_mode(Tab, Mode) ->
 
1410
    schema_transaction(fun() -> do_change_table_access_mode(Tab, Mode) end).
 
1411
 
 
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)).
 
1418
 
 
1419
make_change_table_access_mode(Tab, Mode) ->
 
1420
    ensure_writable(schema),
 
1421
    Cs = incr_version(val({Tab, cstruct})),
 
1422
    ensure_active(Cs),
 
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}].
 
1428
 
 
1429
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1430
 
 
1431
change_table_load_order(Tab, LoadOrder) ->
 
1432
    schema_transaction(fun() -> do_change_table_load_order(Tab, LoadOrder) end).
 
1433
 
 
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)).
 
1440
 
 
1441
make_change_table_load_order(Tab, LoadOrder) ->
 
1442
    ensure_writable(schema),
 
1443
    Cs = incr_version(val({Tab, cstruct})),
 
1444
    ensure_active(Cs),
 
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}].
 
1449
 
 
1450
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1451
 
 
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),
 
1458
    {_, _, Ts} = TidTs,
 
1459
    Store = Ts#tidstore.store,
 
1460
    case change_prop_in_existing_op(Tab, Prop, write_property, Store) of
 
1461
        true ->
 
1462
            dbg_out("change_prop_in_existing_op"
 
1463
                    "(~p,~p,write_property,Store) -> true~n",
 
1464
                    [Tab,Prop]),
 
1465
            %% we have merged the table prop into the create_table op
 
1466
            ok;
 
1467
        false ->
 
1468
            dbg_out("change_prop_in_existing_op"
 
1469
                    "(~p,~p,write_property,Store) -> false~n",
 
1470
                    [Tab,Prop]),
 
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]))
 
1474
    end.
 
1475
 
 
1476
make_write_table_properties(Tab, Props) ->
 
1477
    ensure_writable(schema),
 
1478
    Cs = incr_version(val({Tab, cstruct})),
 
1479
    ensure_active(Cs),
 
1480
    make_write_table_properties(Tab, Props, Cs).
 
1481
 
 
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) ->
 
1492
    [].
 
1493
 
 
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
 
1497
        {true, Ops1} ->
 
1498
            ets:match_delete(Store, '_'),
 
1499
            [ets:insert(Store, Op) || Op <- Ops1],
 
1500
            true;
 
1501
        false ->
 
1502
            false
 
1503
    end.
 
1504
 
 
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.
 
1510
    %% as element 3.
 
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]};
 
1520
        _ ->
 
1521
            update_existing_op(Ops, Tab, Prop, How, [Op|Acc])
 
1522
    end;
 
1523
update_existing_op([Op|Ops], Tab, Prop, How, Acc) ->
 
1524
    update_existing_op(Ops, Tab, Prop, How, [Op|Acc]);
 
1525
update_existing_op([], _, _, _, _) ->
 
1526
    false.
 
1527
 
 
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).
 
1534
 
 
1535
 
 
1536
find_props([{user_properties, P}|_]) -> P;
 
1537
find_props([_H|T]) -> find_props(T).
 
1538
%% we shouldn't reach []
 
1539
 
 
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 []
 
1543
 
 
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).
 
1550
 
 
1551
delete_table_property(Tab, PropKey) ->
 
1552
    schema_transaction(fun() -> do_delete_table_property(Tab, PropKey) end).
 
1553
 
 
1554
do_delete_table_property(Tab, PropKey) ->
 
1555
    TidTs = get_tid_ts_and_lock(schema, write),
 
1556
    {_, _, Ts} = TidTs,
 
1557
    Store = Ts#tidstore.store,
 
1558
    case change_prop_in_existing_op(Tab, PropKey, delete_property, Store) of
 
1559
        true ->
 
1560
            dbg_out("change_prop_in_existing_op"
 
1561
                    "(~p,~p,delete_property,Store) -> true~n",
 
1562
                    [Tab,PropKey]),
 
1563
            %% we have merged the table prop into the create_table op
 
1564
            ok;
 
1565
        false ->
 
1566
            dbg_out("change_prop_in_existing_op"
 
1567
                    "(~p,~p,delete_property,Store) -> false~n",
 
1568
                    [Tab,PropKey]),
 
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]))
 
1573
    end.
 
1574
 
 
1575
make_delete_table_properties(Tab, PropKeys) ->
 
1576
    ensure_writable(schema),
 
1577
    Cs = incr_version(val({Tab, cstruct})),
 
1578
    ensure_active(Cs),
 
1579
    make_delete_table_properties(Tab, PropKeys, Cs).
 
1580
 
 
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) ->
 
1589
    [].
 
1590
 
 
1591
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
1592
 
 
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
 
1597
        [] ->
 
1598
            {false, Commit, optional};
 
1599
        OrigOps ->
 
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)},
 
1605
                       DumperMode},
 
1606
            case DumperMode of
 
1607
                optional ->
 
1608
                    dbg_out("Transaction log dump skipped (~p): ~w~n",
 
1609
                            [DumperMode, InitBy]);
 
1610
                mandatory ->
 
1611
                    case mnesia_controller:sync_dump_log(InitBy) of
 
1612
                        dumped ->
 
1613
                            GoodRes;
 
1614
                        {error, Reason} ->
 
1615
                            mnesia:abort(Reason)
 
1616
                    end
 
1617
            end,
 
1618
            case Ops of
 
1619
                [] -> 
 
1620
                    ignore;
 
1621
                _ ->
 
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()
 
1625
            end,
 
1626
            GoodRes
 
1627
    end.
 
1628
 
 
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);
 
1633
        {true, optional} -> 
 
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)
 
1643
    end;
 
1644
prepare_ops(_Tid, [], _WaitFor, Changed, Acc, DumperMode) ->
 
1645
    {Changed, Acc, DumperMode}.
 
1646
    
 
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
 
1652
%%
 
1653
prepare_op(_Tid, {op, rec, unknown, Rec}, _WaitFor) ->
 
1654
    {{Tab, Key}, Items, _Op} = Rec,
 
1655
    case val({Tab, storage_type}) of
 
1656
        unknown ->
 
1657
            {false, optional};
 
1658
        Storage ->
 
1659
            mnesia_tm:prepare_snmp(Tab, Key, Items), % May exit
 
1660
            {true, [{op, rec, Storage, Rec}], optional}
 
1661
    end;
 
1662
 
 
1663
prepare_op(_Tid, {op, announce_im_running, _Node, SchemaDef, Running, RemoteRunning}, _WaitFor) ->
 
1664
    SchemaCs = list2cs(SchemaDef),
 
1665
    case lists:member(node(), Running) of
 
1666
        true ->
 
1667
            announce_im_running(RemoteRunning -- Running, SchemaCs);
 
1668
        false ->
 
1669
            announce_im_running(Running -- RemoteRunning, SchemaCs)
 
1670
    end,
 
1671
    {false, optional};
 
1672
 
 
1673
prepare_op(_Tid, {op, sync_trans}, {part, CoordPid}) ->    
 
1674
    CoordPid ! {sync_trans, self()},
 
1675
    receive 
 
1676
        {sync_trans, CoordPid} ->
 
1677
            {false, optional};
 
1678
        Else ->
 
1679
            mnesia_lib:verbose("sync_op terminated due to ~p~n", [Else]),
 
1680
            mnesia:abort(Else)
 
1681
    end;
 
1682
 
 
1683
prepare_op(_Tid, {op, sync_trans}, {coord, Nodes}) ->    
 
1684
    case receive_sync(Nodes, []) of
 
1685
        {abort, Reason} ->
 
1686
            mnesia_lib:verbose("sync_op terminated due to ~p~n", [Reason]),
 
1687
            mnesia:abort(Reason);
 
1688
        Pids ->
 
1689
            [Pid ! {sync_trans, self()} || Pid <- Pids],
 
1690
            {false, optional}
 
1691
    end;
 
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,
 
1697
    case Storage of
 
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);
 
1704
        ram_copies ->
 
1705
            create_ram_table(Tab, Cs#cstruct.type),
 
1706
            insert_cstruct(Tid, Cs, false),
 
1707
            {true, optional};
 
1708
        disc_copies ->
 
1709
            create_ram_table(Tab, Cs#cstruct.type),
 
1710
            create_disc_table(Tab),
 
1711
            insert_cstruct(Tid, Cs, false),
 
1712
            {true, optional};   
 
1713
        disc_only_copies ->
 
1714
            create_disc_only_table(Tab,Cs#cstruct.type),
 
1715
            insert_cstruct(Tid, Cs, false),
 
1716
            {true, optional};
 
1717
        unknown -> %% No replica on this node
 
1718
            insert_cstruct(Tid, Cs, false),
 
1719
            {true, optional}
 
1720
    end;
 
1721
 
 
1722
prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}, _WaitFor) ->
 
1723
    Cs = list2cs(TabDef),
 
1724
    Tab = Cs#cstruct.name,
 
1725
 
 
1726
    if
 
1727
        Tab == schema ->
 
1728
            {true, optional}; % Nothing to prepare
 
1729
        Node == node() ->
 
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);
 
1734
                _  ->
 
1735
                    ok
 
1736
            end,   
 
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
 
1740
                {loaded, ok} ->
 
1741
                    {true, optional};
 
1742
                {not_loaded, ErrReason} ->
 
1743
                    Reason = {system_limit, Tab, {Node, ErrReason}},
 
1744
                    mnesia:abort(Reason)
 
1745
            end;
 
1746
        Node /= node() ->
 
1747
            %% Verify that ram table not has been dumped to disc
 
1748
            if
 
1749
                Storage /= ram_copies ->
 
1750
                    case mnesia_lib:schema_cs_to_storage_type(node(), Cs) of
 
1751
                        ram_copies ->
 
1752
                            Dat = mnesia_lib:tab2dcd(Tab),
 
1753
                            case mnesia_lib:exists(Dat) of
 
1754
                                true ->
 
1755
                                    mnesia:abort({combine_error, Tab, Storage,
 
1756
                                                  "Table dumped to disc", node()});
 
1757
                                false ->
 
1758
                                    ok
 
1759
                            end;
 
1760
                        _ ->
 
1761
                            ok
 
1762
                    end;
 
1763
                true ->
 
1764
                    ok
 
1765
            end,
 
1766
            insert_cstruct(Tid, Cs, true),
 
1767
            {true, optional}
 
1768
    end;
 
1769
 
 
1770
prepare_op(Tid, {op, del_table_copy, _Storage, Node, TabDef}, _WaitFor) ->
 
1771
    Cs = list2cs(TabDef),
 
1772
    Tab = Cs#cstruct.name,
 
1773
    
 
1774
    if
 
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()]),
 
1779
            receive 
 
1780
                {Pid, updated} -> 
 
1781
                    {true, optional};
 
1782
                {Pid, FailReason} ->
 
1783
                    mnesia:abort(FailReason);
 
1784
                {'EXIT', Pid, Reason} ->
 
1785
                    mnesia:abort(Reason)
 
1786
            end;        
 
1787
        true ->
 
1788
            {true, optional}
 
1789
    end;
 
1790
 
 
1791
prepare_op(_Tid, {op, change_table_copy_type,  N, FromS, ToS, TabDef}, _WaitFor)
 
1792
  when N == node() ->
 
1793
    Cs = list2cs(TabDef),
 
1794
    Tab = Cs#cstruct.name,
 
1795
 
 
1796
    NotActive = mnesia_lib:not_active_here(Tab),
 
1797
    
 
1798
    if 
 
1799
        NotActive == true ->
 
1800
            mnesia:abort({not_active, Tab, node()});
 
1801
        
 
1802
        Tab == schema ->            
 
1803
            case {FromS, ToS} of
 
1804
                {ram_copies, disc_copies} ->
 
1805
                    case mnesia:system_info(schema_location) of
 
1806
                        opt_disc ->
 
1807
                            ignore;
 
1808
                        _ ->
 
1809
                            mnesia:abort({combine_error,  Tab, node(),
 
1810
                                          "schema_location must be opt_disc"})
 
1811
                    end,
 
1812
                    Dir = mnesia_lib:dir(),    
 
1813
                    case opt_create_dir(true, Dir) of
 
1814
                        ok ->
 
1815
                            purge_dir(Dir, []),
 
1816
                            mnesia_log:purge_all_logs(),
 
1817
                            set(use_dir, true),
 
1818
                            mnesia_log:init(),
 
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),
 
1822
 
 
1823
                            mnesia_dumper:raw_named_dump_table(Tab, dmp),
 
1824
                            mnesia_checkpoint:tm_change_table_copy_type(Tab, FromS, ToS);
 
1825
                        {error, Reason} ->
 
1826
                            mnesia:abort(Reason)
 
1827
                    end;
 
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});
 
1833
                _ ->
 
1834
                    mnesia:abort({combine_error, Tab, ToS})
 
1835
            end;
 
1836
        
 
1837
        FromS == ram_copies ->      
 
1838
            case mnesia_monitor:use_dir() of
 
1839
                true -> 
 
1840
                    Dat = mnesia_lib:tab2dcd(Tab),
 
1841
                    case mnesia_lib:exists(Dat) of
 
1842
                        true ->
 
1843
                            mnesia:abort({combine_error, Tab, node(),
 
1844
                                          "Table dump exists"});
 
1845
                        false ->
 
1846
                            case ToS of 
 
1847
                                disc_copies -> 
 
1848
                                    mnesia_log:ets2dcd(Tab, dmp);
 
1849
                                disc_only_copies ->
 
1850
                                    mnesia_dumper:raw_named_dump_table(Tab, dmp)
 
1851
                            end,
 
1852
                            mnesia_checkpoint:tm_change_table_copy_type(Tab, FromS, ToS)
 
1853
                    end;
 
1854
                false ->
 
1855
                    mnesia:abort({has_no_disc, node()})
 
1856
            end;
 
1857
        
 
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
 
1866
                loaded -> ok;
 
1867
                Reason ->
 
1868
                    Err = "Failed to copy disc data to ram",
 
1869
                    mnesia:abort({system_limit, Tab, {Err,Reason}})
 
1870
            end;
 
1871
        true ->
 
1872
            ignore
 
1873
    end,
 
1874
    {true, mandatory};
 
1875
 
 
1876
prepare_op(_Tid, {op, change_table_copy_type,  N, _FromS, _ToS, _TabDef}, _WaitFor)
 
1877
  when N /= node() ->
 
1878
    {true, mandatory};
 
1879
 
 
1880
prepare_op(_Tid, {op, delete_table, _TabDef}, _WaitFor) ->
 
1881
    {true, mandatory};
 
1882
 
 
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
 
1887
        true ->
 
1888
            case mnesia_monitor:use_dir() of
 
1889
                true -> 
 
1890
                    mnesia_log:ets2dcd(Tab, dmp),
 
1891
                    Size = mnesia:table_info(Tab, size),
 
1892
                    {true, [{op, dump_table, Size, TabDef}], optional};
 
1893
                false ->
 
1894
                    mnesia:abort({has_no_disc, node()})
 
1895
            end;
 
1896
        false ->
 
1897
            {false, optional}
 
1898
    end;
 
1899
 
 
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
 
1903
        unknown ->
 
1904
            {true, optional};
 
1905
        Storage ->
 
1906
            Tab = Cs#cstruct.name,
 
1907
            Stab = mnesia_snmp_hook:create_table(Ustruct, Tab, Storage),
 
1908
            mnesia_lib:set({Tab, {index, snmp}}, Stab),
 
1909
            {true, optional}
 
1910
    end;
 
1911
 
 
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
 
1917
        unknown ->
 
1918
            {true, mandatory};
 
1919
        Storage ->
 
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
 
1929
                {'EXIT', Reason} ->
 
1930
                    mnesia_lib:db_fixtable(Storage, Tab, false),
 
1931
                    exit({"Bad transform function", Tab, Fun, node(), Reason});
 
1932
                Objs ->
 
1933
                    mnesia_lib:db_fixtable(Storage, Tab, false),
 
1934
                    {true, Objs, mandatory}
 
1935
            end
 
1936
    end;
 
1937
 
 
1938
prepare_op(_Tid, _Op, _WaitFor) ->
 
1939
    {true, optional}.
 
1940
 
 
1941
 
 
1942
create_ram_table(Tab, Type) ->
 
1943
    Args = [{keypos, 2}, public, named_table, Type],
 
1944
    case mnesia_monitor:unsafe_mktab(Tab, Args) of
 
1945
        Tab ->
 
1946
            ok;
 
1947
        {error,Reason} ->
 
1948
            Err = "Failed to create ets table",
 
1949
            mnesia:abort({system_limit, Tab, {Err,Reason}})
 
1950
    end.
 
1951
create_disc_table(Tab) ->
 
1952
    File = mnesia_lib:tab2dcd(Tab),
 
1953
    file:delete(File),
 
1954
    FArg = [{file, File}, {name, {mnesia,create}}, 
 
1955
            {repair, false}, {mode, read_write}],
 
1956
    case mnesia_monitor:open_log(FArg) of
 
1957
        {ok,Log} ->
 
1958
            mnesia_monitor:unsafe_close_log(Log),
 
1959
            ok;
 
1960
        {error,Reason} ->
 
1961
            Err = "Failed to create disc table",
 
1962
            mnesia:abort({system_limit, Tab, {Err,Reason}})
 
1963
    end.
 
1964
create_disc_only_table(Tab,Type) ->
 
1965
    File = mnesia_lib:tab2dat(Tab),
 
1966
    file:delete(File),
 
1967
    Args = [{file, mnesia_lib:tab2dat(Tab)},
 
1968
            {type, mnesia_lib:disk_type(Tab, Type)},
 
1969
            {keypos, 2},
 
1970
            {repair, mnesia_monitor:get_env(auto_repair)}],
 
1971
    case mnesia_monitor:unsafe_open_dets(Tab, Args) of
 
1972
        {ok, _} ->
 
1973
            ok;
 
1974
        {error,Reason} ->
 
1975
            Err = "Failed to create disc table",
 
1976
            mnesia:abort({system_limit, Tab, {Err,Reason}})
 
1977
    end.
 
1978
 
 
1979
 
 
1980
receive_sync([], Pids) ->
 
1981
    Pids;
 
1982
receive_sync(Nodes, Pids) ->
 
1983
    receive
 
1984
        {sync_trans, Pid} ->
 
1985
            Node = node(Pid),       
 
1986
            receive_sync(lists:delete(Node, Nodes), [Pid | Pids]);
 
1987
        Else ->
 
1988
            {abort, Else}
 
1989
    end.
 
1990
 
 
1991
lock_del_table(Tab, Node, Cs, Father) ->
 
1992
    Ns = val({schema, active_replicas}),
 
1993
    Lock = fun() ->
 
1994
                   mnesia:write_lock_table(Tab),
 
1995
                   {Res, []} = rpc:multicall(Ns, ?MODULE, set_where_to_read, [Tab, Node, Cs]),
 
1996
                   Filter = fun(ok) ->
 
1997
                                    false;
 
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 
 
2001
                                    %% getting a lock. 
 
2002
                                    false;
 
2003
                               (_) ->
 
2004
                                    true
 
2005
                            end,
 
2006
                   [] = lists:filter(Filter, Res),
 
2007
                   ok
 
2008
           end,    
 
2009
    case mnesia:transaction(Lock) of
 
2010
        {'atomic', ok} ->
 
2011
            Father ! {self(), updated};
 
2012
        {aborted, R} ->
 
2013
            Father ! {self(), R}
 
2014
    end,
 
2015
    unlink(Father),
 
2016
    exit(normal).
 
2017
 
 
2018
set_where_to_read(Tab, Node, Cs) ->
 
2019
    case mnesia_lib:val({Tab, where_to_read}) of 
 
2020
        Node ->
 
2021
            case Cs#cstruct.local_content of
 
2022
                true ->
 
2023
                    ok;
 
2024
                false ->
 
2025
                    mnesia_lib:set_remote_where_to_read(Tab, [Node]),
 
2026
                    ok
 
2027
            end;
 
2028
        _ ->
 
2029
            ok
 
2030
    end.
 
2031
 
 
2032
%% Build up the list in reverse order.
 
2033
transform_objs(_Fun, _Tab, _RT, '$end_of_table', _NewArity, _Storage, _Type, Acc) ->
 
2034
    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),
 
2038
    Oid = {Tab, Key},   
 
2039
    NewObjs = {Ws, Ds} = transform_obj(Tab, RecName, Key, Fun, Objs, A, Type, [], []),
 
2040
    if 
 
2041
        NewObjs == {[], []} -> 
 
2042
            transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type, Acc);
 
2043
        Type == bag ->      
 
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]);
 
2047
        Ds == [] -> 
 
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]);
 
2051
        Ws == [] ->
 
2052
            transform_objs(Fun, Tab, RecName, NextKey, A, Storage, Type,
 
2053
                           [{op, rec, Storage, {Oid, Ds, write}} | Acc]);
 
2054
        true ->
 
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])
 
2058
    end.
 
2059
 
 
2060
transform_obj(Tab, RecName, Key, Fun, [Obj|Rest], NewArity, Type, Ws, Ds) ->
 
2061
    NewObj = Fun(Obj),
 
2062
    if
 
2063
        size(NewObj) /= NewArity ->
 
2064
            exit({"Bad arity", Obj, NewObj});
 
2065
        NewObj == Obj ->
 
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);
 
2070
        NewObj == delete -> 
 
2071
            case Type of 
 
2072
                bag -> %% Just don't write that object
 
2073
                   transform_obj(Tab, RecName, Key, Fun, Rest, 
 
2074
                                 NewArity, Type, Ws, Ds); 
 
2075
                _ ->
 
2076
                    transform_obj(Tab, RecName, Key, Fun, Rest, NewArity, 
 
2077
                                  Type, Ws, [NewObj | Ds])
 
2078
            end;
 
2079
        true ->
 
2080
            exit({"Bad key or Record Name", Obj, NewObj})
 
2081
    end;
 
2082
transform_obj(_Tab, _RecName, _Key, _Fun, [], _NewArity, _Type, Ws, Ds) ->
 
2083
    {lists:reverse(Ws), lists:reverse(Ds)}.
 
2084
 
 
2085
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2086
%% Undo prepare of commit
 
2087
undo_prepare_commit(Tid, Commit) ->
 
2088
    case Commit#commit.schema_ops of
 
2089
        [] ->
 
2090
            ignore;
 
2091
        Ops ->
 
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)
 
2095
    end,
 
2096
    Commit.
 
2097
 
 
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);
 
2103
        _ ->
 
2104
            undo_prepare_ops(Tid, Ops),
 
2105
            undo_prepare_op(Tid, Op)
 
2106
    end;
 
2107
undo_prepare_ops(_Tid, []) ->
 
2108
    [].
 
2109
 
 
2110
undo_prepare_op(_Tid, {op, announce_im_running, _, _, Running, RemoteRunning}) ->
 
2111
    case lists:member(node(), Running) of
 
2112
        true ->
 
2113
            unannounce_im_running(RemoteRunning -- Running);
 
2114
        false ->
 
2115
            unannounce_im_running(Running -- RemoteRunning)
 
2116
    end;
 
2117
 
 
2118
undo_prepare_op(_Tid, {op, sync_trans}) ->
 
2119
    ok;
 
2120
 
 
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
 
2127
        unknown -> 
 
2128
            ok;
 
2129
        ram_copies ->
 
2130
            ram_delete_table(Tab, ram_copies);
 
2131
        disc_copies ->
 
2132
            ram_delete_table(Tab, disc_copies),
 
2133
            DcdFile = mnesia_lib:tab2dcd(Tab),
 
2134
            %%      disc_delete_table(Tab, Storage),
 
2135
            file:delete(DcdFile);
 
2136
        disc_only_copies ->
 
2137
            mnesia_monitor:unsafe_close_dets(Tab),
 
2138
            Dat = mnesia_lib:tab2dat(Tab),
 
2139
            %%      disc_delete_table(Tab, Storage),
 
2140
            file:delete(Dat)
 
2141
    end;
 
2142
            
 
2143
undo_prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}) ->
 
2144
    Cs = list2cs(TabDef),
 
2145
    Tab = Cs#cstruct.name,
 
2146
    if
 
2147
        Tab == schema ->
 
2148
            true; % Nothing to prepare
 
2149
        Node == node() ->
 
2150
            mnesia_checkpoint:tm_del_copy(Tab, Node),
 
2151
            mnesia_controller:unannounce_add_table_copy(Tab, Node),
 
2152
            if
 
2153
                Storage == disc_only_copies; Tab == schema ->
 
2154
                    mnesia_monitor:close_dets(Tab),
 
2155
                    file:delete(mnesia_lib:tab2dat(Tab));
 
2156
                true ->
 
2157
                    file:delete(mnesia_lib:tab2dcd(Tab))
 
2158
            end,
 
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
 
2162
        Node /= node() ->
 
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
 
2166
    end;
 
2167
    
 
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);
 
2173
 
 
2174
 
 
2175
undo_prepare_op(_Tid, {op, change_table_copy_type, N, FromS, ToS, TabDef}) 
 
2176
        when N == node() ->
 
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),
 
2181
    
 
2182
    case {FromS, ToS} of
 
2183
        {ram_copies, disc_copies} when Tab == schema ->
 
2184
            file:delete(Dmp),
 
2185
            mnesia_log:purge_some_logs(),
 
2186
            set(use_dir, false);
 
2187
        {ram_copies, disc_copies} ->
 
2188
            file:delete(Dmp);
 
2189
        {ram_copies, disc_only_copies} ->
 
2190
            file:delete(Dmp);
 
2191
        {disc_only_copies, _} ->
 
2192
            ram_delete_table(Tab, ram_copies);
 
2193
        _ ->
 
2194
            ignore
 
2195
    end;
 
2196
 
 
2197
undo_prepare_op(_Tid, {op, dump_table, _Size, TabDef}) ->
 
2198
    Cs = list2cs(TabDef),
 
2199
    case lists:member(node(), Cs#cstruct.ram_copies) of
 
2200
        true ->
 
2201
            Tab = Cs#cstruct.name,
 
2202
            Dmp = mnesia_lib:tab2dmp(Tab),
 
2203
            file:delete(Dmp);
 
2204
        false ->
 
2205
            ignore
 
2206
    end;
 
2207
 
 
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
 
2211
        unknown ->
 
2212
            true;
 
2213
        _Storage ->
 
2214
            Tab = Cs#cstruct.name,
 
2215
            case ?catch_val({Tab, {index, snmp}}) of
 
2216
                {'EXIT',_} ->
 
2217
                    ignore;
 
2218
                Stab ->
 
2219
                    mnesia_snmp_hook:delete_table(Tab, Stab),
 
2220
                    mnesia_lib:unset({Tab, {index, snmp}})
 
2221
            end
 
2222
    end;
 
2223
 
 
2224
undo_prepare_op(_Tid, _Op) ->
 
2225
    ignore.
 
2226
 
 
2227
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 
2228
 
 
2229
ram_delete_table(Tab, Storage) ->
 
2230
    case Storage of
 
2231
        unknown ->
 
2232
            ignore;
 
2233
        disc_only_copies ->
 
2234
            ignore;
 
2235
        _Else -> 
 
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
 
2241
                {'EXIT', _} ->
 
2242
                    ignore;
 
2243
                Etab ->
 
2244
                    catch mnesia_snmp_hook:delete_table(Tab, Etab)
 
2245
            end,
 
2246
            catch ?ets_delete_table(Tab)
 
2247
    end.
 
2248
 
 
2249
purge_dir(Dir, KeepFiles) ->
 
2250
    Suffixes = known_suffixes(),
 
2251
    purge_dir(Dir, KeepFiles, Suffixes).
 
2252
 
 
2253
purge_dir(Dir, KeepFiles, Suffixes) ->
 
2254
    case dir_exists(Dir) of
 
2255
        true ->
 
2256
            {ok, AllFiles} = file:list_dir(Dir),
 
2257
            purge_known_files(AllFiles, KeepFiles, Dir, Suffixes);
 
2258
        false ->
 
2259
            ok
 
2260
    end.
 
2261
 
 
2262
purge_tmp_files() ->
 
2263
    case mnesia_monitor:use_dir() of
 
2264
        true ->
 
2265
            Dir = mnesia_lib:dir(),
 
2266
            KeepFiles = [],
 
2267
            Exists = mnesia_lib:exists(mnesia_lib:tab2dat(schema)),
 
2268
            case Exists of
 
2269
                true ->
 
2270
                    Suffixes = tmp_suffixes(),
 
2271
                    purge_dir(Dir, KeepFiles, Suffixes);
 
2272
                false ->
 
2273
                    %% Interrupted change of storage type
 
2274
                    %% for schema table
 
2275
                    Suffixes = known_suffixes(),
 
2276
                    purge_dir(Dir, KeepFiles, Suffixes),
 
2277
                    mnesia_lib:set(use_dir, false)
 
2278
                end;
 
2279
 
 
2280
        false ->
 
2281
            ok
 
2282
    end.
 
2283
 
 
2284
purge_known_files([File | Tail], KeepFiles, Dir, Suffixes) ->
 
2285
    case lists:member(File, KeepFiles) of
 
2286
        true ->
 
2287
            ignore;
 
2288
        false ->
 
2289
            case has_known_suffix(File, Suffixes, false) of
 
2290
                false ->
 
2291
                    ignore;
 
2292
                true ->
 
2293
                    AbsFile = filename:join([Dir, File]),
 
2294
                    file:delete(AbsFile)
 
2295
            end
 
2296
    end,
 
2297
    purge_known_files(Tail, KeepFiles, Dir, Suffixes);
 
2298
purge_known_files([], _KeepFiles, _Dir, _Suffixes) ->
 
2299
    ok.
 
2300
 
 
2301
has_known_suffix(_File, _Suffixes, true) ->
 
2302
    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) ->
 
2306
    Bool.
 
2307
            
 
2308
known_suffixes() -> real_suffixes() ++ tmp_suffixes().
 
2309
 
 
2310
real_suffixes() ->  [".DAT", ".LOG", ".BUP", ".DCL", ".DCD"].
 
2311
 
 
2312
tmp_suffixes() -> [".TMP", ".BUPTMP", ".RET", ".DMP"].
 
2313
 
 
2314
info() ->
 
2315
    Tabs = lists:sort(val({schema, tables})),
 
2316
    lists:foreach(fun(T) -> info(T) end, Tabs),
 
2317
    ok.
 
2318
 
 
2319
info(Tab) ->
 
2320
    Props = get_table_properties(Tab),
 
2321
    io:format("-- Properties for ~w table --- ~n",[Tab]),
 
2322
    info2(Tab, Props).
 
2323
info2(Tab, [{cstruct, _V} | Tail]) -> % Ignore cstruct
 
2324
    info2(Tab, Tail);
 
2325
info2(Tab, [{frag_hash, _V} | Tail]) -> % Ignore frag_hash
 
2326
    info2(Tab, Tail);
 
2327
info2(Tab, [{P, V} | Tail]) ->
 
2328
    io:format("~-20w -> ~p~n",[P,V]),
 
2329
    info2(Tab, Tail);
 
2330
info2(_, []) -> 
 
2331
    io:format("~n", []).
 
2332
 
 
2333
get_table_properties(Tab) ->
 
2334
    case catch mnesia_lib:db_match_object(ram_copies, 
 
2335
                                          mnesia_gvar, {{Tab, '_'}, '_'}) of
 
2336
        {'EXIT', _} ->
 
2337
            mnesia:abort({no_exists, Tab, all});
 
2338
        RawGvar ->
 
2339
            case [{Item, Val} || {{_Tab, Item}, Val} <- RawGvar] of
 
2340
                [] ->
 
2341
                    [];
 
2342
                Gvar ->
 
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])
 
2347
            end
 
2348
    end.
 
2349
 
 
2350
%%%%%%%%%%% RESTORE %%%%%%%%%%%
 
2351
 
 
2352
-record(r, {iter = schema,
 
2353
            module,
 
2354
            table_options = [],
 
2355
            default_op = clear_tables,
 
2356
            tables = [],
 
2357
            opaque,
 
2358
            insert_op = error_fun,
 
2359
            recs = error_recs
 
2360
           }).
 
2361
 
 
2362
restore(Opaque) -> 
 
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
 
2373
                {error, Reason} ->
 
2374
                    {aborted, Reason};
 
2375
                BupSchema -> 
 
2376
                    schema_transaction(fun() -> do_restore(R, BupSchema) end)
 
2377
            end;
 
2378
        {'EXIT', Reason} ->
 
2379
            {aborted, Reason}
 
2380
    end;
 
2381
restore(_Opaque, Args, Module) ->
 
2382
    {aborted, {badarg, Args, Module}}.
 
2383
 
 
2384
check_restore_arg({module, Mod}, R) when atom(Mod) ->
 
2385
    R#r{module = Mod};
 
2386
 
 
2387
check_restore_arg({clear_tables, List}, R) when list(List) ->
 
2388
    case lists:member(schema, List) of
 
2389
        false ->
 
2390
            TableList = [{Tab, clear_tables} || Tab <- List],
 
2391
            R#r{table_options = R#r.table_options ++ TableList};
 
2392
        true ->
 
2393
            exit({badarg, {clear_tables, schema}})
 
2394
    end;
 
2395
check_restore_arg({recreate_tables, List}, R) when list(List) ->
 
2396
    case lists:member(schema, List) of
 
2397
        false ->
 
2398
            TableList = [{Tab, recreate_tables} || Tab <- List],
 
2399
            R#r{table_options = R#r.table_options ++ TableList};
 
2400
        true ->
 
2401
            exit({badarg, {recreate_tables, schema}})
 
2402
    end;
 
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) -> 
 
2410
    case Op of 
 
2411
        clear_tables -> ok;
 
2412
        recreate_tables -> ok;
 
2413
        keep_tables -> ok;
 
2414
        skip_tables -> ok;
 
2415
        Else ->
 
2416
            exit({badarg, {bad_default_op, Else}})
 
2417
    end,
 
2418
    R#r{default_op = Op};
 
2419
 
 
2420
check_restore_arg(BadArg,_) ->
 
2421
    exit({badarg, BadArg}).
 
2422
 
 
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].
 
2428
 
 
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)
 
2435
    end.
 
2436
 
 
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}} ->
 
2441
            {Rest, NRecs} =
 
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});
 
2445
        false ->
 
2446
            Rest = skip_tab_items(Recs, Tab),
 
2447
            restore_items(Rest, Header, Schema, R)
 
2448
    end;
 
2449
    
 
2450
restore_items([], _Header, _Schema, R) ->
 
2451
    R.
 
2452
 
 
2453
restore_func(Tab, R) ->
 
2454
    case lists:keysearch(Tab, 1, R#r.table_options) of 
 
2455
        {value, {Tab, OP}} ->
 
2456
            OP;
 
2457
        false ->
 
2458
            R#r.default_op
 
2459
    end.
 
2460
 
 
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.
 
2466
    
 
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
 
2472
        clear_tables -> 
 
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);
 
2479
        recreate_tables -> 
 
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
 
2487
%               {'EXIT', _} ->
 
2488
%                   ignore;
 
2489
%               OldCs when record(OldCs, cstruct) -> 
 
2490
%                   do_delete_table(Tab)
 
2491
%           end,
 
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);
 
2496
        keep_tables -> 
 
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);
 
2503
        skip_tables -> 
 
2504
            restore_schema(Schema, R)
 
2505
    end;
 
2506
 
 
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) ->
 
2512
    R.
 
2513
 
 
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);
 
2518
 
 
2519
restore_tab_items(Rest, _Tab, _RecName, _Where, _Snmp, Recs, _Op) ->
 
2520
    {Rest, Recs}.
 
2521
 
 
2522
skip_tab_items([Rec| Rest], Tab) 
 
2523
  when element(1, Rec) == Tab ->
 
2524
    skip_tab_items(Rest, Tab);
 
2525
skip_tab_items(Recs, _) ->
 
2526
    Recs.
 
2527
 
 
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}}.
 
2533
 
 
2534
do_dump_tables(Tabs) ->
 
2535
    TidTs = get_tid_ts_and_lock(schema, write),
 
2536
    insert_schema_ops(TidTs, make_dump_tables(Tabs)).
 
2537
 
 
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([]) ->
 
2548
    [].
 
2549
 
 
2550
%% Merge the local schema with the schema on other nodes
 
2551
merge_schema() ->
 
2552
    schema_transaction(fun() -> do_merge_schema() end).
 
2553
 
 
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
 
2560
        [Node | _] ->
 
2561
            %% Time for a schema merging party!
 
2562
            mnesia_locker:wlock_no_exist(Tid, Store, schema, [Node]),
 
2563
            
 
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),
 
2569
                    if 
 
2570
                        RemoteRunning /= RemoteRunning1 ->
 
2571
                            mnesia_lib:error("Mnesia on ~p could not connect to node(s) ~p~n", 
 
2572
                                             [node(), RemoteRunning1 -- RemoteRunning]);
 
2573
                        true -> ok
 
2574
                    end,
 
2575
                    NeedsLock = RemoteRunning -- LockedAlready,
 
2576
                    mnesia_locker:wlock_no_exist(Tid, Store, schema, NeedsLock),
 
2577
 
 
2578
                    {value, SchemaCs} =
 
2579
                        lists:keysearch(schema, #cstruct.name, Cstructs),
 
2580
 
 
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),
 
2585
                    
 
2586
                    %% Introduce remote tables to local node
 
2587
                    do_insert_schema_ops(Store, make_merge_schema(Node, Cstructs)),
 
2588
                    
 
2589
                    %% Introduce local tables to remote nodes
 
2590
                    Tabs = val({schema, tables}),
 
2591
                    Ops = [{op, merge_schema, get_create_list(T)}
 
2592
                           || T <- Tabs,
 
2593
                              not lists:keymember(T, #cstruct.name, Cstructs)],
 
2594
                    do_insert_schema_ops(Store, Ops),
 
2595
                    
 
2596
                    %% Ensure that the txn will be committed on all nodes
 
2597
                    announce_im_running(RemoteRunning, SchemaCs), 
 
2598
                    {merged, Running, RemoteRunning};
 
2599
                {error, Reason} ->
 
2600
                    {"Cannot get cstructs", Node, Reason};
 
2601
                {badrpc, Reason} ->
 
2602
                    {"Cannot get cstructs", Node, {badrpc, Reason}}
 
2603
            end;
 
2604
        [] ->
 
2605
            %% No more nodes to merge schema with
 
2606
            not_merged
 
2607
    end.
 
2608
 
 
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, []) ->
 
2613
    [].
 
2614
 
 
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),        
 
2628
    
 
2629
    if
 
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
 
2634
            [];
 
2635
        
 
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
 
2641
            if
 
2642
                HasLocalMaster == true,
 
2643
                HasRemoteMaster == false ->
 
2644
                    %% Choose local cstruct,
 
2645
                    %% since it's the master
 
2646
                    [{op, merge_schema, cs2list(Cs)}];
 
2647
 
 
2648
                HasRemoteMaster == true,
 
2649
                HasLocalMaster == false ->
 
2650
                    %% Choose remote cstruct,
 
2651
                    %% since it's the master
 
2652
                    [{op, merge_schema, cs2list(RemoteCs)}];
 
2653
                
 
2654
                true ->
 
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)]),
 
2659
                    throw(Str)
 
2660
            end;
 
2661
        
 
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]),
 
2666
            throw(Str);    
 
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]),
 
2671
            throw(Str);
 
2672
 
 
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)}];
 
2678
        
 
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)}];
 
2684
 
 
2685
        Cs > RemoteCs ->
 
2686
            %% Choose remote cstruct
 
2687
            MergedCs = merge_cstructs(RemoteCs, Cs, Force),
 
2688
            [{op, merge_schema, cs2list(MergedCs)}];
 
2689
        
 
2690
        true ->
 
2691
            %% Choose local cstruct
 
2692
            MergedCs = merge_cstructs(Cs, RemoteCs, Force),
 
2693
            [{op, merge_schema, cs2list(MergedCs)}]
 
2694
    end;
 
2695
 
 
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
 
2704
        {'EXIT', _} ->
 
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 ->
 
2708
            if
 
2709
                Cs#cstruct.version == RemoteCs#cstruct.version ->
 
2710
                    %% We have exactly the same version of the
 
2711
                    %% table def
 
2712
                    [];
 
2713
                
 
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)}];
 
2721
                
 
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)}]
 
2727
            end;
 
2728
        Cs ->
 
2729
            %% Different cookies, not possible to merge
 
2730
            if
 
2731
                HasLocalMaster == true,
 
2732
                HasRemoteMaster == false ->
 
2733
                    %% Choose local cstruct,
 
2734
                    %% since it's the master
 
2735
                    [{op, merge_schema, cs2list(Cs)}];
 
2736
 
 
2737
                HasRemoteMaster == true,
 
2738
                HasLocalMaster == false ->
 
2739
                    %% Choose remote cstruct,
 
2740
                    %% since it's the master
 
2741
                    [{op, merge_schema, cs2list(RemoteCs)}];
 
2742
                
 
2743
                true ->
 
2744
                    Str = io_lib:format("Bad cookie in table definition"
 
2745
                                        " ~w: ~w = ~w, ~w = ~w~n",
 
2746
                                        [Tab, node(), Cs, Node, RemoteCs]),
 
2747
                    throw(Str)
 
2748
            end
 
2749
    end.
 
2750
 
 
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.
 
2755
%%
 
2756
%% Returns a new cstruct or issues a fatal error
 
2757
merge_cstructs(Cs, RemoteCs, Force) ->
 
2758
    verify_cstruct(Cs),
 
2759
    case catch do_merge_cstructs(Cs, RemoteCs, Force) of
 
2760
        {'EXIT', {aborted, _Reason}} when Force == true ->
 
2761
            Cs;
 
2762
        {'EXIT', Reason} ->
 
2763
            exit(Reason);
 
2764
        MergedCs when record(MergedCs, cstruct) ->
 
2765
            MergedCs;
 
2766
        Other ->
 
2767
            throw(Other)
 
2768
    end.
 
2769
 
 
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),
 
2778
    MergedCs2.
 
2779
 
 
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
 
2784
        {same, _Storage} ->
 
2785
            merge_storage_type(Ns, AnythingNew, Cs, RemoteCs, Force);
 
2786
        {diff, Storage} ->
 
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);
 
2791
        Other ->
 
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]),
 
2795
            throw(Str)
 
2796
    end;
 
2797
merge_storage_type([], AnythingNew, MergedCs, _RemoteCs, _Force) ->
 
2798
    {AnythingNew, MergedCs}.
 
2799
 
 
2800
compare_storage_type(_Retry, Any, Any) ->
 
2801
    {same, Any};
 
2802
compare_storage_type(_Retry, unknown, Any) ->
 
2803
    {diff, 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) ->
 
2811
    incompatible.
 
2812
  
 
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)}.
 
2822
 
 
2823
%% BUGBUG: Verify match of frag info; equalit demanded for all but add_node
 
2824
 
 
2825
merge_versions(AnythingNew, Cs, RemoteCs, Force) ->
 
2826
    if
 
2827
        Cs#cstruct.name == schema ->
 
2828
            ok;
 
2829
        Cs#cstruct.name /= schema,
 
2830
        Cs#cstruct.cookie == RemoteCs#cstruct.cookie ->
 
2831
            ok;
 
2832
        Force == true ->
 
2833
            ok;
 
2834
        true ->
 
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]),
 
2838
            throw(Str)
 
2839
    end,
 
2840
    if
 
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);
 
2851
        Force == true ->
 
2852
            do_merge_versions(AnythingNew, Cs, RemoteCs);
 
2853
        true ->
 
2854
            Str1 = io_lib:format("Cannot merge definitions of "
 
2855
                                "table ~w. Local = ~w, Remote = ~w~n",
 
2856
                                [Cs#cstruct.name, Cs, RemoteCs]),
 
2857
            throw(Str1)
 
2858
    end.
 
2859
 
 
2860
do_merge_versions(AnythingNew, MergedCs, RemoteCs) ->
 
2861
    {{Major1, Minor1}, _Detail1} = MergedCs#cstruct.version,
 
2862
    {{Major2, Minor2}, _Detail2} = RemoteCs#cstruct.version,
 
2863
    if
 
2864
        MergedCs#cstruct.version == RemoteCs#cstruct.version ->
 
2865
            MergedCs;
 
2866
        AnythingNew == false ->
 
2867
            MergedCs;
 
2868
        Major1 == Major2 ->
 
2869
            Minor = lists:max([Minor1, Minor2]),
 
2870
            V = {{Major1, Minor}, dummy},
 
2871
            incr_version(MergedCs#cstruct{version = V});
 
2872
        Major1 /= Major2 ->
 
2873
            Major = lists:max([Major1, Major2]),
 
2874
            V = {{Major, 0}, dummy},
 
2875
            incr_version(MergedCs#cstruct{version = V})
 
2876
    end.
 
2877
 
 
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
 
2881
        true ->
 
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);
 
2885
        false ->
 
2886
            ignore
 
2887
    end,
 
2888
    announce_im_running(Ns, SchemaCs);
 
2889
announce_im_running([], _) ->
 
2890
    [].
 
2891
 
 
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([]) ->
 
2898
    [].
 
2899