%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-2018. All Rights Reserved.
%%
%% Licensed under the Apache License, Version 2.0 (the "License");
%% you may not use this file except in compliance with the License.
%% You may obtain a copy of the License at
%%
%%     http://www.apache.org/licenses/LICENSE-2.0
%%
%% Unless required by applicable law or agreed to in writing, software
%% distributed under the License is distributed on an "AS IS" BASIS,
%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
%% See the License for the specific language governing permissions and
%% limitations under the License.
%%
%% %CopyrightEnd%
%%

%%
-module(mnesia_trans_access_test).
-author('hakan@erix.ericsson.se').

-export([init_per_testcase/2, end_per_testcase/2,
         init_per_group/2, end_per_group/2,
         all/0, groups/0]).

-export([write/1, read/1, wread/1, delete/1, delete_object/1,
         match_object/1, select/1, select14/1, all_keys/1, transaction/1,
         basic_nested/1, mix_of_nested_activities/1,
         nested_trans_both_ok/1, nested_trans_child_dies/1,
         nested_trans_parent_dies/1, nested_trans_both_dies/1,
         index_match_object/1, index_read/1,index_write/1,
         index_update_set/1, index_update_bag/1,
         add_table_index_ram/1, add_table_index_disc/1,
         add_table_index_disc_only/1, create_live_table_index_ram/1,
         create_live_table_index_disc/1,
         create_live_table_index_disc_only/1, del_table_index_ram/1,
         del_table_index_disc/1, del_table_index_disc_only/1,
         idx_schema_changes_ram/1, idx_schema_changes_disc/1,
         idx_schema_changes_disc_only/1]).

-export([do_nested/1]).

-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() ->
    [write, read, wread, delete, delete_object,
     match_object, select, select14, all_keys, transaction,
     {group, nested_activities}, {group, index_tabs},
     {group, index_lifecycle}].

groups() ->
    [{nested_activities, [],
      [basic_nested, {group, nested_transactions},
       mix_of_nested_activities]},
     {nested_transactions, [],
      [nested_trans_both_ok, nested_trans_child_dies,
       nested_trans_parent_dies, nested_trans_both_dies]},
     {index_tabs, [],
      [index_match_object, index_read, {group, index_update},
       index_write]},
     {index_update, [],
      [index_update_set, index_update_bag]},
     {index_lifecycle, [],
      [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,
       {group, idx_schema_changes}]},
     {idx_schema_changes, [],
      [idx_schema_changes_ram, idx_schema_changes_disc,
       idx_schema_changes_disc_only]}].

init_per_group(_GroupName, Config) ->
    Config.

end_per_group(_GroupName, Config) ->
    Config.


%% 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) ->
    [_N1,N2] = Nodes = ?acquire_nodes(2, Config),
    Tab = wread,
    Schema = [{name, Tab}, {type, set}, {attributes, [k, v]}, {ram_copies, Nodes}],
    ?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})),

    ?match({atomic, ok},
	   mnesia:transaction(fun() -> mnesia:write(Tab, {Tab, 42, a}, sticky_write) end)),
    ?match({atomic, [{Tab,42, a}]},
	   rpc:call(N2, mnesia, transaction, [fun() -> mnesia:wread({Tab, 42}) end])),
    ?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,
    Dirty = fun(Fun) -> mnesia:async_dirty(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,{_, ContOne}} = Trans(fun() -> mnesia:select(Tab, OnePat, 1, read) end),
		?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(ContOne) end)),
		?match('$end_of_table',              Dirty(fun() -> mnesia:select(ContOne) end)),

		{atomic,{_, ContAll}} = Trans(fun() -> mnesia:select(Tab, AllPat, 1, read) end),
		?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(ContAll) end)),
		?match({[_], _},                     Dirty(fun() -> mnesia:select(ContAll) 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, []).


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

%% 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_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, bug},{Type,Res}).

read_op(Oid) ->
    case lists:reverse(mnesia:read(Oid)) of
	[] -> [];
	[{_,_,Ops}|_] ->
	    Ops
    end.


%% 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_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:delete_object(Rec5) 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}], lists:keysort(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}], lists:keysort(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}], lists:keysort(1,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


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) ->
    [N1,N2,N3] = Nodes = ?acquire_nodes(3, Config),
    Tab = create_live_table_index,
    Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, Nodes}],
    ?match({atomic, ok}, mnesia:create_table(Schema)),
    ValPos = 3,
    mnesia:dirty_write({Tab, 1, 2}),

    Fun = fun() -> mnesia:write({Tab, 2, 2}) end,
    ?match({atomic, ok}, mnesia:transaction(Fun)),
    ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
    IRead = fun() -> lists:sort(mnesia:index_read(Tab, 2, ValPos)) end,
    ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]}, mnesia:transaction(IRead)),
    ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),

    %% Bug when adding index when table is still unloaded
    %% By setting load order we hopefully will trigger the bug
    mnesia:change_table_copy_type(Tab, N2, ram_copies),
    mnesia:change_table_copy_type(Tab, N3, ram_copies),
    ?match({atomic,ok}, mnesia:change_table_copy_type(schema, N2, ram_copies)),
    ?match({atomic,ok}, mnesia:change_table_copy_type(schema, N3, ram_copies)),

    Create = fun(N) ->
		     TabN = list_to_atom("tab_" ++ integer_to_list(N)),
		     Def = [{ram_copies, Nodes}, {load_order, N}],
		     mnesia:create_table(TabN, Def)
	     end,

    ?match([{atomic,ok}|_], [Create(N) || N <- lists:seq(1,50)]),

    ?match([], mnesia_test_lib:stop_mnesia([N2,N3])),
    Ext = [{schema, ?BACKEND}],
    ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1]}|Ext]])),
    ?match(ok, rpc:call(N3, mnesia, start, [[{extra_db_nodes,[N1]}|Ext]])),

    ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),

    ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]}, mnesia:transaction(IRead)),
    ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]},
	   rpc:call(N2, mnesia, transaction, [IRead])),

    ?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_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, []).