Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit fdd4494387ed0cbaa3c9e33a201aa77e656deccb @odo committed May 6, 2012
91 patches/mnesia.erl.patch
@@ -0,0 +1,91 @@
+--- mnesia_original/src/mnesia.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia.erl 2012-03-14 20:22:07.000000000 +0100
+@@ -1602,11 +1602,11 @@
+ do_dirty_update_counter(async_dirty, Tab, Key, Incr).
+
+ do_dirty_update_counter(SyncMode, Tab, Key, Incr)
+ when is_atom(Tab), Tab /= schema, is_integer(Incr) ->
+ case ?catch_val({Tab, record_validation}) of
+- {RecName, 3, set} ->
++ {RecName, 3, Type} when Type =:= set orelse Type =:= ordered_set ->
+ Oid = {Tab, Key},
+ mnesia_tm:dirty(SyncMode, {Oid, {RecName, Incr}, update_counter});
+ _ ->
+ abort({combine_error, Tab, update_counter})
+ end;
+@@ -1907,10 +1907,12 @@
+ info_reply(catch ?ets_info(Tab, Item), Tab, Item);
+ disc_copies ->
+ info_reply(catch ?ets_info(Tab, Item), Tab, Item);
+ disc_only_copies ->
+ info_reply(catch dets:info(Tab, Item), Tab, Item);
++ {external_copies, Mod} ->
++ info_reply(catch Mod:info(Tab, Item), Tab, Item);
+ unknown ->
+ bad_info_reply(Tab, Item);
+ {'EXIT', _} ->
+ bad_info_reply(Tab, Item)
+ end.
+@@ -2022,18 +2024,19 @@
+ MasterTabs = mnesia_recover:get_master_node_tables(),
+ io:format("master node tables = ~p~n", [lists:sort(MasterTabs)]),
+
+ Tabs = system_info(tables),
+
+- {Unknown, Ram, Disc, DiscOnly} =
+- lists:foldl(fun storage_count/2, {[], [], [], []}, Tabs),
++ {Unknown, Ram, Disc, DiscOnly, Ext} =
++ lists:foldl(fun storage_count/2, {[], [], [], [], []}, Tabs),
+
+ io:format("remote = ~p~n", [lists:sort(Unknown)]),
+ io:format("ram_copies = ~p~n", [lists:sort(Ram)]),
+ io:format("disc_copies = ~p~n", [lists:sort(Disc)]),
+ io:format("disc_only_copies = ~p~n", [lists:sort(DiscOnly)]),
+-
++ io:format("external_copies = ~p~n", [lists:sort(Ext)]),
++
+ Rfoldl = fun(T, Acc) ->
+ Rpat =
+ case val({T, access_mode}) of
+ read_only ->
+ lists:sort([{A, read_only} || A <- val({T, active_replicas})]);
+@@ -2049,16 +2052,17 @@
+ end,
+ Repl = lists:foldl(Rfoldl, [], Tabs),
+ Rdisp = fun({Rpat, Rtabs}) -> io:format("~p = ~p~n", [Rpat, Rtabs]) end,
+ lists:foreach(Rdisp, lists:sort(Repl)).
+
+-storage_count(T, {U, R, D, DO}) ->
++storage_count(T, {U, R, D, DO, Ext}) ->
+ case table_info(T, storage_type) of
+- unknown -> {[T | U], R, D, DO};
+- ram_copies -> {U, [T | R], D, DO};
+- disc_copies -> {U, R, [T | D], DO};
+- disc_only_copies -> {U, R, D, [T | DO]}
++ unknown -> {[T | U], R, D, DO, Ext};
++ ram_copies -> {U, [T | R], D, DO, Ext};
++ disc_copies -> {U, R, [T | D], DO, Ext};
++ disc_only_copies -> {U, R, D, [T | DO], Ext};
++ {external_copies, _} -> {U, R, D, DO, [T | Ext]}
+ end.
+
+ system_info(Item) ->
+ case catch system_info2(Item) of
+ {'EXIT',Error} -> abort(Error);
+@@ -2070,13 +2074,14 @@
+ [{I, system_info(I)} || I <- Items];
+
+ system_info2(db_nodes) ->
+ DiscNs = ?catch_val({schema, disc_copies}),
+ RamNs = ?catch_val({schema, ram_copies}),
++ ExtNs = ?catch_val({schema, external_copies}),
+ if
+- is_list(DiscNs), is_list(RamNs) ->
+- DiscNs ++ RamNs;
++ is_list(DiscNs), is_list(RamNs), is_list(ExtNs) ->
++ DiscNs ++ RamNs ++ ExtNs;
+ true ->
+ case mnesia_schema:read_nodes() of
+ {ok, Nodes} -> Nodes;
+ {error,Reason} -> exit(Reason)
+ end
26 patches/mnesia.hrl.patch
@@ -0,0 +1,26 @@
+--- mnesia_original/src/mnesia.hrl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia.hrl 2012-03-14 18:33:17.000000000 +0100
+@@ -58,10 +58,11 @@
+ -record(cstruct, {name, % Atom
+ type = set, % set | bag
+ ram_copies = [], % [Node]
+ disc_copies = [], % [Node]
+ disc_only_copies = [], % [Node]
++ external_copies = [], % [Node]
+ load_order = 0, % Integer
+ access_mode = read_write, % read_write | read_only
+ majority = false, % true | false
+ index = [], % [Integer]
+ snmp = [], % Snmp Ustruct
+@@ -93,10 +94,11 @@
+ -record(commit, {node,
+ decision, % presume_commit | Decision
+ ram_copies = [],
+ disc_copies = [],
+ disc_only_copies = [],
++ external_copies = [],
+ snmp = [],
+ schema_ops = []
+ }).
+
+ -record(decision, {tid,
65 patches/mnesia_checkpoint.erl.patch
@@ -0,0 +1,65 @@
+--- mnesia_original/src/mnesia_checkpoint.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_checkpoint.erl 2012-03-14 18:33:38.000000000 +0100
+@@ -698,10 +698,14 @@
+
+ tab2retainer({Tab, Name}) ->
+ FlatName = lists:flatten(io_lib:write(Name)),
+ mnesia_lib:dir(lists:concat([?MODULE, "_", Tab, "_", FlatName, ".RET"])).
+
++retainer_create(_Cp, R, Tab, Name, Ext = {external_copies, Mod}) ->
++ {Tab, Name} = Mod:create_table({Tab, Name}, val({Tab, cstruct})),
++ dbg_out("Checkpoint retainer created ~p ~p~n", [Name, Tab]),
++ R#retainer{store = {Ext, {Tab, Name}}, really_retain = true};
+ retainer_create(_Cp, R, Tab, Name, disc_only_copies) ->
+ Fname = tab2retainer({Tab, Name}),
+ file:delete(Fname),
+ Args = [{file, Fname}, {type, set}, {keypos, 2}, {repair, false}],
+ {ok, _} = mnesia_lib:dets_sync_open({Tab, Name}, Args),
+@@ -757,19 +761,27 @@
+ lists:foreach(Fun, Recs),
+ traverse_dcd(mnesia_log:chunk_log(Log, Cont), Log, Fun);
+ traverse_dcd(eof, _Log, _Fun) ->
+ ok.
+
++retainer_get({{external_copies, Mod}, Store}, Key) ->
++ Mod:lookup(Store, Key);
+ retainer_get({ets, Store}, Key) -> ?ets_lookup(Store, Key);
+ retainer_get({dets, Store}, Key) -> dets:lookup(Store, Key).
+
++retainer_put({{external_copies, Mod}, Store}, Val) ->
++ Mod:insert(Store, Val);
+ retainer_put({ets, Store}, Val) -> ?ets_insert(Store, Val);
+ retainer_put({dets, Store}, Val) -> dets:insert(Store, Val).
+
++retainer_first({{external_copies, Mod}, Store}) ->
++ Mod:first(Store);
+ retainer_first({ets, Store}) -> ?ets_first(Store);
+ retainer_first({dets, Store}) -> dets:first(Store).
+
++retainer_next({{external_copies, Mod}, Store}, Key) ->
++ Mod:next(Store, Key);
+ retainer_next({ets, Store}, Key) -> ?ets_next(Store, Key);
+ retainer_next({dets, Store}, Key) -> dets:next(Store, Key).
+
+ %% retainer_next_slot(Tab, Pos) ->
+ %% case retainer_slot(Tab, Pos) of
+@@ -784,15 +796,19 @@
+ %% retainer_slot({ets, Store}, Pos) -> ?ets_next(Store, Pos);
+ %% retainer_slot({dets, Store}, Pos) -> dets:slot(Store, Pos).
+
+ retainer_fixtable(Tab, Bool) when is_atom(Tab) ->
+ mnesia_lib:db_fixtable(val({Tab, storage_type}), Tab, Bool);
++retainer_fixtable({Ext = {external_copies, _}, Tab}, Bool) ->
++ mnesia_lib:db_fixtable(Ext, Tab, Bool);
+ retainer_fixtable({ets, Tab}, Bool) ->
+ mnesia_lib:db_fixtable(ram_copies, Tab, Bool);
+ retainer_fixtable({dets, Tab}, Bool) ->
+ mnesia_lib:db_fixtable(disc_only_copies, Tab, Bool).
+
++retainer_delete({{external_copies, Mod}, Store}) ->
++ Mod:delete_table(Store);
+ retainer_delete({ets, Store}) ->
+ ?ets_delete_table(Store);
+ retainer_delete({dets, Store}) ->
+ mnesia_lib:dets_sync_close(Store),
+ Fname = tab2retainer(Store),
73 patches/mnesia_controller.erl.patch
@@ -0,0 +1,73 @@
+--- mnesia_original/src/mnesia_controller.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_controller.erl 2012-03-14 18:34:05.000000000 +0100
+@@ -366,10 +366,12 @@
+ do_force_load_table(Tab);
+ disc_copies ->
+ do_force_load_table(Tab);
+ disc_only_copies ->
+ do_force_load_table(Tab);
++ {external_copies, _} ->
++ do_force_load_table(Tab);
+ unknown ->
+ set({Tab, load_by_force}, true),
+ cast({force_load_updated, Tab}),
+ wait_for_tables([Tab], infinity);
+ {'EXIT', _} ->
+@@ -1503,12 +1505,16 @@
+ NodeST = mnesia_lib:storage_type_at_node(Node, Tab),
+ ReadST = mnesia_lib:storage_type_at_node(Read, Tab),
+ if %% Avoid reading from disc_only_copies
+ NodeST == disc_only_copies ->
+ ignore;
++ NodeST == external_copies ->
++ ignore;
+ ReadST == disc_only_copies ->
+ mnesia_lib:set_remote_where_to_read(Tab);
++ ReadST == external_copies ->
++ mnesia_lib:set_remote_where_to_read(Tab);
+ true ->
+ ignore
+ end,
+ user_sync_tab(Tab),
+ State;
+@@ -1553,10 +1559,11 @@
+ Cs = val({Tab, cstruct}),
+ Storage = mnesia_lib:cs_to_storage_type(node(), Cs),
+ Ram = Cs#cstruct.ram_copies,
+ Disc = Cs#cstruct.disc_copies,
+ DiscOnly = Cs#cstruct.disc_only_copies,
++ Ext = Cs#cstruct.external_copies,
+ BetterCopies0 = mnesia_lib:remote_copy_holders(Cs) -- Downs,
+ BetterCopies = BetterCopies0 -- Ram,
+ AccessMode = Cs#cstruct.access_mode,
+ Copies = mnesia_lib:copy_holders(Cs),
+ Masters = mnesia_recover:get_master_nodes(Tab),
+@@ -1585,11 +1592,11 @@
+ RemoteMaster == true ->
+ %% Wait for remote master copy
+ false;
+ Storage == ram_copies ->
+ if
+- Disc == [], DiscOnly == [] ->
++ Disc == [], DiscOnly == [], Ext == [] ->
+ %% Nobody has copy on disc
+ {true, {Tab, ram_only}};
+ true ->
+ %% Some other node has copy on disc
+ false
+@@ -1835,10 +1842,15 @@
+ disc_only_copies ->
+ info_format(Tab,
+ dets:info(Tab, size),
+ dets:info(Tab, file_size),
+ "bytes on disc");
++ {external_copies, Mod} ->
++ info_format(Tab,
++ Mod:info(Tab, size),
++ Mod:info(Tab, memory),
++ "words of mem");
+ _ ->
+ info_format(Tab,
+ ?ets_info(Tab, size),
+ ?ets_info(Tab, memory),
+ "words of mem")
130 patches/mnesia_dumper.erl.patch
@@ -0,0 +1,130 @@
+--- mnesia_original/src/mnesia_dumper.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_dumper.erl 2012-03-14 18:36:08.000000000 +0100
+@@ -438,14 +438,21 @@
+ _ when Storage == disc_only_copies ->
+ mnesia_tm:do_update_op(Tid, Storage, Item),
+ Snmp = mnesia_tm:prepare_snmp(Tab, Key, [Item]),
+ mnesia_tm:do_snmp(Tid, Snmp);
+
++ _ when element(1, Storage) == external_copies ->
++ mnesia_tm:do_update_op(Tid, Storage, Item),
++ Snmp = mnesia_tm:prepare_snmp(Tab, Key, [Item]),
++ mnesia_tm:do_snmp(Tid, Snmp);
++
+ _ when Storage == unknown ->
+ ignore
+ end.
+
++disc_delete_table(Tab, {external_copies, Mod}) ->
++ Mod:delete_table(Tab);
+ disc_delete_table(Tab, Storage) ->
+ case mnesia_monitor:use_dir() of
+ true ->
+ if
+ Storage == disc_only_copies; Tab == schema ->
+@@ -469,13 +476,16 @@
+ erase({?MODULE, Tab});
+ false ->
+ ignore
+ end.
+
++disc_delete_indecies(Tab, Cs, {external_copies, Mod}) ->
++ Indecies = Cs#cstruct.index,
++ Mod:delete_index(Tab, Indecies);
+ disc_delete_indecies(_Tab, _Cs, Storage) when Storage /= disc_only_copies ->
+ ignore;
+-disc_delete_indecies(Tab, Cs, disc_only_copies) ->
++disc_delete_indecies(Tab, Cs, disc_only_copies ) ->
+ Indecies = Cs#cstruct.index,
+ mnesia_index:del_transient(Tab, Indecies, disc_only_copies).
+
+ insert_op(Tid, Storage, {{Tab, Key}, Val, Op}, InPlace, InitBy) ->
+ %% Propagate to disc only
+@@ -495,10 +505,14 @@
+
+ insert_op(Tid, _, {op, rec, Storage, Item}, InPlace, InitBy) ->
+ {{Tab, Key}, ValList, Op} = Item,
+ insert(Tid, Storage, Tab, Key, ValList, Op, InPlace, InitBy);
+
++insert_op(_, _, {op, change_table_copy_type, _, FromS, ToS, _}, _, _)
++ when element(1, FromS) == external_copies;
++ element(1, ToS) == external_copies ->
++ ignore;
+ insert_op(Tid, _, {op, change_table_copy_type, N, FromS, ToS, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ Val = mnesia_schema:insert_cstruct(Tid, Cs, true), % Update ram only
+ {schema, Tab, _} = Val,
+ case lists:member(N, val({current, db_nodes})) of
+@@ -660,10 +674,12 @@
+ false,
+ false,
+ read_write),
+ mnesia_log:unsafe_close_log(temp)
+ end;
++ {external_copies, _} ->
++ ignore;
+ _ ->
+ DetsProps = proplists:get_value(dets, StorageProps, []),
+
+ Args = [{file, mnesia_lib:tab2dat(Tab)},
+ {type, mnesia_lib:disk_type(Tab, Cs#cstruct.type)},
+@@ -819,11 +835,17 @@
+ if
+ Tab == schema, Storage == ram_copies ->
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+ Tab /= schema ->
+ mnesia_controller:del_active_replica(Tab, Node),
+- mnesia_lib:del({Tab, Storage}, Node),
++ Storage1 = if
++ element(1, Storage) == external_copies ->
++ external_copies;
++ true ->
++ Storage
++ end,
++ mnesia_lib:del({Tab, Storage1}, Node),
+ if
+ Node == node() ->
+ case Cs#cstruct.local_content of
+ true -> mnesia_lib:set({Tab, where_to_read}, nowhere);
+ false -> mnesia_lib:set_remote_where_to_read(Tab)
+@@ -883,10 +905,13 @@
+ startup when Storage == disc_only_copies ->
+ true = open_files(Tab, Storage, InPlace, InitBy),
+ mnesia_index:init_indecies(Tab, Storage, [Pos]);
+ startup ->
+ ignore;
++ _ when element(1, Storage) == external_copies ->
++ {_, Mod} = Storage,
++ Mod:add_index(Tab, [Pos]);
+ _ ->
+ case val({Tab,where_to_read}) of
+ nowhere -> ignore;
+ _ ->
+ mnesia_index:init_indecies(Tab, Storage, [Pos])
+@@ -900,10 +925,13 @@
+ case InitBy of
+ startup when Storage == disc_only_copies ->
+ mnesia_index:del_index_table(Tab, Storage, Pos);
+ startup ->
+ ignore;
++ _ when element(1, Storage) == external_copies ->
++ {_, Mod} = Storage,
++ Mod:delete_index(Tab, [Pos]);
+ _ ->
+ mnesia_index:del_index_table(Tab, Storage, Pos)
+ end,
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy);
+
+@@ -939,10 +967,12 @@
+
+ insert_op(Tid, _, {op, change_table_frag, _Change, TabDef}, InPlace, InitBy) ->
+ Cs = mnesia_schema:list2cs(TabDef),
+ insert_cstruct(Tid, Cs, true, InPlace, InitBy).
+
++open_files(_, {external_copies, _}, _, _) ->
++ true;
+ open_files(Tab, Storage, UpdateInPlace, InitBy)
+ when Storage /= unknown, Storage /= ram_copies ->
+ case get({?MODULE, Tab}) of
+ undefined ->
+ case ?catch_val({Tab, setorbag}) of
227 patches/mnesia_frag.erl.patch
@@ -0,0 +1,227 @@
+--- mnesia_original/src/mnesia_frag.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_frag.erl 2012-03-14 18:36:26.000000000 +0100
+@@ -180,10 +180,12 @@
+ length(val({Tab, ram_copies}));
+ n_disc_copies ->
+ length(val({Tab, disc_copies}));
+ n_disc_only_copies ->
+ length(val({Tab, disc_only_copies}));
++ n_external_copies ->
++ length(val({Tab, external_copies}));
+
+ frag_names ->
+ frag_names(Tab);
+ frag_dist ->
+ frag_dist(Tab);
+@@ -494,17 +496,17 @@
+ mnesia_schema:verify({alt, [nil, list]}, mnesia_lib:etype(Props),
+ {badarg, Tab, Props}),
+ %% Verify keys
+ ValidKeys = [foreign_key, n_fragments, node_pool,
+ n_ram_copies, n_disc_copies, n_disc_only_copies,
+- hash_module, hash_state],
++ n_external_copies, hash_module, hash_state],
+ Keys = mnesia_schema:check_keys(Tab, Props, ValidKeys),
+ mnesia_schema:check_duplicates(Tab, Keys),
+
+ %% Pick fragmentation props
+ ForeignKey = mnesia_schema:pick(Tab, foreign_key, Props, undefined),
+- {ForeignKey2, N, Pool, DefaultNR, DefaultND, DefaultNDO} =
++ {ForeignKey2, N, Pool, DefaultNR, DefaultND, DefaultNDO, DefaultNExt} =
+ pick_props(Tab, Cs, ForeignKey),
+
+ %% Verify node_pool
+ BadPool = {bad_type, Tab, {node_pool, Pool}},
+ mnesia_schema:verify(list, mnesia_lib:etype(Pool), BadPool),
+@@ -514,20 +516,23 @@
+ mnesia_schema:verify([], [P || P <- Pool, NotAtom(P)], BadPool),
+
+ NR = mnesia_schema:pick(Tab, n_ram_copies, Props, 0),
+ ND = mnesia_schema:pick(Tab, n_disc_copies, Props, 0),
+ NDO = mnesia_schema:pick(Tab, n_disc_only_copies, Props, 0),
++ NExt = mnesia_schema:pick(Tab, n_external_copies, Props, 0),
+
+ PosInt = fun(I) when is_integer(I), I >= 0 -> true;
+ (_I) -> false
+ end,
+ mnesia_schema:verify(true, PosInt(NR),
+ {bad_type, Tab, {n_ram_copies, NR}}),
+ mnesia_schema:verify(true, PosInt(ND),
+ {bad_type, Tab, {n_disc_copies, ND}}),
+ mnesia_schema:verify(true, PosInt(NDO),
+ {bad_type, Tab, {n_disc_only_copies, NDO}}),
++ mnesia_schema:verify(true, PosInt(NExt),
++ {bad_type, Tab, {n_external_copies, NDO}}),
+
+ %% Verify n_fragments
+ Cs2 = verify_n_fragments(N, Cs, Mode),
+
+ %% Verify hash callback
+@@ -538,17 +543,17 @@
+ FH = #frag_state{foreign_key = ForeignKey2,
+ n_fragments = 1,
+ hash_module = HashMod,
+ hash_state = HashState2},
+ if
+- NR == 0, ND == 0, NDO == 0 ->
+- do_expand_cstruct(Cs2, FH, N, Pool, DefaultNR, DefaultND, DefaultNDO, Mode);
++ NR == 0, ND == 0, NDO == 0, NExt == 0 ->
++ do_expand_cstruct(Cs2, FH, N, Pool, DefaultNR, DefaultND, DefaultNDO, DefaultNExt, Mode);
+ true ->
+- do_expand_cstruct(Cs2, FH, N, Pool, NR, ND, NDO, Mode)
++ do_expand_cstruct(Cs2, FH, N, Pool, NR, ND, NDO, NExt, Mode)
+ end.
+
+-do_expand_cstruct(Cs, FH, N, Pool, NR, ND, NDO, Mode) ->
++do_expand_cstruct(Cs, FH, N, Pool, NR, ND, NDO, NExt, Mode) ->
+ Tab = Cs#cstruct.name,
+
+ LC = Cs#cstruct.local_content,
+ mnesia_schema:verify(false, LC,
+ {combine_error, Tab, {local_content, LC}}),
+@@ -558,18 +563,19 @@
+ {combine_error, Tab, {snmp, Snmp}}),
+
+ %% Add empty fragments
+ CommonProps = [{base_table, Tab}],
+ Cs2 = Cs#cstruct{frag_properties = lists:sort(CommonProps)},
+- expand_frag_cstructs(N, NR, ND, NDO, Cs2, Pool, Pool, FH, Mode).
++ expand_frag_cstructs(N, NR, ND, NDO, NExt, Cs2, Pool, Pool, FH, Mode).
+
+ verify_n_fragments(N, Cs, Mode) when is_integer(N), N >= 1 ->
+ case Mode of
+ create ->
+ Cs#cstruct{ram_copies = [],
+ disc_copies = [],
+- disc_only_copies = []};
++ disc_only_copies = [],
++ external_copies = []};
+ activate ->
+ Reason = {combine_error, Cs#cstruct.name, {n_fragments, N}},
+ mnesia_schema:verify(1, N, Reason),
+ Cs
+ end;
+@@ -603,36 +609,38 @@
+
+ Key = {ForeignTab, mnesia_schema:attr_to_pos(Attr, Attrs)},
+ DefaultNR = length(val({ForeignTab, ram_copies})),
+ DefaultND = length(val({ForeignTab, disc_copies})),
+ DefaultNDO = length(val({ForeignTab, disc_only_copies})),
+- {Key, N, Pool, DefaultNR, DefaultND, DefaultNDO};
++ DefaultNExt = length(val({ForeignTab, external_copies})),
++ {Key, N, Pool, DefaultNR, DefaultND, DefaultNDO, DefaultNExt};
+ pick_props(Tab, Cs, undefined) ->
+ Props = Cs#cstruct.frag_properties,
+ DefaultN = 1,
+ DefaultPool = mnesia:system_info(db_nodes),
+ N = mnesia_schema:pick(Tab, n_fragments, Props, DefaultN),
+ Pool = mnesia_schema:pick(Tab, node_pool, Props, DefaultPool),
+ DefaultNR = 1,
+ DefaultND = 0,
+ DefaultNDO = 0,
+- {undefined, N, Pool, DefaultNR, DefaultND, DefaultNDO};
++ DefaultNExt = 0,
++ {undefined, N, Pool, DefaultNR, DefaultND, DefaultNDO, DefaultNExt};
+ pick_props(Tab, _Cs, BadKey) ->
+ mnesia:abort({bad_type, Tab, {foreign_key, BadKey}}).
+
+-expand_frag_cstructs(N, NR, ND, NDO, CommonCs, Dist, Pool, FH, Mode)
++expand_frag_cstructs(N, NR, ND, NDO, NExt, CommonCs, Dist, Pool, FH, Mode)
+ when N > 1, Mode == create ->
+ Frag = n_to_frag_name(CommonCs#cstruct.name, N),
+ Cs = CommonCs#cstruct{name = Frag},
+- {Cs2, RevModDist, RestDist} = set_frag_nodes(NR, ND, NDO, Cs, Dist, []),
++ {Cs2, RevModDist, RestDist} = set_frag_nodes(NR, ND, NDO, NExt, Cs, Dist, []),
+ ModDist = lists:reverse(RevModDist),
+ Dist2 = rearrange_dist(Cs, ModDist, RestDist, Pool),
+ %% Adjusts backwards, but it doesn't matter.
+ {FH2, _FromFrags, _AdditionalWriteFrags} = adjust_before_split(FH),
+- CsList = expand_frag_cstructs(N - 1, NR, ND, NDO, CommonCs, Dist2, Pool, FH2, Mode),
++ CsList = expand_frag_cstructs(N - 1, NR, ND, NDO, NExt, CommonCs, Dist2, Pool, FH2, Mode),
+ [Cs2 | CsList];
+-expand_frag_cstructs(1, NR, ND, NDO, CommonCs, Dist, Pool, FH, Mode) ->
++expand_frag_cstructs(1, NR, ND, NDO, NExt, CommonCs, Dist, Pool, FH, Mode) ->
+ BaseProps = CommonCs#cstruct.frag_properties ++
+ [{foreign_key, FH#frag_state.foreign_key},
+ {hash_module, FH#frag_state.hash_module},
+ {hash_state, FH#frag_state.hash_state},
+ {n_fragments, FH#frag_state.n_fragments},
+@@ -641,29 +649,33 @@
+ BaseCs = CommonCs#cstruct{frag_properties = lists:sort(BaseProps)},
+ case Mode of
+ activate ->
+ [BaseCs];
+ create ->
+- {BaseCs2, _, _} = set_frag_nodes(NR, ND, NDO, BaseCs, Dist, []),
++ {BaseCs2, _, _} = set_frag_nodes(NR, ND, NDO, NExt, BaseCs, Dist, []),
+ [BaseCs2]
+ end.
+
+-set_frag_nodes(NR, ND, NDO, Cs, [Head | Tail], Acc) when NR > 0 ->
++set_frag_nodes(NR, ND, NDO, NExt, Cs, [Head | Tail], Acc) when NR > 0 ->
+ Pos = #cstruct.ram_copies,
+ {Cs2, Head2} = set_frag_node(Cs, Pos, Head),
+- set_frag_nodes(NR - 1, ND, NDO, Cs2, Tail, [Head2 | Acc]);
+-set_frag_nodes(NR, ND, NDO, Cs, [Head | Tail], Acc) when ND > 0 ->
++ set_frag_nodes(NR - 1, ND, NDO, NExt, Cs2, Tail, [Head2 | Acc]);
++set_frag_nodes(NR, ND, NDO, NExt, Cs, [Head | Tail], Acc) when ND > 0 ->
+ Pos = #cstruct.disc_copies,
+ {Cs2, Head2} = set_frag_node(Cs, Pos, Head),
+- set_frag_nodes(NR, ND - 1, NDO, Cs2, Tail, [Head2 | Acc]);
+-set_frag_nodes(NR, ND, NDO, Cs, [Head | Tail], Acc) when NDO > 0 ->
++ set_frag_nodes(NR, ND - 1, NDO, NExt, Cs2, Tail, [Head2 | Acc]);
++set_frag_nodes(NR, ND, NDO, NExt, Cs, [Head | Tail], Acc) when NDO > 0 ->
+ Pos = #cstruct.disc_only_copies,
+ {Cs2, Head2} = set_frag_node(Cs, Pos, Head),
+- set_frag_nodes(NR, ND, NDO - 1, Cs2, Tail, [Head2 | Acc]);
+-set_frag_nodes(0, 0, 0, Cs, RestDist, ModDist) ->
++ set_frag_nodes(NR, ND, NDO - 1, NExt, Cs2, Tail, [Head2 | Acc]);
++set_frag_nodes(NR, ND, NDO, NExt, Cs, [Head | Tail], Acc) when NExt > 0 ->
++ Pos = #cstruct.external_copies,
++ {Cs2, Head2} = set_frag_node(Cs, Pos, Head),
++ set_frag_nodes(NR, ND, NDO, NExt - 1, Cs2, Tail, [Head2 | Acc]);
++set_frag_nodes(0, 0, 0, 0, Cs, RestDist, ModDist) ->
+ {Cs, ModDist, RestDist};
+-set_frag_nodes(_, _, _, Cs, [], _) ->
++set_frag_nodes(_, _, _, _, Cs, [], _) ->
+ mnesia:abort({combine_error, Cs#cstruct.name, "Too few nodes in node_pool"}).
+
+ set_frag_node(Cs, Pos, Head) ->
+ Ns = element(Pos, Cs),
+ {Node, Count2} =
+@@ -836,17 +848,19 @@
+ NewFrag = element(N, FragNames),
+
+ NR = length(Cs#cstruct.ram_copies),
+ ND = length(Cs#cstruct.disc_copies),
+ NDO = length(Cs#cstruct.disc_only_copies),
++ NExt = length(Cs#cstruct.external_copies),
+ NewCs = Cs#cstruct{name = NewFrag,
+ frag_properties = [{base_table, Tab}],
+ ram_copies = [],
+ disc_copies = [],
+- disc_only_copies = []},
++ disc_only_copies = [],
++ external_copies = []},
+
+- {NewCs2, _, _} = set_frag_nodes(NR, ND, NDO, NewCs, SortedNs, []),
++ {NewCs2, _, _} = set_frag_nodes(NR, ND, NDO, NExt, NewCs, SortedNs, []),
+ [NewOp] = mnesia_schema:make_create_table(NewCs2),
+
+ SplitOps = split(Tab, FH2, FromIndecies, FragNames, []),
+
+ Cs2 = replace_frag_hash(Cs, FH2),
+@@ -1311,11 +1325,12 @@
+
+ count_frag([Frag | Frags], Dist) ->
+ Dist2 = incr_nodes(val({Frag, ram_copies}), Dist),
+ Dist3 = incr_nodes(val({Frag, disc_copies}), Dist2),
+ Dist4 = incr_nodes(val({Frag, disc_only_copies}), Dist3),
+- count_frag(Frags, Dist4);
++ Dist5 = incr_nodes(val({Frag, external_copies}), Dist4),
++ count_frag(Frags, Dist5);
+ count_frag([], Dist) ->
+ Dist.
+
+ incr_nodes([Node | Nodes], Dist) ->
+ Dist2 = incr_node(Node, Dist),
15 patches/mnesia_index.erl.patch
@@ -0,0 +1,15 @@
+--- mnesia_original/src/mnesia_index.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_index.erl 2012-03-14 18:36:42.000000000 +0100
+@@ -209,10 +209,12 @@
+ case Storage of
+ unknown ->
+ ignore;
+ disc_only_copies ->
+ init_disc_index(Tab, PosList);
++ {external_copies, Mod} ->
++ Mod:init_index(Tab, PosList);
+ ram_copies ->
+ make_ram_index(Tab, PosList);
+ disc_copies ->
+ make_ram_index(Tab, PosList)
+ end.
291 patches/mnesia_lib.erl.patch
@@ -0,0 +1,291 @@
+--- mnesia_original/src/mnesia_lib.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_lib.erl 2012-03-14 18:47:30.000000000 +0100
+@@ -323,16 +323,25 @@
+ dir(lists:concat([Tab, ".DCL"])).
+
+ storage_type_at_node(Node, Tab) ->
+ search_key(Node, [{disc_copies, val({Tab, disc_copies})},
+ {ram_copies, val({Tab, ram_copies})},
+- {disc_only_copies, val({Tab, disc_only_copies})}]).
++ {disc_only_copies, val({Tab, disc_only_copies})},
++ {external_copies, val({Tab, external_copies})}]).
+
+ cs_to_storage_type(Node, Cs) ->
++ T = Cs#cstruct.type,
++ T1 = if
++ is_tuple(T) andalso element(1, T) == external ->
++ element(3, T);
++ true ->
++ T
++ end,
+ search_key(Node, [{disc_copies, Cs#cstruct.disc_copies},
+ {ram_copies, Cs#cstruct.ram_copies},
+- {disc_only_copies, Cs#cstruct.disc_only_copies}]).
++ {disc_only_copies, Cs#cstruct.disc_only_copies},
++ {{external_copies, T1}, Cs#cstruct.external_copies}]).
+
+ schema_cs_to_storage_type(Node, Cs) ->
+ case cs_to_storage_type(Node, Cs) of
+ unknown when Cs#cstruct.name == schema -> ram_copies;
+ Other -> Other
+@@ -551,10 +560,11 @@
+
+ read_counter(Name) ->
+ ?ets_lookup_element(mnesia_stats, Name, 2).
+
+ cs_to_nodes(Cs) ->
++ Cs#cstruct.external_copies ++
+ Cs#cstruct.disc_only_copies ++
+ Cs#cstruct.disc_copies ++
+ Cs#cstruct.ram_copies.
+
+ overload_types() ->
+@@ -1053,33 +1063,50 @@
+
+ db_get(Tab, Key) ->
+ db_get(val({Tab, storage_type}), Tab, Key).
+ db_get(ram_copies, Tab, Key) -> ?ets_lookup(Tab, Key);
+ db_get(disc_copies, Tab, Key) -> ?ets_lookup(Tab, Key);
+-db_get(disc_only_copies, Tab, Key) -> dets:lookup(Tab, Key).
++db_get(disc_only_copies, Tab, Key) -> dets:lookup(Tab, Key);
++db_get(external_copies, Tab, Key) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_get({external_copies, Mod}, Tab, Key);
++db_get({external_copies, Mod}, Tab, Key) ->
++ Mod:lookup(Tab, Key).
+
+ db_init_chunk(Tab) ->
+ db_init_chunk(val({Tab, storage_type}), Tab, 1000).
+ db_init_chunk(Tab, N) ->
+ db_init_chunk(val({Tab, storage_type}), Tab, N).
+
++db_init_chunk(external_copies, Tab, N) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_init_chunk({external_copies, Mod}, Tab, N);
++db_init_chunk({external_copies, Mod}, Tab, N) ->
++ Mod:select(Tab, [{'_', [], ['$_']}], N);
+ db_init_chunk(disc_only_copies, Tab, N) ->
+ dets:select(Tab, [{'_', [], ['$_']}], N);
+ db_init_chunk(_, Tab, N) ->
+ ets:select(Tab, [{'_', [], ['$_']}], N).
+
++db_chunk({external_copies, Mod}, State) ->
++ Mod:select(State);
+ db_chunk(disc_only_copies, State) ->
+ dets:select(State);
+ db_chunk(_, State) ->
+ ets:select(State).
+
+ db_put(Tab, Val) ->
+ db_put(val({Tab, storage_type}), Tab, Val).
+
+ db_put(ram_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;
+ db_put(disc_copies, Tab, Val) -> ?ets_insert(Tab, Val), ok;
+-db_put(disc_only_copies, Tab, Val) -> dets:insert(Tab, Val).
++db_put(disc_only_copies, Tab, Val) -> dets:insert(Tab, Val);
++db_put(external_copies, Tab, Val) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_put({external_copies, Mod}, Tab, Val);
++db_put({external_copies, Mod}, Tab, Val) ->
++ Mod:insert(Tab, Val), ok.
+
+ db_match_object(Tab, Pat) ->
+ db_match_object(val({Tab, storage_type}), Tab, Pat).
+ db_match_object(Storage, Tab, Pat) ->
+ db_fixtable(Storage, Tab, true),
+@@ -1088,10 +1115,15 @@
+ case Res of
+ {'EXIT', Reason} -> exit(Reason);
+ _ -> Res
+ end.
+
++catch_match_object(external_copies, Tab, Pat) ->
++ {_, Mod} = val({Tab, storage_type}),
++ catch_match_object({external_copies, Mod}, Tab, Pat);
++catch_match_object({external_copies, Mod}, Tab, Pat) ->
++ catch Mod:match_object(Tab, Pat);
+ catch_match_object(disc_only_copies, Tab, Pat) ->
+ catch dets:match_object(Tab, Pat);
+ catch_match_object(_, Tab, Pat) ->
+ catch ets:match_object(Tab, Pat).
+
+@@ -1105,20 +1137,42 @@
+ case Res of
+ {'EXIT', Reason} -> exit(Reason);
+ _ -> Res
+ end.
+
++catch_select(external_copies, Tab, Pat) ->
++ {_, Mod} = val({Tab, storage_type}),
++ catch_select({external_copies, Mod}, Tab, Pat);
++catch_select({external_copies, Mod}, Tab, Pat) ->
++ catch Mod:select(Tab, Pat);
+ catch_select(disc_only_copies, Tab, Pat) ->
+ catch dets:select(Tab, Pat);
+ catch_select(_, Tab, Pat) ->
+ catch ets:select(Tab, Pat).
+
++db_select_init(external_copies, Tab, Pat, Limit) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_select_init({external_copies, Mod}, Tab, Pat, Limit);
++db_select_init({external_copies, Mod}, Tab, Pat, Limit) ->
++ case Mod:select(Tab, Pat, Limit) of
++ {Matches, Continuation} when is_list(Matches) ->
++ {Matches, {Mod, Continuation}};
++ R ->
++ R
++ end;
+ db_select_init(disc_only_copies, Tab, Pat, Limit) ->
+ dets:select(Tab, Pat, Limit);
+ db_select_init(_, Tab, Pat, Limit) ->
+ ets:select(Tab, Pat, Limit).
+
++db_select_cont(external_copies, {Mod, Cont}, _Ms) ->
++ case Mod:select(Cont) of
++ {Matches, Continuation} when is_list(Matches) ->
++ {Matches, {Mod, Continuation}};
++ R ->
++ R
++ end;
+ db_select_cont(disc_only_copies, Cont0, Ms) ->
+ Cont = dets:repair_continuation(Cont0, Ms),
+ dets:select(Cont);
+ db_select_cont(_, Cont0, Ms) ->
+ Cont = ets:repair_continuation(Cont0, Ms),
+@@ -1131,74 +1185,126 @@
+ db_fixtable(disc_copies, Tab, Bool) ->
+ ets:safe_fixtable(Tab, Bool);
+ db_fixtable(dets, Tab, Bool) ->
+ dets:safe_fixtable(Tab, Bool);
+ db_fixtable(disc_only_copies, Tab, Bool) ->
+- dets:safe_fixtable(Tab, Bool).
++ dets:safe_fixtable(Tab, Bool);
++ db_fixtable(external_copies, Tab, Bool) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_fixtable({external_copies, Mod}, Tab, Bool);
++ db_fixtable({external_copies, Mod}, Tab, Bool) ->
++ Mod:fixtable(Tab, Bool).
+
+ db_erase(Tab, Key) ->
+ db_erase(val({Tab, storage_type}), Tab, Key).
+ db_erase(ram_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;
+ db_erase(disc_copies, Tab, Key) -> ?ets_delete(Tab, Key), ok;
+-db_erase(disc_only_copies, Tab, Key) -> dets:delete(Tab, Key).
++db_erase(disc_only_copies, Tab, Key) -> dets:delete(Tab, Key);
++db_erase(external_copies, Tab, Key) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_erase({external_copies, Mod}, Tab, Key);
++db_erase({external_copies, Mod}, Tab, Key) ->
++ Mod:delete(Tab, Key),
++ ok.
+
+ db_match_erase(Tab, '_') ->
+ db_delete_all(val({Tab, storage_type}),Tab);
+ db_match_erase(Tab, Pat) ->
+ db_match_erase(val({Tab, storage_type}), Tab, Pat).
+ db_match_erase(ram_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;
+ db_match_erase(disc_copies, Tab, Pat) -> ?ets_match_delete(Tab, Pat), ok;
+-db_match_erase(disc_only_copies, Tab, Pat) -> dets:match_delete(Tab, Pat).
++db_match_erase(disc_only_copies, Tab, Pat) -> dets:match_delete(Tab, Pat);
++db_match_erase(external_copies, Tab, Pat) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_match_erase({external_copies, Mod}, Tab, Pat);
++db_match_erase({external_copies, Mod}, Tab, Pat) ->
++ Mod:match_delete(Tab, Pat),
++ ok.
+
+ db_delete_all(ram_copies, Tab) -> ets:delete_all_objects(Tab);
+ db_delete_all(disc_copies, Tab) -> ets:delete_all_objects(Tab);
+ db_delete_all(disc_only_copies, Tab) -> dets:delete_all_objects(Tab).
+
+ db_first(Tab) ->
+ db_first(val({Tab, storage_type}), Tab).
+ db_first(ram_copies, Tab) -> ?ets_first(Tab);
+ db_first(disc_copies, Tab) -> ?ets_first(Tab);
+-db_first(disc_only_copies, Tab) -> dets:first(Tab).
++db_first(disc_only_copies, Tab) -> dets:first(Tab);
++db_first(external_copies, Tab) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_first({external_copies, Mod}, Tab);
++db_first({external_copies, Mod}, Tab) ->
++ Mod:first(Tab).
+
+ db_next_key(Tab, Key) ->
+ db_next_key(val({Tab, storage_type}), Tab, Key).
+ db_next_key(ram_copies, Tab, Key) -> ?ets_next(Tab, Key);
+ db_next_key(disc_copies, Tab, Key) -> ?ets_next(Tab, Key);
+-db_next_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key).
++db_next_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key);
++db_next_key(external_copies, Tab, Key) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_next_key({external_copies, Mod}, Tab, Key);
++db_next_key({external_copies, Mod}, Tab, Key) ->
++ Mod:next(Tab, Key).
+
+ db_last(Tab) ->
+ db_last(val({Tab, storage_type}), Tab).
+ db_last(ram_copies, Tab) -> ?ets_last(Tab);
+ db_last(disc_copies, Tab) -> ?ets_last(Tab);
+-db_last(disc_only_copies, Tab) -> dets:first(Tab). %% Dets don't have order
++db_last(disc_only_copies, Tab) -> dets:first(Tab); %% Dets don't have order
++db_last(external_copies, Tab) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_last({external_copies, Mod}, Tab);
++db_last({external_copies, Mod}, Tab) ->
++ Mod:last(Tab).
+
+ db_prev_key(Tab, Key) ->
+ db_prev_key(val({Tab, storage_type}), Tab, Key).
+ db_prev_key(ram_copies, Tab, Key) -> ?ets_prev(Tab, Key);
+ db_prev_key(disc_copies, Tab, Key) -> ?ets_prev(Tab, Key);
+-db_prev_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key). %% Dets don't have order
++db_prev_key(disc_only_copies, Tab, Key) -> dets:next(Tab, Key); %% Dets don't have order
++db_prev_key(external_copies, Tab, Key) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_prev_key({external_copies, Mod}, Tab, Key);
++db_prev_key({external_copies, Mod}, Tab, Key) ->
++ Mod:prev(Tab, Key).
+
+ db_slot(Tab, Pos) ->
+ db_slot(val({Tab, storage_type}), Tab, Pos).
+ db_slot(ram_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);
+ db_slot(disc_copies, Tab, Pos) -> ?ets_slot(Tab, Pos);
+-db_slot(disc_only_copies, Tab, Pos) -> dets:slot(Tab, Pos).
++db_slot(disc_only_copies, Tab, Pos) -> dets:slot(Tab, Pos);
++db_slot(external_copies, Tab, Pos) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_slot({external_copies, Mod}, Tab, Pos);
++db_slot({external_copies, Mod}, Tab, Pos) ->
++ Mod:slot(Tab, Pos).
+
+ db_update_counter(Tab, C, Val) ->
+ db_update_counter(val({Tab, storage_type}), Tab, C, Val).
+ db_update_counter(ram_copies, Tab, C, Val) ->
+ ?ets_update_counter(Tab, C, Val);
+ db_update_counter(disc_copies, Tab, C, Val) ->
+ ?ets_update_counter(Tab, C, Val);
+ db_update_counter(disc_only_copies, Tab, C, Val) ->
+- dets:update_counter(Tab, C, Val).
++ dets:update_counter(Tab, C, Val);
++db_update_counter(external_copies, Tab, C, Val) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_update_counter({external_copies, Mod}, Tab, C, Val);
++db_update_counter({external_copies, Mod}, Tab, C, Val) ->
++ Mod:update_counter(Tab, C, Val).
+
+ db_erase_tab(Tab) ->
+ db_erase_tab(val({Tab, storage_type}), Tab).
+ db_erase_tab(ram_copies, Tab) -> ?ets_delete_table(Tab);
+ db_erase_tab(disc_copies, Tab) -> ?ets_delete_table(Tab);
+-db_erase_tab(disc_only_copies, _Tab) -> ignore.
++db_erase_tab(disc_only_copies, _Tab) -> ignore;
++db_erase_tab(external_copies, Tab) ->
++ {_, Mod} = val({Tab, storage_type}),
++ db_erase_tab({external_copies, Mod}, Tab);
++db_erase_tab({external_copies, Mod}, Tab) ->
++ Mod:delete_table(Tab).
+
+ %% assuming that Tab is a valid ets-table
+ dets_to_ets(Tabname, Tab, File, Type, Rep, Lock) ->
+ {Open, Close} = mkfuns(Lock),
+ case Open(Tabname, [{file, File}, {type, disk_type(Tab, Type)},
136 patches/mnesia_loader.erl.patch
@@ -0,0 +1,136 @@
+--- mnesia_original/src/mnesia_loader.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_loader.erl 2012-03-14 20:30:29.000000000 +0100
+@@ -143,11 +143,17 @@
+ set({Tab, load_reason}, Reason),
+ {loaded, ok};
+ {error, Error} ->
+ {not_loaded, {"Failed to create dets table", Error}}
+ end
+- end.
++ end;
++
++do_get_disc_copy2(Tab, Reason, Storage = {external_copies, _Mod}, _Type) ->
++ mnesia_index:init_index(Tab, Storage),
++ set({Tab, load_node}, node()),
++ set({Tab, load_reason}, Reason),
++ {loaded, ok}.
+
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ %% Load a table from a remote node
+ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+ %%
+@@ -387,11 +393,19 @@
+ case mnesia_monitor:unsafe_mktab(Tab, Args) of
+ Tab ->
+ {Storage, Tab};
+ Else ->
+ Else
+- end
++ end;
++ element(1, Storage) == external_copies ->
++ { _, Mod } = Storage,
++ case mnesia_monitor:unsafe_create_external(Tab, Mod, Cs) of
++ Tab ->
++ {Storage, Tab};
++ Else ->
++ Else
++ end
+ end.
+
+ tab_receiver(Node, Tab, Storage, Cs, OrigTabRec) ->
+ receive
+ {SenderPid, {no_more, DatBin}} ->
+@@ -434,10 +448,20 @@
+ {'EXIT', Pid, Reason} ->
+ handle_exit(Pid, Reason),
+ get_data(Pid, TabRec)
+ end.
+
++init_table(Tab, {external_copies,Mod}, Fun, false, Sender) ->
++ case catch Mod:init_table(Tab, Fun, Sender) of
++ true ->
++ ok;
++ ok -> % "ets-style" is true, "dets-style" is ok;
++ % be nice and accept both :)
++ ok;
++ {'EXIT', Else} -> Else
++ end;
++
+ init_table(Tab, disc_only_copies, Fun, DetsInfo,Sender) ->
+ ErtsVer = erlang:system_info(version),
+ case DetsInfo of
+ {ErtsVer, DetsData} ->
+ Res = (catch dets:is_compatible_bchunk_format(Tab, DetsData)),
+@@ -553,22 +577,27 @@
+ ok = file:rename(Tmp, mnesia_lib:tab2dcd(Tab)),
+ mnesia_lib:unlock_table(Tab),
+ ok;
+ false ->
+ ok
+- end.
++ end;
++
++handle_last(_Storage, _Type, nobin) ->
++ ok.
+
+ down(Tab, Storage) ->
+ case Storage of
+ ram_copies ->
+ catch ?ets_delete_table(Tab);
+ disc_copies ->
+ catch ?ets_delete_table(Tab);
+ disc_only_copies ->
+ TmpFile = mnesia_lib:tab2tmp(Tab),
+ mnesia_lib:dets_sync_close(Tab),
+- file:delete(TmpFile)
++ file:delete(TmpFile);
++ {external_copies, Mod} ->
++ catch Mod:delete_table(Tab)
+ end,
+ mnesia_checkpoint:tm_del_copy(Tab, node()),
+ mnesia_controller:sync_del_table_copy_whereabouts(Tab, node()),
+ mnesia_tm:unblock_tab(Tab),
+ flush_subcrs(),
+@@ -589,25 +618,39 @@
+ db_erase({ram_copies, Tab}, Key) ->
+ true = ?ets_delete(Tab, Key);
+ db_erase({disc_copies, Tab}, Key) ->
+ true = ?ets_delete(Tab, Key);
+ db_erase({disc_only_copies, Tab}, Key) ->
+- ok = dets:delete(Tab, Key).
++ ok = dets:delete(Tab, Key);
++db_erase({{external_copies, Mod}, Tab}, Key) ->
++ ok = Mod:delete(Tab, Key).
+
+ db_match_erase({ram_copies, Tab} , Pat) ->
+ true = ?ets_match_delete(Tab, Pat);
+ db_match_erase({disc_copies, Tab} , Pat) ->
+ true = ?ets_match_delete(Tab, Pat);
+ db_match_erase({disc_only_copies, Tab}, Pat) ->
+- ok = dets:match_delete(Tab, Pat).
++ ok = dets:match_delete(Tab, Pat);
++db_match_erase({{external_copies, Mod}, Tab}, Pat) ->
++ % "ets style" is to return true
++ % "dets style" is to return N | { error, Reason }
++ % or sometimes ok (?)
++ % be nice and accept both
++ case Mod:match_delete(Tab, Pat) of
++ N when is_integer (N) -> ok;
++ true -> ok;
++ ok -> ok
++ end.
+
+ db_put({ram_copies, Tab}, Val) ->
+ true = ?ets_insert(Tab, Val);
+ db_put({disc_copies, Tab}, Val) ->
+ true = ?ets_insert(Tab, Val);
+ db_put({disc_only_copies, Tab}, Val) ->
+- ok = dets:insert(Tab, Val).
++ ok = dets:insert(Tab, Val);
++db_put({{external_copies, Mod}, Tab}, Val) ->
++ ok = Mod:insert(Tab, Val).
+
+ %% This code executes at the remote site where the data is
+ %% executes in a special copier process.
+
+ calc_nokeys(Storage, Tab) ->
26 patches/mnesia_log.erl.patch
@@ -0,0 +1,26 @@
+--- mnesia_original/src/mnesia_log.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_log.erl 2012-03-14 18:49:34.000000000 +0100
+@@ -223,10 +223,11 @@
+ ok = disk_log:log(Log, Term).
+
+ %% Write commit records to the latest_log
+ log(C) when C#commit.disc_copies == [],
+ C#commit.disc_only_copies == [],
++ C#commit.external_copies == [],
+ C#commit.schema_ops == [] ->
+ ignore;
+ log(C) ->
+ case mnesia_monitor:use_dir() of
+ true ->
+@@ -246,10 +247,11 @@
+
+ %% Synced
+
+ slog(C) when C#commit.disc_copies == [],
+ C#commit.disc_only_copies == [],
++ C#commit.external_copies == [],
+ C#commit.schema_ops == [] ->
+ ignore;
+ slog(C) ->
+ case mnesia_monitor:use_dir() of
+ true ->
46 patches/mnesia_monitor.erl.patch
@@ -0,0 +1,46 @@
+--- mnesia_original/src/mnesia_monitor.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_monitor.erl 2012-03-14 18:50:49.000000000 +0100
+@@ -29,10 +29,11 @@
+ detect_inconcistency/2,
+ get_env/1,
+ init/0,
+ mktab/2,
+ unsafe_mktab/2,
++ unsafe_create_external/3,
+ mnesia_down/2,
+ needs_protocol_conversion/1,
+ negotiate_protocol/1,
+ disconnect/1,
+ open_dets/2,
+@@ -122,10 +123,12 @@
+ unsafe_call({close_log, Name}).
+
+ unsafe_close_log(Name) ->
+ unsafe_call({unsafe_close_log, Name}).
+
++unsafe_create_external(Tab, Mod, Cs) ->
++ unsafe_call({unsafe_create_external, Tab, Mod, Cs}).
+
+ disconnect(Node) ->
+ cast({disconnect, Node}).
+
+ %% Returns GoodNoodes
+@@ -396,10 +399,18 @@
+
+ handle_call({unsafe_close_log, Name}, _From, State) ->
+ disk_log:close(Name),
+ {reply, ok, State};
+
++handle_call({unsafe_create_external, Tab, Mod, Cs}, _From, State) ->
++ case catch Mod:create_table(Tab, Cs) of
++ {'EXIT', ExitReason} ->
++ {reply, {error, ExitReason}, State};
++ Reply ->
++ {reply, Reply, State}
++ end;
++
+ handle_call({negotiate_protocol, Mon, _Version, _Protocols}, _From, State)
+ when State#state.tm_started == false ->
+ State2 = State#state{early_connects = [node(Mon) | State#state.early_connects]},
+ {reply, {node(), {reject, self(), uninitialized, uninitialized}}, State2};
+
240 patches/mnesia_schema.erl.patch
@@ -0,0 +1,240 @@
+--- mnesia_original/src/mnesia_schema.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_schema.erl 2012-03-14 18:55:01.000000000 +0100
+@@ -170,17 +170,23 @@
+ do_set_schema(Tab) ->
+ List = get_create_list(Tab),
+ Cs = list2cs(List),
+ do_set_schema(Tab, Cs).
+
++cstruct_type(#cstruct{type = {external, Type, _}}) ->
++ Type;
++cstruct_type(Cs) ->
++ Cs#cstruct.type.
++
+ do_set_schema(Tab, Cs) ->
+- Type = Cs#cstruct.type,
++ Type = cstruct_type(Cs),
+ set({Tab, setorbag}, Type),
+ set({Tab, local_content}, Cs#cstruct.local_content),
+ set({Tab, ram_copies}, Cs#cstruct.ram_copies),
+ set({Tab, disc_copies}, Cs#cstruct.disc_copies),
+ set({Tab, disc_only_copies}, Cs#cstruct.disc_only_copies),
++ set({Tab, external_copies}, Cs#cstruct.external_copies),
+ set({Tab, load_order}, Cs#cstruct.load_order),
+ set({Tab, access_mode}, Cs#cstruct.access_mode),
+ set({Tab, majority}, Cs#cstruct.majority),
+ set({Tab, all_nodes}, mnesia_lib:cs_to_nodes(Cs)),
+ set({Tab, snmp}, Cs#cstruct.snmp),
+@@ -720,12 +726,13 @@
+ Name = pick(unknown, name, List, must),
+ Type = pick(Name, type, List, set),
+ Rc0 = pick(Name, ram_copies, List, []),
+ Dc = pick(Name, disc_copies, List, []),
+ Doc = pick(Name, disc_only_copies, List, []),
+- Rc = case {Rc0, Dc, Doc} of
+- {[], [], []} -> [node()];
++ Ext = pick(Name, external_copies, List, []),
++ Rc = case {Rc0, Dc, Doc, Ext} of
++ {[], [], [], []} -> [node()];
+ _ -> Rc0
+ end,
+ LC = pick(Name, local_content, List, false),
+ RecName = pick(Name, record_name, List, Name),
+ Attrs = pick(Name, attributes, List, [key, val]),
+@@ -771,10 +778,11 @@
+ [CheckProp(Prop, BadDetsOpts) || Prop <- DetsOpts],
+ #cstruct{name = Name,
+ ram_copies = Rc,
+ disc_copies = Dc,
+ disc_only_copies = Doc,
++ external_copies = Ext,
+ type = Type,
+ index = Ix2,
+ snmp = Snmp,
+ load_order = LoadOrder,
+ access_mode = AccessMode,
+@@ -820,11 +828,17 @@
+ attr_to_pos(Attr, Attrs, Pos + 1);
+ attr_to_pos(Attr, _, _) ->
+ mnesia:abort({bad_type, Attr}).
+
+ check_keys(Tab, [{Key, _Val} | Tail], Items) ->
+- case lists:member(Key, Items) of
++ Key1 = if
++ is_tuple(Key) ->
++ element(1, Key);
++ true ->
++ Key
++ end,
++ case lists:member(Key1, Items) of
+ true -> [Key | check_keys(Tab, Tail, Items)];
+ false -> mnesia:abort({badarg, Tab, Key})
+ end;
+ check_keys(_, [], _) ->
+ [];
+@@ -844,17 +858,23 @@
+ end;
+ has_duplicates([]) ->
+ false.
+
+ %% This is the only place where we check the validity of data
++
++verify_cstruct_type({external, Type, _}) ->
++ verify_cstruct_type(Type);
++verify_cstruct_type(Type) ->
++ lists:member(Type, [set, bag, ordered_set]).
++
+ verify_cstruct(Cs) when is_record(Cs, cstruct) ->
+ verify_nodes(Cs),
+
+ Tab = Cs#cstruct.name,
+ verify(atom, mnesia_lib:etype(Tab), {bad_type, Tab}),
+ Type = Cs#cstruct.type,
+- verify(true, lists:member(Type, [set, bag, ordered_set]),
++ verify(true, verify_cstruct_type(Type),
+ {bad_type, Tab, {type, Type}}),
+
+ %% Currently ordered_set is not supported for disk_only_copies.
+ if
+ Type == ordered_set, Cs#cstruct.disc_only_copies /= [] ->
+@@ -949,16 +969,19 @@
+ verify_nodes(Cs) ->
+ Tab = Cs#cstruct.name,
+ Ram = Cs#cstruct.ram_copies,
+ Disc = Cs#cstruct.disc_copies,
+ DiscOnly = Cs#cstruct.disc_only_copies,
++ Ext = Cs#cstruct.external_copies,
+ LoadOrder = Cs#cstruct.load_order,
+
+ verify({alt, [nil, list]}, mnesia_lib:etype(Ram),
+ {bad_type, Tab, {ram_copies, Ram}}),
+ verify({alt, [nil, list]}, mnesia_lib:etype(Disc),
+ {bad_type, Tab, {disc_copies, Disc}}),
++ verify({alt, [nil, list]}, mnesia_lib:etype(Ext),
++ {bad_type, Tab, {external_copies, Ext}}),
+ case Tab of
+ schema ->
+ verify([], DiscOnly, {bad_type, Tab, {disc_only_copies, DiscOnly}});
+ _ ->
+ verify({alt, [nil, list]},
+@@ -966,14 +989,14 @@
+ {bad_type, Tab, {disc_only_copies, DiscOnly}})
+ end,
+ verify(integer, mnesia_lib:etype(LoadOrder),
+ {bad_type, Tab, {load_order, LoadOrder}}),
+
+- Nodes = Ram ++ Disc ++ DiscOnly,
++ Nodes = Ram ++ Disc ++ DiscOnly ++ Ext,
+ verify(list, mnesia_lib:etype(Nodes),
+ {combine_error, Tab,
+- [{ram_copies, []}, {disc_copies, []}, {disc_only_copies, []}]}),
++ [{ram_copies, []}, {disc_copies, []}, {disc_only_copies, []}, {external_copies, []}]}),
+ verify(false, has_duplicates(Nodes), {combine_error, Tab, Nodes}),
+ AtomCheck = fun(N) -> verify(atom, mnesia_lib:etype(N), {bad_type, Tab, N}) end,
+ lists:foreach(AtomCheck, Nodes).
+
+ verify(Expected, Fun, Error) when is_function(Fun) ->
+@@ -1332,17 +1355,22 @@
+ Cs#cstruct{ram_copies = opt_add(Node, Cs#cstruct.ram_copies)};
+ new_cs(Cs, Node, disc_copies, add) ->
+ Cs#cstruct{disc_copies = opt_add(Node, Cs#cstruct.disc_copies)};
+ new_cs(Cs, Node, disc_only_copies, add) ->
+ Cs#cstruct{disc_only_copies = opt_add(Node, Cs#cstruct.disc_only_copies)};
++new_cs(Cs, Node, {external_copies, _Mod}, add) ->
++ Cs#cstruct{external_copies = opt_add(Node, Cs#cstruct.external_copies)};
+ new_cs(Cs, Node, ram_copies, del) ->
+ Cs#cstruct{ram_copies = lists:delete(Node , Cs#cstruct.ram_copies)};
+ new_cs(Cs, Node, disc_copies, del) ->
+ Cs#cstruct{disc_copies = lists:delete(Node , Cs#cstruct.disc_copies)};
+ new_cs(Cs, Node, disc_only_copies, del) ->
+ Cs#cstruct{disc_only_copies =
+ lists:delete(Node , Cs#cstruct.disc_only_copies)};
++new_cs(Cs, Node, {external_copies, _Mod}, del) ->
++ Cs#cstruct{external_copies =
++ lists:delete(Node , Cs#cstruct.external_copies)};
+ new_cs(Cs, _Node, Storage, _Op) ->
+ mnesia:abort({badarg, Cs#cstruct.name, Storage}).
+
+
+ opt_add(N, L) -> [N | lists:delete(N, L)].
+@@ -1935,10 +1963,15 @@
+ disc_only_copies ->
+ mnesia_lib:set({Tab, create_table},true),
+ create_disc_only_table(Tab,Cs),
+ insert_cstruct(Tid, Cs, false),
+ {true, optional};
++ {external_copies, Mod} ->
++ mnesia_lib:set({Tab, create_table},true),
++ create_external_table(Tab, Mod, Cs),
++ insert_cstruct(Tid, Cs, false),
++ {true, optional};
+ unknown -> %% No replica on this node
+ mnesia_lib:set({Tab, create_table},true),
+ insert_cstruct(Tid, Cs, false),
+ {true, optional}
+ end;
+@@ -2213,10 +2246,18 @@
+ {error,Reason} ->
+ Err = "Failed to create disc table",
+ mnesia:abort({system_limit, Tab, {Err,Reason}})
+ end.
+
++create_external_table(Tab, Mod, Cs) ->
++ case mnesia_monitor:unsafe_create_external(Tab, Mod, Cs) of
++ Tab ->
++ ok;
++ {error,Reason} ->
++ Err = "Failed to create external table",
++ mnesia:abort({system_limit, Tab, {Err,Reason}})
++ end.
+
+ receive_sync([], Pids) ->
+ Pids;
+ receive_sync(Nodes, Pids) ->
+ receive
+@@ -2387,11 +2428,13 @@
+ file:delete(DcdFile);
+ disc_only_copies ->
+ mnesia_monitor:unsafe_close_dets(Tab),
+ Dat = mnesia_lib:tab2dat(Tab),
+ %% disc_delete_table(Tab, Storage),
+- file:delete(Dat)
++ file:delete(Dat);
++ {external_copies, Mod} ->
++ Mod:delete_table(Tab)
+ end;
+
+ undo_prepare_op(Tid, {op, add_table_copy, Storage, Node, TabDef}) ->
+ Cs = list2cs(TabDef),
+ Tab = Cs#cstruct.name,
+@@ -2481,10 +2524,14 @@
+
+ ram_delete_table(Tab, Storage) ->
+ case Storage of
+ unknown ->
+ ignore;
++ external_copies ->
++ ignore;
++ {external_copies, _} ->
++ ignore;
+ disc_only_copies ->
+ ignore;
+ _Else ->
+ %% delete possible index files and data .....
+ %% Got to catch this since if no info has been set in the
+@@ -2717,12 +2764,13 @@
+ end.
+
+ where_to_commit(Tab, CsList) ->
+ Ram = [{N, ram_copies} || N <- pick(Tab, ram_copies, CsList, [])],
+ Disc = [{N, disc_copies} || N <- pick(Tab, disc_copies, CsList, [])],
+- DiscO = [{N, disc_only_copies} || N <- pick(Tab, disc_only_copies, CsList, [])],
+- Ram ++ Disc ++ DiscO.
++ DiscO = [{N, external_copies} || N <- pick(Tab, external_copies, CsList, [])],
++ Ext = [{N, disc_only_copies} || N <- pick(Tab, disc_only_copies, CsList, [])],
++ Ram ++ Disc ++ DiscO ++ Ext.
+
+ %% Changes of the Meta info of schema itself is not allowed
+ restore_schema([{schema, schema, _List} | Schema], R) ->
+ restore_schema(Schema, R);
+ restore_schema([{schema, Tab, List} | Schema], R) ->
71 patches/mnesia_tm.erl.patch
@@ -0,0 +1,71 @@
+--- mnesia_original/src/mnesia_tm.erl 2012-03-14 18:58:25.000000000 +0100
++++ mnesia_patch/src/mnesia_tm.erl 2012-03-14 18:57:52.000000000 +0100
+@@ -1161,10 +1161,11 @@
+ [
+ H#commit{
+ ram_copies = lists:reverse(Ram),
+ disc_copies = lists:reverse(DC),
+ disc_only_copies = lists:reverse(DOC),
++ external_copies = lists:reverse(H#commit.external_copies),
+ snmp = lists:reverse(Snmp)
+ }
+ | reverse(R)].
+
+ prep_recs([N | Nodes], Recs) ->
+@@ -1340,11 +1341,14 @@
+ Rec#commit{ram_copies = [Item | Rec#commit.ram_copies]};
+ disc_copies ->
+ Rec#commit{disc_copies = [Item | Rec#commit.disc_copies]};
+ disc_only_copies ->
+ Rec#commit{disc_only_copies =
+- [Item | Rec#commit.disc_only_copies]}
++ [Item | Rec#commit.disc_only_copies]};
++ {external_copies, _} ->
++ Rec#commit{external_copies =
++ [Item | Rec#commit.external_copies]}
+ end,
+ prepare_node(Node, Storage, Items, Rec2, Kind);
+ prepare_node(_Node, _Storage, Items, Rec, Kind)
+ when Kind == schema, Rec#commit.schema_ops == [] ->
+ Rec#commit{schema_ops = Items};
+@@ -1779,12 +1783,13 @@
+ mnesia_dumper:update(Tid, C#commit.schema_ops, DumperMode),
+ R = do_snmp(Tid, C#commit.snmp),
+ R2 = do_update(Tid, ram_copies, C#commit.ram_copies, R),
+ R3 = do_update(Tid, disc_copies, C#commit.disc_copies, R2),
+ R4 = do_update(Tid, disc_only_copies, C#commit.disc_only_copies, R3),
++ R5 = do_update(Tid, external_copies, C#commit.external_copies, R4),
+ mnesia_subscr:report_activity(Tid),
+- R4.
++ R5.
+
+ %% Update the items
+ do_update(Tid, Storage, [Op | Ops], OldRes) ->
+ case catch do_update_op(Tid, Storage, Op) of
+ ok ->
+@@ -1923,10 +1928,11 @@
+ do_snmp(Tid, Tail).
+
+ commit_nodes([C | Tail], AccD, AccR)
+ when C#commit.disc_copies == [],
+ C#commit.disc_only_copies == [],
++ C#commit.external_copies == [],
+ C#commit.schema_ops == [] ->
+ commit_nodes(Tail, AccD, [C#commit.node | AccR]);
+ commit_nodes([C | Tail], AccD, AccR) ->
+ commit_nodes(Tail, [C#commit.node | AccD], AccR);
+ commit_nodes([], AccD, AccR) ->
+@@ -1935,11 +1941,12 @@
+ commit_decision(D, [C | Tail], AccD, AccR) ->
+ N = C#commit.node,
+ {D2, Tail2} =
+ case C#commit.schema_ops of
+ [] when C#commit.disc_copies == [],
+- C#commit.disc_only_copies == [] ->
++ C#commit.disc_only_copies == [],
++ C#commit.external_copies == [] ->
+ commit_decision(D, Tail, AccD, [N | AccR]);
+ [] ->
+ commit_decision(D, Tail, [N | AccD], AccR);
+ Ops ->
+ case ram_only_ops(N, Ops) of

0 comments on commit fdd4494

Please sign in to comment.