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