%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(mnesia_trans_access_test).
-author('[email protected]').
-compile([export_all]).
-include("mnesia_test_lib.hrl").
init_per_testcase(Func, Conf) ->
mnesia_test_lib:init_per_testcase(Func, Conf).
end_per_testcase(Func, Conf) ->
mnesia_test_lib:end_per_testcase(Func, Conf).
-define(receive_messages(Msgs), mnesia_recovery_test:receive_messages(Msgs, ?FILE, ?LINE)).
% First Some debug logging
-define(dgb, true).
-ifdef(dgb).
-define(dl(X, Y), ?verbose("**TRACING: " ++ X ++ "**~n", Y)).
-else.
-define(dl(X, Y), ok).
-endif.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all(doc) ->
["Evil access of records in the scope of transactions",
"Invoke all functions in the API and try to cover all legal uses",
"cases as well the illegal dito. This is a complement to the",
"other more explicit test cases."];
all(suite) ->
[
write, read, wread, delete, delete_object,
match_object, select, select14, all_keys,
transaction, nested_activities,
index_tabs, index_lifecycle
].
%% Write records
write(suite) -> [];
write(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = write,
Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:write([]) end)),
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:write({Tab, 2}) end)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:write({foo, 2}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:write({Tab, 1, 2})),
?verify_mnesia(Nodes, []).
%% Read records
read(suite) -> [];
read(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = read,
Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
OneRec = {Tab, 1, 2},
TwoRec = {Tab, 1, 3},
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:read([]) end)),
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:read({Tab}) end)),
?match({aborted, {bad_type, _}}
, mnesia:transaction(fun() -> mnesia:read(OneRec) end)),
?match({atomic, []},
mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, [OneRec]},
mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
?match({atomic, [OneRec, TwoRec]},
mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:read({Tab, 1})),
?verify_mnesia(Nodes, []).
%% Read records and set write lock
wread(suite) -> [];
wread(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = wread,
Schema = [{name, Tab}, {type, set}, {attributes, [k, v]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
OneRec = {Tab, 1, 2},
TwoRec = {Tab, 1, 3},
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:wread([]) end)),
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:wread({Tab}) end)),
?match({aborted, {bad_type, _}}
, mnesia:transaction(fun() -> mnesia:wread(OneRec) end)),
?match({atomic, []},
mnesia:transaction(fun() -> mnesia:wread({Tab, 1}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, [OneRec]},
mnesia:transaction(fun() -> mnesia:wread({Tab, 1}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
?match({atomic, [TwoRec]},
mnesia:transaction(fun() -> mnesia:wread({Tab, 1}) end)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:wread({Tab, 1})),
?verify_mnesia(Nodes, []).
%% Delete record
delete(suite) -> [];
delete(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = delete,
Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:delete([]) end)),
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:delete({Tab}) end)),
?match({aborted, {bad_type, _}}
, mnesia:transaction(fun() -> mnesia:delete({Tab, 1, 2}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:delete({Tab, 1})),
?verify_mnesia(Nodes, []).
%% Delete matching record
delete_object(suite) -> [];
delete_object(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = delete_object,
Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
OneRec = {Tab, 1, 2},
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:delete_object([]) end)),
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:delete_object({Tab}) end)),
?match({aborted, {bad_type, _}},
mnesia:transaction(fun() -> mnesia:delete_object({Tab, 1}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:delete_object(OneRec)),
?match({aborted, {bad_type, Tab, _}},
mnesia:transaction(fun() -> mnesia:delete_object({Tab, {['_']}, 21}) end)),
?match({aborted, {bad_type, Tab, _}},
mnesia:transaction(fun() -> mnesia:delete_object({Tab, {['$5']}, 21}) end)),
?verify_mnesia(Nodes, []).
%% Read matching records
match_object(suite) -> [];
match_object(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = match,
Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
OneRec = {Tab, 1, 2},
OnePat = {Tab, '$1', 2},
?match({atomic, []},
mnesia:transaction(fun() -> mnesia:match_object(OnePat) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, [OneRec]},
mnesia:transaction(fun() -> mnesia:match_object(OnePat) end)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:match_object({foo, '$1', 2}) end)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:match_object({[], '$1', 2}) end)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:match_object(OnePat)),
?verify_mnesia(Nodes, []).
%% select
select(suite) -> [];
select(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = match,
Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
OneRec = {Tab, 1, 2},
TwoRec = {Tab, 2, 3},
OnePat = [{{Tab, '$1', 2}, [], ['$_']}],
?match({atomic, []},
mnesia:transaction(fun() -> mnesia:select(Tab, OnePat) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
?match({atomic, [OneRec]},
mnesia:transaction(fun() -> mnesia:select(Tab, OnePat) end)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:select(Tab, {match, '$1', 2}) end)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:select(Tab, [{'_', [], '$1'}]) end)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:select(Tab, OnePat)),
?verify_mnesia(Nodes, []).
%% more select
select14(suite) -> [];
select14(Config) when is_list(Config) ->
[Node1,Node2] = Nodes = ?acquire_nodes(2, Config),
Tab1 = select14_ets,
Tab2 = select14_dets,
Tab3 = select14_remote,
Tab4 = select14_remote_dets,
Schemas = [[{name, Tab1}, {attributes, [k, v]}, {ram_copies, [Node1]}],
[{name, Tab2}, {attributes, [k, v]}, {disc_only_copies, [Node1]}],
[{name, Tab3}, {attributes, [k, v]}, {ram_copies, [Node2]}],
[{name, Tab4}, {attributes, [k, v]}, {disc_only_copies, [Node2]}]],
[?match({atomic, ok}, mnesia:create_table(Schema)) || Schema <- Schemas],
%% Some Helpers
Trans = fun(Fun) -> mnesia:transaction(Fun) end,
LoopHelp = fun('$end_of_table',_) -> [];
({Recs,Cont},Fun) ->
Sel = mnesia:select(Cont),
Recs ++ Fun(Sel, Fun)
end,
Loop = fun(Table,Pattern) ->
Sel = mnesia:select(Table, Pattern, 1, read),
Res = LoopHelp(Sel,LoopHelp),
case mnesia:table_info(Table, type) of
ordered_set -> Res;
_ -> lists:sort(Res)
end
end,
Test =
fun(Tab) ->
OneRec = {Tab, 1, 2},
TwoRec = {Tab, 2, 3},
OnePat = [{{Tab, '$1', 2}, [], ['$_']}],
All = [OneRec,TwoRec],
AllPat = [{'_', [], ['$_']}],
?match({atomic, []}, Trans(fun() -> Loop(Tab, OnePat) end)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
?match({atomic, [OneRec]}, Trans(fun() -> Loop(Tab, OnePat) end)),
?match({atomic, All}, Trans(fun() -> Loop(Tab, AllPat) end)),
{atomic,{_, Cont}} = Trans(fun() -> mnesia:select(Tab, OnePat, 1, read) end),
?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(Cont) end)),
?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, {match, '$1', 2},1,read) end)),
?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, [{'_', [], '$1'}],1,read) end)),
?match({aborted, _}, Trans(fun() -> mnesia:select(sune) end)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:select(Tab, OnePat,1,read)),
?match({aborted, {badarg,sune}},
Trans(fun() -> mnesia:select(sune) end))
end,
Test(Tab1),
Test(Tab2),
Test(Tab3),
Test(Tab4),
?verify_mnesia(Nodes, []).
%% Pick all keys from table
all_keys(suite) ->[];
all_keys(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = all_keys,
Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
Write = fun() -> mnesia:write({Tab, 14, 4}) end,
AllKeys = fun() -> mnesia:all_keys(Tab) end,
?match({atomic, []}, mnesia:transaction(AllKeys)),
?match({atomic, ok}, mnesia:transaction(Write)),
?match({atomic, [14]}, mnesia:transaction(AllKeys)),
?match({atomic, ok}, mnesia:transaction(Write)),
?match({atomic, [14]}, mnesia:transaction(AllKeys)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:all_keys(foo) end)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:all_keys([]) end)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:all_keys(Tab)),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Use and misuse transactions
transaction(suite) -> [];
transaction(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
?match({atomic, ali_baba}, mnesia:transaction(fun() -> ali_baba end)),
?match({aborted, _}, mnesia:transaction(no_fun)),
?match({aborted, _}, mnesia:transaction(?MODULE, no_fun, [foo])),
{success, [A, B, C, D, E, F, G, H]} =
?start_activities(lists:duplicate(8, Node1)),
?start_transactions([A, B, C, D, E, F, G, H]),
A ! fun() -> mnesia:abort(abort_bad_trans) end,
?match_receive({A, {aborted, abort_bad_trans}}),
B ! fun() -> erlang:error(exit_here) end,
?match_receive({B, {aborted, _}}),
C ! fun() -> throw(throw_bad_trans) end,
?match_receive({C, {aborted, {throw, throw_bad_trans}}}),
D ! fun() -> exit(exit_bad_trans) end,
?match_receive({D, {aborted, exit_bad_trans}}),
E ! fun() -> exit(normal) end,
?match_receive({E, {aborted, normal}}),
F ! fun() -> exit(abnormal) end,
?match_receive({F, {aborted, abnormal}}),
G ! fun() -> exit(G, abnormal) end,
?match_receive({'EXIT', G, abnormal}),
H ! fun() -> exit(H, kill) end,
?match_receive({'EXIT', H, killed}),
?match({atomic, ali_baba},
mnesia:transaction(fun() -> ali_baba end, infinity)),
?match({atomic, ali_baba}, mnesia:transaction(fun() -> ali_baba end, 1)),
?match({atomic, ali_baba}, mnesia:transaction(fun() -> ali_baba end, 0)),
?match({aborted, Reason8} when element(1, Reason8) == badarg, mnesia:transaction(fun() -> ali_baba end, -1)),
?match({aborted, Reason1} when element(1, Reason1) == badarg, mnesia:transaction(fun() -> ali_baba end, foo)),
Fun = fun() ->
?match(true, mnesia:is_transaction()),
?match({atomic, ok},
mnesia:transaction(fun() -> ?match(true, mnesia:is_transaction()),ok end)), ok end,
?match({atomic, ok}, mnesia:transaction(Fun)),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
nested_activities(suite) ->
[
basic_nested,
nested_transactions,
mix_of_nested_activities
].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% ensure that nested transactions behave correctly
%% We create a particular table that is used by this test only
-record(ntab, {a, b}).
basic_nested(doc) -> ["Test the basic functionality of nested transactions"];
basic_nested(suite) -> [];
basic_nested(Config) when is_list(Config) ->
Nodes = ?acquire_nodes(3, Config),
Args = [{ram_copies, Nodes},
{attributes, record_info(fields, ntab)}],
?match({atomic, ok}, mnesia:create_table(ntab, Args)),
do_nested(top),
case mnesia_test_lib:diskless(Config) of
false ->
lists:foreach(fun(N) ->
?match({atomic, ok},
mnesia:change_table_copy_type(ntab, N, disc_only_copies))
end, Nodes),
do_nested(top);
true ->
skip
end,
?verify_mnesia(Nodes, []).
do_nested(How) ->
F1 = fun() ->
mnesia:write(#ntab{a= 1}),
mnesia:write(#ntab{a= 2})
end,
F2 = fun() ->
mnesia:read({ntab, 1})
end,
?match({atomic, ok}, mnesia:transaction(F1)),
?match({atomic, _}, mnesia:transaction(F2)),
?match({atomic, {aborted, _}},
mnesia:transaction(fun() -> n_f1(),
mnesia:transaction(fun() -> n_f2() end)
end)),
?match({atomic, {aborted, _}},
mnesia:transaction(fun() -> n_f1(),
mnesia:transaction(fun() -> n_f3() end)
end)),
?match({atomic, {atomic, [#ntab{a = 5}]}},
mnesia:transaction(fun() -> mnesia:write(#ntab{a = 5}),
mnesia:transaction(fun() -> n_f4() end)
end)),
Cyclic = fun() -> mnesia:abort({cyclic,a,a,a,a,a}) end, %% Ugly
NodeNotR = fun() -> mnesia:abort({node_not_running, testNode}) end,
TestAbort = fun(Fun) ->
case get(restart_counter) of
undefined ->
put(restart_counter, 1),
Fun();
_ ->
erase(restart_counter),
ok
end
end,
?match({atomic,{atomic,ok}},
mnesia:transaction(fun()->mnesia:transaction(TestAbort,
[Cyclic])end)),
?match({atomic,{atomic,ok}},
mnesia:transaction(fun()->mnesia:transaction(TestAbort,
[NodeNotR])end)),
%% Now try the restart thingie
case How of
top ->
Pids = [spawn(?MODULE, do_nested, [{spawned, self()}]),
spawn(?MODULE, do_nested, [{spawned, self()}]),
spawn(?MODULE, do_nested, [{spawned, self()}]),
spawn(?MODULE, do_nested, [{spawned, self()}])],
?match({info, _, _}, mnesia_tm:get_info(2000)),
lists:foreach(fun(P) -> receive
{P, ok} -> ok
end
end, Pids),
?match([], [Tab || Tab <- ets:all(), mnesia_trans_store == ets:info(Tab, name)]);
{spawned, Pid} ->
?match({info, _, _}, mnesia_tm:get_info(2000)),
Pid ! {self(), ok},
exit(normal)
end.
n_f1() ->
mnesia:read({ntab, 1}),
mnesia:write(#ntab{a = 3}).
n_f2() ->
mnesia:write(#ntab{a = 4}),
erlang:error(exit_here).
n_f3() ->
mnesia:write(#ntab{a = 4}),
throw(funky).
n_f4() ->
mnesia:read({ntab, 5}).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
nested_transactions(doc) ->
["Verify that nested_transactions are handled as expected"];
nested_transactions(suite) ->
[nested_trans_both_ok,
nested_trans_child_dies,
nested_trans_parent_dies,
nested_trans_both_dies].
nested_trans_both_ok(suite) -> [];
nested_trans_both_ok(Config) when is_list(Config) ->
nested_transactions(Config, ok, ok).
nested_trans_child_dies(suite) -> [];
nested_trans_child_dies(Config) when is_list(Config) ->
nested_transactions(Config, abort, ok).
nested_trans_parent_dies(suite) -> [];
nested_trans_parent_dies(Config) when is_list(Config) ->
nested_transactions(Config, ok, abort).
nested_trans_both_dies(suite) -> [];
nested_trans_both_dies(Config) when is_list(Config) ->
nested_transactions(Config, abort, abort).
nested_transactions(Config, Child, Father) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
Tab = nested_trans,
Def =
case mnesia_test_lib:diskless(Config) of
true ->
[{name, Tab}, {ram_copies, Nodes}];
false ->
[{name, Tab}, {ram_copies, [Node1]},
{disc_copies, [Node2]}, {disc_only_copies, [Node3]}]
end,
?match({atomic, ok}, mnesia:create_table(Def)),
?match(ok, mnesia:dirty_write({Tab, father, not_updated})),
?match(ok, mnesia:dirty_write({Tab, child, not_updated})),
ChildOk = fun() -> mnesia:write({Tab, child, updated}) end,
ChildAbort = fun() ->
mnesia:write({Tab, child, updated}),
erlang:error(exit_here)
end,
Child_Fun = % Depending of test case
case Child of
ok -> ChildOk;
abort -> ChildAbort
end,
FatherOk = fun() -> mnesia:transaction(Child_Fun),
mnesia:write({Tab, father, updated})
end,
FatherAbort = fun() -> mnesia:transaction(Child_Fun),
mnesia:write({Tab, father, updated}),
erlang:error(exit_here)
end,
{FatherRes, ChildRes} = % Depending of test case
case Father of
ok -> ?match({atomic, ok}, mnesia:transaction(FatherOk)),
case Child of
ok -> {[{Tab, father, updated}], [{Tab, child, updated}]};
_ -> {[{Tab, father, updated}], [{Tab, child, not_updated}]}
end;
abort -> ?match({aborted, _}, mnesia:transaction(FatherAbort)),
{[{Tab, father, not_updated}], [{Tab, child, not_updated}]}
end,
%% Syncronize things!!
?match({atomic, ok}, mnesia:sync_transaction(fun() -> mnesia:write({Tab, sync, sync}) end)),
?match(ChildRes, rpc:call(Node1, mnesia, dirty_read, [{Tab, child}])),
?match(ChildRes, rpc:call(Node2, mnesia, dirty_read, [{Tab, child}])),
?match(ChildRes, rpc:call(Node3, mnesia, dirty_read, [{Tab, child}])),
?match(FatherRes, rpc:call(Node1, mnesia, dirty_read, [{Tab, father}])),
?match(FatherRes, rpc:call(Node2, mnesia, dirty_read, [{Tab, father}])),
?match(FatherRes, rpc:call(Node3, mnesia, dirty_read, [{Tab, father}])),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
mix_of_nested_activities(doc) ->
["Verify that dirty operations in a transaction are handled like ",
"normal transactions"];
mix_of_nested_activities(suite) -> [];
mix_of_nested_activities(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
Tab = tab,
Def =
case mnesia_test_lib:diskless(Config) of
true -> [{ram_copies, Nodes}];
false ->
[{ram_copies, [Node1]},
{disc_copies, [Node2]},
{disc_only_copies, [Node3]}]
end,
?match({atomic, ok}, mnesia:create_table(Tab, [{type,bag}|Def])),
Activities = [transaction, sync_transaction,
ets, async_dirty, sync_dirty],
%% Make a test for all 3000 combinations
Tests = [[A,B,C,D,E] ||
A <- Activities,
B <- Activities,
C <- Activities,
D <- Activities,
E <- Activities],
Foreach =
fun(Test,No) ->
Result = lists:reverse(Test),
?match({No,Result},{No,catch apply_op({Tab,No},Test)}),
No+1
end,
lists:foldl(Foreach, 0, Tests),
?verify_mnesia(Nodes, []).
apply_op(Oid,[Type]) ->
check_res(Type,mnesia:Type(fun() -> [Type|read_op(Oid)] end));
apply_op(Oid = {Tab,Key},[Type|Next]) ->
check_res(Type,mnesia:Type(fun() ->
Prev = read_op(Oid),
mnesia:write({Tab,Key,[Type|Prev]}),
apply_op(Oid,Next)
end)).
check_res(transaction, {atomic,Res}) ->
Res;
check_res(sync_transaction, {atomic,Res}) ->
Res;
check_res(async_dirty, Res) when is_list(Res) ->
Res;
check_res(sync_dirty, Res) when is_list(Res) ->
Res;
check_res(ets, Res) when is_list(Res) ->
Res;
check_res(Type,Res) ->
?match(bug,{Type,Res}).
read_op(Oid) ->
case lists:reverse(mnesia:read(Oid)) of
[] -> [];
[{_,_,Ops}|_] ->
Ops
end.
index_tabs(suite) ->
[
index_match_object,
index_read,
index_update,
index_write
].
%% Read matching records by using an index
index_match_object(suite) -> [];
index_match_object(Config) when is_list(Config) ->
[Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
Tab = index_match_object,
Schema = [{name, Tab}, {attributes, [k, v, e]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
ValPos = 3,
BadValPos = ValPos + 2,
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
?match({atomic, []},
mnesia:transaction(fun() -> mnesia:index_match_object({Tab, '$1', 2}, ValPos) end)),
OneRec = {Tab, {1, 1}, 2, {1, 1}},
OnePat = {Tab, '$1', 2, '_'},
BadPat = {Tab, '$1', '$2', '_'}, %% See ref guide
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
Imatch = fun(Patt, Pos) ->
mnesia:transaction(fun() -> lists:sort(mnesia:index_match_object(Patt, Pos)) end)
end,
?match({atomic, [OneRec]}, Imatch(OnePat, ValPos)),
?match({aborted, _}, Imatch(OnePat, BadValPos)),
?match({aborted, _}, Imatch({foo, '$1', 2, '_'}, ValPos)),
?match({aborted, _}, Imatch({[], '$1', 2, '_'}, ValPos)),
?match({aborted, _}, Imatch(BadPat, ValPos)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:index_match_object(OnePat, ValPos)),
Another = {Tab, {3,1}, 2, {4,4}},
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(Another) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write({Tab, {4, 4}, 3, {4, 4}}) end)),
?match({atomic, [OneRec]}, Imatch({Tab, {1,1}, 2, {1,1}}, ValPos)),
?match({atomic, [OneRec]}, Imatch({Tab, {1,1}, 2, '$1'}, ValPos)),
?match({atomic, [OneRec]}, Imatch({Tab, '$1', 2, {1,1}}, ValPos)),
?match({atomic, [OneRec]}, Imatch({Tab, '$1', 2, '$1'}, ValPos)),
?match({atomic, [OneRec]}, Imatch({Tab, {1, '$1'}, 2, '_'}, ValPos)),
?match({atomic, [OneRec]}, Imatch({Tab, {'$2', '$1'}, 2, {'_', '$1'}}, ValPos)),
?match({atomic, [OneRec, Another]}, Imatch({Tab, '_', 2, '_'}, ValPos)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write({Tab, 4, 5, {7, 4}}) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write({Tab, 7, 5, {7, 5}}) end)),
?match({atomic, [{Tab, 4, 5, {7, 4}}]}, Imatch({Tab, '$1', 5, {'_', '$1'}}, ValPos)),
?match({atomic, [OneRec]}, rpc:call(Node2, mnesia, transaction,
[fun() ->
lists:sort(mnesia:index_match_object({Tab, {1,1}, 2,
{1,1}}, ValPos))
end])),
?verify_mnesia(Nodes, []).
%% Read records by using an index
index_read(suite) -> [];
index_read(Config) when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = index_read,
Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
ValPos = 3,
BadValPos = ValPos + 1,
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
OneRec = {Tab, 1, 2},
?match({atomic, []},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
?match({atomic, [OneRec]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, BadValPos) end)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:index_read(foo, 2, ValPos) end)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:index_read([], 2, ValPos) end)),
?match({'EXIT', {aborted, no_transaction}}, mnesia:index_read(Tab, 2, ValPos)),
?verify_mnesia(Nodes, []).
index_update(suite) -> [index_update_set, index_update_bag];
index_update(doc) -> ["See Ticket OTP-2083, verifies that a table with a index is "
"update in the correct way i.e. the index finds the correct "
"records after a update"].
index_update_set(suite) -> [];
index_update_set(Config)when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = index_test,
Schema = [{name, Tab}, {attributes, [k, v1, v2, v3]}, {ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
ValPos = v1,
ValPos2 = v3,
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
Pat1 = {Tab, '$1', 2, '$2', '$3'},
Pat2 = {Tab, '$1', '$2', '$3', '$4'},
Rec1 = {Tab, 1, 2, 3, 4},
Rec2 = {Tab, 2, 2, 13, 14},
Rec3 = {Tab, 1, 12, 13, 14},
Rec4 = {Tab, 4, 2, 13, 14},
?match({atomic, []},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
?match({atomic, [Rec1]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(Rec2) end)),
{atomic, R1} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1, Rec2], lists:sort(R1)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(Rec3) end)),
{atomic, R2} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec2], lists:sort(R2)),
?match({atomic, [Rec2]},
mnesia:transaction(fun() -> mnesia:index_match_object(Pat1, ValPos) end)),
{atomic, R3} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
?match([Rec3, Rec2], lists:sort(R3)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
{atomic, R4} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec2, Rec4], lists:sort(R4)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
?match({atomic, [Rec2]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
{atomic, R5} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
?match([Rec3, Rec2, Rec4], lists:sort(R5)),
{atomic, R6} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec2, Rec4], lists:sort(R6)),
?match({atomic, []},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
{atomic, R7} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
?match([Rec3, Rec2, Rec4], lists:sort(R7)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
{atomic, R8} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1, Rec2, Rec4], lists:sort(R8)),
?match({atomic, [Rec1]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
{atomic, R9} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
?match([Rec2, Rec4], lists:sort(R9)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec2) end)),
{atomic, R10} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1, Rec4], lists:sort(R10)),
?match({atomic, [Rec1]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
?match({atomic, [Rec4]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
{atomic, R11} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1], lists:sort(R11)),
?match({atomic, [Rec1]},mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
?match({atomic, []},mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end)),
?verify_mnesia(Nodes, []).
index_update_bag(suite) -> [];
index_update_bag(Config)when is_list(Config) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = index_test,
Schema = [{name, Tab},
{type, bag},
{attributes, [k, v1, v2, v3]},
{ram_copies, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
ValPos = v1,
ValPos2 = v3,
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
Pat1 = {Tab, '$1', 2, '$2', '$3'},
Pat2 = {Tab, '$1', '$2', '$3', '$4'},
Rec1 = {Tab, 1, 2, 3, 4},
Rec2 = {Tab, 2, 2, 13, 14},
Rec3 = {Tab, 1, 12, 13, 14},
Rec4 = {Tab, 4, 2, 13, 4},
Rec5 = {Tab, 1, 2, 234, 14},
%% Simple Index
?match({atomic, []},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
?match({atomic, [Rec1]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(Rec2) end)),
{atomic, R1} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1, Rec2], lists:sort(R1)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(Rec3) end)),
{atomic, R2} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1, Rec2], lists:sort(R2)),
{atomic, R3} = mnesia:transaction(fun() -> mnesia:index_match_object(Pat1, ValPos) end),
?match([Rec1, Rec2], lists:sort(R3)),
{atomic, R4} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
?match([Rec1, Rec3, Rec2], lists:sort(R4)),
?match({atomic, ok},
mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
{atomic, R5} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1, Rec2, Rec4], lists:sort(R5)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
{atomic, R6} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1, Rec2], lists:sort(R6)),
%% OTP-6587 Needs some whitebox testing to see that the index table is cleaned correctly
[IPos] = mnesia_lib:val({Tab,index}),
ITab = mnesia_lib:val({index_test,{index, IPos}}),
io:format("~n Index ~p @ ~p => ~p ~n~n",[IPos,ITab, ets:tab2list(ITab)]),
?match([{2,1},{2,2},{12,1}], ets:tab2list(ITab)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec5) end)),
{atomic, R60} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1,Rec5,Rec2], lists:sort(R60)),
?match([{2,1},{2,2},{12,1}], ets:tab2list(ITab)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec3) end)),
{atomic, R61} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1,Rec5,Rec2], lists:sort(R61)),
{atomic, R62} = mnesia:transaction(fun() -> mnesia:index_read(Tab,12, ValPos) end),
?match([], lists:sort(R62)),
?match([{2,1},{2,2}], ets:tab2list(ITab)),
%% reset for rest of testcase
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec3) end)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec5) end)),
{atomic, R6} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1, Rec2], lists:sort(R6)),
%% OTP-6587
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec1) end)),
?match({atomic, [Rec2]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
{atomic, R7} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
?match([Rec3, Rec2], lists:sort(R7)),
%% Two indexies
?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
{atomic, R8} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1, Rec2, Rec4], lists:sort(R8)),
{atomic, R9} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end),
?match([Rec1, Rec4], lists:sort(R9)),
{atomic, R10} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
?match([Rec3, Rec2], lists:sort(R10)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec5) end)),
{atomic, R11} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec1, Rec5, Rec2, Rec4], lists:sort(R11)),
{atomic, R12} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end),
?match([Rec1, Rec4], lists:sort(R12)),
{atomic, R13} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
?match([Rec5, Rec3, Rec2], lists:sort(R13)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec1) end)),
{atomic, R14} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec5, Rec2, Rec4], lists:sort(R14)),
?match({atomic, [Rec4]},
mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
{atomic, R15} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
?match([Rec5, Rec3, Rec2], lists:sort(R15)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec5) end)),
{atomic, R16} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec2, Rec4], lists:sort(R16)),
?match({atomic, [Rec4]}, mnesia:transaction(fun()->mnesia:index_read(Tab, 4, ValPos2) end)),
{atomic, R17} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
?match([Rec3, Rec2], lists:sort(R17)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
{atomic, R18} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
?match([Rec2, Rec4], lists:sort(R18)),
?match({atomic, [Rec4]}, mnesia:transaction(fun()->mnesia:index_read(Tab, 4, ValPos2) end)),
{atomic, R19} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
?match([Rec2], lists:sort(R19)),
?verify_mnesia(Nodes, []).
index_write(suite) -> [];
index_write(doc) -> ["See ticket OTP-8072"];
index_write(Config)when is_list(Config) ->
Nodes = ?acquire_nodes(1, Config),
mnesia:create_table(a, [{index, [val]}]),
mnesia:create_table(counter, []),
CreateIfNonExist =
fun(Index) ->
case mnesia:index_read(a, Index, 3) of
[] ->
Id = mnesia:dirty_update_counter(counter, id, 1),
New = {a, Id, Index},
mnesia:write(New),
New;
[Found] ->
Found
end
end,
Trans = fun(A) ->
mnesia:transaction(CreateIfNonExist, [A])
%% This works better most of the time
%% And it is allowed to fail since it's dirty
%% mnesia:async_dirty(CreateIfNonExist, [A])
end,
Self = self(),
Update = fun() ->
Res = lists:map(Trans, lists:seq(1,10)),
Self ! {self(), Res}
end,
Pids = [spawn(Update) || _ <- lists:seq(1,5)],
Gather = fun(Pid, Acc) -> receive {Pid, Res} -> [Res|Acc] end end,
Results = lists:foldl(Gather, [], Pids),
Expected = hd(Results),
Check = fun(Res) -> ?match(Expected, Res) end,
lists:foreach(Check, Results),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Add and drop indecies
index_lifecycle(suite) ->
[
add_table_index_ram,
add_table_index_disc,
add_table_index_disc_only,
create_live_table_index_ram,
create_live_table_index_disc,
create_live_table_index_disc_only,
del_table_index_ram,
del_table_index_disc,
del_table_index_disc_only,
idx_schema_changes
].
add_table_index_ram(suite) -> [];
add_table_index_ram(Config) when is_list(Config) ->
add_table_index(Config, ram_copies).
add_table_index_disc(suite) -> [];
add_table_index_disc(Config) when is_list(Config) ->
add_table_index(Config, disc_copies).
add_table_index_disc_only(suite) -> [];
add_table_index_disc_only(Config) when is_list(Config) ->
add_table_index(Config, disc_only_copies).
%% Add table index
add_table_index(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = add_table_index,
Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
ValPos = 3,
BadValPos = ValPos + 1,
?match({aborted, Reason41 } when element(1, Reason41) == bad_type,
mnesia:add_table_index(Tab, BadValPos)),
?match({aborted,Reason42 } when element(1, Reason42) == bad_type,
mnesia:add_table_index(Tab, 2)),
?match({aborted, Reason43 } when element(1, Reason43) == bad_type,
mnesia:add_table_index(Tab, 1)),
?match({aborted, Reason44 } when element(1, Reason44) == bad_type,
mnesia:add_table_index(Tab, 0)),
?match({aborted, Reason45 } when element(1, Reason45) == bad_type,
mnesia:add_table_index(Tab, -1)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
?match({aborted, Reason46 } when element(1, Reason46) == already_exists,
mnesia:add_table_index(Tab, ValPos)),
NestedFun = fun() ->
?match({aborted, nested_transaction},
mnesia:add_table_index(Tab, ValPos)),
ok
end,
?match({atomic, ok}, mnesia:transaction(NestedFun)),
?verify_mnesia(Nodes, []).
create_live_table_index_ram(suite) -> [];
create_live_table_index_ram(Config) when is_list(Config) ->
create_live_table_index(Config, ram_copies).
create_live_table_index_disc(suite) -> [];
create_live_table_index_disc(Config) when is_list(Config) ->
create_live_table_index(Config, disc_copies).
create_live_table_index_disc_only(suite) -> [];
create_live_table_index_disc_only(Config) when is_list(Config) ->
create_live_table_index(Config, disc_only_copies).
create_live_table_index(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = create_live_table_index,
Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
ValPos = 3,
mnesia:dirty_write({Tab, 1, 2}),
Fun = fun() ->
?match(ok, mnesia:write({Tab, 2, 2})),
ok
end,
?match({atomic, ok}, mnesia:transaction(Fun)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]},
mnesia:transaction(fun() -> lists:sort(mnesia:index_read(Tab, 2, ValPos))
end)),
?verify_mnesia(Nodes, []).
%% Drop table index
del_table_index_ram(suite) ->[];
del_table_index_ram(Config) when is_list(Config) ->
del_table_index(Config, ram_copies).
del_table_index_disc(suite) ->[];
del_table_index_disc(Config) when is_list(Config) ->
del_table_index(Config, disc_copies).
del_table_index_disc_only(suite) ->[];
del_table_index_disc_only(Config) when is_list(Config) ->
del_table_index(Config, disc_only_copies).
del_table_index(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = del_table_index,
Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Schema)),
ValPos = 3,
BadValPos = ValPos + 1,
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
?match({aborted,Reason} when element(1, Reason) == no_exists,
mnesia:del_table_index(Tab, BadValPos)),
?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
?match({aborted,Reason1} when element(1, Reason1) == no_exists,
mnesia:del_table_index(Tab, ValPos)),
NestedFun =
fun() ->
?match({aborted, nested_transaction},
mnesia:del_table_index(Tab, ValPos)),
ok
end,
?match({atomic, ok}, mnesia:transaction(NestedFun)),
?verify_mnesia(Nodes, []).
idx_schema_changes(suite) -> [idx_schema_changes_ram,
idx_schema_changes_disc,
idx_schema_changes_disc_only];
idx_schema_changes(doc) ->
["Tests that index tables are handled correctly when schema changes.",
"For example when a replica is deleted or inserted",
"TICKET OTP-2XXX (ELVIRA)"].
idx_schema_changes_ram(suite) -> [];
idx_schema_changes_ram(Config) when is_list(Config) ->
idx_schema_changes(Config, ram_copies).
idx_schema_changes_disc(suite) -> [];
idx_schema_changes_disc(Config) when is_list(Config) ->
idx_schema_changes(Config, disc_copies).
idx_schema_changes_disc_only(suite) -> [];
idx_schema_changes_disc_only(Config) when is_list(Config) ->
idx_schema_changes(Config, disc_only_copies).
idx_schema_changes(Config, Storage) ->
[N1, N2] = Nodes = ?acquire_nodes(2, Config),
Tab = index_schema_changes,
Idx = 3,
Schema = [{name, Tab}, {index, [Idx]}, {attributes, [k, v]}, {Storage, Nodes}],
?match({atomic, ok}, mnesia:create_table(Schema)),
{Storage1, Storage2} =
case Storage of
disc_only_copies ->
{ram_copies, disc_copies};
disc_copies ->
{disc_only_copies, ram_copies};
ram_copies ->
{disc_copies, disc_only_copies}
end,
Write = fun(N) ->
mnesia:write({Tab, N, N+50})
end,
[mnesia:sync_transaction(Write, [N]) || N <- lists:seq(1, 10)],
?match([{Tab, 1, 51}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 51, Idx])),
?match([{Tab, 1, 51}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 51, Idx])),
?match({atomic, ok}, mnesia:change_table_copy_type(Tab, N1, Storage1)),
?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [17]])),
?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [18]])),
?match([{Tab, 17, 67}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 67, Idx])),
?match([{Tab, 18, 68}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 68, Idx])),
?match({atomic, ok}, mnesia:del_table_copy(Tab, N1)),
?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [11]])),
?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [12]])),
?match([{Tab, 11, 61}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 61, Idx])),
?match([{Tab, 12, 62}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 62, Idx])),
?match({atomic, ok}, mnesia:move_table_copy(Tab, N2, N1)),
?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [19]])),
?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [20]])),
?match([{Tab, 19, 69}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 69, Idx])),
?match([{Tab, 20, 70}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 70, Idx])),
?match({atomic, ok}, mnesia:add_table_copy(Tab, N2, Storage)),
?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [13]])),
?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [14]])),
?match([{Tab, 13, 63}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 63, Idx])),
?match([{Tab, 14, 64}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 64, Idx])),
?match({atomic, ok}, mnesia:change_table_copy_type(Tab, N2, Storage2)),
?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [15]])),
?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [16]])),
?match([{Tab, 15, 65}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 65, Idx])),
?match([{Tab, 16, 66}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 66, Idx])),
?verify_mnesia(Nodes, []).