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