4
%% Copyright Ericsson AB 2004-2010. All Rights Reserved.
6
%% The contents of this file are subject to the Erlang Public License,
7
%% Version 1.1, (the "License"); you may not use this file except in
8
%% compliance with the License. You should have received a copy of the
9
%% Erlang Public License along with this software. If not, it can be
10
%% retrieved online at http://www.erlang.org/.
12
%% Software distributed under the License is distributed on an "AS IS"
13
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
14
%% the License for the specific language governing rights and limitations
21
-module(mnesia_qlc_test).
25
-export([all/0,groups/0,init_per_group/2,end_per_group/2]).
27
-include("mnesia_test_lib.hrl").
28
-include_lib("stdlib/include/qlc.hrl").
30
init_per_testcase(Func, Conf) ->
32
mnesia_test_lib:init_per_testcase(Func, Conf).
34
end_per_testcase(Func, Conf) ->
35
mnesia_test_lib:end_per_testcase(Func, Conf).
38
case code:which(qlc) of
45
[dirty_nice_ram_copies, dirty_nice_disc_copies,
46
dirty_nice_disc_only_copies]},
48
[trans_nice_ram_copies, trans_nice_disc_copies,
49
trans_nice_disc_only_copies, {group, atomic}]},
50
{atomic, [], [atomic_eval]}].
52
init_per_group(_GroupName, Config) ->
55
end_per_group(_GroupName, Config) ->
60
[{group, dirty}, {group, trans}, frag, info,
63
init_testcases(Type,Config) ->
64
Nodes = [N1,N2] = ?acquire_nodes(2, Config),
65
?match({atomic, ok}, mnesia:create_table(a, [{Type,[N1]}, {index,[3]}])),
66
?match({atomic, ok}, mnesia:create_table(b, [{Type,[N2]}])),
68
ok = mnesia:write({a, {a,Id}, 100 - Id}),
69
ok = mnesia:write({b, {b,100-Id}, Id})
71
All = fun() -> [Write(Id) || Id <- lists:seq(1,10)], ok end,
72
?match({atomic, ok}, mnesia:sync_transaction(All)),
77
dirty_nice_ram_copies(Setup) -> dirty_nice(Setup,ram_copies).
78
dirty_nice_disc_copies(Setup) -> dirty_nice(Setup,disc_copies).
79
dirty_nice_disc_only_copies(Setup) -> dirty_nice(Setup,disc_only_copies).
81
dirty_nice(suite, _) -> [];
82
dirty_nice(doc, _) -> [];
83
dirty_nice(Config, Type) when is_list(Config) ->
84
Ns = init_testcases(Type,Config),
85
QA = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(a),"
86
" Val == 90 + Key]">>),
87
QB = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(b),"
88
" Key == 90 + Val]">>),
89
QC = qlc:sort(mnesia:table(a, [{n_objects,1}, {lock,write}, {traverse, select}])),
90
QD = qlc:sort(mnesia:table(a, [{n_objects,1}, {traverse,{select,[{'$1',[],['$1']}]}}])),
92
FA = fun() -> qlc:e(QA) end,
93
FB = fun() -> qlc:e(QB) end,
94
FC = fun() -> qlc:e(QC) end,
95
FD = fun() -> qlc:e(QD) end,
97
%% Currently unsupported
98
?match({'EXIT',{aborted,no_transaction}}, FA()),
99
?match({'EXIT',{aborted,no_transaction}}, FB()),
101
CRes = lists:sort(mnesia:dirty_match_object(a, {'_','_','_'})),
102
?match([{a,{a,5},95}], mnesia:async_dirty(FA)),
103
?match([{b,{b,95},5}], mnesia:async_dirty(FB)),
104
?match(CRes, mnesia:async_dirty(FC)),
105
?match(CRes, mnesia:async_dirty(FD)),
106
?match([{a,{a,5},95}], mnesia:sync_dirty(FA)),
107
?match([{b,{b,95},5}], mnesia:sync_dirty(FB)),
108
?match(CRes, mnesia:sync_dirty(FC)),
109
?match([{a,{a,5},95}], mnesia:activity(async_dirty, FA)),
110
?match([{b,{b,95},5}], mnesia:activity(async_dirty, FB)),
111
?match([{a,{a,5},95}], mnesia:activity(sync_dirty, FA)),
112
?match([{b,{b,95},5}], mnesia:activity(sync_dirty, FB)),
113
?match(CRes, mnesia:activity(async_dirty,FC)),
115
disc_only_copies -> skip;
117
?match([{a,{a,5},95}], mnesia:ets(FA)),
118
?match([{a,{a,5},95}], mnesia:activity(ets, FA))
120
?verify_mnesia(Ns, []).
123
trans_nice_ram_copies(Setup) -> trans_nice(Setup,ram_copies).
124
trans_nice_disc_copies(Setup) -> trans_nice(Setup,disc_copies).
125
trans_nice_disc_only_copies(Setup) -> trans_nice(Setup,disc_only_copies).
127
trans_nice(suite, _) -> [];
128
trans_nice(doc, _) -> [];
129
trans_nice(Config, Type) when is_list(Config) ->
130
Ns = init_testcases(Type,Config),
131
QA = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(a),"
132
" Val == 90 + Key]">>),
133
QB = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(b),"
134
" Key == 90 + Val]">>),
136
<<"[Q || Q = #a{v=91} <- mnesia:table(a)]"
139
QD = qlc:sort(mnesia:table(a, [{n_objects,1}, {lock,write}, {traverse, select}])),
140
QE = qlc:sort(mnesia:table(a, [{n_objects,1}, {traverse,{select,[{'$1',[],['$1']}]}}])),
142
DRes = lists:sort(mnesia:dirty_match_object(a, {'_','_','_'})),
144
FA = fun() -> qlc:e(QA) end,
145
FB = fun() -> qlc:e(QB) end,
146
FC = fun() -> qlc:e(QC) end,
147
FD = fun() -> qlc:e(QD) end,
148
FE = fun() -> qlc:e(QE) end,
150
?match({atomic,[{a,{a,5},95}]}, mnesia:transaction(FA)),
151
?match({atomic,[{b,{b,95},5}]}, mnesia:transaction(FB)),
152
?match({atomic,[{a,{a,9},91}]}, mnesia:transaction(FC)),
153
?match({atomic,[{a,{a,5},95}]}, mnesia:sync_transaction(FA)),
154
?match({atomic,[{b,{b,95},5}]}, mnesia:sync_transaction(FB)),
155
?match({atomic,[{a,{a,9},91}]}, mnesia:sync_transaction(FC)),
156
?match([{a,{a,5},95}], mnesia:activity(transaction,FA)),
157
?match([{b,{b,95},5}], mnesia:activity(transaction,FB)),
158
?match([{a,{a,9},91}], mnesia:activity(transaction,FC)),
159
?match([{a,{a,5},95}], mnesia:activity(sync_transaction,FA)),
160
?match([{b,{b,95},5}], mnesia:activity(sync_transaction,FB)),
161
?match([{a,{a,9},91}], mnesia:activity(sync_transaction,FC)),
163
?match({atomic, DRes}, mnesia:transaction(FD)),
164
?match({atomic, DRes}, mnesia:transaction(FE)),
166
Rest = fun(Cursor,Loop) ->
167
case qlc:next_answers(Cursor, 1) of
169
[A]-> [A|Loop(Cursor,Loop)]
173
Cursor = qlc:cursor(QD),
176
?match({atomic, DRes}, mnesia:transaction(Loop)),
178
?verify_mnesia(Ns, []).
180
%% -record(a, {k,v}).
181
%% -record(b, {k,v}).
182
%% -record(k, {t,v}).
185
<<"-record(a, {k,v}). "
186
"-record(b, {k,v}). "
187
"-record(k, {t,v}). "
191
atomic_eval(suite) -> [];
192
atomic_eval(doc) -> [];
193
atomic_eval(Config) ->
194
Ns = init_testcases(ram_copies, Config),
196
<<"[Q || Q = #a{k={_,9}} <- mnesia:table(a)]"
200
mnesia:system_info(held_locks)}
203
?match({[{a,{a,9},91}], [{{a,'______WHOLETABLE_____'},read,{tid,_,Self}}]},
207
<<"[Q || Q = #a{k={a,9}} <- mnesia:table(a)]"
210
?match({[{a,{a,9},91}],[{{a,{a,9}},read,{tid,_,Self}}]},
213
Flush = fun(Loop) -> %% Clean queue
214
receive _ -> Loop(Loop)
220
GrabLock = fun(Father) ->
221
mnesia:read(a, {a,9}, write),
223
receive cont -> ok end end,
225
Pid1 = spawn(fun() -> ?match(ok, ok(GrabLock, [Self])) end),
226
?match(locked,receive locked -> locked after 5000 -> timeout end), %% Wait
229
Restart = fun(Locker,Fun) ->
231
case {Count,(catch Fun())} of
242
?match({1,{[{a,{a,9},91}], [{{a,'______WHOLETABLE_____'},read,{tid,_,Self}}]}},
243
ok(Restart,[Pid1,fun() -> Eval(Q1) end])),
245
Pid2 = spawn(fun() -> ?match(ok, ok(GrabLock, [Self])) end),
246
?match(locked,receive locked -> locked after 5000 -> timeout end), %% Wait
248
?match({1,{[{a,{a,9},91}],[{{a,{a,9}},read,{tid,_,Self}}]}},
249
ok(Restart,[Pid2, fun() -> Eval(Q2) end])),
257
?match([{a,{a,9},91}], ok(Cursor, [])),
260
Pid3 = spawn(fun() -> ?match(ok, ok(GrabLock, [Self])) end),
261
?match(locked,receive locked -> locked after 5000 -> timeout end), %% Wait
264
?match({1,[{a,{a,9},91}]}, ok(Restart,[Pid3, Cursor])),
265
QC1 = ok(fun() -> qlc:cursor(Q1) end, []),
266
?match({'EXIT', _}, qlc:next_answers(QC1)),
267
?match({aborted,_}, ok(fun()->qlc:next_answers(QC1)end,[])),
268
?verify_mnesia(Ns, []).
274
Ns = init_testcases(ram_copies,Config),
275
QA = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(a),"
276
" Val == 90 + Key]">>),
277
QB = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(b),"
278
" Key == 90 + Val]">>),
282
?match({atomic,ok},mnesia:change_table_frag(Tab, {activate, []})),
283
Dist = mnesia_frag_test:frag_dist(Tab),
284
?match({atomic,ok},mnesia:change_table_frag(Tab,{add_frag,Dist}))
289
Fun = fun(Tab) -> mnesia:table_info(Tab, frag_names) end,
290
FTs = mnesia:activity(sync_dirty, Fun, [a], mnesia_frag) ++
291
mnesia:activity(sync_dirty, Fun, [b], mnesia_frag),
292
Size = fun(Tab) -> mnesia:dirty_rpc(Tab, mnesia, table_info, [Tab,size]) end,
294
%% Verify that all data doesn't belong to the same frag.
295
?match([], [{Tab,Size(Tab)} || Tab <- FTs,
298
FA = fun() -> qlc:e(QA) end,
299
FB = fun() -> qlc:e(QB) end,
300
?match([{a,{a,5},95}], mnesia:activity(transaction,FA,[],mnesia_frag)),
301
?match([{b,{b,95},5}], mnesia:activity(transaction,FB,[],mnesia_frag)),
303
?verify_mnesia(Ns, []).
308
Ns = init_testcases(ram_copies, Config),
310
<<"[Q || Q = #a{k={_,9}} <- mnesia:table(a)]"
314
<<"[Q || Q = #a{k={a,9}} <- mnesia:table(a)]"
318
<<"[Q || Q = #a{v=91} <- mnesia:table(a)]"
321
%% FIXME compile and check results!
323
?match(ok,io:format("~s~n",[qlc:info(Q1)])),
324
?match(ok,io:format("~s~n",[qlc:info(Q2)])),
325
?match(ok,io:format("~s~n",[qlc:info(Q3)])),
327
?verify_mnesia(Ns, []).
330
case mnesia:transaction(Fun,A) of
336
mnesia_down(suite) -> [];
338
["Test bug OTP-7968, which crashed mnesia when a"
339
"mnesia_down came after qlc had been invoked"];
340
mnesia_down(Config) when is_list(Config) ->
341
[N1,N2] = init_testcases(ram_copies,Config),
342
QB = handle(<<"[Q || Q = {_,{_,Key},Val} <- mnesia:table(b),"
343
" Val == Key - 90]">>),
348
Cursor = qlc:cursor(QB), %% Forces another process
349
Res = qlc:next_answers(Cursor),
350
Tester ! {qlc, self(), Res},
351
{Mod, Tid, Ts} = get(mnesia_activity_state),
354
io:format("Continuing ~p ~p ~n",[self(), {Mod, Tid, Ts}]),
355
io:format("ETS ~p~n",[ets:tab2list(element(2,Ts))]),
356
io:format("~p~n",[process_info(self(),messages)]),
360
spawn(fun() -> TransRes = mnesia:transaction(Eval), Tester ! {test,TransRes} end),
363
TmInfo = mnesia_tm:get_info(5000),
364
mnesia_tm:display_info(user, TmInfo)
368
?match([{b,{b,95},5}], QRes),
370
mnesia_test_lib:kill_mnesia([N2]),
379
?match({atomic, [{b,{b,95},5}]}, QRes2)
384
?verify_mnesia([N1], [N2]).
387
nested_qlc(suite) -> [];
389
["Test bug in OTP-7968 (the second problem) where nested"
390
"transaction don't work as expected"];
391
nested_qlc(Config) when is_list(Config) ->
392
Ns = init_testcases(ram_copies,Config),
395
top_as_with_some_bs(10),
397
?verify_mnesia(Ns, []).
402
find(qlc:q([ B || B={_,_,F_id} <- mnesia:table(b), F_id == A_id])).
405
find(qlc:q([ {A,bs_by_a_id(Id)} ||
406
A = {_, {a,Id}, _} <- mnesia:table(a)])).
408
top_as_with_some_bs(Limit) ->
410
qlc:q([ {A,bs_by_a_id(Id)} ||
411
A = {_, {a,Id}, _} <- mnesia:table(a)]),
413
fun(A1,A2) -> A1 < A2 end
419
F = fun() -> qlc:e(Q) end,
420
{atomic, Res} = mnesia:transaction(F),
423
% --- it returns top Limit results from query Q ordered by Order sort function
424
top(Q, Limit, Order) ->
426
OQ = qlc:sort(Q, [{order,Order}]),
428
Res = qlc:next_answers(QC, Limit),
429
qlc:delete_cursor(QC),
432
{atomic, Res} = mnesia:transaction(Do),
435
%% To keep mnesia suite backward compatible,
436
%% we compile the queries in runtime when qlc is available
437
%% Compiles and returns a handle to a qlc
440
handle(Records,Expr) ->
441
case catch handle2(Records,Expr) of
448
handle2(Records,Expr) ->
449
{FN,Mod} = temp_name(),
450
ModStr = list_to_binary("-module(" ++ atom_to_list(Mod) ++ ").\n"),
453
"-include_lib(\"stdlib/include/qlc.hrl\").\n",
454
"-export([tmp/0]).\n",
457
%% " _ = (catch throw(fvalue_not_reset)),"
459
Expr/binary,").\n">>,
461
?match(ok,file:write_file(FN,Prog)),
462
{ok,Forms} = epp:parse_file(FN,"",""),
463
{ok,Mod,Bin} = compile:forms(Forms),
464
code:load_binary(Mod,FN,Bin),
468
put(mts_config,Config),
469
put(mts_tf_counter,0).
472
Conf = get(mts_config),
473
C = get(mts_tf_counter),
474
put(mts_tf_counter,C+1),
475
{filename:join([proplists:get_value(priv_dir,Conf, "."),
476
"tempfile"++integer_to_list(C)++".tmp"]),
477
list_to_atom("tmp" ++ integer_to_list(C))}.