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