%%
%% %CopyrightBegin%
%%
%% Copyright Ericsson AB 1996-2010. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
%% compliance with the License. You should have received a copy of the
%% Erlang Public License along with this software. If not, it can be
%% retrieved online at http://www.erlang.org/.
%%
%% Software distributed under the License is distributed on an "AS IS"
%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
%% the License for the specific language governing rights and limitations
%% under the License.
%%
%% %CopyrightEnd%
%%
%%
-module(mnesia_dirty_access_test).
-author('[email protected]').
-compile([export_all]).
-include("mnesia_test_lib.hrl").
init_per_testcase(Func, Conf) ->
mnesia_test_lib:init_per_testcase(Func, Conf).
fin_per_testcase(Func, Conf) ->
mnesia_test_lib:fin_per_testcase(Func, Conf).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all(doc) ->
["Evil dirty access, regardless of transaction scope.",
"Invoke all functions in the API and try to cover all legal uses",
"cases as well the illegal dito. This is a complement to the",
"other more explicit test cases."];
all(suite) ->
[
dirty_write,
dirty_read,
dirty_update_counter,
dirty_delete,
dirty_delete_object,
dirty_match_object,
dirty_index,
dirty_iter,
admin_tests
].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Write records dirty
dirty_write(suite) ->
[
dirty_write_ram,
dirty_write_disc,
dirty_write_disc_only
].
dirty_write_ram(suite) -> [];
dirty_write_ram(Config) when is_list(Config) ->
dirty_write(Config, ram_copies).
dirty_write_disc(suite) -> [];
dirty_write_disc(Config) when is_list(Config) ->
dirty_write(Config, disc_copies).
dirty_write_disc_only(suite) -> [];
dirty_write_disc_only(Config) when is_list(Config) ->
dirty_write(Config, disc_only_copies).
dirty_write(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_write,
Def = [{attributes, [k, v]}, {Storage, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
?match({'EXIT', _}, mnesia:dirty_write([])),
?match({'EXIT', _}, mnesia:dirty_write({Tab, 2})),
?match({'EXIT', _}, mnesia:dirty_write({foo, 2})),
?match(ok, mnesia:dirty_write({Tab, 1, 2})),
?match({atomic, ok}, mnesia:transaction(fun() ->
mnesia:dirty_write({Tab, 1, 2}) end)),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Read records dirty
dirty_read(suite) ->
[
dirty_read_ram,
dirty_read_disc,
dirty_read_disc_only
].
dirty_read_ram(suite) -> [];
dirty_read_ram(Config) when is_list(Config) ->
dirty_read(Config, ram_copies).
dirty_read_disc(suite) -> [];
dirty_read_disc(Config) when is_list(Config) ->
dirty_read(Config, disc_copies).
dirty_read_disc_only(suite) -> [];
dirty_read_disc_only(Config) when is_list(Config) ->
dirty_read(Config, disc_only_copies).
dirty_read(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_read,
Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
?match({'EXIT', _}, mnesia:dirty_read([])),
?match({'EXIT', _}, mnesia:dirty_read({Tab})),
?match({'EXIT', _}, mnesia:dirty_read({Tab, 1, 2})),
?match([], mnesia:dirty_read({Tab, 1})),
?match(ok, mnesia:dirty_write({Tab, 1, 2})),
?match([{Tab, 1, 2}], mnesia:dirty_read({Tab, 1})),
?match(ok, mnesia:dirty_write({Tab, 1, 3})),
?match([{Tab, 1, 2}, {Tab, 1, 3}], mnesia:dirty_read({Tab, 1})),
?match({atomic, [{Tab, 1, 2}, {Tab, 1, 3}]},
mnesia:transaction(fun() -> mnesia:dirty_read({Tab, 1}) end)),
?match(false, mnesia:async_dirty(fun() -> mnesia:is_transaction() end)),
?match(false, mnesia:sync_dirty(fun() -> mnesia:is_transaction() end)),
?match(false, mnesia:ets(fun() -> mnesia:is_transaction() end)),
?match(false, mnesia:activity(async_dirty, fun() -> mnesia:is_transaction() end)),
?match(false, mnesia:activity(sync_dirty, fun() -> mnesia:is_transaction() end)),
?match(false, mnesia:activity(ets, fun() -> mnesia:is_transaction() end)),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Update counter record dirty
dirty_update_counter(suite) ->
[
dirty_update_counter_ram,
dirty_update_counter_disc,
dirty_update_counter_disc_only
].
dirty_update_counter_ram(suite) -> [];
dirty_update_counter_ram(Config) when is_list(Config) ->
dirty_update_counter(Config, ram_copies).
dirty_update_counter_disc(suite) -> [];
dirty_update_counter_disc(Config) when is_list(Config) ->
dirty_update_counter(Config, disc_copies).
dirty_update_counter_disc_only(suite) -> [];
dirty_update_counter_disc_only(Config) when is_list(Config) ->
dirty_update_counter(Config, disc_only_copies).
dirty_update_counter(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_update_counter,
Def = [{attributes, [k, v]}, {Storage, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
?match(ok, mnesia:dirty_write({Tab, 1, 2})),
?match({'EXIT', _}, mnesia:dirty_update_counter({Tab, 1}, [])),
?match({'EXIT', _}, mnesia:dirty_update_counter({Tab}, 3)),
?match({'EXIT', _}, mnesia:dirty_update_counter({foo, 1}, 3)),
?match(5, mnesia:dirty_update_counter({Tab, 1}, 3)),
?match([{Tab, 1, 5}], mnesia:dirty_read({Tab, 1})),
?match({atomic, 8}, mnesia:transaction(fun() ->
mnesia:dirty_update_counter({Tab, 1}, 3) end)),
?match(1, mnesia:dirty_update_counter({Tab, foo}, 1)),
?match([{Tab, foo,1}], mnesia:dirty_read({Tab,foo})),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Delete record dirty
dirty_delete(suite) ->
[
dirty_delete_ram,
dirty_delete_disc,
dirty_delete_disc_only
].
dirty_delete_ram(suite) -> [];
dirty_delete_ram(Config) when is_list(Config) ->
dirty_delete(Config, ram_copies).
dirty_delete_disc(suite) -> [];
dirty_delete_disc(Config) when is_list(Config) ->
dirty_delete(Config, disc_copies).
dirty_delete_disc_only(suite) -> [];
dirty_delete_disc_only(Config) when is_list(Config) ->
dirty_delete(Config, disc_only_copies).
dirty_delete(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_delete,
Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
?match({'EXIT', _}, mnesia:dirty_delete([])),
?match({'EXIT', _}, mnesia:dirty_delete({Tab})),
?match({'EXIT', _}, mnesia:dirty_delete({Tab, 1, 2})),
?match(ok, mnesia:dirty_delete({Tab, 1})),
?match(ok, mnesia:dirty_write({Tab, 1, 2})),
?match(ok, mnesia:dirty_delete({Tab, 1})),
?match(ok, mnesia:dirty_write({Tab, 1, 2})),
?match(ok, mnesia:dirty_write({Tab, 1, 2})),
?match(ok, mnesia:dirty_delete({Tab, 1})),
?match(ok, mnesia:dirty_write({Tab, 1, 2})),
?match({atomic, ok}, mnesia:transaction(fun() ->
mnesia:dirty_delete({Tab, 1}) end)),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Delete matching record dirty
dirty_delete_object(suite) ->
[
dirty_delete_object_ram,
dirty_delete_object_disc,
dirty_delete_object_disc_only
].
dirty_delete_object_ram(suite) -> [];
dirty_delete_object_ram(Config) when is_list(Config) ->
dirty_delete_object(Config, ram_copies).
dirty_delete_object_disc(suite) -> [];
dirty_delete_object_disc(Config) when is_list(Config) ->
dirty_delete_object(Config, disc_copies).
dirty_delete_object_disc_only(suite) -> [];
dirty_delete_object_disc_only(Config) when is_list(Config) ->
dirty_delete_object(Config, disc_only_copies).
dirty_delete_object(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_delete_object,
Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
OneRec = {Tab, 1, 2},
?match({'EXIT', _}, mnesia:dirty_delete_object([])),
?match({'EXIT', _}, mnesia:dirty_delete_object({Tab})),
?match({'EXIT', _}, mnesia:dirty_delete_object({Tab, 1})),
?match(ok, mnesia:dirty_delete_object(OneRec)),
?match(ok, mnesia:dirty_write(OneRec)),
?match(ok, mnesia:dirty_delete_object(OneRec)),
?match(ok, mnesia:dirty_write(OneRec)),
?match(ok, mnesia:dirty_write(OneRec)),
?match(ok, mnesia:dirty_delete_object(OneRec)),
?match(ok, mnesia:dirty_write(OneRec)),
?match({atomic, ok}, mnesia:transaction(fun() ->
mnesia:dirty_delete_object(OneRec) end)),
?match({'EXIT', {aborted, {bad_type, Tab, _}}}, mnesia:dirty_delete_object(Tab, {Tab, {['_']}, 21})),
?match({'EXIT', {aborted, {bad_type, Tab, _}}}, mnesia:dirty_delete_object(Tab, {Tab, {['$5']}, 21})),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Read matching records dirty
dirty_match_object(suite) ->
[
dirty_match_object_ram,
dirty_match_object_disc,
dirty_match_object_disc_only
].
dirty_match_object_ram(suite) -> [];
dirty_match_object_ram(Config) when is_list(Config) ->
dirty_match_object(Config, ram_copies).
dirty_match_object_disc(suite) -> [];
dirty_match_object_disc(Config) when is_list(Config) ->
dirty_match_object(Config, disc_copies).
dirty_match_object_disc_only(suite) -> [];
dirty_match_object_disc_only(Config) when is_list(Config) ->
dirty_match_object(Config, disc_only_copies).
dirty_match_object(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_match,
Def = [{attributes, [k, v]}, {Storage, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
OneRec = {Tab, 1, 2},
OnePat = {Tab, '$1', 2},
?match([], mnesia:dirty_match_object(OnePat)),
?match(ok, mnesia:dirty_write(OneRec)),
?match([OneRec], mnesia:dirty_match_object(OnePat)),
?match({atomic, [OneRec]}, mnesia:transaction(fun() ->
mnesia:dirty_match_object(OnePat) end)),
?match({'EXIT', _}, mnesia:dirty_match_object({foo, '$1', 2})),
?match({'EXIT', _}, mnesia:dirty_match_object({[], '$1', 2})),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
dirty_index(suite) ->
[
dirty_index_match_object,
dirty_index_read,
dirty_index_update
].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Dirty read matching records by using an index
dirty_index_match_object(suite) ->
[
dirty_index_match_object_ram,
dirty_index_match_object_disc,
dirty_index_match_object_disc_only
].
dirty_index_match_object_ram(suite) -> [];
dirty_index_match_object_ram(Config) when is_list(Config) ->
dirty_index_match_object(Config, ram_copies).
dirty_index_match_object_disc(suite) -> [];
dirty_index_match_object_disc(Config) when is_list(Config) ->
dirty_index_match_object(Config, disc_copies).
dirty_index_match_object_disc_only(suite) -> [];
dirty_index_match_object_disc_only(Config) when is_list(Config) ->
dirty_index_match_object(Config, disc_only_copies).
dirty_index_match_object(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_index_match_object,
ValPos = 3,
BadValPos = ValPos + 1,
Def = [{attributes, [k, v]}, {Storage, [Node1]}, {index, [ValPos]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
?match([], mnesia:dirty_index_match_object({Tab, '$1', 2}, ValPos)),
OneRec = {Tab, 1, 2},
?match(ok, mnesia:dirty_write(OneRec)),
?match([OneRec], mnesia:dirty_index_match_object({Tab, '$1', 2}, ValPos)),
?match({'EXIT', _}, mnesia:dirty_index_match_object({Tab, '$1', 2}, BadValPos)),
?match({'EXIT', _}, mnesia:dirty_index_match_object({foo, '$1', 2}, ValPos)),
?match({'EXIT', _}, mnesia:dirty_index_match_object({[], '$1', 2}, ValPos)),
?match({atomic, [OneRec]}, mnesia:transaction(fun() ->
mnesia:dirty_index_match_object({Tab, '$1', 2}, ValPos) end)),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Read records by using an index
dirty_index_read(suite) ->
[
dirty_index_read_ram,
dirty_index_read_disc,
dirty_index_read_disc_only
].
dirty_index_read_ram(suite) -> [];
dirty_index_read_ram(Config) when is_list(Config) ->
dirty_index_read(Config, ram_copies).
dirty_index_read_disc(suite) -> [];
dirty_index_read_disc(Config) when is_list(Config) ->
dirty_index_read(Config, disc_copies).
dirty_index_read_disc_only(suite) -> [];
dirty_index_read_disc_only(Config) when is_list(Config) ->
dirty_index_read(Config, disc_only_copies).
dirty_index_read(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_index_read,
ValPos = 3,
BadValPos = ValPos + 1,
Def = [{type, set},
{attributes, [k, v]},
{Storage, [Node1]},
{index, [ValPos]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
OneRec = {Tab, 1, 2},
?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
?match(ok, mnesia:dirty_write(OneRec)),
?match([OneRec], mnesia:dirty_index_read(Tab, 2, ValPos)),
?match({atomic, [OneRec]},
mnesia:transaction(fun() -> mnesia:dirty_index_read(Tab, 2, ValPos) end)),
?match(42, mnesia:dirty_update_counter({Tab, 1}, 40)),
?match([{Tab,1,42}], mnesia:dirty_read({Tab, 1})),
?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
?match([{Tab, 1, 42}], mnesia:dirty_index_read(Tab, 42, ValPos)),
?match({'EXIT', _}, mnesia:dirty_index_read(Tab, 2, BadValPos)),
?match({'EXIT', _}, mnesia:dirty_index_read(foo, 2, ValPos)),
?match({'EXIT', _}, mnesia:dirty_index_read([], 2, ValPos)),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
dirty_index_update(suite) ->
[
dirty_index_update_set_ram,
dirty_index_update_set_disc,
dirty_index_update_set_disc_only,
dirty_index_update_bag_ram,
dirty_index_update_bag_disc,
dirty_index_update_bag_disc_only
];
dirty_index_update(doc) ->
["See Ticket OTP-2083, verifies that a table with a index is "
"update in the correct way i.e. the index finds the correct "
"records after a update"].
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
dirty_index_update_set_ram(suite) -> [];
dirty_index_update_set_ram(Config) when is_list(Config) ->
dirty_index_update_set(Config, ram_copies).
dirty_index_update_set_disc(suite) -> [];
dirty_index_update_set_disc(Config) when is_list(Config) ->
dirty_index_update_set(Config, disc_copies).
dirty_index_update_set_disc_only(suite) -> [];
dirty_index_update_set_disc_only(Config) when is_list(Config) ->
dirty_index_update_set(Config, disc_only_copies).
dirty_index_update_set(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = index_test,
ValPos = v1,
ValPos2 = v3,
Def = [{attributes, [k, v1, v2, v3]},
{Storage, [Node1]},
{index, [ValPos]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
Pat1 = {Tab, '$1', 2, '$2', '$3'},
Pat2 = {Tab, '$1', '$2', '$3', '$4'},
Rec1 = {Tab, 1, 2, 3, 4},
Rec2 = {Tab, 2, 2, 13, 14},
Rec3 = {Tab, 1, 12, 13, 14},
Rec4 = {Tab, 4, 2, 13, 14},
?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
?match(ok, mnesia:dirty_write(Rec1)),
?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
?match(ok, mnesia:dirty_write(Rec2)),
R1 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec2], lists:sort(R1)),
?match(ok, mnesia:dirty_write(Rec3)),
R2 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec2], lists:sort(R2)),
?match([Rec2], mnesia:dirty_index_match_object(Pat1, ValPos)),
{atomic, R3} = mnesia:transaction(fun() -> mnesia:match_object(Pat2) end),
?match([Rec3, Rec2], lists:sort(R3)),
?match(ok, mnesia:dirty_write(Rec4)),
R4 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec2, Rec4], lists:sort(R4)),
?match(ok, mnesia:dirty_delete({Tab, 4})),
?match([Rec2], mnesia:dirty_index_read(Tab, 2, ValPos)),
?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
R5 = mnesia:dirty_match_object(Pat2),
?match([Rec3, Rec2, Rec4], lists:sort(R5)),
R6 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec2, Rec4], lists:sort(R6)),
?match([], mnesia:dirty_index_read(Tab, 4, ValPos2)),
R7 = mnesia:dirty_index_read(Tab, 14, ValPos2),
?match([Rec3, Rec2, Rec4], lists:sort(R7)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
R8 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec2, Rec4], lists:sort(R8)),
?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
R9 = mnesia:dirty_index_read(Tab, 14, ValPos2),
?match([Rec2, Rec4], lists:sort(R9)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec2) end)),
R10 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec4], lists:sort(R10)),
?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
?match([Rec4], mnesia:dirty_index_read(Tab, 14, ValPos2)),
?match(ok, mnesia:dirty_delete({Tab, 4})),
R11 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1], lists:sort(R11)),
?match([Rec1], mnesia:dirty_index_read(Tab, 4, ValPos2)),
?match([], mnesia:dirty_index_read(Tab, 14, ValPos2)),
?verify_mnesia(Nodes, []).
dirty_index_update_bag_ram(suite) -> [];
dirty_index_update_bag_ram(Config)when is_list(Config) ->
dirty_index_update_bag(Config, ram_copies).
dirty_index_update_bag_disc(suite) -> [];
dirty_index_update_bag_disc(Config)when is_list(Config) ->
dirty_index_update_bag(Config, disc_copies).
dirty_index_update_bag_disc_only(suite) -> [];
dirty_index_update_bag_disc_only(Config)when is_list(Config) ->
dirty_index_update_bag(Config, disc_only_copies).
dirty_index_update_bag(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = index_test,
ValPos = v1,
ValPos2 = v3,
Def = [{type, bag},
{attributes, [k, v1, v2, v3]},
{Storage, [Node1]},
{index, [ValPos]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
Pat1 = {Tab, '$1', 2, '$2', '$3'},
Pat2 = {Tab, '$1', '$2', '$3', '$4'},
Rec1 = {Tab, 1, 2, 3, 4},
Rec2 = {Tab, 2, 2, 13, 14},
Rec3 = {Tab, 1, 12, 13, 14},
Rec4 = {Tab, 4, 2, 13, 4},
Rec5 = {Tab, 1, 2, 234, 14},
%% Simple Index
?match([], mnesia:dirty_index_read(Tab, 2, ValPos)),
?match(ok, mnesia:dirty_write(Rec1)),
?match([Rec1], mnesia:dirty_index_read(Tab, 2, ValPos)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec2) end)),
R1 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec2], lists:sort(R1)),
?match(ok, mnesia:dirty_write(Rec3)),
R2 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec2], lists:sort(R2)),
R3 = mnesia:dirty_index_match_object(Pat1, ValPos),
?match([Rec1, Rec2], lists:sort(R3)),
R4 = mnesia:dirty_match_object(Pat2),
?match([Rec1, Rec3, Rec2], lists:sort(R4)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
R5 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec2, Rec4], lists:sort(R5)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete({Tab, 4}) end)),
R6 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec2], lists:sort(R6)),
?match(ok, mnesia:dirty_delete_object(Rec1)),
?match([Rec2], mnesia:dirty_index_read(Tab, 2, ValPos)),
R7 = mnesia:dirty_match_object(Pat2),
?match([Rec3, Rec2], lists:sort(R7)),
%% Two indexies
?match({atomic, ok}, mnesia:del_table_index(Tab, ValPos)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec1) end)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec4) end)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos)),
?match({atomic, ok}, mnesia:add_table_index(Tab, ValPos2)),
R8 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec2, Rec4], lists:sort(R8)),
R9 = mnesia:dirty_index_read(Tab, 4, ValPos2),
?match([Rec1, Rec4], lists:sort(R9)),
R10 = mnesia:dirty_index_read(Tab, 14, ValPos2),
?match([Rec3, Rec2], lists:sort(R10)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(Rec5) end)),
R11 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec1, Rec5, Rec2, Rec4], lists:sort(R11)),
R12 = mnesia:dirty_index_read(Tab, 4, ValPos2),
?match([Rec1, Rec4], lists:sort(R12)),
R13 = mnesia:dirty_index_read(Tab, 14, ValPos2),
?match([Rec5, Rec3, Rec2], lists:sort(R13)),
?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:delete_object(Rec1) end)),
R14 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec5, Rec2, Rec4], lists:sort(R14)),
?match([Rec4], mnesia:dirty_index_read(Tab, 4, ValPos2)),
R15 = mnesia:dirty_index_read(Tab, 14, ValPos2),
?match([Rec5, Rec3, Rec2], lists:sort(R15)),
?match(ok, mnesia:dirty_delete_object(Rec5)),
R16 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec2, Rec4], lists:sort(R16)),
?match([Rec4], mnesia:dirty_index_read(Tab, 4, ValPos2)),
R17 = mnesia:dirty_index_read(Tab, 14, ValPos2),
?match([Rec3, Rec2], lists:sort(R17)),
?match(ok, mnesia:dirty_write(Rec1)),
?match(ok, mnesia:dirty_delete({Tab, 1})),
R18 = mnesia:dirty_index_read(Tab, 2, ValPos),
?match([Rec2, Rec4], lists:sort(R18)),
?match([Rec4], mnesia:dirty_index_read(Tab, 4, ValPos2)),
R19 = mnesia:dirty_index_read(Tab, 14, ValPos2),
?match([Rec2], lists:sort(R19)),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Dirty iteration
%% dirty_slot, dirty_first, dirty_next
dirty_iter(suite) ->
[
dirty_iter_ram,
dirty_iter_disc,
dirty_iter_disc_only
].
dirty_iter_ram(suite) -> [];
dirty_iter_ram(Config) when is_list(Config) ->
dirty_iter(Config, ram_copies).
dirty_iter_disc(suite) -> [];
dirty_iter_disc(Config) when is_list(Config) ->
dirty_iter(Config, disc_copies).
dirty_iter_disc_only(suite) -> [];
dirty_iter_disc_only(Config) when is_list(Config) ->
dirty_iter(Config, disc_only_copies).
dirty_iter(Config, Storage) ->
[Node1] = Nodes = ?acquire_nodes(1, Config),
Tab = dirty_iter,
Def = [{type, bag}, {attributes, [k, v]}, {Storage, [Node1]}],
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
?match([], all_slots(Tab)),
?match([], all_nexts(Tab)),
Keys = lists:seq(1, 5),
Records = [{Tab, A, B} || A <- Keys, B <- lists:seq(1, 2)],
lists:foreach(fun(Rec) -> ?match(ok, mnesia:dirty_write(Rec)) end, Records),
SortedRecords = lists:sort(Records),
?match(SortedRecords, lists:sort(all_slots(Tab))),
?match(Keys, lists:sort(all_nexts(Tab))),
?match({'EXIT', _}, mnesia:dirty_first(foo)),
?match({'EXIT', _}, mnesia:dirty_next(foo, foo)),
?match({'EXIT', _}, mnesia:dirty_slot(foo, 0)),
?match({'EXIT', _}, mnesia:dirty_slot(foo, [])),
?match({atomic, Keys},
mnesia:transaction(fun() -> lists:sort(all_nexts(Tab)) end)),
?verify_mnesia(Nodes, []).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Returns a list of all keys in table
all_slots(Tab) ->
all_slots(Tab, [], 0).
all_slots(_Tab, '$end_of_table', _) ->
[];
all_slots(Tab, PrevRecords, PrevSlot) ->
Records = mnesia:dirty_slot(Tab, PrevSlot),
PrevRecords ++ all_slots(Tab, Records, PrevSlot + 1).
%% Returns a list of all keys in table
all_nexts(Tab) ->
FirstKey = mnesia:dirty_first(Tab),
all_nexts(Tab, FirstKey).
all_nexts(_Tab, '$end_of_table') ->
[];
all_nexts(Tab, PrevKey) ->
Key = mnesia:dirty_next(Tab, PrevKey),
[PrevKey] ++ all_nexts(Tab, Key).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
admin_tests(doc) ->
["Verifies that dirty operations work during schema operations"];
admin_tests(suite) ->
[del_table_copy_1,
del_table_copy_2,
del_table_copy_3,
add_table_copy_1,
add_table_copy_2,
add_table_copy_3,
add_table_copy_4,
move_table_copy_1,
move_table_copy_2,
move_table_copy_3,
move_table_copy_4].
update_trans(Tab, Key, Acc) ->
Update =
fun() ->
Res = (catch mnesia:read({Tab, Key})),
case Res of
[{Tab, Key, Extra, Acc}] ->
mnesia:write({Tab,Key,Extra, Acc+1});
Val ->
{read, Val, {acc, Acc}}
end
end,
receive
{Pid, quit} -> Pid ! {self(), Acc}
after
3 ->
case catch mnesia:sync_dirty(Update) of
ok ->
update_trans(Tab, Key, Acc+1);
Else ->
?error("Dirty Operation failed on ~p (update no ~p) with ~p~n"
"Info w2read ~p w2write ~p w2commit ~p storage ~p ~n",
[node(),
Acc,
Else,
mnesia:table_info(Tab, where_to_read),
mnesia:table_info(Tab, where_to_write),
mnesia:table_info(Tab, where_to_commit),
mnesia:table_info(Tab, storage_type)])
end
end.
del_table_copy_1(suite) -> [];
del_table_copy_1(Config) when is_list(Config) ->
[_Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
del_table(Node2, Node2, Nodes). %Called on same Node as deleted
del_table_copy_2(suite) -> [];
del_table_copy_2(Config) when is_list(Config) ->
[Node1, Node2, _Node3] = Nodes = ?acquire_nodes(3, Config),
del_table(Node1, Node2, Nodes). %Called from other Node
del_table_copy_3(suite) -> [];
del_table_copy_3(Config) when is_list(Config) ->
[_Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
del_table(Node3, Node2, Nodes). %Called from Node w.o. table
del_table(CallFrom, DelNode, [Node1, Node2, Node3]) ->
Tab = schema_ops,
Def = [{disc_only_copies, [Node1]}, {ram_copies, [Node2]},
{attributes, [key, attr1, attr2]}],
?log("Test case removing table from ~w, with ~w~n", [DelNode, Def]),
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
insert(Tab, 1000),
Pid1 = spawn_link(Node1, ?MODULE, update_trans, [Tab, 1, 0]),
Pid2 = spawn_link(Node2, ?MODULE, update_trans, [Tab, 2, 0]),
Pid3 = spawn_link(Node3, ?MODULE, update_trans, [Tab, 3, 0]),
dbg:tracer(process, {fun(Msg,_) -> tracer(Msg) end, void}),
%% dbg:n(Node2),
%% dbg:n(Node3),
%% dbg:tp('_', []),
%% dbg:tpl(dets, [timestamp]),
dbg:p(Pid1, [m,c,timestamp]),
?match({atomic, ok},
rpc:call(CallFrom, mnesia, del_table_copy, [Tab, DelNode])),
Pid1 ! {self(), quit}, R1 =
receive {Pid1, Res1} -> Res1
after
5000 -> io:format("~p~n",[process_info(Pid1)]),error
end,
Pid2 ! {self(), quit}, R2 =
receive {Pid2, Res2} -> Res2
after
5000 -> error
end,
Pid3 ! {self(), quit}, R3 =
receive {Pid3, Res3} -> Res3
after
5000 -> error
end,
verify_oids(Tab, Node1, Node2, Node3, R1, R2, R3),
?verify_mnesia([Node1, Node2, Node3], []).
tracer({trace_ts, _, send, Msg, Pid, {_,S,Ms}}) ->
io:format("~p:~p ~p >> ~w ~n",[S,Ms,Pid,Msg]);
tracer({trace_ts, _, 'receive', Msg, {_,S,Ms}}) ->
io:format("~p:~p << ~w ~n",[S,Ms,Msg]);
tracer(Msg) ->
io:format("UMsg ~p ~n",[Msg]),
ok.
add_table_copy_1(suite) -> [];
add_table_copy_1(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
Def = [{ram_copies, [Node1, Node2]},
{attributes, [key, attr1, attr2]}],
add_table(Node1, Node3, Nodes, Def).
%% Not so much diff from 1 but I got a feeling of a bug
%% should behave exactly the same but just checking the internal ordering
add_table_copy_2(suite) -> [];
add_table_copy_2(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
Def = [{ram_copies, [Node1, Node2]},
{attributes, [key, attr1, attr2]}],
add_table(Node2, Node3, Nodes, Def).
add_table_copy_3(suite) -> [];
add_table_copy_3(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
Def = [{ram_copies, [Node1, Node2]},
{attributes, [key, attr1, attr2]}],
add_table(Node3, Node3, Nodes, Def).
add_table_copy_4(suite) -> [];
add_table_copy_4(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
Def = [{disc_only_copies, [Node1]},
{attributes, [key, attr1, attr2]}],
add_table(Node2, Node3, Nodes, Def).
add_table(CallFrom, AddNode, [Node1, Node2, Node3], Def) ->
?log("Test case adding table at ~w, with ~w~n", [AddNode, Def]),
Tab = schema_ops,
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
insert(Tab, 1002),
Pid1 = spawn_link(Node1, ?MODULE, update_trans, [Tab, 1, 0]),
Pid2 = spawn_link(Node2, ?MODULE, update_trans, [Tab, 2, 0]),
Pid3 = spawn_link(Node3, ?MODULE, update_trans, [Tab, 3, 0]),
?match({atomic, ok}, rpc:call(CallFrom, mnesia, add_table_copy,
[Tab, AddNode, ram_copies])),
Pid1 ! {self(), quit}, R1 = receive {Pid1, Res1} -> Res1 after 5000 -> error end,
Pid2 ! {self(), quit}, R2 = receive {Pid2, Res2} -> Res2 after 5000 -> error end,
Pid3 ! {self(), quit}, R3 = receive {Pid3, Res3} -> Res3 after 5000 -> error end,
verify_oids(Tab, Node1, Node2, Node3, R1, R2, R3),
?verify_mnesia([Node1, Node2, Node3], []).
move_table_copy_1(suite) -> [];
move_table_copy_1(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
Def = [{ram_copies, [Node1, Node2]},
{attributes, [key, attr1, attr2]}],
move_table(Node1, Node1, Node3, Nodes, Def).
move_table_copy_2(suite) -> [];
move_table_copy_2(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
Def = [{ram_copies, [Node1, Node2]},
{attributes, [key, attr1, attr2]}],
move_table(Node2, Node1, Node3, Nodes, Def).
move_table_copy_3(suite) -> [];
move_table_copy_3(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
Def = [{ram_copies, [Node1, Node2]},
{attributes, [key, attr1, attr2]}],
move_table(Node3, Node1, Node3, Nodes, Def).
move_table_copy_4(suite) -> [];
move_table_copy_4(Config) when is_list(Config) ->
[Node1, Node2, Node3] = Nodes = ?acquire_nodes(3, Config),
Def = [{ram_copies, [Node1]},
{attributes, [key, attr1, attr2]}],
move_table(Node2, Node1, Node3, Nodes, Def).
move_table(CallFrom, FromNode, ToNode, [Node1, Node2, Node3], Def) ->
?log("Test case move table from ~w to ~w, with ~w~n", [FromNode, ToNode, Def]),
Tab = schema_ops,
?match({atomic, ok}, mnesia:create_table(Tab, Def)),
insert(Tab, 1002),
Pid1 = spawn_link(Node1, ?MODULE, update_trans, [Tab, 1, 0]),
Pid2 = spawn_link(Node2, ?MODULE, update_trans, [Tab, 2, 0]),
Pid3 = spawn_link(Node3, ?MODULE, update_trans, [Tab, 3, 0]),
?match({atomic, ok}, rpc:call(CallFrom, mnesia, move_table_copy,
[Tab, FromNode, ToNode])),
Pid1 ! {self(), quit},
R1 = receive {Pid1, Res1} -> Res1 after 5000 -> ?error("timeout pid1~n", []) end,
Pid2 ! {self(), quit},
R2 = receive {Pid2, Res2} -> Res2 after 5000 -> ?error("timeout pid2~n", []) end,
Pid3 ! {self(), quit},
R3 = receive {Pid3, Res3} -> Res3 after 5000 -> ?error("timeout pid3~n", []) end,
verify_oids(Tab, Node1, Node2, Node3, R1, R2, R3),
?verify_mnesia([Node1, Node2, Node3], []).
% Verify consistency between different nodes
% Due to limitations in the current dirty_ops this can wrong from time to time!
verify_oids(Tab, N1, N2, N3, R1, R2, R3) ->
io:format("DEBUG 1=>~p 2=>~p 3=>~p~n", [R1,R2,R3]),
?match([{_, _, _, R1}], rpc:call(N1, mnesia, dirty_read, [{Tab, 1}])),
?match([{_, _, _, R1}], rpc:call(N2, mnesia, dirty_read, [{Tab, 1}])),
?match([{_, _, _, R1}], rpc:call(N3, mnesia, dirty_read, [{Tab, 1}])),
?match([{_, _, _, R2}], rpc:call(N1, mnesia, dirty_read, [{Tab, 2}])),
?match([{_, _, _, R2}], rpc:call(N2, mnesia, dirty_read, [{Tab, 2}])),
?match([{_, _, _, R2}], rpc:call(N3, mnesia, dirty_read, [{Tab, 2}])),
?match([{_, _, _, R3}], rpc:call(N1, mnesia, dirty_read, [{Tab, 3}])),
?match([{_, _, _, R3}], rpc:call(N2, mnesia, dirty_read, [{Tab, 3}])),
?match([{_, _, _, R3}], rpc:call(N3, mnesia, dirty_read, [{Tab, 3}])).
insert(_Tab, 0) -> ok;
insert(Tab, N) when N > 0 ->
ok = mnesia:sync_dirty(fun() -> false = mnesia:is_transaction(), mnesia:write({Tab, N, N, 0}) end),
insert(Tab, N-1).
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%