1: %%
    2: %% %CopyrightBegin%
    3: %%
    4: %% Copyright Ericsson AB 1996-2013. 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_dirty_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: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   33: all() -> 
   34:     [{group, dirty_write}, {group, dirty_read},
   35:      {group, dirty_update_counter}, {group, dirty_delete},
   36:      {group, dirty_delete_object},
   37:      {group, dirty_match_object}, {group, dirty_index},
   38:      {group, dirty_iter}, {group, admin_tests}].
   39: 
   40: groups() -> 
   41:     [{dirty_write, [],
   42:       [dirty_write_ram, dirty_write_disc,
   43:        dirty_write_disc_only]},
   44:      {dirty_read, [],
   45:       [dirty_read_ram, dirty_read_disc,
   46:        dirty_read_disc_only]},
   47:      {dirty_update_counter, [],
   48:       [dirty_update_counter_ram, dirty_update_counter_disc,
   49:        dirty_update_counter_disc_only]},
   50:      {dirty_delete, [],
   51:       [dirty_delete_ram, dirty_delete_disc,
   52:        dirty_delete_disc_only]},
   53:      {dirty_delete_object, [],
   54:       [dirty_delete_object_ram, dirty_delete_object_disc,
   55:        dirty_delete_object_disc_only]},
   56:      {dirty_match_object, [],
   57:       [dirty_match_object_ram, dirty_match_object_disc,
   58:        dirty_match_object_disc_only]},
   59:      {dirty_index, [],
   60:       [{group, dirty_index_match_object},
   61:        {group, dirty_index_read},
   62:        {group, dirty_index_update}]},
   63:      {dirty_index_match_object, [],
   64:       [dirty_index_match_object_ram,
   65:        dirty_index_match_object_disc,
   66:        dirty_index_match_object_disc_only]},
   67:      {dirty_index_read, [],
   68:       [dirty_index_read_ram, dirty_index_read_disc,
   69:        dirty_index_read_disc_only]},
   70:      {dirty_index_update, [],
   71:       [dirty_index_update_set_ram,
   72:        dirty_index_update_set_disc,
   73:        dirty_index_update_set_disc_only,
   74:        dirty_index_update_bag_ram, dirty_index_update_bag_disc,
   75:        dirty_index_update_bag_disc_only]},
   76:      {dirty_iter, [],
   77:       [dirty_iter_ram, dirty_iter_disc,
   78:        dirty_iter_disc_only]},
   79:      {admin_tests, [],
   80:       [del_table_copy_1, del_table_copy_2, del_table_copy_3,
   81:        add_table_copy_1, add_table_copy_2, add_table_copy_3,
   82:        add_table_copy_4, move_table_copy_1, move_table_copy_2,
   83:        move_table_copy_3, move_table_copy_4]}].
   84: 
   85: init_per_group(_GroupName, Config) ->
   86:     Config.
   87: 
   88: end_per_group(_GroupName, Config) ->
   89:     Config.
   90: 
   91: 
   92: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
   93: %% Write records dirty
   94: 
   95: 
   96: dirty_write_ram(suite) -> [];
   97: dirty_write_ram(Config) when is_list(Config) ->
   98:     dirty_write(Config, ram_copies).
   99: 
  100: dirty_write_disc(suite) -> [];
  101: dirty_write_disc(Config) when is_list(Config) ->
  102:     dirty_write(Config, disc_copies).
  103: 
  104: dirty_write_disc_only(suite) -> [];
  105: dirty_write_disc_only(Config) when is_list(Config) ->
  106:     dirty_write(Config, disc_only_copies).
  107: 
  108: dirty_write(Config, Storage) ->
  109:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  110:     Tab = dirty_write, 
  111:     Def = [{attributes, [k, v]}, {Storage, [Node1]}], 
  112:     ?match({atomic, ok},  mnesia:create_table(Tab, Def)), 
  113:     
  114:     ?match({'EXIT', _},  mnesia:dirty_write([])), 
  115:     ?match({'EXIT', _},  mnesia:dirty_write({Tab, 2})), 
  116:     ?match({'EXIT', _},  mnesia:dirty_write({foo, 2})), 
  117:     ?match(ok,  mnesia:dirty_write({Tab, 1, 2})), 
  118: 
  119:     ?match({atomic, ok},   mnesia:transaction(fun() -> 
  120: 	    mnesia:dirty_write({Tab, 1, 2}) end)), 
  121:     ?verify_mnesia(Nodes, []).
  122: 
  123: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  124: %% Read records dirty
  125: 
  126: 
  127: dirty_read_ram(suite) -> [];
  128: dirty_read_ram(Config) when is_list(Config) ->
  129:     dirty_read(Config, ram_copies).
  130: 
  131: dirty_read_disc(suite) -> [];
  132: dirty_read_disc(Config) when is_list(Config) ->
  133:     dirty_read(Config, disc_copies).
  134: 
  135: dirty_read_disc_only(suite) -> [];
  136: dirty_read_disc_only(Config) when is_list(Config) ->
  137:     dirty_read(Config, disc_only_copies).
  138: 
  139: dirty_read(Config, Storage) ->
  140:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  141:     Tab = dirty_read, 
  142:     Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}], 
  143:     ?match({atomic, ok},  mnesia:create_table(Tab, Def)), 
  144:     
  145:     ?match({'EXIT', _},  mnesia:dirty_read([])), 
  146:     ?match({'EXIT', _},  mnesia:dirty_read({Tab})), 
  147:     ?match({'EXIT', _},   mnesia:dirty_read({Tab, 1, 2})), 
  148:     ?match([],  mnesia:dirty_read({Tab, 1})), 
  149:     ?match(ok,  mnesia:dirty_write({Tab, 1, 2})), 
  150:     ?match([{Tab, 1, 2}],  mnesia:dirty_read({Tab, 1})), 
  151:     ?match(ok,  mnesia:dirty_write({Tab, 1, 3})), 
  152:     ?match([{Tab, 1, 2}, {Tab, 1, 3}],  mnesia:dirty_read({Tab, 1})), 
  153: 
  154:     ?match({atomic, [{Tab, 1, 2}, {Tab, 1, 3}]},  
  155: 	   mnesia:transaction(fun() -> mnesia:dirty_read({Tab, 1}) end)), 
  156: 
  157:     ?match(false, mnesia:async_dirty(fun() -> mnesia:is_transaction() end)),
  158:     ?match(false, mnesia:sync_dirty(fun() -> mnesia:is_transaction() end)),
  159:     ?match(false, mnesia:ets(fun() -> mnesia:is_transaction() end)),
  160:     ?match(false, mnesia:activity(async_dirty, fun() -> mnesia:is_transaction() end)),
  161:     ?match(false, mnesia:activity(sync_dirty,  fun() -> mnesia:is_transaction() end)),
  162:     ?match(false, mnesia:activity(ets,         fun() -> mnesia:is_transaction() end)),
  163: 
  164:     ?verify_mnesia(Nodes, []).
  165: 
  166: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  167: %% Update counter record dirty
  168: 
  169: 
  170: dirty_update_counter_ram(suite) -> [];
  171: dirty_update_counter_ram(Config) when is_list(Config) ->
  172:     dirty_update_counter(Config, ram_copies).
  173: 
  174: dirty_update_counter_disc(suite) -> [];
  175: dirty_update_counter_disc(Config) when is_list(Config) ->
  176:     dirty_update_counter(Config, disc_copies).
  177: 
  178: dirty_update_counter_disc_only(suite) -> [];
  179: dirty_update_counter_disc_only(Config) when is_list(Config) ->
  180:     dirty_update_counter(Config, disc_only_copies).
  181: 
  182: dirty_update_counter(Config, Storage) ->
  183:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  184:     Tab = dirty_update_counter, 
  185:     Def = [{attributes, [k, v]}, {Storage, [Node1]}], 
  186:     ?match({atomic, ok},  mnesia:create_table(Tab, Def)), 
  187:     ?match(ok,  mnesia:dirty_write({Tab, 1, 2})), 
  188:         
  189:     ?match({'EXIT', _},  mnesia:dirty_update_counter({Tab, 1}, [])), 
  190:     ?match({'EXIT', _},  mnesia:dirty_update_counter({Tab}, 3)), 
  191:     ?match({'EXIT', _},  mnesia:dirty_update_counter({foo, 1}, 3)), 
  192:     ?match(5,  mnesia:dirty_update_counter({Tab, 1}, 3)), 
  193:     ?match([{Tab, 1, 5}],  mnesia:dirty_read({Tab, 1})), 
  194: 
  195:     ?match({atomic, 8},  mnesia:transaction(fun() ->
  196: 	   mnesia:dirty_update_counter({Tab, 1}, 3) end)), 
  197:  
  198:     ?match(1,  mnesia:dirty_update_counter({Tab, foo}, 1)),
  199:     ?match([{Tab, foo,1}], mnesia:dirty_read({Tab,foo})),
  200: 
  201:     ?match({ok,_}, mnesia:subscribe({table, Tab, detailed})),
  202: 
  203:     ?match(2, mnesia:dirty_update_counter({Tab, foo}, 1)),
  204:     ?match([{Tab, foo,2}], mnesia:dirty_read({Tab,foo})),
  205: 
  206:     ?verify_mnesia(Nodes, []).
  207: 
  208: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  209: %% Delete record dirty
  210: 
  211: 
  212: dirty_delete_ram(suite) -> [];
  213: dirty_delete_ram(Config) when is_list(Config) ->
  214:     dirty_delete(Config, ram_copies).
  215: 
  216: dirty_delete_disc(suite) -> [];
  217: dirty_delete_disc(Config) when is_list(Config) ->
  218:     dirty_delete(Config, disc_copies).
  219: 
  220: dirty_delete_disc_only(suite) -> [];
  221: dirty_delete_disc_only(Config) when is_list(Config) ->
  222:     dirty_delete(Config, disc_only_copies).
  223: 
  224: dirty_delete(Config, Storage) ->
  225:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  226:     Tab = dirty_delete, 
  227:     Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}], 
  228:     ?match({atomic, ok},  mnesia:create_table(Tab, Def)), 
  229:     
  230:     ?match({'EXIT', _},  mnesia:dirty_delete([])), 
  231:     ?match({'EXIT', _},  mnesia:dirty_delete({Tab})), 
  232:     ?match({'EXIT', _},  mnesia:dirty_delete({Tab, 1, 2})), 
  233:     ?match(ok,  mnesia:dirty_delete({Tab, 1})), 
  234:     ?match(ok,  mnesia:dirty_write({Tab, 1, 2})), 
  235:     ?match(ok,  mnesia:dirty_delete({Tab, 1})), 
  236:     ?match(ok,  mnesia:dirty_write({Tab, 1, 2})), 
  237:     ?match(ok,  mnesia:dirty_write({Tab, 1, 2})), 
  238:     ?match(ok,  mnesia:dirty_delete({Tab, 1})), 
  239: 
  240:     ?match(ok,  mnesia:dirty_write({Tab, 1, 2})), 
  241:     ?match({atomic, ok},  mnesia:transaction(fun() ->
  242: 	   mnesia:dirty_delete({Tab, 1}) end)), 
  243:     ?verify_mnesia(Nodes, []).
  244: 
  245: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  246: %% Delete matching record dirty
  247: 
  248: 
  249: dirty_delete_object_ram(suite) -> [];
  250: dirty_delete_object_ram(Config) when is_list(Config) ->
  251:     dirty_delete_object(Config, ram_copies).
  252: 
  253: dirty_delete_object_disc(suite) -> [];
  254: dirty_delete_object_disc(Config) when is_list(Config) ->
  255:     dirty_delete_object(Config, disc_copies).
  256: 
  257: dirty_delete_object_disc_only(suite) -> [];
  258: dirty_delete_object_disc_only(Config) when is_list(Config) ->
  259:     dirty_delete_object(Config, disc_only_copies).
  260: 
  261: dirty_delete_object(Config, Storage) ->
  262:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  263:     Tab = dirty_delete_object, 
  264:     Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}], 
  265:     ?match({atomic, ok},  mnesia:create_table(Tab, Def)), 
  266: 
  267:     OneRec = {Tab, 1, 2}, 
  268:     ?match({'EXIT', _},  mnesia:dirty_delete_object([])), 
  269:     ?match({'EXIT', _},  mnesia:dirty_delete_object({Tab})), 
  270:     ?match({'EXIT', _},  mnesia:dirty_delete_object({Tab, 1})), 
  271:     ?match(ok,  mnesia:dirty_delete_object(OneRec)), 
  272:     ?match(ok,  mnesia:dirty_write(OneRec)), 
  273:     ?match(ok,  mnesia:dirty_delete_object(OneRec)), 
  274:     ?match(ok,  mnesia:dirty_write(OneRec)), 
  275:     ?match(ok,  mnesia:dirty_write(OneRec)), 
  276:     ?match(ok,  mnesia:dirty_delete_object(OneRec)), 
  277: 
  278:     ?match(ok,  mnesia:dirty_write(OneRec)), 
  279:     ?match({atomic, ok},  mnesia:transaction(fun() ->
  280: 	   mnesia:dirty_delete_object(OneRec) end)), 
  281: 
  282:     ?match({'EXIT', {aborted, {bad_type, Tab, _}}}, mnesia:dirty_delete_object(Tab, {Tab, {['_']}, 21})),
  283:     ?match({'EXIT', {aborted, {bad_type, Tab, _}}}, mnesia:dirty_delete_object(Tab, {Tab, {['$5']}, 21})),
  284: 
  285:     ?verify_mnesia(Nodes, []).
  286: 
  287: 
  288: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  289: %% Read matching records dirty
  290: 
  291: 
  292: dirty_match_object_ram(suite) -> [];
  293: dirty_match_object_ram(Config) when is_list(Config) ->
  294:     dirty_match_object(Config, ram_copies).
  295: 
  296: dirty_match_object_disc(suite) -> [];
  297: dirty_match_object_disc(Config) when is_list(Config) ->
  298:     dirty_match_object(Config, disc_copies).
  299: 
  300: dirty_match_object_disc_only(suite) -> [];
  301: dirty_match_object_disc_only(Config) when is_list(Config) ->
  302:     dirty_match_object(Config, disc_only_copies).
  303: 
  304: dirty_match_object(Config, Storage) ->
  305:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  306:     Tab = dirty_match, 
  307:     Def = [{attributes, [k, v]}, {Storage, [Node1]}], 
  308:     ?match({atomic, ok},  mnesia:create_table(Tab, Def)), 
  309: 
  310:     OneRec = {Tab, 1, 2}, 
  311:     OnePat = {Tab, '$1', 2}, 
  312:     ?match([], mnesia:dirty_match_object(OnePat)), 
  313:     ?match(ok,  mnesia:dirty_write(OneRec)), 
  314:     ?match([OneRec],  mnesia:dirty_match_object(OnePat)), 
  315:     ?match({atomic, [OneRec]},  mnesia:transaction(fun() ->
  316: 	 mnesia:dirty_match_object(OnePat) end)), 
  317:     
  318:     ?match({'EXIT', _},  mnesia:dirty_match_object({foo, '$1', 2})), 
  319:     ?match({'EXIT', _},  mnesia:dirty_match_object({[], '$1', 2})), 
  320:     ?verify_mnesia(Nodes, []).
  321: 
  322: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  323: 
  324: 
  325: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  326: %% Dirty read matching records by using an index
  327: 
  328: 
  329: dirty_index_match_object_ram(suite) -> [];
  330: dirty_index_match_object_ram(Config) when is_list(Config) ->
  331:     dirty_index_match_object(Config, ram_copies).
  332: 
  333: dirty_index_match_object_disc(suite) -> [];
  334: dirty_index_match_object_disc(Config) when is_list(Config) ->
  335:     dirty_index_match_object(Config, disc_copies).
  336: 
  337: dirty_index_match_object_disc_only(suite) -> [];
  338: dirty_index_match_object_disc_only(Config) when is_list(Config) ->
  339:     dirty_index_match_object(Config, disc_only_copies).
  340: 
  341: dirty_index_match_object(Config, Storage) ->
  342:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  343:     Tab = dirty_index_match_object, 
  344:     ValPos = 3, 
  345:     BadValPos = ValPos + 1, 
  346:     Def = [{attributes, [k, v]}, {Storage, [Node1]}, {index, [ValPos]}], 
  347:     ?match({atomic, ok},  mnesia:create_table(Tab, Def)), 
  348:     
  349:     ?match([],  mnesia:dirty_index_match_object({Tab, '$1', 2}, ValPos)), 
  350:     OneRec = {Tab, 1, 2}, 
  351:     ?match(ok,  mnesia:dirty_write(OneRec)), 
  352:     
  353:     ?match([OneRec],  mnesia:dirty_index_match_object({Tab, '$1', 2}, ValPos)), 
  354:     ?match({'EXIT', _},  mnesia:dirty_index_match_object({Tab, '$1', 2}, BadValPos)), 
  355:     ?match({'EXIT', _},  mnesia:dirty_index_match_object({foo, '$1', 2}, ValPos)), 
  356:     ?match({'EXIT', _},  mnesia:dirty_index_match_object({[], '$1', 2}, ValPos)), 
  357:     ?match({atomic, [OneRec]},  mnesia:transaction(fun() ->
  358:            mnesia:dirty_index_match_object({Tab, '$1', 2}, ValPos) end)),     
  359:     
  360:     ?verify_mnesia(Nodes, []).
  361: 
  362: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  363: %% Read records by using an index
  364: 
  365: 
  366: dirty_index_read_ram(suite) -> [];
  367: dirty_index_read_ram(Config) when is_list(Config) ->
  368:     dirty_index_read(Config, ram_copies).
  369: 
  370: dirty_index_read_disc(suite) -> [];
  371: dirty_index_read_disc(Config) when is_list(Config) ->
  372:     dirty_index_read(Config, disc_copies).
  373: 
  374: dirty_index_read_disc_only(suite) -> [];
  375: dirty_index_read_disc_only(Config) when is_list(Config) ->
  376:     dirty_index_read(Config, disc_only_copies).
  377: 
  378: dirty_index_read(Config, Storage) ->
  379:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  380:     Tab = dirty_index_read, 
  381:     ValPos = 3, 
  382:     BadValPos = ValPos + 1, 
  383:     Def = [{type, set},
  384: 	   {attributes, [k, v]},
  385: 	   {Storage, [Node1]},
  386: 	   {index, [ValPos]}], 
  387:     ?match({atomic, ok},  mnesia:create_table(Tab, Def)), 
  388: 
  389:     OneRec = {Tab, 1, 2}, 
  390:     ?match([],  mnesia:dirty_index_read(Tab, 2, ValPos)), 
  391:     ?match(ok,  mnesia:dirty_write(OneRec)), 
  392:     ?match([OneRec],  mnesia:dirty_index_read(Tab, 2, ValPos)), 
  393:     ?match({atomic, [OneRec]},  
  394: 	   mnesia:transaction(fun() -> mnesia:dirty_index_read(Tab, 2, ValPos) end)),
  395:     ?match(42, mnesia:dirty_update_counter({Tab, 1}, 40)),
  396:     ?match([{Tab,1,42}], mnesia:dirty_read({Tab, 1})), 
  397:     ?match([],  mnesia:dirty_index_read(Tab, 2, ValPos)), 
  398:     ?match([{Tab, 1, 42}],  mnesia:dirty_index_read(Tab, 42, ValPos)),
  399: 
  400:     ?match({'EXIT', _},  mnesia:dirty_index_read(Tab, 2, BadValPos)), 
  401:     ?match({'EXIT', _},  mnesia:dirty_index_read(foo, 2, ValPos)), 
  402:     ?match({'EXIT', _},  mnesia:dirty_index_read([], 2, ValPos)), 
  403:     
  404:     ?verify_mnesia(Nodes, []).
  405: 
  406: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  407: 
  408: 
  409: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  410: dirty_index_update_set_ram(suite) -> [];
  411: dirty_index_update_set_ram(Config) when is_list(Config) ->
  412:     dirty_index_update_set(Config, ram_copies).
  413: 
  414: dirty_index_update_set_disc(suite) -> [];
  415: dirty_index_update_set_disc(Config) when is_list(Config) ->
  416:     dirty_index_update_set(Config, disc_copies).
  417: 
  418: dirty_index_update_set_disc_only(suite) -> [];
  419: dirty_index_update_set_disc_only(Config) when is_list(Config) ->
  420:     dirty_index_update_set(Config, disc_only_copies).
  421: 
  422: dirty_index_update_set(Config, Storage) ->
  423:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  424:     Tab = index_test, 
  425:     ValPos = v1, 
  426:     ValPos2 = v3,
  427:     Def = [{attributes, [k, v1, v2, v3]},
  428: 	   {Storage, [Node1]},
  429: 	   {index, [ValPos]}], 
  430:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 
  431:     
  432:     Pat1 = {Tab, '$1',  2,   '$2', '$3'},
  433:     Pat2 = {Tab, '$1', '$2', '$3', '$4'}, 
  434:     
  435:     Rec1 = {Tab, 1, 2, 3, 4}, 
  436:     Rec2 = {Tab, 2, 2, 13, 14},
  437:     Rec3 = {Tab, 1, 12, 13, 14}, 
  438:     Rec4 = {Tab, 4, 2, 13, 14}, 
  439:     
  440:     ?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
  441:     ?match(ok, mnesia:dirty_write(Rec1)),
  442:     ?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
  443:     
  444:     ?match(ok, mnesia:dirty_write(Rec2)),
  445:     R1 = mnesia:dirty_index_read(Tab, 2, ValPos),
  446:     ?match([Rec1, Rec2], lists:sort(R1)),
  447:     
  448:     ?match(ok, mnesia:dirty_write(Rec3)),
  449:     R2 = mnesia:dirty_index_read(Tab, 2, ValPos),
  450:     ?match([Rec2], lists:sort(R2)),
  451:     ?match([Rec2], mnesia:dirty_index_match_object(Pat1, ValPos)),
  452:     
  453:     {atomic, R3} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end), 
  454:     ?match([Rec3, Rec2], lists:sort(R3)),
  455:     
  456:     ?match(ok, mnesia:dirty_write(Rec4)),
  457:     R4 = mnesia:dirty_index_read(Tab, 2, ValPos),
  458:     ?match([Rec2, Rec4], lists:sort(R4)),
  459:     
  460:     ?match(ok, mnesia:dirty_delete({Tab, 4})),
  461:     ?match([Rec2], mnesia:dirty_index_read(Tab, 2, ValPos)),
  462:     
  463:     ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
  464:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
  465:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
  466:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
  467:     
  468:     R5 = mnesia:dirty_match_object(Pat2),
  469:     ?match([Rec3, Rec2, Rec4], lists:sort(R5)),
  470:     
  471:     R6 = mnesia:dirty_index_read(Tab, 2, ValPos),
  472:     ?match([Rec2, Rec4], lists:sort(R6)),    
  473:     ?match([], mnesia:dirty_index_read(Tab, 4, ValPos2)),
  474:     R7 = mnesia:dirty_index_read(Tab, 14, ValPos2),
  475:     ?match([Rec3, Rec2, Rec4], lists:sort(R7)),
  476: 
  477:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
  478:     R8 = mnesia:dirty_index_read(Tab, 2, ValPos),
  479:     ?match([Rec1, Rec2, Rec4], lists:sort(R8)),
  480:     ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
  481:     R9 = mnesia:dirty_index_read(Tab, 14, ValPos2),
  482:     ?match([Rec2, Rec4], lists:sort(R9)),
  483: 
  484:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec2) end)),
  485:     R10 = mnesia:dirty_index_read(Tab, 2, ValPos),
  486:     ?match([Rec1, Rec4], lists:sort(R10)),
  487:     ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
  488:     ?match([Rec4], mnesia:dirty_index_read(Tab, 14, ValPos2)),
  489: 
  490:     ?match(ok, mnesia:dirty_delete({Tab, 4})),
  491:     R11 = mnesia:dirty_index_read(Tab, 2, ValPos),
  492:     ?match([Rec1], lists:sort(R11)),
  493:     ?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
  494:     ?match([], mnesia:dirty_index_read(Tab, 14, ValPos2)),
  495:     
  496:     ?verify_mnesia(Nodes, []).
  497: 
  498: dirty_index_update_bag_ram(suite) -> [];
  499: dirty_index_update_bag_ram(Config)when is_list(Config) ->
  500:     dirty_index_update_bag(Config, ram_copies).
  501: 
  502: dirty_index_update_bag_disc(suite) -> [];
  503: dirty_index_update_bag_disc(Config)when is_list(Config) ->
  504:     dirty_index_update_bag(Config, disc_copies).
  505: 
  506: dirty_index_update_bag_disc_only(suite) -> [];
  507: dirty_index_update_bag_disc_only(Config)when is_list(Config) ->
  508:     dirty_index_update_bag(Config, disc_only_copies).
  509: 
  510: dirty_index_update_bag(Config, Storage) ->
  511:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  512:     Tab = index_test, 
  513:     ValPos = v1, 
  514:     ValPos2 = v3,
  515:     Def = [{type, bag},
  516: 	   {attributes, [k, v1, v2, v3]}, 
  517: 	   {Storage, [Node1]},
  518: 	   {index, [ValPos]}], 
  519:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 
  520: 
  521:     Pat1 = {Tab, '$1',  2,   '$2', '$3'},
  522:     Pat2 = {Tab, '$1', '$2', '$3', '$4'}, 
  523: 
  524:     Rec1 = {Tab, 1, 2, 3, 4},    
  525:     Rec2 = {Tab, 2, 2, 13, 14},  
  526:     Rec3 = {Tab, 1, 12, 13, 14}, 
  527:     Rec4 = {Tab, 4, 2, 13, 4}, 
  528:     Rec5 = {Tab, 1, 2, 234, 14},
  529: 
  530:     %% Simple Index
  531:     ?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
  532:     ?match(ok, mnesia:dirty_write(Rec1)),
  533:     ?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
  534: 
  535:     ?match(ok, mnesia:dirty_delete_object(Rec5)),
  536:     ?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
  537: 
  538:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec2) end)), 
  539:     R1 = mnesia:dirty_index_read(Tab, 2, ValPos),
  540:     ?match([Rec1, Rec2], lists:sort(R1)),
  541: 
  542:     ?match(ok, mnesia:dirty_write(Rec3)),
  543:     R2 = mnesia:dirty_index_read(Tab, 2, ValPos),
  544:     ?match([Rec1, Rec2], lists:sort(R2)),
  545: 
  546:     R3 = mnesia:dirty_index_match_object(Pat1, ValPos),
  547:     ?match([Rec1, Rec2], lists:sort(R3)),
  548:  
  549:     R4 = mnesia:dirty_match_object(Pat2),
  550:     ?match([Rec1, Rec3, Rec2], lists:sort(R4)),
  551:  
  552:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)), 
  553:     R5 = mnesia:dirty_index_read(Tab, 2, ValPos),
  554:     ?match([Rec1, Rec2, Rec4], lists:sort(R5)),
  555: 
  556:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)), 
  557:     R6 = mnesia:dirty_index_read(Tab, 2, ValPos),
  558:     ?match([Rec1, Rec2], lists:sort(R6)),
  559: 
  560:     ?match(ok, mnesia:dirty_delete_object(Rec1)),
  561:     ?match([Rec2], mnesia:dirty_index_read(Tab, 2, ValPos)),
  562:     R7 = mnesia:dirty_match_object(Pat2),
  563:     ?match([Rec3, Rec2], lists:sort(R7)),
  564:     
  565:     %% Two indexies
  566:     ?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
  567:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
  568:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
  569:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
  570:     ?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
  571:     
  572:     R8 = mnesia:dirty_index_read(Tab, 2, ValPos),
  573:     ?match([Rec1, Rec2, Rec4], lists:sort(R8)),
  574: 
  575:     R9 = mnesia:dirty_index_read(Tab, 4, ValPos2),
  576:     ?match([Rec1, Rec4], lists:sort(R9)),
  577:     R10 = mnesia:dirty_index_read(Tab, 14, ValPos2),
  578:     ?match([Rec3, Rec2], lists:sort(R10)),
  579: 
  580:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec5) end)),
  581:     R11 = mnesia:dirty_index_read(Tab, 2, ValPos),
  582:     ?match([Rec1, Rec5, Rec2, Rec4], lists:sort(R11)),
  583:     R12 = mnesia:dirty_index_read(Tab, 4, ValPos2),
  584:     ?match([Rec1, Rec4], lists:sort(R12)),
  585:     R13 = mnesia:dirty_index_read(Tab, 14, ValPos2),
  586:     ?match([Rec5, Rec3, Rec2], lists:sort(R13)),
  587: 
  588:     ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec1) end)),
  589:     R14 = mnesia:dirty_index_read(Tab, 2, ValPos),
  590:     ?match([Rec5, Rec2, Rec4], lists:sort(R14)),
  591:     ?match([Rec4], mnesia:dirty_index_read(Tab, 4, ValPos2)),
  592:     R15 = mnesia:dirty_index_read(Tab, 14, ValPos2),
  593:     ?match([Rec5, Rec3, Rec2], lists:sort(R15)),
  594: 
  595:     ?match(ok, mnesia:dirty_delete_object(Rec5)),
  596:     R16 = mnesia:dirty_index_read(Tab, 2, ValPos),
  597:     ?match([Rec2, Rec4], lists:sort(R16)),
  598:     ?match([Rec4], mnesia:dirty_index_read(Tab, 4, ValPos2)),
  599:     R17 = mnesia:dirty_index_read(Tab, 14, ValPos2),
  600:     ?match([Rec3, Rec2], lists:sort(R17)),
  601: 
  602:     ?match(ok, mnesia:dirty_write(Rec1)),
  603:     ?match(ok, mnesia:dirty_delete({Tab, 1})),
  604:     R18 = mnesia:dirty_index_read(Tab, 2, ValPos),
  605:     ?match([Rec2, Rec4], lists:sort(R18)),
  606:     ?match([Rec4], mnesia:dirty_index_read(Tab, 4, ValPos2)),
  607:     R19 = mnesia:dirty_index_read(Tab, 14, ValPos2),
  608:     ?match([Rec2], lists:sort(R19)),
  609: 
  610:     ?verify_mnesia(Nodes, []).
  611: 
  612: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  613: %% Dirty iteration
  614: %% dirty_slot,  dirty_first,  dirty_next
  615: 
  616: 
  617: dirty_iter_ram(suite) -> [];
  618: dirty_iter_ram(Config) when is_list(Config) ->
  619:     dirty_iter(Config, ram_copies).
  620: 
  621: dirty_iter_disc(suite) -> [];
  622: dirty_iter_disc(Config) when is_list(Config) ->
  623:     dirty_iter(Config, disc_copies).
  624: 
  625: dirty_iter_disc_only(suite) -> [];
  626: dirty_iter_disc_only(Config) when is_list(Config) ->
  627:     dirty_iter(Config, disc_only_copies).
  628: 
  629: dirty_iter(Config, Storage) ->
  630:     [Node1] = Nodes = ?acquire_nodes(1, Config), 
  631:     Tab = dirty_iter, 
  632:     Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}], 
  633:     ?match({atomic, ok},  mnesia:create_table(Tab, Def)), 
  634: 
  635:     ?match([], all_slots(Tab)), 
  636:     ?match([], all_nexts(Tab)), 
  637: 
  638:     Keys = lists:seq(1, 5), 
  639:     Records = [{Tab, A, B} || A <- Keys,  B <- lists:seq(1, 2)], 
  640:     lists:foreach(fun(Rec) -> ?match(ok, mnesia:dirty_write(Rec)) end, Records), 
  641: 
  642:     SortedRecords = lists:sort(Records), 
  643:     ?match(SortedRecords, lists:sort(all_slots(Tab))), 
  644:     ?match(Keys, lists:sort(all_nexts(Tab))), 
  645: 
  646:     ?match({'EXIT', _}, mnesia:dirty_first(foo)), 
  647:     ?match({'EXIT', _}, mnesia:dirty_next(foo, foo)), 
  648:     ?match({'EXIT', _}, mnesia:dirty_slot(foo, 0)), 
  649:     ?match({'EXIT', _}, mnesia:dirty_slot(foo, [])), 
  650:     ?match({atomic, Keys},
  651: 	   mnesia:transaction(fun() -> lists:sort(all_nexts(Tab)) end)),     
  652:     ?verify_mnesia(Nodes, []).
  653: 
  654: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  655: 
  656: %% Returns a list of all keys in table
  657: all_slots(Tab) ->
  658:     all_slots(Tab, [], 0).
  659: 
  660: all_slots(_Tab, '$end_of_table', _) ->
  661:     [];
  662: all_slots(Tab, PrevRecords, PrevSlot) ->
  663:     Records = mnesia:dirty_slot(Tab, PrevSlot), 
  664:     PrevRecords ++ all_slots(Tab, Records, PrevSlot + 1).
  665: 
  666: %% Returns a list of all keys in table
  667: 
  668: all_nexts(Tab) ->
  669:     FirstKey = mnesia:dirty_first(Tab), 
  670:     all_nexts(Tab, FirstKey).
  671: 
  672: all_nexts(_Tab, '$end_of_table') ->
  673:     [];
  674: all_nexts(Tab, PrevKey) ->
  675:     Key = mnesia:dirty_next(Tab, PrevKey), 
  676:     [PrevKey] ++ all_nexts(Tab, Key).
  677: 
  678: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  679: 
  680: update_trans(Tab, Key, Acc) ->
  681:     Update = 
  682: 	fun() -> 
  683: 		Res = (catch mnesia:read({Tab, Key})),
  684: 		case Res of 
  685: 		    [{Tab, Key, Extra, Acc}] ->
  686: 			mnesia:write({Tab,Key,Extra, Acc+1});
  687: 		    Val ->
  688: 			{read, Val, {acc, Acc}}
  689: 		end
  690: 	end,
  691:     receive 
  692: 	{Pid, quit} -> Pid ! {self(), Acc}
  693:     after
  694: 	3 -> 
  695: 	    case catch mnesia:sync_dirty(Update) of
  696: 		ok -> 	    
  697: 		    update_trans(Tab, Key, Acc+1);
  698: 		Else -> 
  699: 		    ?error("Dirty Operation failed on ~p (update no ~p) with ~p~n"
  700: 			   "Info w2read ~p w2write ~p w2commit ~p storage ~p ~n", 
  701: 			   [node(), 
  702: 			    Acc,
  703: 			    Else,
  704: 			    mnesia:table_info(Tab, where_to_read),
  705: 			    mnesia:table_info(Tab, where_to_write),
  706: 			    mnesia:table_info(Tab, where_to_commit),
  707: 			    mnesia:table_info(Tab, storage_type)])
  708: 	    end
  709:     end.
  710: 
  711: del_table_copy_1(suite) -> [];
  712: del_table_copy_1(Config) when is_list(Config) ->
  713:     [_Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
  714:     del_table(Node2, Node2, Nodes). %Called on same Node as deleted
  715: del_table_copy_2(suite) -> [];
  716: del_table_copy_2(Config) when is_list(Config) ->
  717:     [Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
  718:     del_table(Node1, Node2, Nodes). %Called from other Node 
  719: del_table_copy_3(suite) -> [];
  720: del_table_copy_3(Config) when is_list(Config) ->
  721:     [_Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  722:     del_table(Node3, Node2, Nodes). %Called from Node w.o. table
  723: 
  724: del_table(CallFrom, DelNode, [Node1, Node2, Node3]) ->
  725:     Tab = schema_ops,
  726:     Def = [{disc_only_copies, [Node1]}, {ram_copies, [Node2]}, 
  727: 	   {attributes, [key, attr1, attr2]}],
  728:     ?log("Test case removing table from ~w, with ~w~n", [DelNode, Def]),
  729:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  730:     insert(Tab, 1000),
  731:     
  732:     Pid1 = spawn_link(Node1, ?MODULE, update_trans, [Tab, 1, 0]),
  733:     Pid2 = spawn_link(Node2, ?MODULE, update_trans, [Tab, 2, 0]),
  734:     Pid3 = spawn_link(Node3, ?MODULE, update_trans, [Tab, 3, 0]),
  735: 
  736: 
  737:     dbg:tracer(process, {fun(Msg,_) -> tracer(Msg) end, void}),          
  738:     %%    dbg:n(Node2),
  739:     %%    dbg:n(Node3),
  740:     %% dbg:tp('_', []),     
  741:     %% dbg:tpl(dets, [timestamp]), 
  742:     dbg:p(Pid1, [m,c,timestamp]),  
  743:     
  744:     ?match({atomic, ok}, 
  745: 	   rpc:call(CallFrom, mnesia, del_table_copy, [Tab, DelNode])),
  746: 
  747:     Pid1 ! {self(), quit}, R1 = 
  748: 	receive {Pid1, Res1} -> Res1
  749: 	after 
  750: 	    5000 -> io:format("~p~n",[process_info(Pid1)]),error 
  751: 	end,
  752:     Pid2 ! {self(), quit}, R2 = 
  753: 	receive {Pid2, Res2} -> Res2 
  754: 	after 
  755: 	    5000 -> error 
  756: 	end,
  757:     Pid3 ! {self(), quit}, R3 = 
  758: 	receive {Pid3, Res3} -> Res3 
  759: 	after 
  760: 	    5000 -> error 
  761: 	end,
  762:     verify_oids(Tab, Node1, Node2, Node3, R1, R2, R3),
  763:     ?verify_mnesia([Node1, Node2, Node3], []).
  764:     
  765: tracer({trace_ts, _, send, Msg, Pid, {_,S,Ms}}) ->
  766:     io:format("~p:~p ~p >> ~w ~n",[S,Ms,Pid,Msg]);
  767: tracer({trace_ts, _, 'receive', Msg, {_,S,Ms}}) ->
  768:     io:format("~p:~p << ~w ~n",[S,Ms,Msg]);
  769: 
  770: 
  771: tracer(Msg) ->
  772:     io:format("UMsg ~p ~n",[Msg]),
  773:     ok.
  774: 
  775: 
  776: 
  777: add_table_copy_1(suite) -> [];
  778: add_table_copy_1(Config) when is_list(Config) ->
  779:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  780:     Def = [{ram_copies, [Node1, Node2]}, 
  781: 	   {attributes, [key, attr1, attr2]}], 
  782:     add_table(Node1, Node3, Nodes, Def).
  783: %% Not so much diff from 1 but I got a feeling of a bug
  784: %% should behave exactly the same but just checking the internal ordering 
  785: add_table_copy_2(suite) -> [];
  786: add_table_copy_2(Config) when is_list(Config) ->
  787:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  788:     Def = [{ram_copies, [Node1, Node2]}, 
  789: 	   {attributes, [key, attr1, attr2]}], 
  790:     add_table(Node2, Node3, Nodes, Def).
  791: add_table_copy_3(suite) -> [];
  792: add_table_copy_3(Config) when is_list(Config) ->
  793:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  794:     Def = [{ram_copies, [Node1, Node2]},
  795: 	   {attributes, [key, attr1, attr2]}], 
  796:     add_table(Node3, Node3, Nodes, Def).
  797: add_table_copy_4(suite) -> [];
  798: add_table_copy_4(Config) when is_list(Config) ->
  799:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  800:     Def = [{disc_only_copies, [Node1]}, 
  801: 	   {attributes, [key, attr1, attr2]}], 
  802:     add_table(Node2, Node3, Nodes, Def).
  803: 
  804: add_table(CallFrom, AddNode, [Node1, Node2, Node3], Def) ->
  805:     ?log("Test case adding table at ~w, with ~w~n", [AddNode, Def]),
  806:     Tab = schema_ops,
  807:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  808:     insert(Tab, 1002),
  809:     
  810:     Pid1 = spawn_link(Node1, ?MODULE, update_trans, [Tab, 1, 0]),
  811:     Pid2 = spawn_link(Node2, ?MODULE, update_trans, [Tab, 2, 0]),
  812:     Pid3 = spawn_link(Node3, ?MODULE, update_trans, [Tab, 3, 0]),
  813:     
  814:     ?match({atomic, ok}, rpc:call(CallFrom, mnesia, add_table_copy, 
  815: 				  [Tab, AddNode, ram_copies])),
  816:     Pid1 ! {self(), quit}, R1 = receive {Pid1, Res1} -> Res1 after 5000 -> error end,
  817:     Pid2 ! {self(), quit}, R2 = receive {Pid2, Res2} -> Res2 after 5000 -> error end,
  818:     Pid3 ! {self(), quit}, R3 = receive {Pid3, Res3} -> Res3 after 5000 -> error end,
  819:     verify_oids(Tab, Node1, Node2, Node3, R1, R2, R3),
  820:     ?verify_mnesia([Node1, Node2, Node3], []).
  821: 
  822: move_table_copy_1(suite) -> [];
  823: move_table_copy_1(Config) when is_list(Config) ->
  824:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  825:     Def = [{ram_copies, [Node1, Node2]},
  826: 	   {attributes, [key, attr1, attr2]}], 
  827:     move_table(Node1, Node1, Node3, Nodes, Def).
  828: move_table_copy_2(suite) -> [];
  829: move_table_copy_2(Config) when is_list(Config) ->
  830:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  831:     Def = [{ram_copies, [Node1, Node2]},
  832: 	   {attributes, [key, attr1, attr2]}], 
  833:     move_table(Node2, Node1, Node3, Nodes, Def).
  834: move_table_copy_3(suite) -> [];
  835: move_table_copy_3(Config) when is_list(Config) ->
  836:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  837:     Def = [{ram_copies, [Node1, Node2]},
  838: 	   {attributes, [key, attr1, attr2]}], 
  839:     move_table(Node3, Node1, Node3, Nodes, Def).
  840: move_table_copy_4(suite) -> [];
  841: move_table_copy_4(Config) when is_list(Config) ->
  842:     [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
  843:     Def = [{ram_copies, [Node1]},
  844: 	   {attributes, [key, attr1, attr2]}], 
  845:     move_table(Node2, Node1, Node3, Nodes, Def).
  846: 
  847: move_table(CallFrom, FromNode, ToNode, [Node1, Node2, Node3], Def) ->
  848:     ?log("Test case move table from ~w to ~w, with ~w~n", [FromNode, ToNode, Def]),
  849:     Tab = schema_ops,
  850:     ?match({atomic, ok}, mnesia:create_table(Tab, Def)),
  851:     insert(Tab, 1002),
  852:     
  853:     Pid1 = spawn_link(Node1, ?MODULE, update_trans, [Tab, 1, 0]),
  854:     Pid2 = spawn_link(Node2, ?MODULE, update_trans, [Tab, 2, 0]),
  855:     Pid3 = spawn_link(Node3, ?MODULE, update_trans, [Tab, 3, 0]),
  856:     
  857:     ?match({atomic, ok}, rpc:call(CallFrom, mnesia, move_table_copy, 
  858: 				  [Tab, FromNode, ToNode])),
  859:     Pid1 ! {self(), quit}, 
  860:     R1 = receive {Pid1, Res1} -> Res1 after 5000 -> ?error("timeout pid1~n", []) end,
  861:     Pid2 ! {self(), quit}, 
  862:     R2 = receive {Pid2, Res2} -> Res2 after 5000 -> ?error("timeout pid2~n", []) end,
  863:     Pid3 ! {self(), quit}, 
  864:     R3 = receive {Pid3, Res3} -> Res3 after 5000 -> ?error("timeout pid3~n", []) end,
  865:     verify_oids(Tab, Node1, Node2, Node3, R1, R2, R3),
  866:     ?verify_mnesia([Node1, Node2, Node3], []).
  867: 
  868: % Verify consistency between different nodes
  869: % Due to limitations in the current dirty_ops this can wrong from time to time!
  870: verify_oids(Tab, N1, N2, N3, R1, R2, R3) ->
  871:     io:format("DEBUG 1=>~p 2=>~p 3=>~p~n", [R1,R2,R3]),
  872:     ?match([{_, _, _, R1}], rpc:call(N1, mnesia, dirty_read, [{Tab, 1}])),
  873:     ?match([{_, _, _, R1}], rpc:call(N2, mnesia, dirty_read, [{Tab, 1}])),
  874:     ?match([{_, _, _, R1}], rpc:call(N3, mnesia, dirty_read, [{Tab, 1}])),
  875:     ?match([{_, _, _, R2}], rpc:call(N1, mnesia, dirty_read, [{Tab, 2}])),
  876:     ?match([{_, _, _, R2}], rpc:call(N2, mnesia, dirty_read, [{Tab, 2}])),
  877:     ?match([{_, _, _, R2}], rpc:call(N3, mnesia, dirty_read, [{Tab, 2}])),
  878:     ?match([{_, _, _, R3}], rpc:call(N1, mnesia, dirty_read, [{Tab, 3}])),
  879:     ?match([{_, _, _, R3}], rpc:call(N2, mnesia, dirty_read, [{Tab, 3}])),
  880:     ?match([{_, _, _, R3}], rpc:call(N3, mnesia, dirty_read, [{Tab, 3}])).
  881: 
  882: insert(_Tab, 0) -> ok;
  883: insert(Tab, N) when N > 0 ->
  884:     ok = mnesia:sync_dirty(fun() -> false = mnesia:is_transaction(), mnesia:write({Tab, N, N, 0}) end),
  885:     insert(Tab, N-1).
  886: 
  887: 
  888: %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%