aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/stdlib/test/ets_SUITE.erl161
1 files changed, 155 insertions, 6 deletions
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index ebf7dbff62..e0e8436a8c 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -40,7 +40,7 @@
-export([foldl_ordered/1, foldr_ordered/1, foldl/1, foldr/1, fold_empty/1]).
-export([t_delete_object/1, t_init_table/1, t_whitebox/1,
t_delete_all_objects/1, t_insert_list/1, t_test_ms/1,
- t_select_delete/1,t_ets_dets/1]).
+ t_select_delete/1,t_select_replace/1,t_ets_dets/1]).
-export([ordered/1, ordered_match/1, interface_equality/1,
fixtable_next/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1,
@@ -64,7 +64,7 @@
meta_lookup_named_read/1, meta_lookup_named_write/1,
meta_newdel_unnamed/1, meta_newdel_named/1]).
-export([smp_insert/1, smp_fixed_delete/1, smp_unfix_fix/1, smp_select_delete/1,
- otp_8166/1, otp_8732/1]).
+ smp_select_replace/1, otp_8166/1, otp_8732/1]).
-export([exit_large_table_owner/1,
exit_many_large_table_owner/1,
exit_many_tables_owner/1,
@@ -118,14 +118,14 @@ all() ->
update_counter_table_growth,
match_heavy, {group, fold}, member, t_delete_object,
t_init_table, t_whitebox, t_delete_all_objects,
- t_insert_list, t_test_ms, t_select_delete, t_ets_dets,
- memory, t_select_reverse, t_bucket_disappears,
+ t_insert_list, t_test_ms, t_select_delete, t_select_replace,
+ t_ets_dets, memory, t_select_reverse, t_bucket_disappears,
select_fail, t_insert_new, t_repair_continuation,
otp_5340, otp_6338, otp_6842_select_1000, otp_7665,
otp_8732, meta_wb, grow_shrink, grow_pseudo_deleted,
shrink_pseudo_deleted, {group, meta_smp}, smp_insert,
- smp_fixed_delete, smp_unfix_fix, smp_select_delete,
- otp_8166, exit_large_table_owner,
+ smp_fixed_delete, smp_unfix_fix, smp_select_replace,
+ smp_select_delete, otp_8166, exit_large_table_owner,
exit_many_large_table_owner, exit_many_tables_owner,
exit_many_many_tables_owner, write_concurrency, heir,
give_away, setopts, bad_table, types,
@@ -1136,6 +1136,119 @@ t_select_delete(Config) when is_list(Config) ->
lists:foreach(fun(Tab) -> ets:delete(Tab) end,Tables),
verify_etsmem(EtsMem).
+%% Tests the ets:select_replace/2 BIF
+t_select_replace(Config) when is_list(Config) ->
+ EtsMem = etsmem(),
+ Tables = fill_sets_int(10000) ++ fill_sets_int(10000, [{write_concurrency,true}]),
+ lists:foreach(
+ fun(Table) ->
+ TableType = ets:info(Table, type),
+
+ % Replacements are differently-sized objects
+ MatchSpec1_A = [{{'$1','$2'},
+ [{'<', {'rem', '$1', 5}, 2}],
+ [{{'$1', [$x | '$2'], stuff}}]}],
+ MatchSpec1_B = [{{'$1','$2','_'},
+ [],
+ [{{'$1','$2'}}]}],
+ 4000 = ets:select_replace(Table, MatchSpec1_A),
+ 4000 = ets:select_replace(Table, MatchSpec1_B),
+
+ % Replacement changes key to float equivalent
+ MatchSpec2 = [{{'$1', '$2'},
+ [{'=:=', {'band', '$1', 2#11}, 2#11},
+ {'=/=', {'hd', '$2'}, $x}],
+ [{{{'*', '$1', 1.0}, '$2'}}]}],
+ case TableType of
+ ordered_set -> 1500 = ets:select_replace(Table, MatchSpec2);
+ set -> 0 = ets:select_replace(Table, MatchSpec2);
+ bag -> 0 = ets:select_replace(Table, MatchSpec2);
+ duplicate_bag -> 0 = ets:select_replace(Table, MatchSpec2)
+ end,
+
+ % Replacement is an equal object
+ MatchSpec3 = [{{'$1', '$2'},
+ [{'>', {'rem', '$1', 5}, 3}],
+ [{{'$1', '$2'}}]}],
+ case TableType of
+ ordered_set -> 1500 = ets:select_replace(Table, MatchSpec3);
+ set -> 2000 = ets:select_replace(Table, MatchSpec3);
+ bag -> 2000 = ets:select_replace(Table, MatchSpec3);
+ duplicate_bag -> 2000 = ets:select_replace(Table, MatchSpec3)
+ end,
+
+ check(Table,
+ fun ({N, [$x, C | _]}) when ((N rem 5) < 2) -> (C >= $0) andalso (C =< $9);
+ ({N, [C | _]}) when is_float(N) -> (C >= $0) andalso (C =< $9);
+ ({N, [C | _]}) when ((N rem 5) > 3) -> (C >= $0) andalso (C =< $9);
+ ({_, [C | _]}) -> (C >= $0) andalso (C =< $9)
+ end,
+ 10000),
+
+ % Replace unbound range (>)
+ MatchSpec4 = [{{'$1', '$2'},
+ [{'>', '$1', 7000}],
+ [{{'$1', {{gt_range, '$2'}}}}]}],
+ case TableType of
+ ordered_set -> 3000 = ets:select_replace(Table, MatchSpec4);
+ set -> 3000 = ets:select_replace(Table, MatchSpec4);
+ bag -> 3000 = ets:select_replace(Table, MatchSpec4);
+ duplicate_bag -> 3000 = ets:select_replace(Table, MatchSpec4)
+ end,
+
+ % Replace unbound range (<)
+ MatchSpec5 = [{{'$1', '$2'},
+ [{'<', '$1', 3000}],
+ [{{'$1', {{le_range, '$2'}}}}]}],
+ case TableType of
+ ordered_set -> 2999 = ets:select_replace(Table, MatchSpec5);
+ set -> 2999 = ets:select_replace(Table, MatchSpec5);
+ bag -> 2998 = ets:select_replace(Table, MatchSpec5);
+ duplicate_bag -> 2998 = ets:select_replace(Table, MatchSpec5)
+ end,
+
+ % Replace bound range
+ MatchSpec6 = [{{'$1', '$2'},
+ [{'>=', '$1', 3001},
+ {'<', '$1', 7000}],
+ [{{'$1', {{range, '$2'}}}}]}],
+ case TableType of
+ ordered_set -> 3999 = ets:select_replace(Table, MatchSpec6);
+ set -> 3999 = ets:select_replace(Table, MatchSpec6);
+ bag -> 3998 = ets:select_replace(Table, MatchSpec6);
+ duplicate_bag -> 3998 = ets:select_replace(Table, MatchSpec6)
+ end,
+
+ % Replace particular keys
+ MatchSpec7 = [{{'$1', '$2'},
+ [{'==', '$1', 3000}],
+ [{{'$1', {{specific1, '$2'}}}}]},
+ {{'$1', '$2'},
+ [{'==', '$1', 7000}],
+ [{{'$1', {{specific2, '$2'}}}}]}],
+ case TableType of
+ ordered_set -> 2 = ets:select_replace(Table, MatchSpec7);
+ set -> 2 = ets:select_replace(Table, MatchSpec7);
+ bag -> 4 = ets:select_replace(Table, MatchSpec7);
+ duplicate_bag -> 4 = ets:select_replace(Table, MatchSpec7)
+ end,
+
+ check(Table,
+ fun ({N, {gt_range, _}}) -> N > 7000;
+ ({N, {le_range, _}}) -> N < 3000;
+ ({N, {range, _}}) -> (N >= 3001) andalso (N < 7000);
+ ({N, {specific1, _}}) -> N == 3000;
+ ({N, {specific2, _}}) -> N == 7000
+ end,
+ 10000),
+
+ 10000 = ets:select_delete(Table, [{'_',[],[true]}]),
+ check(Table, fun (_) -> false end, 0)
+ end,
+ Tables),
+ lists:foreach(fun(Tab) -> ets:delete(Tab) end,Tables),
+ verify_etsmem(EtsMem).
+
%% Test that partly bound keys gives faster matches.
partly_bound(Config) when is_list(Config) ->
case os:type() of
@@ -5419,6 +5532,42 @@ smp_select_delete(Config) when is_list(Config) ->
false = ets:info(T,fixed),
ets:delete(T).
+smp_select_replace(Config) when is_list(Config) ->
+ lists:foreach(
+ fun (TableType) ->
+ T = ets_new(smp_select_replace, [TableType, named_table, public,
+ {write_concurrency, true}]),
+ WorkerCount = 20,
+ CounterIterations = 10000,
+ InitF = fun (_) -> no_state end,
+ ExecF = fun (State) ->
+ lists:foreach(
+ fun F(IterId) ->
+ CounterId = rand:uniform(WorkerCount),
+ Match = [{{'$1', '$2'},
+ [{'=:=', '$1', CounterId}],
+ [{{'$1', {'+', '$2', 1}}}]}],
+ case ets:select_replace(T, Match) of
+ 1 -> ok;
+ 0 ->
+ ets:insert_new(T, {CounterId, 1}) orelse
+ F(IterId)
+ end
+ end,
+ lists:seq(1, CounterIterations)),
+ State
+ end,
+ FiniF = fun (State) -> State end,
+ run_workers_do(InitF, ExecF, FiniF, WorkerCount),
+ FinalCounts = ets:select(T, [{{'_', '$1'}, [], ['$1']}]),
+ TotalIterations = WorkerCount * CounterIterations * erlang:system_info(schedulers),
+ TotalIterations = lists:sum(FinalCounts),
+ WorkerCount = ets:select_delete(T, [{{'_', '_'}, [], [true]}]),
+ 0 = ets:info(T, size),
+ ets:delete(T)
+ end,
+ [ordered_set, set, bag, duplicate_bag]).
+
%% Test different types.
types(Config) when is_list(Config) ->
init_externals(),