Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:Ledest:erlang:26
erlang
2401-Add-ets-update_element-4-that-accepts-a-de...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 2401-Add-ets-update_element-4-that-accepts-a-default-obje.patch of Package erlang
From c888beb7f67b496fe85919700c4bd0eb0f3a6519 Mon Sep 17 00:00:00 2001 From: Jan Uhlig <juhlig@hnc-agency.org> Date: Wed, 8 Nov 2023 12:12:36 +0100 Subject: [PATCH] Add ets:update_element/4 that accepts a default object Co-authored-by: Maria Scott <maria-12648430@hnc-agency.org> --- erts/emulator/beam/bif.tab | 1 + erts/emulator/beam/erl_db.c | 70 ++++++++++++++++-------- lib/stdlib/doc/src/ets.xml | 10 ++++ lib/stdlib/src/erl_stdlib_errors.erl | 22 ++++++++ lib/stdlib/src/ets.erl | 18 ++++++- lib/stdlib/test/ets_SUITE.erl | 79 ++++++++++++++++++++++------ 6 files changed, 161 insertions(+), 39 deletions(-) diff --git a/erts/emulator/beam/bif.tab b/erts/emulator/beam/bif.tab index 614e8357c8..99344495c0 100644 --- a/erts/emulator/beam/bif.tab +++ b/erts/emulator/beam/bif.tab @@ -793,3 +793,4 @@ bif code:get_coverage_mode/1 bif code:get_coverage/2 bif code:reset_coverage/1 bif code:set_coverage_mode/1 +bif ets:update_element/4 diff --git a/erts/emulator/beam/erl_db.c b/erts/emulator/beam/erl_db.c index 8f5e1a9543..009a0d2dd4 100644 --- a/erts/emulator/beam/erl_db.c +++ b/erts/emulator/beam/erl_db.c @@ -1203,35 +1203,29 @@ BIF_RETTYPE ets_take_2(BIF_ALIST_2) BIF_RET(ret); } -/* -** update_element(Tab, Key, {Pos, Value}) -** update_element(Tab, Key, [{Pos, Value}]) -*/ -BIF_RETTYPE ets_update_element_3(BIF_ALIST_3) +static BIF_RETTYPE do_update_element(Process *p, DbTable *tb, + Eterm key, Eterm pos_val, Eterm default_obj) { - DbTable* tb; int cret = DB_ERROR_BADITEM; Eterm list; Eterm iter; - DeclareTmpHeap(cell,2,BIF_P); + DeclareTmpHeap(cell,2,p); DbUpdateHandle handle; - DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE_REC, BIF_ets_update_element_3); - - UseTmpHeap(2,BIF_P); + UseTmpHeap(2,p); if (!(tb->common.status & (DB_SET | DB_ORDERED_SET | DB_CA_ORDERED_SET))) { - BIF_P->fvalue = EXI_TAB_TYPE; + p->fvalue = EXI_TAB_TYPE; cret = DB_ERROR_BADPARAM; goto bail_out; } - if (is_tuple(BIF_ARG_3)) { - list = CONS(cell, BIF_ARG_3, NIL); + if (is_tuple(pos_val)) { + list = CONS(cell, pos_val, NIL); } else { - list = BIF_ARG_3; + list = pos_val; } - if (!tb->common.meth->db_lookup_dbterm(BIF_P, tb, BIF_ARG_2, THE_NON_VALUE, &handle)) { + if (!tb->common.meth->db_lookup_dbterm(p, tb, key, default_obj, &handle)) { cret = DB_ERROR_BADKEY; goto bail_out; } @@ -1256,12 +1250,13 @@ BIF_RETTYPE ets_update_element_3(BIF_ALIST_3) } position = signed_val(pvp[1]); if (position == tb->common.keypos) { - BIF_P->fvalue = EXI_KEY_POS; + p->fvalue = EXI_KEY_POS; cret = DB_ERROR_UNSPEC; goto finalize; } - if (position < 1 || position == tb->common.keypos || - position > arityval(handle.dbterm->tpl[0])) { + if (position < 1 || position > arityval(handle.dbterm->tpl[0])) { + p->fvalue = EXI_POSITION; + cret = DB_ERROR_UNSPEC; goto finalize; } } @@ -1278,7 +1273,7 @@ finalize: tb->common.meth->db_finalize_dbterm(cret, &handle); bail_out: - UnUseTmpHeap(2,BIF_P); + UnUseTmpHeap(2,p); db_unlock(tb, LCK_WRITE_REC); switch (cret) { @@ -1287,13 +1282,44 @@ bail_out: case DB_ERROR_BADKEY: BIF_RET(am_false); case DB_ERROR_SYSRES: - BIF_ERROR(BIF_P, SYSTEM_LIMIT); + BIF_ERROR(p, SYSTEM_LIMIT); case DB_ERROR_UNSPEC: - BIF_ERROR(BIF_P, BADARG | EXF_HAS_EXT_INFO); + BIF_ERROR(p, BADARG | EXF_HAS_EXT_INFO); default: break; } - BIF_ERROR(BIF_P, BADARG); + BIF_ERROR(p, BADARG); +} + +/* +** update_element(Tab, Key, {Pos, Value}) +** update_element(Tab, Key, [{Pos, Value}]) +*/ +BIF_RETTYPE ets_update_element_3(BIF_ALIST_3) +{ + DbTable* tb; + + DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE_REC, BIF_ets_update_element_3); + + return do_update_element(BIF_P, tb, BIF_ARG_2, BIF_ARG_3, THE_NON_VALUE); +} + +/* +** update_element(Tab, Key, {Pos, Value}, Default) +** update_element(Tab, Key, [{Pos, Value}], Default) +*/ +BIF_RETTYPE ets_update_element_4(BIF_ALIST_4) +{ + DbTable* tb; + + DB_BIF_GET_TABLE(tb, DB_WRITE, LCK_WRITE_REC, BIF_ets_update_element_4); + + if (is_not_tuple(BIF_ARG_4)) { + db_unlock(tb, LCK_WRITE_REC); + BIF_ERROR(BIF_P, BADARG); + } + + return do_update_element(BIF_P, tb, BIF_ARG_2, BIF_ARG_3, BIF_ARG_4); } static BIF_RETTYPE diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 855f38d2ae..cc9eb6d4a9 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -2294,13 +2294,16 @@ true</pre> <func> <name name="update_element" arity="3" clause_i="1" since=""/> + <name name="update_element" arity="4" clause_i="1" since="OTP 27.0"/> <name name="update_element" arity="3" clause_i="2" since=""/> + <name name="update_element" arity="4" clause_i="2" since="OTP 27.0"/> <fsummary>Update the <c>Pos</c>:th element of the object with a specified key in an ETS table.</fsummary> <type variable="Table"/> <type variable="Key"/> <type variable="Value"/> <type variable="Pos"/> + <type variable="Default"/> <desc> <p>This function provides an efficient way to update one or more elements within an object, without the trouble of having to look up, @@ -2324,12 +2327,19 @@ true</pre> <c>ordered_set</c> table (for details on the difference, see <seemfa marker="#lookup/2"><c>lookup/2</c></seemfa> and <seemfa marker="#new/2"><c>new/2</c></seemfa>).</p> + <p>If a default object <c><anno>Default</anno></c> is specified, + it is used + as the object to be updated if the key is missing from the table. The + value in place of the key is ignored and replaced by the proper key + value.</p> <p>The function fails with reason <c>badarg</c> in the following situations:</p> <list type="bulleted"> <item>The table type is not <c>set</c> or <c>ordered_set</c>.</item> <item><c><anno>Pos</anno></c> < 1.</item> <item><c><anno>Pos</anno></c> > object arity.</item> + <item>The default object arity is smaller than + <c><![CDATA[<keypos>]]></c>.</item> <item>The element to update is also the key.</item> </list> </desc> diff --git a/lib/stdlib/src/erl_stdlib_errors.erl b/lib/stdlib/src/erl_stdlib_errors.erl index 2379695acf..60996113c2 100644 --- a/lib/stdlib/src/erl_stdlib_errors.erl +++ b/lib/stdlib/src/erl_stdlib_errors.erl @@ -772,6 +772,8 @@ format_ets_error(update_element, [_,_,ElementSpec]=Args, Cause) -> case Cause of keypos -> [same_as_keypos]; + position -> + [update_op_range]; _ -> case is_element_spec_top(ElementSpec) of true -> @@ -785,6 +787,26 @@ format_ets_error(update_element, [_,_,ElementSpec]=Args, Cause) -> [<<"is not a valid element specification">>] end end]; +format_ets_error(update_element, [_, _, ElementSpec, Default]=Args, Cause) -> + TabCause = format_cause(Args, Cause), + ArgsCause = case Cause of + keypos -> + [same_as_keypos]; + position -> + [update_op_range]; + _ -> + case {is_element_spec_top(ElementSpec), format_tuple(Default)} of + {true, [""]} -> + [range]; + {true, TupleCause} -> + ["" | TupleCause]; + {false, [""]} -> + [<<"is not a valid element specification">>]; + {false, TupleCause} -> + ["" | TupleCause] + end + end, + [TabCause, "" | ArgsCause]; format_ets_error(whereis, _Args, _Cause) -> [bad_table_name]; format_ets_error(_, Args, Cause) -> diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index 8628a7e29f..54f195a409 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -77,7 +77,7 @@ select_count/2, select_delete/2, select_replace/2, select_reverse/1, select_reverse/2, select_reverse/3, setopts/2, slot/2, take/2, - update_counter/3, update_counter/4, update_element/3, + update_counter/3, update_counter/4, update_element/3, update_element/4, whereis/1]). %% internal exports @@ -551,6 +551,22 @@ update_counter(_, _, _, _) -> update_element(_, _, _) -> erlang:nif_error(undef). +-spec update_element(Table, Key, ElementSpec :: {Pos, Value}, Default) -> true when + Table :: table(), + Key :: term(), + Pos :: pos_integer(), + Value :: term(), + Default :: tuple(); + (Table, Key, ElementSpec :: [{Pos, Value}], Default) -> true when + Table :: table(), + Key :: term(), + Pos :: pos_integer(), + Value :: term(), + Default :: tuple(). + +update_element(_, _, _, _) -> + erlang:nif_error(undef). + -spec whereis(TableName) -> tid() | undefined when TableName :: atom(). whereis(_) -> diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 73fd3df43a..a4105a3c81 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -62,7 +62,7 @@ -export([ordered/1, ordered_match/1, interface_equality/1, fixtable_next/1, fixtable_iter_bag/1, fixtable_insert/1, rename/1, rename_unnamed/1, evil_rename/1, - update_element/1, update_counter/1, evil_update_counter/1, partly_bound/1, match_heavy/1]). + update_element/1, update_element_default/1, update_counter/1, evil_update_counter/1, partly_bound/1, match_heavy/1]). -export([update_counter_with_default/1]). -export([update_counter_with_default_bad_pos/1]). -export([update_counter_table_growth/1]). @@ -147,7 +147,7 @@ all() -> {group, lookup_element}, {group, misc}, {group, files}, {group, heavy}, {group, insert_list}, ordered, ordered_match, interface_equality, fixtable_next, fixtable_iter_bag, fixtable_insert, - rename, rename_unnamed, evil_rename, update_element, + rename, rename_unnamed, evil_rename, update_element, update_element_default, update_counter, evil_update_counter, update_counter_with_default, update_counter_with_default_bad_pos, @@ -2552,22 +2552,26 @@ update_element_do(Tab,Tuple,Key,UpdPos) -> Length = tuple_size(Values), 29 = Length, - PosValArgF = fun(ToIx, ResList, [Pos | PosTail], Rand, MeF) -> + PosValArgF = fun MeF(ToIx, ResList, [Pos | PosTail], Rand) -> NextIx = (ToIx+Rand) rem Length, - MeF(NextIx, [{Pos,element(ToIx+1,Values)} | ResList], PosTail, Rand, MeF); + MeF(NextIx, [{Pos,element(ToIx+1,Values)} | ResList], PosTail, Rand); - (_ToIx, ResList, [], _Rand, _MeF) -> + MeF(_ToIx, ResList, [], _Rand) -> ResList; - (ToIx, [], Pos, _Rand, _MeF) -> + MeF(ToIx, [], Pos, _Rand) -> {Pos, element(ToIx+1,Values)} % single {pos,value} arg end, UpdateF = fun(ToIx,Rand) -> - PosValArg = PosValArgF(ToIx,[],UpdPos,Rand,PosValArgF), + PosValArg = PosValArgF(ToIx,[],UpdPos,Rand), %%io:format("update_element(~p)~n",[PosValArg]), ArgHash = erlang:phash2({Tab,Key,PosValArg}), true = ets:update_element(Tab, Key, PosValArg), + [DefaultObj] = ets:lookup(Tab, Key), + NewKey = make_ref(), + true = ets:update_element(Tab, NewKey, PosValArg, DefaultObj), + true = [update_tuple({ets:info(Tab, keypos), NewKey}, DefaultObj)] =:= ets:lookup(Tab, NewKey), ArgHash = erlang:phash2({Tab,Key,PosValArg}), NewTuple = update_tuple(PosValArg,Tuple), [NewTuple] = ets:lookup(Tab,Key), @@ -2578,27 +2582,27 @@ update_element_do(Tab,Tuple,Key,UpdPos) -> || I <- lists:seq(1, tuple_size(NewTuple))] end, - LoopF = fun(_FromIx, Incr, _Times, Checksum, _MeF) when Incr >= Length -> + LoopF = fun MeF(_FromIx, Incr, _Times, Checksum) when Incr >= Length -> Checksum; % done - (FromIx, Incr, 0, Checksum, MeF) -> - MeF(FromIx, Incr+1, Length, Checksum, MeF); + MeF(FromIx, Incr, 0, Checksum) -> + MeF(FromIx, Incr+1, Length, Checksum); - (FromIx, Incr, Times, Checksum, MeF) -> + MeF(FromIx, Incr, Times, Checksum) -> ToIx = (FromIx + Incr) rem Length, UpdateF(ToIx,Checksum), if Incr =:= 0 -> UpdateF(ToIx,Checksum); % extra update to same value true -> true end, - MeF(ToIx, Incr, Times-1, Checksum+ToIx+1, MeF) + MeF(ToIx, Incr, Times-1, Checksum+ToIx+1) end, FirstTuple = Tuple, true = ets:insert(Tab,FirstTuple), [FirstTuple] = ets:lookup(Tab,Key), - Checksum = LoopF(0, 1, Length, 0, LoopF), + Checksum = LoopF(0, 1, Length, 0), Checksum = (Length-1)*Length*(Length+1) div 2, % if Length is a prime ok. @@ -2616,6 +2620,7 @@ update_element_neg(Opts) -> update_element_neg_do(Set), ets:delete(Set), {'EXIT',{badarg,_}} = (catch ets:update_element(Set,key,{2,1})), + {'EXIT',{badarg,_}} = (catch ets:update_element(Set,key,{2,1},{a,b})), run_if_valid_opts( [ordered_set | Opts], @@ -2623,13 +2628,16 @@ update_element_neg(Opts) -> OrdSet = ets_new(ordered_set, OptsOrdSet), update_element_neg_do(OrdSet), ets:delete(OrdSet), - {'EXIT',{badarg,_}} = (catch ets:update_element(OrdSet,key,{2,1})) + {'EXIT',{badarg,_}} = (catch ets:update_element(OrdSet,key,{2,1})), + {'EXIT',{badarg,_}} = (catch ets:update_element(OrdSet,key2,{2,1},{a,b})) end), Bag = ets_new(bag,[bag | Opts]), DBag = ets_new(duplicate_bag,[duplicate_bag | Opts]), {'EXIT',{badarg,_}} = (catch ets:update_element(Bag,key,{2,1})), + {'EXIT',{badarg,_}} = (catch ets:update_element(Bag,key,{2,1},{key,0})), {'EXIT',{badarg,_}} = (catch ets:update_element(DBag,key,{2,1})), + {'EXIT',{badarg,_}} = (catch ets:update_element(DBag,key,{2,1},{key,0})), true = ets:delete(Bag), true = ets:delete(DBag), ok. @@ -2643,6 +2651,8 @@ update_element_neg_do(T) -> ArgHash = erlang:phash2({T,key,Arg3}), {'EXIT',{badarg,_}} = (catch ets:update_element(T,key,Arg3)), ArgHash = erlang:phash2({T,key,Arg3}), + {'EXIT',{badarg,_}} = (catch ets:update_element(T,key2,Arg3,Object)), + ArgHash = erlang:phash2({T,key,Arg3}), [Object] = ets:lookup(T,key) end, @@ -2667,6 +2677,32 @@ update_element_neg_do(T) -> ok. +update_element_default(Config) when is_list(Config) -> + EtsMem = etsmem(), + repeat_for_opts(fun update_element_default_opts/1), + verify_etsmem(EtsMem). + + +update_element_default_opts(Opts) -> + lists:foreach( + fun({Type, {Key, Pos}}) -> + run_if_valid_opts( + [Type, {keypos, Pos} | Opts], + fun(TabOpts) -> + Tab = ets_new(Type, TabOpts), + true = ets:update_element(Tab, Key, {3, b}, {key1, key2, a, x}), + [{key1, key2, b, x}] = ets:lookup(Tab, Key), + true = ets:update_element(Tab, Key, {3, c}, {key1, key2, a, y}), + [{key1, key2, c, x}] = ets:lookup(Tab, Key), + ets:delete(Tab) + end + ) + end, + [{Type, KeyPos} || Type <- [set, ordered_set], KeyPos <- [{key1, 1}, {key2, 2}]] + ), + ok. + + %% test various variants of update_counter. update_counter(Config) when is_list(Config) -> EtsMem = etsmem(), @@ -9515,9 +9551,20 @@ error_info(_Config) -> {update_element, ['$Tab', no_key, {2, new}], [no_fail]}, {update_element, [BagTab, no_key, {2, bagged}]}, {update_element, [OneKeyTab, one, not_tuple]}, - {update_element, [OneKeyTab, one, {0, new}]}, + {update_element, [OneKeyTab, one, {0, new}], [{error_term, position}]}, {update_element, [OneKeyTab, one, {1, new}], [{error_term,keypos}]}, - {update_element, [OneKeyTab, one, {4, new}]}, + {update_element, [OneKeyTab, one, {4, new}], [{error_term, position}]}, + + {update_element, ['$Tab', no_key, {2, new}, {no_key, old}], [no_fail]}, + {update_element, ['$Tab', no_key, {0, new}, {no_key, old}], [{error_term, position}]}, + {update_element, ['$Tab', no_key, {1, new}, {no_key, old}], [{error_term, keypos}]}, + {update_element, ['$Tab', no_key, {4, new}, {no_key, old}], [{error_term, position}]}, + {update_element, ['$Tab', no_key, {4, new}, not_tuple]}, + {update_element, [BagTab, no_key, {1, bagged}, {no_key, old}], []}, + {update_element, [OneKeyTab, no_key, {0, new}, {no_key, old}], [{error_term, position}]}, + {update_element, [OneKeyTab, no_key, {1, new}, {no_key, old}], [{error_term, keypos}]}, + {update_element, [OneKeyTab, no_key, {4, new}, {no_key, old}], [{error_term, position}]}, + {update_element, [OneKeyTab, no_key, {4, new}, not_tuple]}, {whereis, [{bad,name}], [no_table]} ], -- 2.35.3
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor