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