1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 1996-2012. All Rights Reserved.
    5: %%
    6: %% The contents of this file are subject to the Erlang Public License,
    7: %% Version 1.1, (the "License"); you may not use this file except in
    8: %% compliance with the License. You should have received a copy of the
    9: %% Erlang Public License along with this software. If not, it can be
   10: %% retrieved online at http://www.erlang.org/.
   11: %%
   12: %% Software distributed under the License is distributed on an "AS IS"
   13: %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
   14: %% the License for the specific language governing rights and limitations
   15: %% under the License.
   16: %%
   17: %% %CopyrightEnd%
   18: %%
   19: 
   20: %%
   21: -module(mnesia_trans_access_test).
   22: -author('hakan@erix.ericsson.se').
   23: -compile([export_all]).
   24: -include("mnesia_test_lib.hrl").
   25: 
   26: init_per_testcase(Func, Conf) ->
   27:     mnesia_test_lib:init_per_testcase(Func, Conf).
   28: 
   29: end_per_testcase(Func, Conf) ->
   30:     mnesia_test_lib:end_per_testcase(Func, Conf).
   31: 
   32: -define(receive_messages(Msgs), mnesia_recovery_test:receive_messages(Msgs, ?FILE, ?LINE)).
   33: 
   34: % First Some debug logging
   35: -define(dgb, true).
   36: -ifdef(dgb).
   37: -define(dl(X, Y), ?verbose("**TRACING: " ++ X ++ "**~n", Y)).
   38: -else.
   39: -define(dl(X, Y), ok).
   40: -endif.
   41: 
   42: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   43: all() ->
   44:     [write, read, wread, delete, delete_object,
   45:      match_object, select, select14, all_keys, transaction,
   46:      {group, nested_activities}, {group, index_tabs},
   47:      {group, index_lifecycle}].
   48: 
   49: groups() ->
   50:     [{nested_activities, [],
   51:       [basic_nested, {group, nested_transactions},
   52:        mix_of_nested_activities]},
   53:      {nested_transactions, [],
   54:       [nested_trans_both_ok, nested_trans_child_dies,
   55:        nested_trans_parent_dies, nested_trans_both_dies]},
   56:      {index_tabs, [],
   57:       [index_match_object, index_read, {group, index_update},
   58:        index_write]},
   59:      {index_update, [],
   60:       [index_update_set, index_update_bag]},
   61:      {index_lifecycle, [],
   62:       [add_table_index_ram, add_table_index_disc,
   63:        add_table_index_disc_only, create_live_table_index_ram,
   64:        create_live_table_index_disc,
   65:        create_live_table_index_disc_only, del_table_index_ram,
   66:        del_table_index_disc, del_table_index_disc_only,
   67:        {group, idx_schema_changes}]},
   68:      {idx_schema_changes, [],
   69:       [idx_schema_changes_ram, idx_schema_changes_disc,
   70:        idx_schema_changes_disc_only]}].
   71: 
   72: init_per_group(_GroupName, Config) ->
   73:     Config.
   74: 
   75: end_per_group(_GroupName, Config) ->
   76:     Config.
   77: 
   78: 
   79: %% Write records
   80: 
   81: write(suite) -> [];
   82: write(Config) when is_list(Config) ->
   83:     [Node1] = Nodes = ?acquire_nodes(1, Config),
   84:     Tab = write,
   85:     Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
   86:     ?match({atomic, ok},  mnesia:create_table(Schema)),
   87: 
   88:     ?match({aborted, {bad_type, _}},
   89: 	   mnesia:transaction(fun() -> mnesia:write([]) end)),
   90:     ?match({aborted, {bad_type, _}},
   91: 	   mnesia:transaction(fun() -> mnesia:write({Tab, 2}) end)),
   92:     ?match({aborted, _},
   93: 	   mnesia:transaction(fun() -> mnesia:write({foo, 2}) end)),
   94:     ?match({atomic, ok},
   95: 	   mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
   96: 
   97:     ?match({'EXIT', {aborted, no_transaction}},  mnesia:write({Tab, 1, 2})),
   98:     ?verify_mnesia(Nodes, []).
   99: 
  100: %% Read records
  101: 
  102: read(suite) -> [];
  103: read(Config) when is_list(Config) ->
  104:     [Node1] = Nodes = ?acquire_nodes(1, Config),
  105:     Tab = read,
  106:     Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
  107:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  108: 
  109:     OneRec = {Tab, 1, 2},
  110:     TwoRec = {Tab, 1, 3},
  111:     ?match({aborted, {bad_type, _}},
  112: 	   mnesia:transaction(fun() -> mnesia:read([]) end)),
  113:     ?match({aborted, {bad_type, _}},
  114: 	   mnesia:transaction(fun() -> mnesia:read({Tab}) end)),
  115:     ?match({aborted, {bad_type, _}}
  116: 	   ,  mnesia:transaction(fun() -> mnesia:read(OneRec) end)),
  117:     ?match({atomic, []},
  118: 	   mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
  119:     ?match({atomic, ok},
  120: 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
  121:     ?match({atomic, [OneRec]},
  122: 	   mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
  123:     ?match({atomic, ok},
  124: 	   mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
  125:     ?match({atomic, [OneRec, TwoRec]},
  126: 	   mnesia:transaction(fun() -> mnesia:read({Tab, 1}) end)),
  127: 
  128:     ?match({'EXIT', {aborted, no_transaction}},  mnesia:read({Tab, 1})),
  129:     ?verify_mnesia(Nodes, []).
  130: 
  131: %% Read records and set write lock
  132: 
  133: wread(suite) -> [];
  134: wread(Config) when is_list(Config) ->
  135:     [_N1,N2] = Nodes = ?acquire_nodes(2, Config),
  136:     Tab = wread,
  137:     Schema = [{name, Tab}, {type, set}, {attributes, [k, v]}, {ram_copies, Nodes}],
  138:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  139: 
  140:     OneRec = {Tab, 1, 2},
  141:     TwoRec = {Tab, 1, 3},
  142:     ?match({aborted, {bad_type, _}},
  143: 	   mnesia:transaction(fun() -> mnesia:wread([]) end)),
  144:     ?match({aborted, {bad_type, _}},
  145: 	   mnesia:transaction(fun() -> mnesia:wread({Tab}) end)),
  146:     ?match({aborted, {bad_type, _}}
  147: 	   ,  mnesia:transaction(fun() -> mnesia:wread(OneRec) end)),
  148: 
  149:     ?match({atomic, []},
  150: 	   mnesia:transaction(fun() -> mnesia:wread({Tab, 1}) end)),
  151:     ?match({atomic, ok},
  152: 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
  153: 
  154:     ?match({atomic, [OneRec]},
  155: 	   mnesia:transaction(fun() -> mnesia:wread({Tab, 1}) end)),
  156:     ?match({atomic, ok},
  157: 	   mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
  158:     ?match({atomic, [TwoRec]},
  159: 	   mnesia:transaction(fun() -> mnesia:wread({Tab, 1}) end)),
  160: 
  161:     ?match({'EXIT', {aborted, no_transaction}},  mnesia:wread({Tab, 1})),
  162: 
  163:     ?match({atomic, ok},
  164: 	   mnesia:transaction(fun() -> mnesia:write(Tab, {Tab, 42, a}, sticky_write) end)),
  165:     ?match({atomic, [{Tab,42, a}]},
  166: 	   rpc:call(N2, mnesia, transaction, [fun() -> mnesia:wread({Tab, 42}) end])),
  167:     ?verify_mnesia(Nodes, []).
  168: 
  169: %% Delete record
  170: 
  171: delete(suite) -> [];
  172: delete(Config) when is_list(Config) ->
  173:     [Node1] = Nodes = ?acquire_nodes(1, Config),
  174:     Tab = delete,
  175:     Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
  176:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  177: 
  178:     ?match({aborted, {bad_type, _}},
  179: 	   mnesia:transaction(fun() -> mnesia:delete([]) end)),
  180:     ?match({aborted, {bad_type, _}},
  181: 	   mnesia:transaction(fun() -> mnesia:delete({Tab}) end)),
  182:     ?match({aborted, {bad_type, _}}
  183: 	   ,  mnesia:transaction(fun() -> mnesia:delete({Tab, 1, 2}) end)),
  184:     ?match({atomic, ok},
  185: 	   mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
  186:     ?match({atomic, ok},
  187: 	   mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
  188:     ?match({atomic, ok},
  189: 	   mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
  190:     ?match({atomic, ok},
  191: 	   mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
  192:     ?match({atomic, ok},
  193: 	   mnesia:transaction(fun() -> mnesia:write({Tab, 1, 2}) end)),
  194:     ?match({atomic, ok},
  195: 	   mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
  196: 
  197:     ?match({'EXIT', {aborted, no_transaction}},  mnesia:delete({Tab, 1})),
  198:     ?verify_mnesia(Nodes, []).
  199: 
  200: %% Delete matching record
  201: 
  202: delete_object(suite) -> [];
  203: delete_object(Config) when is_list(Config) ->
  204:     [Node1] = Nodes = ?acquire_nodes(1, Config),
  205:     Tab = delete_object,
  206:     Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
  207:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  208: 
  209:     OneRec = {Tab, 1, 2},
  210:     ?match({aborted, {bad_type, _}},
  211: 	   mnesia:transaction(fun() -> mnesia:delete_object([]) end)),
  212:     ?match({aborted, {bad_type, _}},
  213: 	   mnesia:transaction(fun() -> mnesia:delete_object({Tab}) end)),
  214:     ?match({aborted, {bad_type, _}},
  215: 	   mnesia:transaction(fun() -> mnesia:delete_object({Tab, 1}) end)),
  216:     ?match({atomic, ok},
  217: 	   mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
  218:     ?match({atomic, ok},
  219: 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
  220:     ?match({atomic, ok},
  221: 	   mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
  222:     ?match({atomic, ok},
  223: 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
  224:     ?match({atomic, ok},
  225: 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
  226:     ?match({atomic, ok},
  227: 	   mnesia:transaction(fun() -> mnesia:delete_object(OneRec) end)),
  228: 
  229:     ?match({'EXIT', {aborted, no_transaction}},  mnesia:delete_object(OneRec)),
  230: 
  231:     ?match({aborted, {bad_type, Tab, _}},
  232: 	   mnesia:transaction(fun() -> mnesia:delete_object({Tab, {['_']}, 21}) end)),
  233:     ?match({aborted, {bad_type, Tab, _}},
  234: 	   mnesia:transaction(fun() -> mnesia:delete_object({Tab, {['$5']}, 21}) end)),
  235: 
  236:     ?verify_mnesia(Nodes, []).
  237: 
  238: %% Read matching records
  239: 
  240: match_object(suite) -> [];
  241: match_object(Config) when is_list(Config) ->
  242:     [Node1] = Nodes = ?acquire_nodes(1, Config),
  243:     Tab = match,
  244:     Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
  245:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  246: 
  247:     OneRec = {Tab, 1, 2},
  248:     OnePat = {Tab, '$1', 2},
  249:     ?match({atomic, []},
  250: 	   mnesia:transaction(fun() -> mnesia:match_object(OnePat) end)),
  251:     ?match({atomic, ok},
  252: 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
  253:     ?match({atomic, [OneRec]},
  254: 	   mnesia:transaction(fun() -> mnesia:match_object(OnePat) end)),
  255: 
  256:     ?match({aborted, _},
  257: 	   mnesia:transaction(fun() -> mnesia:match_object({foo, '$1', 2}) end)),
  258:     ?match({aborted, _},
  259: 	   mnesia:transaction(fun() -> mnesia:match_object({[], '$1', 2}) end)),
  260: 
  261:     ?match({'EXIT', {aborted, no_transaction}},  mnesia:match_object(OnePat)),
  262:     ?verify_mnesia(Nodes, []).
  263: 
  264: %% select
  265: select(suite) -> [];
  266: select(Config) when is_list(Config) ->
  267:     [Node1] = Nodes = ?acquire_nodes(1, Config),
  268:     Tab = match,
  269:     Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
  270:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  271: 
  272:     OneRec = {Tab, 1, 2},
  273:     TwoRec = {Tab, 2, 3},
  274:     OnePat = [{{Tab, '$1', 2}, [], ['$_']}],
  275:     ?match({atomic, []},
  276: 	   mnesia:transaction(fun() -> mnesia:select(Tab, OnePat) end)),
  277:     ?match({atomic, ok},
  278: 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
  279:     ?match({atomic, ok},
  280: 	   mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
  281:     ?match({atomic, [OneRec]},
  282: 	   mnesia:transaction(fun() -> mnesia:select(Tab, OnePat) end)),
  283: 
  284:     ?match({aborted, _},
  285: 	   mnesia:transaction(fun() -> mnesia:select(Tab, {match, '$1', 2}) end)),
  286:     ?match({aborted, _},
  287: 	   mnesia:transaction(fun() -> mnesia:select(Tab, [{'_', [], '$1'}]) end)),
  288: 
  289:     ?match({'EXIT', {aborted, no_transaction}},  mnesia:select(Tab, OnePat)),
  290:     ?verify_mnesia(Nodes, []).
  291: 
  292: 
  293: %% more select
  294: select14(suite) -> [];
  295: select14(Config) when is_list(Config) ->
  296:     [Node1,Node2] = Nodes = ?acquire_nodes(2, Config),
  297:     Tab1 = select14_ets,
  298:     Tab2 = select14_dets,
  299:     Tab3 = select14_remote,
  300:     Tab4 = select14_remote_dets,
  301:     Schemas = [[{name, Tab1}, {attributes, [k, v]}, {ram_copies, [Node1]}],
  302: 	       [{name, Tab2}, {attributes, [k, v]}, {disc_only_copies, [Node1]}],
  303: 	       [{name, Tab3}, {attributes, [k, v]}, {ram_copies, [Node2]}],
  304: 	       [{name, Tab4}, {attributes, [k, v]}, {disc_only_copies, [Node2]}]],
  305:     [?match({atomic, ok},  mnesia:create_table(Schema)) || Schema <- Schemas],
  306: 
  307:     %% Some Helpers
  308:     Trans = fun(Fun) -> mnesia:transaction(Fun) end,
  309:     LoopHelp = fun('$end_of_table',_) -> [];
  310: 		  ({Recs,Cont},Fun) ->
  311: 		       Sel = mnesia:select(Cont),
  312: 		       Recs ++ Fun(Sel, Fun)
  313: 	       end,
  314:     Loop = fun(Table,Pattern) ->
  315: 		   Sel = mnesia:select(Table, Pattern, 1, read),
  316: 		   Res = LoopHelp(Sel,LoopHelp),
  317: 		   case mnesia:table_info(Table, type) of
  318: 		       ordered_set -> Res;
  319: 		       _ -> lists:sort(Res)
  320: 		   end
  321: 	   end,
  322:     Test =
  323: 	fun(Tab) ->
  324: 		OneRec = {Tab, 1, 2},
  325: 		TwoRec = {Tab, 2, 3},
  326: 		OnePat = [{{Tab, '$1', 2}, [], ['$_']}],
  327: 		All = [OneRec,TwoRec],
  328: 		AllPat = [{'_', [], ['$_']}],
  329: 
  330: 		?match({atomic, []}, Trans(fun() -> Loop(Tab, OnePat) end)),
  331: 		?match({atomic, ok},   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
  332: 		?match({atomic, ok},   mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
  333: 		?match({atomic, [OneRec]}, Trans(fun() -> Loop(Tab, OnePat) end)),
  334: 		?match({atomic, All}, Trans(fun() -> Loop(Tab, AllPat) end)),
  335: 
  336: 		{atomic,{_, Cont}} = Trans(fun() -> mnesia:select(Tab, OnePat, 1, read) end),
  337: 		?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select(Cont) end)),
  338: 
  339: 		?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, {match, '$1', 2},1,read) end)),
  340: 		?match({aborted, _}, Trans(fun() -> mnesia:select(Tab, [{'_', [], '$1'}],1,read) end)),
  341: 		?match({aborted, _}, Trans(fun() -> mnesia:select(sune) end)),
  342: 		?match({'EXIT', {aborted, no_transaction}},  mnesia:select(Tab, OnePat,1,read)),
  343: 		?match({aborted, {badarg,sune}},
  344: 		       Trans(fun() -> mnesia:select(sune) end))
  345: 	end,
  346:     Test(Tab1),
  347:     Test(Tab2),
  348:     Test(Tab3),
  349:     Test(Tab4),
  350:     ?verify_mnesia(Nodes, []).
  351: 
  352: 
  353: %% Pick all keys from table
  354: 
  355: all_keys(suite) ->[];
  356: all_keys(Config) when is_list(Config) ->
  357:     [Node1] = Nodes = ?acquire_nodes(1, Config),
  358:     Tab = all_keys,
  359:     Schema = [{name, Tab}, {type, bag}, {attributes, [k, v]}, {ram_copies, [Node1]}],
  360:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  361: 
  362:     Write = fun() -> mnesia:write({Tab, 14, 4}) end,
  363:     AllKeys = fun() -> mnesia:all_keys(Tab) end,
  364: 
  365:     ?match({atomic, []},  mnesia:transaction(AllKeys)),
  366: 
  367:     ?match({atomic, ok},  mnesia:transaction(Write)),
  368:     ?match({atomic, [14]},  mnesia:transaction(AllKeys)),
  369: 
  370:     ?match({atomic, ok},  mnesia:transaction(Write)),
  371:     ?match({atomic, [14]},  mnesia:transaction(AllKeys)),
  372: 
  373:     ?match({aborted, _},
  374: 	   mnesia:transaction(fun() -> mnesia:all_keys(foo) end)),
  375:     ?match({aborted, _},
  376: 	   mnesia:transaction(fun() -> mnesia:all_keys([]) end)),
  377: 
  378:     ?match({'EXIT', {aborted, no_transaction}},  mnesia:all_keys(Tab)),
  379:     ?verify_mnesia(Nodes, []).
  380: 
  381: 
  382: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  383: %% Use and misuse transactions
  384: 
  385: transaction(suite) -> [];
  386: transaction(Config) when is_list(Config) ->
  387:     [Node1] = Nodes = ?acquire_nodes(1, Config),
  388:     ?match({atomic, ali_baba}, mnesia:transaction(fun() -> ali_baba end)),
  389:     ?match({aborted, _}, mnesia:transaction(no_fun)),
  390:     ?match({aborted, _}, mnesia:transaction(?MODULE, no_fun, [foo])),
  391: 
  392:     {success, [A, B, C, D, E, F, G, H]} =
  393:         ?start_activities(lists:duplicate(8, Node1)),
  394:     ?start_transactions([A, B, C, D, E, F, G, H]),
  395: 
  396:     A ! fun() -> mnesia:abort(abort_bad_trans) end,
  397:     ?match_receive({A, {aborted, abort_bad_trans}}),
  398: 
  399:     B ! fun() -> erlang:error(exit_here) end,
  400:     ?match_receive({B, {aborted, _}}),
  401: 
  402:     C ! fun() -> throw(throw_bad_trans) end,
  403:     ?match_receive({C, {aborted, {throw, throw_bad_trans}}}),
  404: 
  405:     D ! fun() -> exit(exit_bad_trans) end,
  406:     ?match_receive({D, {aborted, exit_bad_trans}}),
  407: 
  408:     E ! fun() -> exit(normal) end,
  409:     ?match_receive({E, {aborted, normal}}),
  410: 
  411:     F ! fun() -> exit(abnormal) end,
  412:     ?match_receive({F, {aborted, abnormal}}),
  413: 
  414:     G ! fun() -> exit(G, abnormal) end,
  415:     ?match_receive({'EXIT', G, abnormal}),
  416: 
  417:     H ! fun() -> exit(H, kill) end,
  418:     ?match_receive({'EXIT', H, killed}),
  419: 
  420:     ?match({atomic, ali_baba},
  421:            mnesia:transaction(fun() -> ali_baba end, infinity)),
  422:     ?match({atomic, ali_baba}, mnesia:transaction(fun() -> ali_baba end, 1)),
  423:     ?match({atomic, ali_baba}, mnesia:transaction(fun() -> ali_baba end, 0)),
  424:     ?match({aborted, Reason8} when element(1, Reason8) == badarg, mnesia:transaction(fun() -> ali_baba end, -1)),
  425:     ?match({aborted, Reason1} when element(1, Reason1) == badarg, mnesia:transaction(fun() -> ali_baba end, foo)),
  426:     Fun = fun() ->
  427: 		  ?match(true, mnesia:is_transaction()),
  428: 		  ?match({atomic, ok},
  429: 			 mnesia:transaction(fun() -> ?match(true, mnesia:is_transaction()),ok end)), ok end,
  430:     ?match({atomic, ok}, mnesia:transaction(Fun)),
  431:     ?verify_mnesia(Nodes, []).
  432: 
  433: 
  434: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  435: 
  436: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  437: 
  438: %% ensure that nested transactions behave correctly
  439: %% We create a particular table that is used by this test only
  440: -record(ntab,  {a,  b}).
  441: basic_nested(doc) -> ["Test the basic functionality of nested transactions"];
  442: basic_nested(suite) -> [];
  443: basic_nested(Config) when is_list(Config) ->
  444:     Nodes = ?acquire_nodes(3, Config),
  445:     Args =  [{ram_copies,  Nodes},
  446: 	     {attributes,  record_info(fields,  ntab)}],
  447:     ?match({atomic,  ok},  mnesia:create_table(ntab,  Args)),
  448:     do_nested(top),
  449:     case mnesia_test_lib:diskless(Config) of
  450: 	false ->
  451: 	    lists:foreach(fun(N) ->
  452: 				  ?match({atomic,  ok},
  453: 					 mnesia:change_table_copy_type(ntab,  N,  disc_only_copies))
  454: 			  end,  Nodes),
  455: 	    do_nested(top);
  456: 	true ->
  457: 	    skip
  458:     end,
  459:     ?verify_mnesia(Nodes, []).
  460: 
  461: do_nested(How) ->
  462:     F1 = fun() ->
  463: 		mnesia:write(#ntab{a= 1}),
  464: 		mnesia:write(#ntab{a= 2})
  465: 	end,
  466:     F2 = fun() ->
  467: 		 mnesia:read({ntab,  1})
  468: 	 end,
  469:     ?match({atomic,  ok},  mnesia:transaction(F1)),
  470:     ?match({atomic,  _},  mnesia:transaction(F2)),
  471: 
  472:     ?match({atomic,  {aborted,  _}},
  473: 	   mnesia:transaction(fun() -> n_f1(),
  474: 				mnesia:transaction(fun() -> n_f2() end)
  475: 		       end)),
  476: 
  477:     ?match({atomic,  {aborted,  _}},
  478: 	   mnesia:transaction(fun() -> n_f1(),
  479: 				mnesia:transaction(fun() -> n_f3() end)
  480: 		       end)),
  481:     ?match({atomic,  {atomic,  [#ntab{a = 5}]}},
  482: 	   mnesia:transaction(fun() -> mnesia:write(#ntab{a = 5}),
  483: 				       mnesia:transaction(fun() -> n_f4() end)
  484: 			      end)),
  485:     Cyclic = fun() -> mnesia:abort({cyclic,a,a,a,a,a}) end,  %% Ugly
  486:     NodeNotR = fun() -> mnesia:abort({node_not_running, testNode}) end,
  487: 
  488:     TestAbort = fun(Fun) ->
  489: 			case get(restart_counter) of
  490: 			    undefined ->
  491: 				put(restart_counter, 1),
  492: 				Fun();
  493: 			    _ ->
  494: 				erase(restart_counter),
  495: 				ok
  496: 			end
  497: 		end,
  498: 
  499:     ?match({atomic,{atomic,ok}},
  500: 	   mnesia:transaction(fun()->mnesia:transaction(TestAbort,
  501: 							[Cyclic])end)),
  502: 
  503:     ?match({atomic,{atomic,ok}},
  504: 	   mnesia:transaction(fun()->mnesia:transaction(TestAbort,
  505: 							[NodeNotR])end)),
  506: 
  507:     %% Now try the restart thingie
  508:     case How of
  509: 	top ->
  510: 	    Pids = [spawn(?MODULE,  do_nested,  [{spawned,  self()}]),
  511: 		    spawn(?MODULE,  do_nested,  [{spawned,  self()}]),
  512: 		    spawn(?MODULE,  do_nested,  [{spawned,  self()}]),
  513: 		    spawn(?MODULE,  do_nested,  [{spawned,  self()}])],
  514: 	    ?match({info, _, _}, mnesia_tm:get_info(2000)),
  515: 	    lists:foreach(fun(P) -> receive
  516: 					{P,  ok} -> ok
  517: 				    end
  518: 			  end,  Pids),
  519: 	    ?match([],  [Tab || Tab <- ets:all(), mnesia_trans_store == ets:info(Tab, name)]);
  520: 
  521: 	{spawned,  Pid} ->
  522: 	    ?match({info, _, _}, mnesia_tm:get_info(2000)),
  523: 	    Pid ! {self(),  ok},
  524: 	    exit(normal)
  525:     end.
  526: 
  527: 
  528: n_f1() ->
  529:     mnesia:read({ntab,  1}),
  530:     mnesia:write(#ntab{a = 3}).
  531: 
  532: n_f2() ->
  533:     mnesia:write(#ntab{a = 4}),
  534:     erlang:error(exit_here).
  535: 
  536: n_f3() ->
  537:     mnesia:write(#ntab{a = 4}),
  538:     throw(funky).
  539: 
  540: n_f4() ->
  541:     mnesia:read({ntab,  5}).
  542: 
  543: 
  544: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  545: 
  546: nested_trans_both_ok(suite) -> [];
  547: nested_trans_both_ok(Config) when is_list(Config) ->
  548:     nested_transactions(Config, ok, ok).
  549: 
  550: nested_trans_child_dies(suite) -> [];
  551: nested_trans_child_dies(Config) when is_list(Config) ->
  552:     nested_transactions(Config, abort, ok).
  553: 
  554: nested_trans_parent_dies(suite) -> [];
  555: nested_trans_parent_dies(Config) when is_list(Config) ->
  556:     nested_transactions(Config, ok, abort).
  557: 
  558: nested_trans_both_dies(suite) -> [];
  559: nested_trans_both_dies(Config) when is_list(Config) ->
  560:     nested_transactions(Config, abort, abort).
  561: 
  562: nested_transactions(Config, Child, Father) ->
  563:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  564:     Tab = nested_trans,
  565: 
  566:     Def =
  567: 	case mnesia_test_lib:diskless(Config) of
  568: 	    true ->
  569: 		[{name, Tab}, {ram_copies, Nodes}];
  570: 	    false ->
  571: 		[{name, Tab}, {ram_copies, [Node1]},
  572: 		 {disc_copies, [Node2]}, {disc_only_copies, [Node3]}]
  573: 	end,
  574: 
  575:     ?match({atomic, ok}, mnesia:create_table(Def)),
  576:     ?match(ok, mnesia:dirty_write({Tab, father, not_updated})),
  577:     ?match(ok, mnesia:dirty_write({Tab, child, not_updated})),
  578: 
  579:     ChildOk = fun() -> mnesia:write({Tab, child, updated}) end,
  580:     ChildAbort = fun() ->
  581: 			 mnesia:write({Tab, child, updated}),
  582: 			 erlang:error(exit_here)
  583: 		 end,
  584: 
  585:     Child_Fun =   % Depending of test case
  586: 	case Child of
  587: 	    ok -> ChildOk;
  588: 	    abort -> ChildAbort
  589: 	end,
  590: 
  591:     FatherOk = fun() -> mnesia:transaction(Child_Fun),
  592: 			mnesia:write({Tab, father, updated})
  593: 	       end,
  594: 
  595:     FatherAbort = fun() -> mnesia:transaction(Child_Fun),
  596: 			   mnesia:write({Tab, father, updated}),
  597: 			   erlang:error(exit_here)
  598: 		  end,
  599: 
  600:     {FatherRes, ChildRes} = % Depending of test case
  601: 	case Father of
  602: 	    ok -> ?match({atomic, ok}, mnesia:transaction(FatherOk)),
  603: 		  case Child of
  604: 		      ok -> {[{Tab, father, updated}], [{Tab, child, updated}]};
  605: 		      _ -> {[{Tab, father, updated}], [{Tab, child, not_updated}]}
  606: 		  end;
  607: 	    abort -> ?match({aborted, _}, mnesia:transaction(FatherAbort)),
  608: 		     {[{Tab, father, not_updated}], [{Tab, child, not_updated}]}
  609: 	end,
  610: 
  611:     %% Syncronize things!!
  612:     ?match({atomic, ok}, mnesia:sync_transaction(fun() -> mnesia:write({Tab, sync, sync}) end)),
  613: 
  614:     ?match(ChildRes, rpc:call(Node1, mnesia, dirty_read, [{Tab, child}])),
  615:     ?match(ChildRes, rpc:call(Node2, mnesia, dirty_read, [{Tab, child}])),
  616:     ?match(ChildRes, rpc:call(Node3, mnesia, dirty_read, [{Tab, child}])),
  617: 
  618:     ?match(FatherRes, rpc:call(Node1, mnesia, dirty_read, [{Tab, father}])),
  619:     ?match(FatherRes, rpc:call(Node2, mnesia, dirty_read, [{Tab, father}])),
  620:     ?match(FatherRes, rpc:call(Node3, mnesia, dirty_read, [{Tab, father}])),
  621:     ?verify_mnesia(Nodes, []).
  622: 
  623: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  624: mix_of_nested_activities(doc) ->
  625:     ["Verify that dirty operations in a transaction are handled like ",
  626:      "normal transactions"];
  627: mix_of_nested_activities(suite) ->   [];
  628: mix_of_nested_activities(Config) when is_list(Config) ->
  629:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  630:     Tab = tab,
  631: 
  632:     Def =
  633: 	case mnesia_test_lib:diskless(Config) of
  634: 	    true -> [{ram_copies, Nodes}];
  635: 	    false ->
  636: 		[{ram_copies, [Node1]},
  637: 		 {disc_copies, [Node2]},
  638: 		 {disc_only_copies, [Node3]}]
  639: 	end,
  640: 
  641:     ?match({atomic, ok}, mnesia:create_table(Tab, [{type,bag}|Def])),
  642:     Activities = [transaction, sync_transaction,
  643: 		  ets, async_dirty, sync_dirty],
  644:     %% Make a test for all 3000 combinations
  645:     Tests = [[A,B,C,D,E] ||
  646: 		A <- Activities,
  647: 		B <- Activities,
  648: 		C <- Activities,
  649: 		D <- Activities,
  650: 		E <- Activities],
  651:     Foreach =
  652: 	fun(Test,No) ->
  653: 		Result = lists:reverse(Test),
  654: 		?match({No,Result},{No,catch apply_op({Tab,No},Test)}),
  655: 		No+1
  656: 	end,
  657:     lists:foldl(Foreach, 0, Tests),
  658:     ?verify_mnesia(Nodes, []).
  659: 
  660: apply_op(Oid,[Type]) ->
  661:     check_res(Type,mnesia:Type(fun() -> [Type|read_op(Oid)] end));
  662: apply_op(Oid = {Tab,Key},[Type|Next]) ->
  663:     check_res(Type,mnesia:Type(fun() ->
  664: 				       Prev = read_op(Oid),
  665: 				       mnesia:write({Tab,Key,[Type|Prev]}),
  666: 				       apply_op(Oid,Next)
  667: 			       end)).
  668: 
  669: check_res(transaction, {atomic,Res}) ->
  670:     Res;
  671: check_res(sync_transaction, {atomic,Res}) ->
  672:     Res;
  673: check_res(async_dirty, Res) when is_list(Res) ->
  674:     Res;
  675: check_res(sync_dirty, Res) when is_list(Res) ->
  676:     Res;
  677: check_res(ets, Res) when is_list(Res) ->
  678:     Res;
  679: check_res(Type,Res) ->
  680:     ?match(bug,{Type,Res}).
  681: 
  682: read_op(Oid) ->
  683:     case lists:reverse(mnesia:read(Oid)) of
  684: 	[] -> [];
  685: 	[{_,_,Ops}|_] ->
  686: 	    Ops
  687:     end.
  688: 
  689: 
  690: %% Read matching records by using an index
  691: 
  692: index_match_object(suite) -> [];
  693: index_match_object(Config) when is_list(Config) ->
  694:     [Node1, Node2] = Nodes = ?acquire_nodes(2, Config),
  695:     Tab = index_match_object,
  696:     Schema = [{name, Tab}, {attributes, [k, v, e]}, {ram_copies, [Node1]}],
  697:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  698:     ValPos = 3,
  699:     BadValPos = ValPos + 2,
  700:     ?match({atomic, ok},  mnesia:add_table_index(Tab, ValPos)),
  701: 
  702:     ?match({atomic, []},
  703: 	   mnesia:transaction(fun() -> mnesia:index_match_object({Tab, '$1', 2}, ValPos) end)),
  704:     OneRec = {Tab, {1, 1}, 2, {1, 1}},
  705:     OnePat = {Tab, '$1', 2, '_'},
  706:     BadPat = {Tab, '$1', '$2', '_'},  %% See ref guide
  707: 
  708:     ?match({atomic, ok},
  709: 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
  710: 
  711:     Imatch = fun(Patt, Pos) ->
  712: 		     mnesia:transaction(fun() -> lists:sort(mnesia:index_match_object(Patt, Pos)) end)
  713: 	     end,
  714:     ?match({atomic, [OneRec]}, Imatch(OnePat, ValPos)),
  715:     ?match({aborted, _}, Imatch(OnePat, BadValPos)),
  716:     ?match({aborted, _}, Imatch({foo, '$1', 2, '_'}, ValPos)),
  717:     ?match({aborted, _}, Imatch({[], '$1', 2, '_'}, ValPos)),
  718:     ?match({aborted, _}, Imatch(BadPat, ValPos)),
  719:     ?match({'EXIT', {aborted, no_transaction}},  mnesia:index_match_object(OnePat, ValPos)),
  720: 
  721:     Another = {Tab, {3,1}, 2, {4,4}},
  722:     ?match({atomic, ok},
  723: 	   mnesia:transaction(fun() -> mnesia:write(Another) end)),
  724:     ?match({atomic, ok},
  725: 	   mnesia:transaction(fun() -> mnesia:write({Tab, {4, 4}, 3, {4, 4}}) end)),
  726: 
  727:     ?match({atomic, [OneRec]}, Imatch({Tab, {1,1}, 2, {1,1}}, ValPos)),
  728:     ?match({atomic, [OneRec]}, Imatch({Tab, {1,1}, 2, '$1'}, ValPos)),
  729:     ?match({atomic, [OneRec]}, Imatch({Tab, '$1', 2, {1,1}}, ValPos)),
  730:     ?match({atomic, [OneRec]}, Imatch({Tab, '$1', 2, '$1'}, ValPos)),
  731:     ?match({atomic, [OneRec]}, Imatch({Tab, {1, '$1'}, 2, '_'}, ValPos)),
  732:     ?match({atomic, [OneRec]}, Imatch({Tab, {'$2', '$1'}, 2, {'_', '$1'}}, ValPos)),
  733:     ?match({atomic, [OneRec, Another]}, Imatch({Tab, '_', 2, '_'}, ValPos)),
  734: 
  735:     ?match({atomic, ok},
  736: 	   mnesia:transaction(fun() -> mnesia:write({Tab, 4, 5, {7, 4}}) end)),
  737:     ?match({atomic, ok},
  738: 	   mnesia:transaction(fun() -> mnesia:write({Tab, 7, 5, {7, 5}}) end)),
  739: 
  740:     ?match({atomic, [{Tab, 4, 5, {7, 4}}]}, Imatch({Tab, '$1', 5, {'_', '$1'}}, ValPos)),
  741: 
  742:     ?match({atomic, [OneRec]}, rpc:call(Node2, mnesia, transaction,
  743: 					[fun() ->
  744: 						lists:sort(mnesia:index_match_object({Tab, {1,1}, 2,
  745: 										      {1,1}}, ValPos))
  746: 					 end])),
  747:     ?verify_mnesia(Nodes, []).
  748: 
  749: %% Read records by using an index
  750: 
  751: index_read(suite) -> [];
  752: index_read(Config) when is_list(Config) ->
  753:     [Node1] = Nodes = ?acquire_nodes(1, Config),
  754:     Tab = index_read,
  755:     Schema = [{name, Tab}, {attributes, [k, v]}, {ram_copies, [Node1]}],
  756:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  757:     ValPos = 3,
  758:     BadValPos = ValPos + 1,
  759:     ?match({atomic, ok},  mnesia:add_table_index(Tab, ValPos)),
  760: 
  761:     OneRec = {Tab, 1, 2},
  762:     ?match({atomic, []},
  763: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
  764:     ?match({atomic, ok},
  765: 	   mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
  766:     ?match({atomic, [OneRec]},
  767: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
  768:     ?match({aborted, _},
  769: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, BadValPos) end)),
  770:     ?match({aborted, _},
  771: 	   mnesia:transaction(fun() -> mnesia:index_read(foo, 2, ValPos) end)),
  772:     ?match({aborted, _},
  773: 	   mnesia:transaction(fun() -> mnesia:index_read([], 2, ValPos) end)),
  774: 
  775:     ?match({'EXIT', {aborted, no_transaction}},  mnesia:index_read(Tab, 2, ValPos)),
  776:     ?verify_mnesia(Nodes, []).
  777: 
  778: index_update_set(suite) -> [];
  779: index_update_set(Config)when is_list(Config) ->
  780:     [Node1] = Nodes = ?acquire_nodes(1, Config),
  781:     Tab = index_test,
  782:     Schema = [{name, Tab}, {attributes, [k, v1, v2, v3]}, {ram_copies, [Node1]}],
  783:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  784:     ValPos = v1,
  785:     ValPos2 = v3,
  786:     ?match({atomic, ok},  mnesia:add_table_index(Tab, ValPos)),
  787: 
  788:     Pat1 = {Tab, '$1',  2,   '$2', '$3'},
  789:     Pat2 = {Tab, '$1', '$2', '$3', '$4'},
  790: 
  791:     Rec1 = {Tab, 1, 2, 3, 4},
  792:     Rec2 = {Tab, 2, 2, 13, 14},
  793:     Rec3 = {Tab, 1, 12, 13, 14},
  794:     Rec4 = {Tab, 4, 2, 13, 14},
  795: 
  796:     ?match({atomic, []},
  797: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
  798:     ?match({atomic, ok},
  799: 	   mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
  800:     ?match({atomic, [Rec1]},
  801: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
  802: 
  803:     ?match({atomic, ok},
  804: 	   mnesia:transaction(fun() -> mnesia:write(Rec2) end)),
  805:     {atomic, R1} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  806:     ?match([Rec1, Rec2], lists:sort(R1)),
  807: 
  808:     ?match({atomic, ok},
  809: 	   mnesia:transaction(fun() -> mnesia:write(Rec3) end)),
  810:     {atomic, R2} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  811:     ?match([Rec2], lists:sort(R2)),
  812:     ?match({atomic, [Rec2]},
  813: 	   mnesia:transaction(fun() -> mnesia:index_match_object(Pat1, ValPos) end)),
  814: 
  815:     {atomic, R3} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
  816:     ?match([Rec3, Rec2], lists:sort(R3)),
  817: 
  818:     ?match({atomic, ok},
  819: 	   mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
  820:     {atomic, R4} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  821:     ?match([Rec2, Rec4], lists:sort(R4)),
  822: 
  823:     ?match({atomic, ok},
  824: 	   mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
  825:     ?match({atomic, [Rec2]},
  826: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
  827: 
  828:     ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
  829:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
  830:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
  831:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
  832: 
  833:     {atomic, R5} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
  834:     ?match([Rec3, Rec2, Rec4], lists:sort(R5)),
  835: 
  836:     {atomic, R6} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  837:     ?match([Rec2, Rec4], lists:sort(R6)),
  838: 
  839:     ?match({atomic, []},
  840: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
  841:     {atomic, R7} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
  842:     ?match([Rec3, Rec2, Rec4], lists:sort(R7)),
  843: 
  844:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
  845:     {atomic, R8} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  846:     ?match([Rec1, Rec2, Rec4], lists:sort(R8)),
  847:     ?match({atomic, [Rec1]},
  848: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
  849:     {atomic, R9} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
  850:     ?match([Rec2, Rec4], lists:sort(R9)),
  851: 
  852:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec2) end)),
  853:     {atomic, R10} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  854:     ?match([Rec1, Rec4], lists:sort(R10)),
  855:     ?match({atomic, [Rec1]},
  856: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
  857:     ?match({atomic, [Rec4]},
  858: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end)),
  859: 
  860:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
  861:     {atomic, R11} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  862:     ?match([Rec1], lists:sort(R11)),
  863:     ?match({atomic, [Rec1]},mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
  864:     ?match({atomic, []},mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end)),
  865: 
  866:     ?verify_mnesia(Nodes, []).
  867: 
  868: index_update_bag(suite) -> [];
  869: index_update_bag(Config)when is_list(Config) ->
  870:     [Node1] = Nodes = ?acquire_nodes(1, Config),
  871:     Tab = index_test,
  872:     Schema = [{name, Tab},
  873: 	      {type, bag},
  874: 	      {attributes, [k, v1, v2, v3]},
  875: 	      {ram_copies, [Node1]}],
  876:     ?match({atomic, ok},  mnesia:create_table(Schema)),
  877:     ValPos = v1,
  878:     ValPos2 = v3,
  879: 
  880:     ?match({atomic, ok},  mnesia:add_table_index(Tab, ValPos)),
  881: 
  882:     Pat1 = {Tab, '$1',  2,   '$2', '$3'},
  883:     Pat2 = {Tab, '$1', '$2', '$3', '$4'},
  884: 
  885:     Rec1 = {Tab, 1, 2, 3, 4},
  886:     Rec2 = {Tab, 2, 2, 13, 14},
  887:     Rec3 = {Tab, 1, 12, 13, 14},
  888:     Rec4 = {Tab, 4, 2, 13, 4},
  889:     Rec5 = {Tab, 1, 2, 234, 14},
  890: 
  891:     %% Simple Index
  892:     ?match({atomic, []},
  893: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
  894:     ?match({atomic, ok},
  895: 	   mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
  896:     ?match({atomic, [Rec1]},
  897: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
  898: 
  899:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec5) end)),
  900:     ?match({atomic, [Rec1]},
  901: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
  902: 
  903:     ?match({atomic, ok},
  904: 	   mnesia:transaction(fun() -> mnesia:write(Rec2) end)),
  905:     {atomic, R1} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  906:     ?match([Rec1, Rec2], lists:sort(R1)),
  907: 
  908:     ?match({atomic, ok},
  909: 	   mnesia:transaction(fun() -> mnesia:write(Rec3) end)),
  910:     {atomic, R2} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  911:     ?match([Rec1, Rec2], lists:sort(R2)),
  912: 
  913:     {atomic, R3} = mnesia:transaction(fun() -> mnesia:index_match_object(Pat1, ValPos) end),
  914:     ?match([Rec1, Rec2], lists:sort(R3)),
  915: 
  916:     {atomic, R4} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
  917:     ?match([Rec1, Rec3, Rec2], lists:sort(R4)),
  918: 
  919:     ?match({atomic, ok},
  920: 	   mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
  921:     {atomic, R5} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  922:     ?match([Rec1, Rec2, Rec4], lists:sort(R5)),
  923: 
  924:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
  925:     {atomic, R6} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  926:     ?match([Rec1, Rec2], lists:sort(R6)),
  927: 
  928:     %% OTP-6587  Needs some whitebox testing to see that the index table is cleaned correctly
  929: 
  930:     [IPos] = mnesia_lib:val({Tab,index}),
  931:     ITab = mnesia_lib:val({index_test,{index, IPos}}),
  932:     io:format("~n Index ~p @ ~p => ~p ~n~n",[IPos,ITab, ets:tab2list(ITab)]),
  933:     ?match([{2,1},{2,2},{12,1}], ets:tab2list(ITab)),
  934: 
  935:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec5) end)),
  936:     {atomic, R60} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  937:     ?match([Rec1,Rec5,Rec2], lists:sort(R60)),
  938: 
  939:     ?match([{2,1},{2,2},{12,1}], ets:tab2list(ITab)),
  940: 
  941:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec3) end)),
  942:     {atomic, R61} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  943:     ?match([Rec1,Rec5,Rec2], lists:sort(R61)),
  944:     {atomic, R62} = mnesia:transaction(fun() -> mnesia:index_read(Tab,12, ValPos) end),
  945:     ?match([], lists:sort(R62)),
  946:     ?match([{2,1},{2,2}], ets:tab2list(ITab)),
  947: 
  948:     %% reset for rest of testcase
  949:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec3) end)),
  950:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec5) end)),
  951:     {atomic, R6} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  952:     ?match([Rec1, Rec2], lists:sort(R6)),
  953:     %% OTP-6587
  954: 
  955:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec1) end)),
  956:     ?match({atomic, [Rec2]},
  957: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end)),
  958:     {atomic, R7} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
  959:     ?match([Rec3, Rec2], lists:sort(R7)),
  960: 
  961:     %% Two indexies
  962:     ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
  963:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
  964:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
  965:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
  966:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
  967: 
  968:     {atomic, R8} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  969:     ?match([Rec1, Rec2, Rec4], lists:sort(R8)),
  970: 
  971:     {atomic, R9} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end),
  972:     ?match([Rec1, Rec4], lists:sort(R9)),
  973:     {atomic, R10} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
  974:     ?match([Rec3, Rec2], lists:sort(R10)),
  975: 
  976:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec5) end)),
  977:     {atomic, R11} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  978:     ?match([Rec1, Rec5, Rec2, Rec4], lists:sort(R11)),
  979:     {atomic, R12} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end),
  980:     ?match([Rec1, Rec4], lists:sort(R12)),
  981:     {atomic, R13} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
  982:     ?match([Rec5, Rec3, Rec2], lists:sort(R13)),
  983: 
  984:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec1) end)),
  985:     {atomic, R14} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  986:     ?match([Rec5, Rec2, Rec4], lists:sort(R14)),
  987:     ?match({atomic, [Rec4]},
  988: 	   mnesia:transaction(fun() -> mnesia:index_read(Tab, 4, ValPos2) end)),
  989:     {atomic, R15} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
  990:     ?match([Rec5, Rec3, Rec2], lists:sort(R15)),
  991: 
  992:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec5) end)),
  993:     {atomic, R16} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
  994:     ?match([Rec2, Rec4], lists:sort(R16)),
  995:     ?match({atomic, [Rec4]}, mnesia:transaction(fun()->mnesia:index_read(Tab, 4, ValPos2) end)),
  996:     {atomic, R17} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
  997:     ?match([Rec3, Rec2], lists:sort(R17)),
  998: 
  999:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
 1000:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 1}) end)),
 1001:     {atomic, R18} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 2, ValPos) end),
 1002:     ?match([Rec2, Rec4], lists:sort(R18)),
 1003:     ?match({atomic, [Rec4]}, mnesia:transaction(fun()->mnesia:index_read(Tab, 4, ValPos2) end)),
 1004:     {atomic, R19} = mnesia:transaction(fun() -> mnesia:index_read(Tab, 14, ValPos2) end),
 1005:     ?match([Rec2], lists:sort(R19)),
 1006: 
 1007:     ?verify_mnesia(Nodes, []).
 1008: 
 1009: 
 1010: index_write(suite) -> [];
 1011: index_write(doc) -> ["See ticket OTP-8072"];
 1012: index_write(Config)when is_list(Config) ->
 1013:     Nodes = ?acquire_nodes(1, Config),
 1014:     mnesia:create_table(a, [{index, [val]}]),
 1015:     mnesia:create_table(counter, []),
 1016: 
 1017:     CreateIfNonExist =
 1018: 	fun(Index) ->
 1019: 		case mnesia:index_read(a, Index, 3) of
 1020: 		    [] ->
 1021: 			Id = mnesia:dirty_update_counter(counter, id, 1),
 1022: 			New = {a, Id, Index},
 1023: 			mnesia:write(New),
 1024: 			New;
 1025: 		    [Found] ->
 1026: 			Found
 1027: 		end
 1028: 	end,
 1029: 
 1030:     Trans = fun(A) ->
 1031: 		    mnesia:transaction(CreateIfNonExist, [A])
 1032: 		    %% This works better most of the time
 1033: 		    %% And it is allowed to fail since it's dirty
 1034: 		    %% mnesia:async_dirty(CreateIfNonExist, [A])
 1035: 	    end,
 1036: 
 1037:     Self = self(),
 1038:     Update = fun() ->
 1039: 		     Res = lists:map(Trans, lists:seq(1,10)),
 1040: 		     Self ! {self(), Res}
 1041: 	     end,
 1042: 
 1043:     Pids = [spawn(Update) || _ <- lists:seq(1,5)],
 1044: 
 1045:     Gather = fun(Pid, Acc) -> receive {Pid, Res} -> [Res|Acc] end end,
 1046:     Results = lists:foldl(Gather, [], Pids),
 1047:     Expected = hd(Results),
 1048:     Check = fun(Res) -> ?match(Expected, Res) end,
 1049:     lists:foreach(Check, Results),
 1050:     ?verify_mnesia(Nodes, []).
 1051: 
 1052: 
 1053: 
 1054: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 1055: %% Add and drop indecies
 1056: 
 1057: 
 1058: add_table_index_ram(suite) -> [];
 1059: add_table_index_ram(Config) when is_list(Config) ->
 1060:     add_table_index(Config, ram_copies).
 1061: 
 1062: add_table_index_disc(suite) -> [];
 1063: add_table_index_disc(Config) when is_list(Config) ->
 1064:     add_table_index(Config, disc_copies).
 1065: 
 1066: add_table_index_disc_only(suite) -> [];
 1067: add_table_index_disc_only(Config) when is_list(Config) ->
 1068:     add_table_index(Config, disc_only_copies).
 1069: 
 1070: %% Add table index
 1071: 
 1072: add_table_index(Config, Storage) ->
 1073:     [Node1] = Nodes = ?acquire_nodes(1, Config),
 1074:     Tab = add_table_index,
 1075:     Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, [Node1]}],
 1076:     ?match({atomic, ok}, mnesia:create_table(Schema)),
 1077:     ValPos = 3,
 1078:     BadValPos = ValPos + 1,
 1079:     ?match({aborted, Reason41 } when element(1, Reason41) == bad_type,
 1080:            mnesia:add_table_index(Tab, BadValPos)),
 1081:     ?match({aborted,Reason42 } when element(1, Reason42) == bad_type,
 1082:            mnesia:add_table_index(Tab, 2)),
 1083:     ?match({aborted, Reason43 } when element(1, Reason43) == bad_type,
 1084:            mnesia:add_table_index(Tab, 1)),
 1085:     ?match({aborted, Reason44 } when element(1, Reason44) == bad_type,
 1086:            mnesia:add_table_index(Tab, 0)),
 1087:     ?match({aborted, Reason45 } when element(1, Reason45) == bad_type,
 1088:            mnesia:add_table_index(Tab, -1)),
 1089:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
 1090:     ?match({aborted, Reason46 } when element(1, Reason46) == already_exists,
 1091:            mnesia:add_table_index(Tab, ValPos)),
 1092: 
 1093:     NestedFun = fun() ->
 1094:                         ?match({aborted, nested_transaction},
 1095:                                mnesia:add_table_index(Tab, ValPos)),
 1096:                         ok
 1097:                 end,
 1098:     ?match({atomic, ok}, mnesia:transaction(NestedFun)),
 1099:     ?verify_mnesia(Nodes, []).
 1100: 
 1101: create_live_table_index_ram(suite) -> [];
 1102: create_live_table_index_ram(Config) when is_list(Config) ->
 1103:     create_live_table_index(Config, ram_copies).
 1104: 
 1105: create_live_table_index_disc(suite) -> [];
 1106: create_live_table_index_disc(Config) when is_list(Config) ->
 1107:     create_live_table_index(Config, disc_copies).
 1108: 
 1109: create_live_table_index_disc_only(suite) -> [];
 1110: create_live_table_index_disc_only(Config) when is_list(Config) ->
 1111:     create_live_table_index(Config, disc_only_copies).
 1112: 
 1113: create_live_table_index(Config, Storage) ->
 1114:     [N1,N2,N3] = Nodes = ?acquire_nodes(3, Config),
 1115:     Tab = create_live_table_index,
 1116:     Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, Nodes}],
 1117:     ?match({atomic, ok}, mnesia:create_table(Schema)),
 1118:     ValPos = 3,
 1119:     mnesia:dirty_write({Tab, 1, 2}),
 1120: 
 1121:     Fun = fun() ->
 1122:                   ?match(ok, mnesia:write({Tab, 2, 2})),
 1123:                   ok
 1124:           end,
 1125:     ?match({atomic, ok}, mnesia:transaction(Fun)),
 1126:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
 1127:     IRead = fun() -> lists:sort(mnesia:index_read(Tab, 2, ValPos)) end,
 1128:     ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]}, mnesia:transaction(IRead)),
 1129:     ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
 1130: 
 1131:     %% Bug when adding index when table is still unloaded
 1132:     %% By setting load order we hopefully will trigger the bug
 1133:     mnesia:change_table_copy_type(Tab, N2, ram_copies),
 1134:     mnesia:change_table_copy_type(Tab, N3, ram_copies),
 1135:     ?match({atomic,ok}, mnesia:change_table_copy_type(schema, N2, ram_copies)),
 1136:     ?match({atomic,ok}, mnesia:change_table_copy_type(schema, N3, ram_copies)),
 1137: 
 1138:     Create = fun(N) ->
 1139: 		     TabN = list_to_atom("tab_" ++ integer_to_list(N)),
 1140: 		     Def = [{ram_copies, Nodes}, {load_order, N}],
 1141: 		     mnesia:create_table(TabN, Def)
 1142: 	     end,
 1143: 
 1144:     ?match([{atomic,ok}|_], [Create(N) || N <- lists:seq(1,50)]),
 1145: 
 1146:     ?match([], mnesia_test_lib:stop_mnesia([N2,N3])),
 1147:     ?match(ok, rpc:call(N2, mnesia, start, [[{extra_db_nodes,[N1]}]])),
 1148:     ?match(ok, rpc:call(N3, mnesia, start, [[{extra_db_nodes,[N1]}]])),
 1149: 
 1150:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
 1151: 
 1152:     ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]}, mnesia:transaction(IRead)),
 1153:     ?match({atomic, [{Tab, 1, 2},{Tab, 2, 2}]},
 1154: 	   rpc:call(N2, mnesia, transaction, [IRead])),
 1155: 
 1156:     ?verify_mnesia(Nodes, []).
 1157: 
 1158: %% Drop table index
 1159: 
 1160: del_table_index_ram(suite) ->[];
 1161: del_table_index_ram(Config) when is_list(Config) ->
 1162:     del_table_index(Config, ram_copies).
 1163: 
 1164: del_table_index_disc(suite) ->[];
 1165: del_table_index_disc(Config) when is_list(Config) ->
 1166:     del_table_index(Config, disc_copies).
 1167: 
 1168: del_table_index_disc_only(suite) ->[];
 1169: del_table_index_disc_only(Config) when is_list(Config) ->
 1170:     del_table_index(Config, disc_only_copies).
 1171: 
 1172: del_table_index(Config, Storage) ->
 1173:     [Node1] = Nodes = ?acquire_nodes(1, Config),
 1174:     Tab = del_table_index,
 1175:     Schema = [{name, Tab}, {attributes, [k, v]}, {Storage, [Node1]}],
 1176:     ?match({atomic, ok}, mnesia:create_table(Schema)),
 1177:     ValPos = 3,
 1178:     BadValPos = ValPos + 1,
 1179:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
 1180:     ?match({aborted,Reason} when element(1, Reason) == no_exists,
 1181:            mnesia:del_table_index(Tab, BadValPos)),
 1182:     ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
 1183: 
 1184:     ?match({aborted,Reason1} when element(1, Reason1) == no_exists,
 1185:            mnesia:del_table_index(Tab, ValPos)),
 1186:     NestedFun =
 1187:         fun() ->
 1188:                 ?match({aborted, nested_transaction},
 1189:                        mnesia:del_table_index(Tab, ValPos)),
 1190:                 ok
 1191:         end,
 1192:     ?match({atomic, ok}, mnesia:transaction(NestedFun)),
 1193:     ?verify_mnesia(Nodes, []).
 1194: 
 1195: 
 1196: idx_schema_changes_ram(suite) -> [];
 1197: idx_schema_changes_ram(Config) when is_list(Config) ->
 1198:     idx_schema_changes(Config, ram_copies).
 1199: idx_schema_changes_disc(suite) -> [];
 1200: idx_schema_changes_disc(Config) when is_list(Config) ->
 1201:     idx_schema_changes(Config, disc_copies).
 1202: idx_schema_changes_disc_only(suite) -> [];
 1203: idx_schema_changes_disc_only(Config) when is_list(Config) ->
 1204:     idx_schema_changes(Config, disc_only_copies).
 1205: 
 1206: idx_schema_changes(Config, Storage) ->
 1207:     [N1, N2] = Nodes = ?acquire_nodes(2, Config),
 1208:     Tab = index_schema_changes,
 1209:     Idx = 3,
 1210:     Schema = [{name, Tab}, {index, [Idx]}, {attributes, [k, v]}, {Storage, Nodes}],
 1211:     ?match({atomic, ok}, mnesia:create_table(Schema)),
 1212: 
 1213:     {Storage1, Storage2} =
 1214: 	case Storage of
 1215: 	    disc_only_copies ->
 1216: 		{ram_copies, disc_copies};
 1217: 	    disc_copies ->
 1218: 		{disc_only_copies, ram_copies};
 1219: 	    ram_copies ->
 1220: 		{disc_copies, disc_only_copies}
 1221: 	end,
 1222: 
 1223:     Write = fun(N) ->
 1224: 		    mnesia:write({Tab, N, N+50})
 1225: 	    end,
 1226: 
 1227:     [mnesia:sync_transaction(Write, [N]) || N <- lists:seq(1, 10)],
 1228:     ?match([{Tab, 1, 51}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 51, Idx])),
 1229:     ?match([{Tab, 1, 51}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 51, Idx])),
 1230: 
 1231:     ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, N1, Storage1)),
 1232: 
 1233:     ?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [17]])),
 1234:     ?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [18]])),
 1235: 
 1236:     ?match([{Tab, 17, 67}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 67, Idx])),
 1237:     ?match([{Tab, 18, 68}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 68, Idx])),
 1238: 
 1239:     ?match({atomic, ok}, mnesia:del_table_copy(Tab, N1)),
 1240:     ?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [11]])),
 1241:     ?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [12]])),
 1242: 
 1243:     ?match([{Tab, 11, 61}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 61, Idx])),
 1244:     ?match([{Tab, 12, 62}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 62, Idx])),
 1245: 
 1246:     ?match({atomic, ok}, mnesia:move_table_copy(Tab, N2, N1)),
 1247:     ?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [19]])),
 1248:     ?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [20]])),
 1249: 
 1250:     ?match([{Tab, 19, 69}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 69, Idx])),
 1251:     ?match([{Tab, 20, 70}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 70, Idx])),
 1252: 
 1253:     ?match({atomic, ok}, mnesia:add_table_copy(Tab, N2, Storage)),
 1254:     ?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [13]])),
 1255:     ?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [14]])),
 1256: 
 1257:     ?match([{Tab, 13, 63}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 63, Idx])),
 1258:     ?match([{Tab, 14, 64}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 64, Idx])),
 1259: 
 1260:     ?match({atomic, ok}, mnesia:change_table_copy_type(Tab, N2, Storage2)),
 1261: 
 1262:     ?match({atomic, ok}, rpc:call(N1, mnesia, sync_transaction, [Write, [15]])),
 1263:     ?match({atomic, ok}, rpc:call(N2, mnesia, sync_transaction, [Write, [16]])),
 1264: 
 1265:     ?match([{Tab, 15, 65}], rpc:call(N2, mnesia, dirty_index_read, [Tab, 65, Idx])),
 1266:     ?match([{Tab, 16, 66}], rpc:call(N1, mnesia, dirty_index_read, [Tab, 66, Idx])),
 1267: 
 1268:     ?verify_mnesia(Nodes, []).