1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 1999-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_frag_test). 23-author('hakan@erix.ericsson.se'). 24-include("mnesia_test_lib.hrl"). 25 26-export([init_per_testcase/2, end_per_testcase/2, 27 init_per_group/2, end_per_group/2, 28 all/0, groups/0]). 29 30 31-export([nice_single/1, nice_multi/1, nice_access/1, iter_access/1, 32 consistency/1, evil_create/1, evil_delete/1, evil_change/1, evil_combine/1, 33 evil_loop/1, evil_delete_db_node/1]). 34 35 36-export([frag_dist/1]). 37 38init_per_testcase(Func, Conf) -> 39 mnesia_test_lib:init_per_testcase(Func, Conf). 40 41end_per_testcase(Func, Conf) -> 42 mnesia_test_lib:end_per_testcase(Func, Conf). 43 44-define(match_dist(ExpectedRes, Expr), 45 case ?match(ExpectedRes, Expr) of 46 47 mnesia_test_lib:error(Format, Args,?FILE,?LINE)). 48 49%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 50 51all() -> 52 [{group, light}, {group, medium}]. 53 54groups() -> 55 [{light, [], [{group, nice}, {group, evil}]}, 56 {medium, [], [consistency]}, 57 {nice, [], 58 [nice_single, nice_multi, nice_access, iter_access]}, 59 {evil, [], 60 [evil_create, evil_delete, evil_change, evil_combine, 61 evil_loop, evil_delete_db_node]}]. 62 63init_per_group(_GroupName, Config) -> 64 Config. 65 66end_per_group(_GroupName, Config) -> 67 Config. 68 69 70 71 72%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 73 74 75nice_single(suite) -> []; 76nice_single(Config) when is_list(Config) -> 77 [Node1, Node2] = Nodes = ?acquire_nodes(2, Config), 78 79 %% Create a table with 2 fragments and 12 records 80 Tab = nice_frag, 81 Props = [{n_fragments, 2}, {node_pool, [Node1]}], 82 ?match({atomic, ok}, mnesia:create_table(Tab, [{frag_properties, Props}])), 83 Records = [{Tab, N, -N} || N <- lists:seq(1, 12)], 84 [frag_write(Tab, R) || R <- Records], 85 ?match([{Node1, 2}], frag_dist(Tab)), 86 ?match([8, 4], frag_rec_dist(Tab)), 87 88 %% Adding a new node to pool should not affect distribution 89 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_node, Node2})), 90 Dist = frag_dist(Tab), 91 ?match([{Node2, 0}, {Node1, 2}], Dist), 92 ?match([8, 4], frag_rec_dist(Tab)), 93 94 %% Add new fragment hopefully on the new node 95 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist})), 96 Dist2 = frag_dist(Tab), 97 ?match([{Node2, 1}, {Node1, 2}], Dist2), 98 ?match([3, 4, 5], frag_rec_dist(Tab)), 99 100 %% Add new fragment hopefully on the new node 101 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist2})), 102 Dist3 = frag_dist(Tab), 103 ?match([{Node1, 2}, {Node2, 2}], Dist3), 104 ?match([3, 2, 5, 2], frag_rec_dist(Tab)), 105 106 %% Add new fragment hopefully on the new node 107 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist3})), 108 Dist4 = frag_dist(Tab), 109 ?match([{Node2, 2}, {Node1, 3}], Dist4), 110 ?match([_, _, _, _, _], frag_rec_dist(Tab)), 111 112 %% Dropping a node in pool should not affect distribution 113 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {del_node, Node1})), 114 ?match([{Node2, 2}, {Node1, 3}], frag_dist(Tab)), 115 ?match([_, _, _, _, _], frag_rec_dist(Tab)), 116 117 %% Dropping a fragment 118 ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)), 119 Dist5 = frag_dist(Tab), 120 ?match([{Node2, 2}, {Node1, 2}], Dist5), 121 ?match([3, 2, 5, 2], frag_rec_dist(Tab)), 122 123 %% Add new fragment hopefully on the new node 124 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist5})), 125 Dist6 = frag_dist(Tab), 126 ?match([{Node2, 3}, {Node1, 2}], Dist6), 127 ?match([_, _, _, _, _], frag_rec_dist(Tab)), 128 129 %% Dropping all fragments but one 130 ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)), 131 ?match([3, 2, 5, 2], frag_rec_dist(Tab)), 132 ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)), 133 ?match([3, 4, 5], frag_rec_dist(Tab)), 134 ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)), 135 ?match([8, 4], frag_rec_dist(Tab)), 136 ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)), 137 ?match([{Node2, 0}, {Node1, 1}], frag_dist(Tab)), 138 ?match([12], frag_rec_dist(Tab)), 139 140 %% Defragmenting the table clears frag_properties 141 ?match(Len when Len > 0, 142 length(mnesia:table_info(Tab, frag_properties))), 143 ?match({atomic, ok}, mnesia:change_table_frag(Tab, deactivate)), 144 ?match(0, length(mnesia:table_info(Tab, frag_properties))), 145 146 %% Making the table fragmented again 147 Props2 = [{n_fragments, 1}], 148 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {activate, Props2})), 149 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, frag_dist(Tab)})), 150 Dist7 = frag_dist(Tab), 151 ?match([{Node1, 1}, {Node2, 1}], Dist7), 152 ?match([8, 4], frag_rec_dist(Tab)), 153 154 %% Deleting the fragmented table 155 ?match({atomic, ok}, mnesia:delete_table(Tab)), 156 ?match(false, lists:member(Tab, mnesia:system_info(tables))), 157 158 ?verify_mnesia(Nodes, []). 159 160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 161 162nice_multi(doc) -> 163 ["Extending the nice case with one more node, ", 164 "one more replica and a foreign key"]; 165nice_multi(suite) -> []; 166nice_multi(Config) when is_list(Config) -> 167 [Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config), 168 169 %% Create a table with 2 fragments and 8 records 170 Tab = frag_master, 171 Name = frag_rec, 172 Type = case mnesia_test_lib:diskless(Config) of 173 true -> n_ram_copies; 174 false -> n_disc_copies 175 end, 176 Props = [{n_fragments, 2}, 177 {Type, 2}, 178 {node_pool, [Node2, Node1]}], 179 Def = [{frag_properties, Props}, 180 {attributes, [id, data]}, 181 {record_name, Name}], 182 ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 183 [frag_write(Tab, {Name, Id, -Id}) || Id <- lists:seq(1, 8)], 184 ?match([6, 2], frag_rec_dist(Tab)), 185 ?match([{Node2, 2}, {Node1, 2}], frag_dist(Tab)), 186 187 %% And connect another table to it, via a foreign key 188 TabF = frag_slave, 189 PropsF = [{foreign_key, {Tab, foreign_id}}], 190 DefF = [{frag_properties, PropsF}, 191 {attributes, [id, foreign_id]}], 192 193 ?match({atomic, ok}, mnesia:create_table(TabF, DefF)), 194 [frag_write(TabF, {TabF, {Id}, Id}) || Id <- lists:seq(1, 16)], 195 ?match([10, 6], frag_rec_dist(TabF)), 196 ?match([{Node2, 2}, {Node1, 2}], frag_dist(TabF)), 197 198 %% Adding a new node to pool should not affect distribution 199 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_node, Node3})), 200 Dist = frag_dist(Tab), 201 ?match([{Node3, 0}, {Node2, 2}, {Node1, 2}], Dist), 202 ?match([6, 2], frag_rec_dist(Tab)), 203 DistF = frag_dist(TabF), 204 ?match([{Node3, 0}, {Node2, 2}, {Node1, 2}], DistF), 205 ?match([10, 6], frag_rec_dist(TabF)), 206 207 %% Add new fragment hopefully on the new node 208 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist})), 209 Dist2 = frag_dist(Tab), 210 ?match([{Node3, 1},{Node1, 2},{Node2,3}], Dist2), 211 ?match([_, _, _], frag_rec_dist(Tab)), 212 DistF2 = frag_dist(TabF), 213 ?match([{Node3, 1},{Node1, 2},{Node2,3}], DistF2), 214 ?match([_, _, _], frag_rec_dist(TabF)), 215 216 %% Add new fragment hopefully on the new node 217 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist2})), 218 Dist3 = frag_dist(Tab), 219 ?match([{Node3, 2},{Node2,3},{Node1, 3}], Dist3), 220 ?match([3, 0, 3, 2], frag_rec_dist(Tab)), 221 DistF3 = frag_dist(TabF), 222 ?match([{Node3, 2},{Node2,3},{Node1, 3}], DistF3), 223 ?match([3, 3, 7, 3], frag_rec_dist(TabF)), 224 225 %% Add new fragment hopefully on the new node 226 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist3})), 227 Dist4 = frag_dist(Tab), 228 ?match([{Node1, 3}, {Node3, 3},{Node2, 4}], Dist4), 229 ?match([_, _, _, _, _], frag_rec_dist(Tab)), 230 DistF4 = frag_dist(TabF), 231 ?match([{Node1, 3}, {Node3, 3},{Node2, 4}], DistF4), 232 ?match([_, _, _, _, _], frag_rec_dist(TabF)), 233 234 %% Dropping a node in pool should not affect distribution 235 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {del_node, Node1})), 236 ?match([{Node3, 3},{Node2, 4}, {Node1, 3}], frag_dist(Tab)), 237 ?match([_, _, _, _, _], frag_rec_dist(Tab)), 238 ?match([{Node3, 3},{Node2, 4}, {Node1, 3}], frag_dist(TabF)), 239 ?match([_, _, _, _, _], frag_rec_dist(TabF)), 240 241 %% Dropping a fragment 242 ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)), 243 Dist5 = frag_dist(Tab), 244 ?match([{Node3, 2},{Node2,3},{Node1, 3}], Dist5), 245 ?match([3, 0, 3, 2], frag_rec_dist(Tab)), 246 DistF5 = frag_dist(Tab), 247 ?match([{Node3, 2},{Node2,3},{Node1, 3}], DistF5), 248 ?match([3, 3, 7, 3], frag_rec_dist(TabF)), 249 250 %% Add new fragment hopefully on the new node 251 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist5})), 252 Dist6 = frag_dist(Tab), 253 ?match([{Node3, 3},{Node2, 4},{Node1, 3}], Dist6), 254 ?match([_, _, _, _, _], frag_rec_dist(Tab)), 255 DistF6 = frag_dist(TabF), 256 ?match([{Node3, 3},{Node2, 4},{Node1, 3}], DistF6), 257 ?match([_, _, _, _, _], frag_rec_dist(TabF)), 258 259 %% Dropping all fragments but one 260 ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)), 261 ?match([3, 0, 3, 2], frag_rec_dist(Tab)), 262 ?match([3, 3, 7, 3], frag_rec_dist(TabF)), 263 ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)), 264 ?match([_, _, _], frag_rec_dist(Tab)), 265 ?match([_, _, _], frag_rec_dist(TabF)), 266 ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)), 267 ?match([6, 2], frag_rec_dist(Tab)), 268 ?match([10, 6], frag_rec_dist(TabF)), 269 ?match({atomic, ok}, mnesia:change_table_frag(Tab, del_frag)), 270 ?match([{Node3, 0}, {Node2, 1}, {Node1, 1}], frag_dist(Tab)), 271 ?match([8], frag_rec_dist(Tab)), 272 ?match([{Node3, 0}, {Node2, 1}, {Node1, 1}], frag_dist(TabF)), 273 ?match([16], frag_rec_dist(TabF)), 274 275 %% Defragmenting the tables clears frag_properties 276 ?match(Len when Len > 0, 277 length(mnesia:table_info(TabF, frag_properties))), 278 ?match({atomic, ok}, mnesia:change_table_frag(TabF, deactivate)), 279 ?match(0, length(mnesia:table_info(TabF, frag_properties))), 280 ?match(Len when Len > 0, 281 length(mnesia:table_info(Tab, frag_properties))), 282 ?match({atomic, ok}, mnesia:change_table_frag(Tab, deactivate)), 283 ?match(0, length(mnesia:table_info(Tab, frag_properties))), 284 285 %% Making the tables fragmented again 286 Props2 = [{n_fragments, 1}, {node_pool, [Node1, Node2]}], 287 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {activate, Props2})), 288 ?match({atomic, ok}, mnesia:delete_table(TabF)), 289 ?match({atomic, ok}, mnesia:create_table(TabF, DefF)), 290 [frag_write(TabF, {TabF, {Id}, Id}) || Id <- lists:seq(1, 16)], 291 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, frag_dist(Tab)})), 292 ?match([{Node1, 2}, {Node2, 2}], frag_dist(Tab)), 293 ?match([6, 2], frag_rec_dist(Tab)), 294 ?match([{Node1, 2}, {Node2, 2}], frag_dist(TabF)), 295 ?match([10, 6], frag_rec_dist(TabF)), 296 297 %% Deleting the fragmented tables 298 ?match({atomic, ok}, mnesia:delete_table(TabF)), 299 ?match(false, lists:member(TabF, mnesia:system_info(tables))), 300 ?match({atomic, ok}, mnesia:delete_table(Tab)), 301 ?match(false, lists:member(Tab, mnesia:system_info(tables))), 302 303 ?verify_mnesia(Nodes, []). 304 305%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 306 307nice_access(doc) -> 308 ["Cover entire callback interface"]; 309nice_access(suite) -> []; 310nice_access(Config) when is_list(Config) -> 311 Nodes = ?acquire_nodes(3, Config), 312 313 Tab = frag_access, 314 Pool = lists:sort(Nodes), 315 Props = [{n_fragments, 20}, 316 {n_ram_copies, 2}, 317 {node_pool, Pool}], 318 Def = [{frag_properties, Props}, 319 {type, ordered_set}, 320 {index, [val]}], 321 ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 322 [frag_write(Tab, {Tab, Id, Id}) || Id <- lists:seq(1, 400)], 323 324 %% And connect another table to it, via a foreign key 325 TabF = frag_access_slave, 326 PropsF = [{foreign_key, {Tab, val}}], 327 DefF = [{frag_properties, PropsF}, 328 {index, [val]}], 329 ?match({atomic, ok}, mnesia:create_table(TabF, DefF)), 330 [frag_write(TabF, {TabF, Id, Id}) || Id <- lists:seq(1, 400)], 331 332 ?match(done, mnesia:activity(transaction, fun do_access/3, [Tab, Tab, Pool], mnesia_frag)), 333 ?match(done, mnesia:activity(transaction, fun do_access/3, [TabF, Tab, Pool], mnesia_frag)), 334 335 ?verify_mnesia(Nodes, []). 336 337do_access(Tab, Master, Pool) -> 338 ?match(20, mnesia:table_info(Tab, n_fragments)), 339 ?match(Pool, mnesia:table_info(Tab, node_pool)), 340 ?match(2, mnesia:table_info(Tab, n_ram_copies)), 341 ?match(0, mnesia:table_info(Tab, n_disc_copies)), 342 ?match(0, mnesia:table_info(Tab, n_disc_only_copies)), 343 ?match(20, length(mnesia:table_info(Tab, frag_names))), 344 ?match(20, length(mnesia:table_info(Tab, frag_size))), 345 ?match(20, length(mnesia:table_info(Tab, frag_memory))), 346 PoolSize = length(Pool), 347 ?match(PoolSize, length(mnesia:table_info(Tab, frag_dist))), 348 ?match(400, mnesia:table_info(Tab, size)), 349 ?match(I when is_integer(I), mnesia:table_info(Tab, memory)), 350 ?match(Tab, mnesia:table_info(Tab, base_table)), 351 352 Foreign = 353 if 354 Master == Tab -> 355 ?match(undefined, mnesia:table_info(Tab, foreign_key)), 356 ?match([_], mnesia:table_info(Tab, foreigners)), 357 ?match({'EXIT', {aborted, {combine_error, Tab, frag_properties, {foreign_key, undefined}}}}, 358 mnesia:read({Tab, 5}, 5, read)), 359 fun({T, _K}) -> T end; 360 true -> 361 ?match({Master, 3}, mnesia:table_info(Tab, foreign_key)), 362 ?match([], mnesia:table_info(Tab, foreigners)), 363 fun({T, K}) -> {T, K} end 364 end, 365 366 Attr = val, 367 ?match(400, mnesia:table_info(Tab, size)), 368 Count = fun(_, N) -> N + 1 end, 369 ?match(400, mnesia:foldl(Count, 0, Tab)), 370 ?match(400, mnesia:foldr(Count, 0, Tab)), 371 ?match(ok, mnesia:write({Tab, [-1], 1})), 372 ?match(401, length(mnesia:match_object(Tab, {Tab, '_', '_'}, read))), 373 ?match(401, length(mnesia:select(Tab, [{{Tab, '_', '$1'}, [], ['$1']}], read))), 374 375 First = mnesia:select(Tab, [{{Tab, '_', '$1'}, [], ['$1']}], 10, read), 376 TestCont = fun('$end_of_table', Total, _This) -> 377 Total; 378 ({Res,Cont1}, Total, This) -> 379 Cont = mnesia:select(Cont1), 380 This(Cont, length(Res) + Total, This) 381 end, 382 ?match(401, TestCont(First, 0, TestCont)), 383 384 %% OTP 385 [_, Frag2|_] = frag_names(Tab), 386 Frag2key = mnesia:dirty_first(Frag2), 387 ?match({[Frag2key],_},mnesia:select(Tab,[{{Tab,Frag2key,'$1'},[],['$1']}],100,read)), 388 389 ?match([{Tab, [-1], 1}], mnesia:read(Foreign({Tab, 1}), [-1], read)), 390 ?match(401, mnesia:foldl(Count, 0, Tab)), 391 ?match(401, mnesia:foldr(Count, 0, Tab)), 392 ?match(ok, mnesia:delete(Foreign({Tab, 2}), 2, write)), 393 ?match([], mnesia:read(Foreign({Tab, 2}), 2, read)), 394 ?match([{Tab, 3, 3}], mnesia:read(Foreign({Tab, 3}), 3, read)), 395 ?match(400, mnesia:foldl(Count, 0, Tab)), 396 ?match(400, mnesia:foldr(Count, 0, Tab)), 397 ?match(ok, mnesia:delete_object({Tab, 3, 3})), 398 ?match([], mnesia:read(Foreign({Tab, 3}), 3, read)), 399 One = lists:sort([{Tab, 1, 1}, {Tab, [-1], 1}]), 400 Pat = {Tab, '$1', 1}, 401 ?match(One, lists:sort(mnesia:match_object(Tab, Pat, read))), 402 ?match([1,[-1]], lists:sort(mnesia:select(Tab, [{Pat, [], ['$1']}], read))), 403 ?match([[[-1]]], lists:sort(mnesia:select(Tab, [{Pat, [{is_list, '$1'}], [['$1']]}], read))), 404 ?match([[1, 100]], lists:sort(mnesia:select(Tab, [{Pat, [{is_integer, '$1'}], [['$1',100]]}], read))), 405 ?match([1,[-1]], lists:sort(mnesia:select(Tab, [{Pat, [{is_list, '$1'}], ['$1']},{Pat, [{is_integer, '$1'}], ['$1']}], read))), 406 ?match(One, lists:sort(mnesia:index_match_object(Tab, Pat, Attr, read) )), 407 ?match(One, lists:sort(mnesia:index_read(Tab, 1, Attr))), 408 Keys = mnesia:all_keys(Tab), 409 ?match([-1], lists:max(Keys)), %% OTP-3779 410 ?match(399, length(Keys)), 411 ?match(399, mnesia:foldl(Count, 0, Tab)), 412 ?match(399, mnesia:foldr(Count, 0, Tab)), 413 414 ?match(Pool, lists:sort(mnesia:lock({table, Tab}, write))), 415 416 done. 417 418%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 419 420iter_access(doc) -> 421 ["Cover table iteration via callback interface"]; 422iter_access(suite) -> []; 423iter_access(Config) when is_list(Config) -> 424 Nodes = ?acquire_nodes(3, Config), 425 426 Tab = frag_access, 427 Pool = lists:sort(Nodes), 428 Props = [{n_fragments, 20}, 429 {n_ram_copies, 2}, 430 {node_pool, Pool}], 431 Def = [{frag_properties, Props}, 432 {type, ordered_set}, 433 {index, [val]}], 434 ?match({atomic, ok}, mnesia:create_table(Tab, Def)), 435 [frag_write(Tab, {Tab, Id, Id}) || Id <- lists:seq(1, 400)], 436 437 FragNames = frag_names(Tab), 438 RawRead = 439 fun(Frag) -> 440 Node = mnesia:table_info(Frag, where_to_read), 441 {Frag, rpc:call(Node, ets, tab2list, [Frag])} 442 end, 443 444 ?match(done, mnesia:activity(transaction, fun nice_iter_access/3, [Tab, FragNames, RawRead], mnesia_frag)), 445 446 FragNames = frag_names(Tab), 447 [First, Second | _] = FragNames, 448 [Last, LastButOne | _] = lists:reverse(FragNames), 449 450 ?match({atomic, ok}, mnesia:clear_table(First)), 451 ?match({atomic, ok}, mnesia:clear_table(Second)), 452 ?match({atomic, ok}, mnesia:clear_table(lists:nth(8, FragNames))), 453 ?match({atomic, ok}, mnesia:clear_table(lists:nth(9, FragNames))), 454 ?match({atomic, ok}, mnesia:clear_table(lists:nth(10, FragNames))), 455 ?match({atomic, ok}, mnesia:clear_table(lists:nth(11, FragNames))), 456 ?match({atomic, ok}, mnesia:clear_table(LastButOne)), 457 ?match({atomic, ok}, mnesia:clear_table(Last)), 458 459 ?match(done, mnesia:activity(transaction, fun evil_iter_access/3, [Tab, FragNames, RawRead], mnesia_frag)), 460 Size = fun(Table) -> mnesia:table_info(Table, size) end, 461 ?match(true, 0 < mnesia:activity(transaction, Size, [Tab], mnesia_frag)), 462 ?match({atomic, ok}, mnesia:activity(ets, fun() -> mnesia:clear_table(Tab) end, mnesia_frag)), 463 ?match(0, mnesia:activity(transaction, Size, [Tab], mnesia_frag)), 464 465 ?verify_mnesia(Nodes, []). 466 467nice_iter_access(Tab, FragNames, RawRead) -> 468 RawData = ?ignore(lists:map(RawRead, FragNames)), 469 Keys = [K || {_, Recs} <- RawData, {_, K, _} <- Recs], 470 ExpectedFirst = hd(Keys), 471 ?match(ExpectedFirst, mnesia:first(Tab)), 472 ExpectedLast = lists:last(Keys), 473 ?match(ExpectedLast, mnesia:last(Tab)), 474 475 ExpectedAllPrev = ['$end_of_table' | lists:droplast(Keys)], 476 ?match(ExpectedAllPrev, lists:map(fun(K) -> mnesia:prev(Tab, K) end, Keys)), 477 478 ExpectedAllNext = tl(Keys) ++ ['$end_of_table'], 479 ?match(ExpectedAllNext, lists:map(fun(K) -> mnesia:next(Tab, K) end, Keys)), 480 481 done. 482 483evil_iter_access(Tab, FragNames, RawRead) -> 484 RawData = ?ignore(lists:map(RawRead, FragNames)), 485 Keys = [K || {_, Recs} <- RawData, {_, K, _} <- Recs], 486 ExpectedFirst = hd(Keys), 487 ?match(ExpectedFirst, mnesia:first(Tab)), 488 ExpectedLast = lists:last(Keys), 489 ?match(ExpectedLast, mnesia:last(Tab)), 490 491 ExpectedAllPrev = ['$end_of_table' | lists:droplast(Keys)], 492 ?match(ExpectedAllPrev, lists:map(fun(K) -> mnesia:prev(Tab, K) end, Keys)), 493 494 ExpectedAllNext = tl(Keys) ++ ['$end_of_table'], 495 ?match(ExpectedAllNext, lists:map(fun(K) -> mnesia:next(Tab, K) end, Keys)), 496 497 done. 498 499 500%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 501 502consistency(doc) -> 503 ["Add and delete fragments during TPC-B"]; 504consistency(suite) -> []; 505consistency(Config) when is_list(Config) -> 506 ?skip("Not yet implemented (NYI).~n", []), 507 Nodes = ?acquire_nodes(2, Config), 508 ?verify_mnesia(Nodes, []). 509 510%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 511 512 513evil_create(suite) -> []; 514evil_create(Config) when is_list(Config) -> 515 [Node1, _Node2] = Nodes = ?acquire_nodes(2, Config), 516 517 Create = fun(T, D, P) -> mnesia:create_table(T, [{frag_properties, P}| D]) end, 518 519 Tab = evil_create, 520 %% Props in general 521 ?match({aborted, {badarg, Tab, {frag_properties, no_list}}}, 522 Create(Tab, [], no_list)), 523 ?match({aborted, {badarg,Tab , [no_tuple]}}, 524 Create(Tab, [], [no_tuple])), 525 ?match({aborted,{badarg, Tab, bad_key}}, 526 Create(Tab, [], [{bad_key, 7}])), 527 528 %% n_fragments 529 ?match({aborted,{badarg, Tab, [{n_fragments}]}}, 530 Create(Tab, [], [{n_fragments}])), 531 ?match({aborted,{badarg, Tab, [{n_fragments, 1, 1}]}}, 532 Create(Tab, [], [{n_fragments, 1, 1}])), 533 ?match({aborted, {bad_type,Tab, {n_fragments, a}}}, 534 Create(Tab, [], [{n_fragments, a}])), 535 ?match({aborted, {bad_type, Tab, {n_fragments, 0}}}, 536 Create(Tab, [], [{n_fragments, 0}])), 537 538 %% *_copies 539 ?match({aborted, {bad_type, Tab, {n_ram_copies, -1}}}, 540 Create(Tab, [], [{n_ram_copies, -1}, {n_fragments, 1}])), 541 ?match({aborted, {bad_type, Tab, {n_disc_copies, -1}}}, 542 Create(Tab, [], [{n_disc_copies, -1}, {n_fragments, 1}])), 543 ?match({aborted, {bad_type, Tab, {n_disc_only_copies, -1}}}, 544 Create(Tab, [], [{n_disc_only_copies, -1}, {n_fragments, 1}])), 545 546 %% node_pool 547 ?match({aborted, {bad_type, Tab, {node_pool, 0}}}, 548 Create(Tab, [], [{node_pool, 0}])), 549 ?match({aborted, {combine_error, Tab, "Too few nodes in node_pool"}}, 550 Create(Tab, [], [{n_ram_copies, 2}, {node_pool, [Node1]}])), 551 552 %% foreign_key 553 ?match({aborted, {bad_type, Tab, {foreign_key, bad_key}}}, 554 Create(Tab, [], [{foreign_key, bad_key}])), 555 ?match({aborted,{bad_type, Tab, {foreign_key, {bad_key}}}}, 556 Create(Tab, [], [{foreign_key, {bad_key}}])), 557 ?match({aborted, {no_exists, {bad_tab, frag_properties}}}, 558 Create(Tab, [], [{foreign_key, {bad_tab, val}}])), 559 ?match({aborted, {combine_error, Tab, {Tab, val}}}, 560 Create(Tab, [], [{foreign_key, {Tab, val}}])), 561 ?match({atomic, ok}, 562 Create(Tab, [], [{n_fragments, 1}])), 563 564 ?match({aborted, {already_exists, Tab}}, 565 Create(Tab, [], [{n_fragments, 1}])), 566 567 Tab2 = evil_create2, 568 ?match({aborted, {bad_type, no_attr}}, 569 Create(Tab2, [], [{foreign_key, {Tab, no_attr}}])), 570 ?match({aborted, {combine_error, Tab2, _, _, _}}, 571 Create(Tab2, [], [{foreign_key, {Tab, val}}, 572 {node_pool, [Node1]}])), 573 ?match({aborted, {combine_error, Tab2, _, _, _}}, 574 Create(Tab2, [], [{foreign_key, {Tab, val}}, 575 {n_fragments, 2}])), 576 ?match({atomic, ok}, 577 Create(Tab2, [{attributes, [a, b, c]}], [{foreign_key, {Tab, c}}])), 578 Tab3 = evil_create3, 579 ?match({aborted, {combine_error, Tab3, _, _, _}}, 580 Create(Tab3, [{attributes, [a, b]}], [{foreign_key, {Tab2, b}}])), 581 ?match({atomic, ok}, 582 Create(Tab3, [{attributes, [a, b]}], [{foreign_key, {Tab, b}}])), 583 584 ?verify_mnesia(Nodes, []). 585 586%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 587 588evil_delete(suite) -> []; 589evil_delete(Config) when is_list(Config) -> 590 ?skip("Not yet implemented (NYI).~n", []), 591 Nodes = ?acquire_nodes(2, Config), 592 ?verify_mnesia(Nodes, []). 593 594%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 595 596evil_change(suite) -> []; 597evil_change(Config) when is_list(Config) -> 598 [N1,N2,_N3] = Nodes = ?acquire_nodes(3, Config), 599 Create = fun(T, D, P) -> mnesia:create_table(T, [{frag_properties, P}| D]) end, 600 Props1 = [{n_fragments, 2}, {node_pool, [N1]}], 601 Tab1 = evil_change_ram, 602 ?match({atomic, ok}, Create(Tab1, [], Props1)), 603 604 ?match({atomic,ok}, mnesia:change_table_frag(Tab1, {add_frag, Nodes})), 605 Dist10 = frag_dist(Tab1), 606 ?match([{N1,3}], Dist10), 607 ?match({atomic, ok}, mnesia:change_table_frag(Tab1, {add_node, N2})), 608 Dist11 = frag_dist(Tab1), 609 ?match([{N2,0},{N1,3}], Dist11), 610 mnesia_test_lib:kill_mnesia([N2]), 611 ?match({aborted,_}, mnesia:change_table_frag(Tab1, {add_frag, [N2,N1]})), 612 ?verbose("~p~n",[frag_dist(Tab1)]), 613 mnesia_test_lib:start_mnesia([N2]), 614 615 Tab2 = evil_change_disc, 616 ?match({atomic,ok}, Create(Tab2,[],[{n_disc_copies,1},{n_fragments,1},{node_pool,[N1,N2]}])), 617 ?verbose("~p~n", [frag_dist(Tab2)]), 618 ?match({atomic,ok}, mnesia:change_table_frag(Tab2, {add_frag, [N1,N2]})), 619 _Dist20 = frag_dist(Tab2), 620 mnesia_test_lib:kill_mnesia([N2]), 621 ?match({atomic,ok}, mnesia:change_table_frag(Tab2, {add_frag, [N1,N2]})), 622 ?match({aborted,_}, mnesia:change_table_frag(Tab2, {add_frag, [N2,N1]})), 623 624 mnesia_test_lib:start_mnesia([N2]), 625 ?verify_mnesia(Nodes, []). 626 627%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 628 629evil_combine(doc) -> ["Bug in mnesia_4.1.5. and earlier"]; 630evil_combine(suite) -> []; 631evil_combine(Config) when is_list(Config) -> 632 [Node1] = Nodes = ?acquire_nodes(1, Config), 633 ?match({atomic, ok},mnesia:create_table(tab1, [{disc_copies, [Node1]}, 634 {frag_properties, [{n_fragments, 2}, 635 {node_pool, [Node1]}, 636 {n_disc_copies, 1}]}])), 637 ?match({atomic, ok},mnesia:create_table(tab2, [{disc_copies, [Node1]}])), 638 mnesia:wait_for_tables([tab1, tab2], infinity), 639 640 Add2 = fun() -> 641 mnesia:transaction(fun() -> 642 mnesia:write({tab2,1,1}) 643 end) 644 end, 645 Fun = fun() -> 646 Add2(), 647 mnesia:write({tab1,9,10}) 648 end, 649 ?match(ok, mnesia:activity({transaction, 1}, Fun, [], mnesia_frag)), 650 651 Read = fun(T, K) -> 652 mnesia:read(T, K, read) 653 end, 654 655 ?match([{tab1,9,10}],mnesia:activity(async_dirty, Read, [tab1, 9], mnesia_frag)), 656 ?match([{tab2,1,1}],mnesia:activity(async_dirty, Read, [tab2, 1], mnesia_frag)), 657 658 ?verify_mnesia(Nodes, []). 659 660%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 661 662evil_loop(doc) -> ["Test select/[14]"]; 663evil_loop(suite) -> []; 664evil_loop(Config) when is_list(Config) -> 665 [Node1,_Node2] = ?acquire_nodes(2, Config), 666 Tab1 = ss_oset, 667 Tab2 = ss_set, 668 Tab3 = ss_bag, 669 Tabs = [Tab1, Tab2, Tab3], 670 RecName = ss, 671 ?match({atomic, ok}, mnesia:create_table([{name, Tab1}, 672 {ram_copies, [Node1]}, 673 {record_name, RecName}, 674 {type, ordered_set}])), 675 ?match({atomic, ok}, mnesia:create_table([{name, Tab2}, 676 {record_name, RecName}, 677 {ram_copies, [Node1]}, 678 {type, set}])), 679 ?match({atomic, ok}, mnesia:create_table([{name, Tab3}, 680 {record_name, RecName}, 681 {ram_copies, [Node1]}, 682 {type, bag}])), 683 Keys = [-3, -2] ++ lists:seq(1, 5, 2) ++ lists:seq(6, 10), 684 Recs = [{RecName, K, K} || K <- Keys], 685 [mnesia:dirty_write(Tab1, R) || R <- Recs], 686 [mnesia:dirty_write(Tab2, R) || R <- Recs], 687 [mnesia:dirty_write(Tab3, R) || R <- Recs], 688 689 Activate = 690 fun(Tab) -> 691 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {activate, []})), 692 Dist = frag_dist(Tab), 693 ?match({atomic, ok}, mnesia:change_table_frag(Tab, {add_frag, Dist})) 694 end, 695 696 Activate(Tab1), 697 Activate(Tab2), 698 Activate(Tab3), 699 700 Match = fun(Tab) -> mnesia:match_object(Tab, {'_', '_', '_'}, write) end, 701 Select = fun(Tab) -> mnesia:select(Tab, [{'_', [], ['$_']}]) end, 702 Trans = fun(Fun, Args) -> mnesia:activity(transaction, Fun, Args, mnesia_frag) end, 703 LoopHelp = fun('$end_of_table',_) -> 704 []; 705 ({Res,Cont},Fun) -> 706 Sel = mnesia:select(Cont), 707 Res ++ Fun(Sel, Fun) 708 end, 709 SelLoop = fun(Table) -> 710 Sel = mnesia:select(Table, [{'_', [], ['$_']}], 1, read), 711 LoopHelp(Sel, LoopHelp) 712 end, 713 714 R1 = {RecName, 2, 2}, 715 R2 = {RecName, 4, 4}, 716 R3 = {RecName, 2, 3}, 717 R4 = {RecName, 3, 1}, 718 R5 = {RecName, 104, 104}, 719 W1 = fun(Tab,Search) -> 720 mnesia:write(Tab,R1,write), 721 mnesia:write(Tab,R2,write), 722 Search(Tab) 723 end, 724 S1 = lists:sort([R1, R2| Recs]), 725 ?match(S1, sort_res(Trans(W1, [Tab1, Select]))), 726 ?match(S1, sort_res(Trans(W1, [Tab1, Match]))), 727 ?match(S1, sort_res(Trans(W1, [Tab1, SelLoop]))), 728 ?match(S1, sort_res(Trans(W1, [Tab2, Select]))), 729 ?match(S1, sort_res(Trans(W1, [Tab2, SelLoop]))), 730 ?match(S1, sort_res(Trans(W1, [Tab2, Match]))), 731 ?match(S1, sort_res(Trans(W1, [Tab3, Select]))), 732 ?match(S1, sort_res(Trans(W1, [Tab3, SelLoop]))), 733 ?match(S1, sort_res(Trans(W1, [Tab3, Match]))), 734 [mnesia:dirty_delete_object(Frag, R) || R <- [R1, R2], 735 Tab <- Tabs, 736 Frag <- frag_names(Tab)], 737 738 W2 = fun(Tab, Search) -> 739 mnesia:write(Tab, R3, write), 740 mnesia:write(Tab, R1, write), 741 Search(Tab) 742 end, 743 S2 = lists:sort([R1 | Recs]), 744 S2Bag = lists:sort([R1, R3 | Recs]), 745 io:format("S2 = ~p\n", [S2]), 746 ?match(S2, sort_res(Trans(W2, [Tab1, Select]))), 747 ?match(S2, sort_res(Trans(W2, [Tab1, SelLoop]))), 748 ?match(S2, sort_res(Trans(W2, [Tab1, Match]))), 749 ?match(S2, sort_res(Trans(W2, [Tab2, Select]))), 750 ?match(S2, sort_res(Trans(W2, [Tab2, SelLoop]))), 751 ?match(S2, sort_res(Trans(W2, [Tab2, Match]))), 752 io:format("S2Bag = ~p\n", [S2Bag]), 753 ?match(S2Bag, sort_res(Trans(W2, [Tab3, Select]))), 754 ?match(S2Bag, sort_res(Trans(W2, [Tab3, SelLoop]))), 755 ?match(S2Bag, sort_res(Trans(W2, [Tab3, Match]))), 756 757 W3 = fun(Tab,Search) -> 758 mnesia:write(Tab, R4, write), 759 mnesia:delete(Tab, element(2, R1), write), 760 Search(Tab) 761 end, 762 S3Bag = lists:sort([R4 | lists:delete(R1, Recs)]), 763 S3 = lists:delete({RecName, 3, 3}, S3Bag), 764 ?match(S3, sort_res(Trans(W3, [Tab1, Select]))), 765 ?match(S3, sort_res(Trans(W3, [Tab1, SelLoop]))), 766 ?match(S3, sort_res(Trans(W3, [Tab1, Match]))), 767 ?match(S3, sort_res(Trans(W3, [Tab2, SelLoop]))), 768 ?match(S3, sort_res(Trans(W3, [Tab2, Select]))), 769 ?match(S3, sort_res(Trans(W3, [Tab2, Match]))), 770 ?match(S3Bag, sort_res(Trans(W3, [Tab3, Select]))), 771 ?match(S3Bag, sort_res(Trans(W3, [Tab3, SelLoop]))), 772 ?match(S3Bag, sort_res(Trans(W3, [Tab3, Match]))), 773 774 W4 = fun(Tab,Search) -> 775 mnesia:delete(Tab, -1, write), 776 mnesia:delete(Tab, 4 , write), 777 mnesia:delete(Tab, 17, write), 778 mnesia:delete_object(Tab, {RecName, -1, x}, write), 779 mnesia:delete_object(Tab, {RecName, 4, x}, write), 780 mnesia:delete_object(Tab, {RecName, 42, x}, write), 781 mnesia:delete_object(Tab, R2, write), 782 mnesia:write(Tab, R5, write), 783 Search(Tab) 784 end, 785 S4Bag = lists:sort([R5 | S3Bag]), 786 S4 = lists:sort([R5 | S3]), 787 ?match(S4, sort_res(Trans(W4, [Tab1, Select]))), 788 ?match(S4, sort_res(Trans(W4, [Tab1, SelLoop]))), 789 ?match(S4, sort_res(Trans(W4, [Tab1, Match]))), 790 ?match(S4, sort_res(Trans(W4, [Tab2, Select]))), 791 ?match(S4, sort_res(Trans(W4, [Tab2, SelLoop]))), 792 ?match(S4, sort_res(Trans(W4, [Tab2, Match]))), 793 ?match(S4Bag, sort_res(Trans(W4, [Tab3, Select]))), 794 ?match(S4Bag, sort_res(Trans(W4, [Tab3, SelLoop]))), 795 ?match(S4Bag, sort_res(Trans(W4, [Tab3, Match]))), 796 [mnesia:dirty_delete_object(Tab, R) || R <- [{RecName, 3, 3}, R5], Tab <- Tabs], 797 798 %% hmmm anything more?? 799 800 ?verify_mnesia([Node1], []). 801 802%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 803 804evil_delete_db_node(doc) -> 805 ["Delete db_node with a repicated table with foreign key"]; 806evil_delete_db_node(suite) -> []; 807evil_delete_db_node(Config) when is_list(Config) -> 808 Nodes = lists:sort(?acquire_nodes(2, Config)), 809 Local = node(), 810 Remote = hd(Nodes -- [Local]), 811 812 Type = case mnesia_test_lib:diskless(Config) of 813 true -> n_ram_copies; 814 false -> n_disc_copies 815 end, 816 Tab = frag_master, 817 ?match({atomic, ok}, mnesia:create_table(Tab, [{frag_properties, [{Type, 2}, {node_pool, Nodes}]}])), 818 ExtraTab = frag_foreigner, 819 ?match({atomic, ok}, mnesia:create_table(ExtraTab, [{frag_properties, [{foreign_key, {Tab, key}}, {node_pool, Nodes}]}])), 820 821 GetPool = fun(T) -> 822 case lists:keysearch(node_pool, 1, mnesia:table_info (T, frag_properties)) of 823 {value, {node_pool, N}} -> lists:sort(N); 824 false -> [] 825 end 826 end, 827 ?match(Nodes, GetPool(Tab)), 828 ?match(Nodes, GetPool(ExtraTab)), 829 830 831 ?match(stopped, rpc:call(Remote, mnesia, stop, [])), 832 ?match({atomic, ok}, mnesia:del_table_copy(schema, Remote)), 833 834 ?match([Local], GetPool(Tab)), 835 ?match([Local], GetPool(ExtraTab)), 836 837 ?verify_mnesia([Local], []). 838 839%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 840%% Misc convenient helpers 841 842frag_write(Tab, Rec) -> 843 Fun = fun() -> mnesia:write(Tab, Rec, write) end, 844 mnesia:activity(sync_dirty, Fun, mnesia_frag). 845 846frag_dist(Tab) -> 847 Fun = fun() -> mnesia:table_info(Tab, frag_dist) end, 848 mnesia:activity(sync_dirty, Fun, mnesia_frag). 849 850frag_names(Tab) -> 851 Fun = fun() -> mnesia:table_info(Tab, frag_names) end, 852 mnesia:activity(sync_dirty, Fun, mnesia_frag). 853 854frag_rec_dist(Tab) -> 855 Fun = fun() -> mnesia:table_info(Tab, frag_size) end, 856 [Size || {_, Size} <- mnesia:activity(sync_dirty, Fun, mnesia_frag)]. 857 858sort_res(List) when is_list(List) -> 859 lists:sort(List); 860sort_res(Else) -> 861 Else. 862