File 2452-mnesia-add-l-select_reverse-coverage-docs-and-mnesia.patch of Package erlang
From cda466cebfa809e9fcf7a629538b82018affe4d3 Mon Sep 17 00:00:00 2001
From: Bentheburrito <github.arson718@passmail.net>
Date: Tue, 11 Mar 2025 22:59:48 -0700
Subject: [PATCH 2/8] mnesia: add'l select_reverse coverage, docs, and
mnesia_frag impl
also fix an apparent bug in ext_test_server's repair_continuation
handle_call clause
---
lib/mnesia/doc/guides/mnesia_app_b.md | 10 +++
lib/mnesia/doc/guides/mnesia_chap4.md | 9 +++
lib/mnesia/src/mnesia_frag.erl | 9 +++
lib/mnesia/test/ext_test.erl | 15 ++++-
lib/mnesia/test/ext_test_server.erl | 61 ++++++++++++-----
lib/mnesia/test/mnesia_evil_coverage_test.erl | 6 ++
lib/mnesia/test/mnesia_frag_test.erl | 35 ++++++++++
lib/mnesia/test/mnesia_trans_access_test.erl | 66 +++++++++++++++++--
8 files changed, 190 insertions(+), 21 deletions(-)
diff --git a/lib/mnesia/doc/guides/mnesia_app_b.md b/lib/mnesia/doc/guides/mnesia_app_b.md
index 926d1f0e46..bddbd4a26e 100644
--- a/lib/mnesia/doc/guides/mnesia_app_b.md
+++ b/lib/mnesia/doc/guides/mnesia_app_b.md
@@ -32,6 +32,8 @@ limitations under the License.
write/5, delete/5, delete_object/5,
read/5, match_object/5, all_keys/4,
select/5, select/6, select_cont/3,
+ select_cont/4, select_reverse/5,
+ select_reverse/6,
index_match_object/6, index_read/6,
foldl/6, foldr/6, table_info/4,
first/3, next/4, prev/4, last/3,
@@ -82,6 +84,14 @@ select(ActivityId, Opaque, Tab, MatchSpec, LockKind) ->
select(ActivityId, Opaque, Tab, MatchSpec, Limit, LockKind) ->
init_select(ActivityId, Opaque, Tab, MatchSpec, Limit, LockKind).
+select_reverse(ActivityId, Opaque, Tab, MatchSpec, LockKind) ->
+ select(ActivityId, Opaque, Tab, MatchSpec, LockKind).
+
+select_reverse(ActivityId, Opaque, Tab, MatchSpec, Limit, LockKind) ->
+ select(ActivityId, Opaque, Tab, MatchSpec, Limit, LockKind).
+
+select_cont(Tid,Ts,Cont,_Dir) -> select_cont(Tid,Ts,Cont).
+
select_cont(_Tid,_,{frag_cont, '$end_of_table', [],_}) -> '$end_of_table';
select_cont(Tid,Ts,{frag_cont, '$end_of_table', [{Tab,Node,Type}|Rest],Args}) ->
{Spec,LockKind,Limit} = Args,
diff --git a/lib/mnesia/doc/guides/mnesia_chap4.md b/lib/mnesia/doc/guides/mnesia_chap4.md
index cfc9217029..e50b21b60a 100644
--- a/lib/mnesia/doc/guides/mnesia_chap4.md
+++ b/lib/mnesia/doc/guides/mnesia_chap4.md
@@ -453,6 +453,8 @@ locks are acquired. The following functions are available:
equivalent of `mnesia:match_object/1`.
- [`mnesia:dirty_select(Tab, Pat)`](`mnesia:dirty_select/2`) is the dirty
equivalent of `mnesia:select/2`.
+- [`mnesia:dirty_select_reverse(Tab, Pat)`](`mnesia:dirty_select_reverse/2`) is
+ the dirty equivalent of `mnesia:select_reverse/2`.
- [`mnesia:dirty_index_match_object(Pat, Pos)`](`mnesia:dirty_index_match_object/2`)
is the dirty equivalent of `mnesia:index_match_object/2`.
- [`mnesia:dirty_index_read(Tab, SecondaryKey, Pos)`](`mnesia:dirty_index_read/3`)
@@ -577,6 +579,8 @@ operations, as listed here, can be passed on as arguments to the function
- `mnesia:read/3` (`mnesia:read/1`, `mnesia:wread/1`)
- [`mnesia:match_object/2`](`mnesia:match_object/3`) (`mnesia:match_object/1`)
- [`mnesia:select/3`](`mnesia:select/2`) (`mnesia:select/2`)
+- [`mnesia:select_reverse/3`](`mnesia:select_reverse/2`)
+ (`mnesia:select_reverse/2`)
- `mnesia:foldl/3` (`mnesia:foldl/4`, `mnesia:foldr/3`, `mnesia:foldr/4`)
- `mnesia:all_keys/1`
- `mnesia:index_match_object/4` (`mnesia:index_match_object/2`)
@@ -845,6 +849,11 @@ or less results than specified with `NObjects` can be returned in the result
list, even the empty list can be returned even if there are more results to
collect.
+There is also [`select_reverse/1,2,3,4`](`mnesia:select_reverse/2`), should you
+want to traverse from the end of the result set. This is only applicable to
+`ram_copies` or `disc_copies` tables of type `ordered_set`. For other table
+configurations, it behaves the same as `mnesia:select/1,2,3,4`.
+
> #### Warning {: .warning }
>
> There is a severe performance penalty in using `mnesia:select/1,2,3,4` after
diff --git a/lib/mnesia/src/mnesia_frag.erl b/lib/mnesia/src/mnesia_frag.erl
index 6973fe1a5f..d0422d17d4 100644
--- a/lib/mnesia/src/mnesia_frag.erl
+++ b/lib/mnesia/src/mnesia_frag.erl
@@ -37,6 +37,8 @@
write/5, delete/5, delete_object/5,
read/5, match_object/5, all_keys/4,
select/5,select/6,select_cont/3,
+ select_cont/4, select_reverse/5,
+ select_reverse/6,
index_match_object/6, index_read/6,
foldl/6, foldr/6, table_info/4,
first/3, next/4, prev/4, last/3,
@@ -118,6 +120,11 @@ select(ActivityId, Opaque, Tab, MatchSpec, LockKind) ->
select(ActivityId, Opaque, Tab, MatchSpec, Limit, LockKind) ->
init_select(ActivityId, Opaque, Tab, MatchSpec, Limit, LockKind).
+select_reverse(ActivityId, Opaque, Tab, MatchSpec, LockKind) ->
+ select(ActivityId, Opaque, Tab, MatchSpec, LockKind).
+
+select_reverse(ActivityId, Opaque, Tab, MatchSpec, Limit, LockKind) ->
+ select(ActivityId, Opaque, Tab, MatchSpec, Limit, LockKind).
all_keys(ActivityId, Opaque, Tab, LockKind) ->
Match = [mnesia:all_keys(ActivityId, Opaque, Frag, LockKind)
@@ -336,6 +343,8 @@ init_select(Tid,Opaque,Tab,Pat,Limit,LockKind) ->
frag_sel_cont(Res, NameNodes, {Pat,LockKind,Limit})
end.
+select_cont(Tid,Ts,Cont,_Dir) -> select_cont(Tid,Ts,Cont).
+
select_cont(_Tid,_,{frag_cont, '$end_of_table', [],_}) -> '$end_of_table';
select_cont(Tid,Ts,{frag_cont, '$end_of_table', [{Tab,Node,Type}|Rest],Args}) ->
{Spec,LockKind,Limit} = Args,
diff --git a/lib/mnesia/test/ext_test.erl b/lib/mnesia/test/ext_test.erl
index 711503fc87..92d073751d 100644
--- a/lib/mnesia/test/ext_test.erl
+++ b/lib/mnesia/test/ext_test.erl
@@ -49,7 +49,8 @@
insert/3, update_counter/4,
lookup/3,
delete/3, match_delete/3,
- select/1, select/3, select/4, repair_continuation/2
+ select/1, select/3, select/4, select_reverse/1,
+ select_reverse/3, select_reverse/4, repair_continuation/2
]).
semantics(ext_ram_copies, storage) -> ram_copies;
@@ -248,6 +249,18 @@ select(Alias, Tab, Ms, Limit) ->
?DBG({Alias, ext_test_server:tab_to_list(Tab), Ms, Limit}),
call({?FUNCTION_NAME, Alias, Tab, Ms, Limit}).
+select_reverse(Continuation) ->
+ ?DBG(Continuation),
+ call({?FUNCTION_NAME, Continuation}).
+
+select_reverse(Alias, Tab, Ms) ->
+ ?DBG({Alias, ext_test_server:tab_to_list(Tab), Ms}),
+ call({?FUNCTION_NAME, Alias, Tab, Ms}).
+
+select_reverse(Alias, Tab, Ms, Limit) ->
+ ?DBG({Alias, ext_test_server:tab_to_list(Tab), Ms, Limit}),
+ call({?FUNCTION_NAME, Alias, Tab, Ms, Limit}).
+
repair_continuation(Cont, Ms) ->
?DBG({Cont, Ms}),
call({?FUNCTION_NAME, Cont, Ms}).
diff --git a/lib/mnesia/test/ext_test_server.erl b/lib/mnesia/test/ext_test_server.erl
index 7a87895e80..7e022da266 100644
--- a/lib/mnesia/test/ext_test_server.erl
+++ b/lib/mnesia/test/ext_test_server.erl
@@ -123,28 +123,37 @@ receive_data(Data, Alias, Tab, Sender, {Name, Sender} = MnesiaState, State) ->
?DBG({Data, Alias, tab_to_list(Tab), State}),
receive_data(Data, Alias, Tab, Sender, {Name, Tab, Sender}, State).
-select(Alias, Tab, Ms, State) ->
- Res = select(Alias, Tab, Ms, 100000, State),
- select_1(Alias, Res).
+select(Alias, Tab, Ms, State, Dir) ->
+ Res = select(Alias, Tab, Ms, 100000, State, Dir),
+ select_1(Alias, Res, Dir).
-select_1(_Alias, '$end_of_table') -> [];
-select_1(ext_ram_copies, {Acc, C}) ->
+select_1(_Alias, '$end_of_table', _Dir) -> [];
+select_1(ext_ram_copies, {Acc, C}, forward) ->
case ets:select(C) of
'$end_of_table' -> Acc;
{New, Cont} ->
- select_1(ext_ram_copies, {New ++ Acc, Cont})
+ select_1(ext_ram_copies, {New ++ Acc, Cont}, forward)
end;
-select_1(ext_disc_only_copies, {Acc, C}) ->
+select_1(ext_ram_copies, {Acc, C}, reverse) ->
+ case ets:select_reverse(C) of
+ '$end_of_table' -> Acc;
+ {New, Cont} ->
+ select_1(ext_ram_copies, {New ++ Acc, Cont}, reverse)
+ end;
+select_1(ext_disc_only_copies, {Acc, C}, Dir) ->
case dets:select(C) of
'$end_of_table' -> Acc;
{New, Cont} ->
- select_1(ext_disc_only_copies, {New ++ Acc, Cont})
+ select_1(ext_disc_only_copies, {New ++ Acc, Cont}, Dir)
end.
-select(ext_ram_copies, Tab, Ms, Limit, State) when is_integer(Limit); Limit =:= infinity ->
+select(ext_ram_copies, Tab, Ms, Limit, State, forward) when is_integer(Limit); Limit =:= infinity ->
?DBG({ext_ram_copies, tab_to_list(Tab), Ms, Limit}),
ets:select(tab_to_tid(Tab, State), Ms, Limit);
-select(ext_disc_only_copies, Tab, Ms, Limit, State) when is_integer(Limit); Limit =:= infinity ->
+select(ext_ram_copies, Tab, Ms, Limit, State, reverse) when is_integer(Limit); Limit =:= infinity ->
+ ?DBG({ext_ram_copies, tab_to_list(Tab), Ms, Limit}),
+ ets:select_reverse(tab_to_tid(Tab, State), Ms, Limit);
+select(ext_disc_only_copies, Tab, Ms, Limit, State, _Dir) when is_integer(Limit); Limit =:= infinity ->
?DBG({ext_disc_only_copies, tab_to_list(Tab), Ms, Limit}),
dets:select(tab_to_tid(Tab, State), Ms, Limit).
@@ -377,12 +386,34 @@ handle_call({select, C}, _From, State) ->
handle_call({select, Alias, Tab, Ms}, _From, State) ->
?DBG({select, Alias, tab_to_list(Tab), Ms}),
- Res = ?TRY(select(Alias, Tab, Ms, State)),
+ Res = ?TRY(select(Alias, Tab, Ms, State, forward)),
{reply, Res, State};
handle_call({select, Alias, Tab, Ms, Limit}, _From, State) ->
?DBG({select, Alias, tab_to_list(Tab), Ms, Limit}),
- Res = ?TRY(select(Alias, Tab, Ms, Limit, State)),
+ Res = ?TRY(select(Alias, Tab, Ms, Limit, State, forward)),
+ {reply, Res, State};
+
+handle_call({select_reverse, '$end_of_table' = End}, _From, State) ->
+ ?DBG({select_reverse, End}),
+ {reply, End, State};
+handle_call({select_reverse, C}, _From, State) when element(1, C) == dets_cont ->
+ ?DBG({select_reverse, {ext_disc_only_copies, C}}),
+ Res = ?TRY(dets:select(C)),
+ {reply, Res, State};
+handle_call({select_reverse, C}, _From, State) ->
+ ?DBG({select_reverse, {ext_ram_copies, C}}),
+ Res = ?TRY(ets:select_reverse(C)),
+ {reply, Res, State};
+
+handle_call({select_reverse, Alias, Tab, Ms}, _From, State) ->
+ ?DBG({select_reverse, Alias, tab_to_list(Tab), Ms}),
+ Res = ?TRY(select(Alias, Tab, Ms, State, reverse)),
+ {reply, Res, State};
+
+handle_call({select_reverse, Alias, Tab, Ms, Limit}, _From, State) ->
+ ?DBG({select_reverse, Alias, tab_to_list(Tab), Ms, Limit}),
+ Res = ?TRY(select(Alias, Tab, Ms, Limit, State, reverse)),
{reply, Res, State};
handle_call({repair_continuation, '$end_of_table' = Cont, _Ms}, _From, State) ->
@@ -392,8 +423,8 @@ handle_call({repair_continuation, Cont, Ms}, _From, State) when element(1, Cont)
?DBG({repair_continuation, ext_disc_only_copies, Cont, Ms}),
Res = ?TRY(dets:repair_continuation(Cont, Ms)),
{reply, Res, State};
-handle_call({repair_continuation, ext_ram_copies, Cont, Ms}, _From, State) ->
- ?DBG({repair_continuation, Cont, Ms}),
+handle_call({repair_continuation, Cont, Ms}, _From, State) ->
+ ?DBG({repair_continuation, ext_ram_copies, Cont, Ms}),
Res = ?TRY(ets:repair_continuation(Cont, Ms)),
{reply, Res, State}.
@@ -427,4 +458,4 @@ tab_to_filename(Tab) ->
tab_to_tid(Tab, #state{tables = Tables}) ->
Table = maps:get(Tab, Tables),
- Table#table.tid.
\ No newline at end of file
+ Table#table.tid.
diff --git a/lib/mnesia/test/mnesia_evil_coverage_test.erl b/lib/mnesia/test/mnesia_evil_coverage_test.erl
index 1d5e30211b..31df7a5864 100644
--- a/lib/mnesia/test/mnesia_evil_coverage_test.erl
+++ b/lib/mnesia/test/mnesia_evil_coverage_test.erl
@@ -2476,9 +2476,11 @@ record_name_dirty_access(Storage, Config) ->
?match(ok, mnesia:dirty_delete_object(Tab, {RecName, 2, 21})),
Tens = ?sort([{RecName, 1, 10}, {RecName, 3, 10}]),
+ RevTens = lists:reverse(Tens),
TenPat = {RecName, '_', 10},
?match(Tens, ?sort(mnesia:dirty_match_object(Tab, TenPat))),
?match(Tens, ?sort(mnesia:dirty_select(Tab, [{TenPat, [], ['$_']}]))),
+ ?match(RevTens, lists:reverse(?sort(mnesia:dirty_select_reverse(Tab, [{TenPat, [], ['$_']}])))),
%% Subscription test
E = mnesia_table_event,
@@ -2510,6 +2512,10 @@ record_name_dirty_access(Storage, Config) ->
?match(Twos, ?sort(mnesia:dirty_select(Tab,
[{mnesia:table_info(Tab, wild_pattern),
[],['$_']}]))),
+ RevTwos = lists:reverse(Twos),
+ ?match(RevTwos, lists:reverse(?sort(mnesia:dirty_select_reverse(Tab,
+ [{mnesia:table_info(Tab, wild_pattern),
+ [],['$_']}])))),
%% Traverse backup test
diff --git a/lib/mnesia/test/mnesia_frag_test.erl b/lib/mnesia/test/mnesia_frag_test.erl
index f2553faa07..09a9486f3e 100644
--- a/lib/mnesia/test/mnesia_frag_test.erl
+++ b/lib/mnesia/test/mnesia_frag_test.erl
@@ -701,6 +701,7 @@ evil_loop(Config) when is_list(Config) ->
Match = fun(Tab) -> mnesia:match_object(Tab, {'_', '_', '_'}, write) end,
Select = fun(Tab) -> mnesia:select(Tab, [{'_', [], ['$_']}]) end,
+ SelectRev = fun(Tab) -> mnesia:select_reverse(Tab, [{'_', [], ['$_']}]) end,
Trans = fun(Fun, Args) -> mnesia:activity(transaction, Fun, Args, mnesia_frag) end,
LoopHelp = fun('$end_of_table',_) ->
[];
@@ -712,6 +713,16 @@ evil_loop(Config) when is_list(Config) ->
Sel = mnesia:select(Table, [{'_', [], ['$_']}], 1, read),
LoopHelp(Sel, LoopHelp)
end,
+ RevLoopHelp = fun('$end_of_table',_) ->
+ [];
+ ({Res,Cont},Fun) ->
+ Sel = mnesia:select_reverse(Cont),
+ Res ++ Fun(Sel, Fun)
+ end,
+ SelRevLoop = fun(Table) ->
+ Sel = mnesia:select_reverse(Table, [{'_', [], ['$_']}], 1, read),
+ RevLoopHelp(Sel, RevLoopHelp)
+ end,
R1 = {RecName, 2, 2},
R2 = {RecName, 4, 4},
@@ -725,13 +736,19 @@ evil_loop(Config) when is_list(Config) ->
end,
S1 = lists:sort([R1, R2| Recs]),
?match(S1, sort_res(Trans(W1, [Tab1, Select]))),
+ ?match(S1, sort_res(Trans(W1, [Tab1, SelectRev]))),
?match(S1, sort_res(Trans(W1, [Tab1, Match]))),
?match(S1, sort_res(Trans(W1, [Tab1, SelLoop]))),
+ ?match(S1, sort_res(Trans(W1, [Tab1, SelRevLoop]))),
?match(S1, sort_res(Trans(W1, [Tab2, Select]))),
+ ?match(S1, sort_res(Trans(W1, [Tab2, SelectRev]))),
?match(S1, sort_res(Trans(W1, [Tab2, SelLoop]))),
+ ?match(S1, sort_res(Trans(W1, [Tab2, SelRevLoop]))),
?match(S1, sort_res(Trans(W1, [Tab2, Match]))),
?match(S1, sort_res(Trans(W1, [Tab3, Select]))),
+ ?match(S1, sort_res(Trans(W1, [Tab3, SelectRev]))),
?match(S1, sort_res(Trans(W1, [Tab3, SelLoop]))),
+ ?match(S1, sort_res(Trans(W1, [Tab3, SelRevLoop]))),
?match(S1, sort_res(Trans(W1, [Tab3, Match]))),
[mnesia:dirty_delete_object(Frag, R) || R <- [R1, R2],
Tab <- Tabs,
@@ -746,14 +763,20 @@ evil_loop(Config) when is_list(Config) ->
S2Bag = lists:sort([R1, R3 | Recs]),
io:format("S2 = ~p\n", [S2]),
?match(S2, sort_res(Trans(W2, [Tab1, Select]))),
+ ?match(S2, sort_res(Trans(W2, [Tab1, SelectRev]))),
?match(S2, sort_res(Trans(W2, [Tab1, SelLoop]))),
+ ?match(S2, sort_res(Trans(W2, [Tab1, SelRevLoop]))),
?match(S2, sort_res(Trans(W2, [Tab1, Match]))),
?match(S2, sort_res(Trans(W2, [Tab2, Select]))),
+ ?match(S2, sort_res(Trans(W2, [Tab2, SelectRev]))),
?match(S2, sort_res(Trans(W2, [Tab2, SelLoop]))),
+ ?match(S2, sort_res(Trans(W2, [Tab2, SelRevLoop]))),
?match(S2, sort_res(Trans(W2, [Tab2, Match]))),
io:format("S2Bag = ~p\n", [S2Bag]),
?match(S2Bag, sort_res(Trans(W2, [Tab3, Select]))),
+ ?match(S2Bag, sort_res(Trans(W2, [Tab3, SelectRev]))),
?match(S2Bag, sort_res(Trans(W2, [Tab3, SelLoop]))),
+ ?match(S2Bag, sort_res(Trans(W2, [Tab3, SelRevLoop]))),
?match(S2Bag, sort_res(Trans(W2, [Tab3, Match]))),
W3 = fun(Tab,Search) ->
@@ -764,13 +787,19 @@ evil_loop(Config) when is_list(Config) ->
S3Bag = lists:sort([R4 | lists:delete(R1, Recs)]),
S3 = lists:delete({RecName, 3, 3}, S3Bag),
?match(S3, sort_res(Trans(W3, [Tab1, Select]))),
+ ?match(S3, sort_res(Trans(W3, [Tab1, SelectRev]))),
?match(S3, sort_res(Trans(W3, [Tab1, SelLoop]))),
+ ?match(S3, sort_res(Trans(W3, [Tab1, SelRevLoop]))),
?match(S3, sort_res(Trans(W3, [Tab1, Match]))),
?match(S3, sort_res(Trans(W3, [Tab2, SelLoop]))),
+ ?match(S3, sort_res(Trans(W3, [Tab2, SelRevLoop]))),
?match(S3, sort_res(Trans(W3, [Tab2, Select]))),
+ ?match(S3, sort_res(Trans(W3, [Tab2, SelectRev]))),
?match(S3, sort_res(Trans(W3, [Tab2, Match]))),
?match(S3Bag, sort_res(Trans(W3, [Tab3, Select]))),
+ ?match(S3Bag, sort_res(Trans(W3, [Tab3, SelectRev]))),
?match(S3Bag, sort_res(Trans(W3, [Tab3, SelLoop]))),
+ ?match(S3Bag, sort_res(Trans(W3, [Tab3, SelRevLoop]))),
?match(S3Bag, sort_res(Trans(W3, [Tab3, Match]))),
W4 = fun(Tab,Search) ->
@@ -787,13 +816,19 @@ evil_loop(Config) when is_list(Config) ->
S4Bag = lists:sort([R5 | S3Bag]),
S4 = lists:sort([R5 | S3]),
?match(S4, sort_res(Trans(W4, [Tab1, Select]))),
+ ?match(S4, sort_res(Trans(W4, [Tab1, SelectRev]))),
?match(S4, sort_res(Trans(W4, [Tab1, SelLoop]))),
+ ?match(S4, sort_res(Trans(W4, [Tab1, SelRevLoop]))),
?match(S4, sort_res(Trans(W4, [Tab1, Match]))),
?match(S4, sort_res(Trans(W4, [Tab2, Select]))),
+ ?match(S4, sort_res(Trans(W4, [Tab2, SelectRev]))),
?match(S4, sort_res(Trans(W4, [Tab2, SelLoop]))),
+ ?match(S4, sort_res(Trans(W4, [Tab2, SelRevLoop]))),
?match(S4, sort_res(Trans(W4, [Tab2, Match]))),
?match(S4Bag, sort_res(Trans(W4, [Tab3, Select]))),
+ ?match(S4Bag, sort_res(Trans(W4, [Tab3, SelectRev]))),
?match(S4Bag, sort_res(Trans(W4, [Tab3, SelLoop]))),
+ ?match(S4Bag, sort_res(Trans(W4, [Tab3, SelRevLoop]))),
?match(S4Bag, sort_res(Trans(W4, [Tab3, Match]))),
[mnesia:dirty_delete_object(Tab, R) || R <- [{RecName, 3, 3}, R5], Tab <- Tabs],
diff --git a/lib/mnesia/test/mnesia_trans_access_test.erl b/lib/mnesia/test/mnesia_trans_access_test.erl
index 1dddd3ed34..3347388d28 100644
--- a/lib/mnesia/test/mnesia_trans_access_test.erl
+++ b/lib/mnesia/test/mnesia_trans_access_test.erl
@@ -30,7 +30,8 @@
-export([write/1, read/1, wread/1, delete/1,
delete_object_bag/1, delete_object_set/1,
- match_object/1, select/1, select14/1, select_reverse/1, select_reverse14/1, all_keys/1,
+ match_object/1, select/1, select14/1, select_reverse/1,
+ select_reverse14/1, select_reverse_index/1, all_keys/1,
transaction/1, transaction_counters/1,
basic_nested/1, mix_of_nested_activities/1,
nested_trans_both_ok/1, nested_trans_child_dies/1,
@@ -68,7 +69,8 @@ end_per_testcase(Func, Conf) ->
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
all() ->
[write, read, wread, delete, delete_object_bag, delete_object_set,
- match_object, select, select14, select_reverse, select_reverse14, all_keys,
+ match_object, select, select14, select_reverse, select_reverse14,
+ select_reverse_index, all_keys,
transaction, transaction_counters,
{group, nested_activities}, {group, index_tabs},
{group, index_lifecycle}].
@@ -477,7 +479,10 @@ select_reverse(Config) when is_list(Config) ->
OneRec = {Tab, 1, 2},
TwoRec = {Tab, 2, 3},
+ ThreeRec = {Tab, 3, 4},
OnePat = [{{Tab, '$1', 2}, [], ['$_']}],
+ All = [ThreeRec,TwoRec,OneRec],
+ AllPat = [{'_', [], ['$_']}],
?match({atomic, []},
mnesia:transaction(fun() -> mnesia:select_reverse(Tab, OnePat) end)),
?match({atomic, ok},
@@ -486,6 +491,13 @@ select_reverse(Config) when is_list(Config) ->
mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
?match({atomic, [OneRec]},
mnesia:transaction(fun() -> mnesia:select_reverse(Tab, OnePat) end)),
+ %% Need to rpc:call here, as mnesia:ets assumes the ETS table is local to the node
+ ?match([OneRec], rpc:call(Node1, mnesia, ets, [fun() -> mnesia:select_reverse(Tab, OnePat) end])),
+
+ ?match({atomic, [OneRec]}, mnesia:transaction(fun() ->
+ mnesia:write(ThreeRec),
+ mnesia:select_reverse(Tab, OnePat)
+ end)),
?match({aborted, _},
mnesia:transaction(fun() -> mnesia:select_reverse(Tab, {match, '$1', 2}) end)),
@@ -503,10 +515,12 @@ select_reverse14(Config) when is_list(Config) ->
Tab2 = select_reverse14_dets,
Tab3 = select_reverse14_remote,
Tab4 = select_reverse14_remote_dets,
+ Tab5 = select_reverse14_xets,
Schemas = [[{name, Tab1}, {type, ordered_set}, {attributes, [k, v]}, {ram_copies, [Node1]}],
[{name, Tab2}, {attributes, [k, v]}, {disc_only_copies, [Node1]}],
[{name, Tab3}, {type, ordered_set}, {attributes, [k, v]}, {ram_copies, [Node2]}],
- [{name, Tab4}, {attributes, [k, v]}, {disc_only_copies, [Node2]}]],
+ [{name, Tab4}, {attributes, [k, v]}, {disc_only_copies, [Node2]}],
+ [{name, Tab5}, {type, ordered_set}, {attributes, [k, v]}, {ext_ram_copies, [Node1]}]],
[?match({atomic, ok}, mnesia:create_table(Schema)) || Schema <- Schemas],
%% Some Helpers
@@ -535,12 +549,13 @@ select_reverse14(Config) when is_list(Config) ->
AllPat = [{'_', [], ['$_']}],
?match({atomic, []}, Trans(fun() -> Loop(Tab, OnePat) end)),
- ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
- ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
+ ?match({atomic, ok}, mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, [OneRec]}, mnesia:transaction(fun() -> mnesia:write(TwoRec), Loop(Tab, OnePat) end)),
?match({atomic, [OneRec]}, Trans(fun() -> Loop(Tab, OnePat) end)),
?match({atomic, All}, Trans(fun() -> Loop(Tab, AllPat) end)),
{atomic,{_, ContOne}} = Trans(fun() -> mnesia:select_reverse(Tab, OnePat, 1, read) end),
+ ?match({'EXIT', {aborted, no_transaction}}, mnesia:select_reverse(ContOne)),
?match({aborted, wrong_transaction}, Trans(fun() -> mnesia:select_reverse(ContOne) end)),
?match('$end_of_table', Dirty(fun() -> mnesia:select_reverse(ContOne) end)),
@@ -571,6 +586,47 @@ select_reverse14(Config) when is_list(Config) ->
Test(Tab2, dets),
Test(Tab3, ets),
Test(Tab4, dets),
+ Test(Tab5, ets),
+
+ OneRec = {Tab1, 1, 2},
+ TwoRec = {Tab1, 2, 3},
+ All = [TwoRec,OneRec],
+ AllPat = [{'_', [], ['$_']}],
+ ?match(All, rpc:call(Node1, mnesia, ets, [fun() -> Loop(Tab1, AllPat) end])),
+
+ ?verify_mnesia(Nodes, []).
+
+%% select_reverse, lookup with an index
+select_reverse_index(suite) -> [];
+select_reverse_index(Config) when is_list(Config) ->
+ [Node1] = Nodes = ?acquire_nodes(1, Config),
+ Tab1 = select_reverse_index_tab_ordset,
+ Tab2 = select_reverse_index_tab_set,
+ Schema1 = [{name, Tab1}, {attributes, [k, v, alt_key]}, {index, [alt_key]},
+ {type, ordered_set}, {ram_copies, [Node1]}],
+ Schema2 = [{name, Tab2}, {attributes, [k, v, alt_key]}, {index, [alt_key]},
+ {type, set}, {ram_copies, [Node1]}],
+ ?match({atomic, ok}, mnesia:create_table(Schema1)),
+ ?match({atomic, ok}, mnesia:create_table(Schema2)),
+
+ Test =
+ fun(Tab) ->
+ OneRec = {Tab, 1, 2, 3},
+ TwoRec = {Tab, 2, 3, 4},
+ OnePat = [{{Tab, '$1', '_', 3}, [], ['$_']}],
+ ?match({atomic, []},
+ mnesia:transaction(fun() -> mnesia:select_reverse(Tab, OnePat) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(OneRec) end)),
+ ?match({atomic, ok},
+ mnesia:transaction(fun() -> mnesia:write(TwoRec) end)),
+ ?match({atomic, [OneRec]},
+ mnesia:transaction(fun() -> mnesia:select_reverse(Tab, OnePat) end))
+ end,
+
+ Test(Tab1),
+ Test(Tab2),
+
?verify_mnesia(Nodes, []).
%% Pick all keys from table
--
2.43.0