Managing incremental counters in a mnesia DBMS?

I realized that mnesia does not support the automatic increment function, like MySQL or another DBMS does. The counters mentioned in the mnesia documentation are not well explained. For example, I found sofar one function in all the documentation that manages counters

mnesia:dirty_update_counter({Tab::atom(),Key::any()}, Val::positive_integer())

So, this bothered me for a while, because it works with records like

{TabName, Key, Integer}
This is also unclear and possibly because no erlang or mnesia documentation is an example to explain this. This forced me to implement my own manipulator APIs. Since I wanted to have access to my records and manage them using counters, I had to include a field called “counter” in my records, then at the time of adding the record to the table that is intended for counters, I do this as follows:
#recordname {field1 = Val1, ..., counter = auto_increment (? THIS_TABLE)}

The position of the counter fields does not matter. The API is implemented as follows:

%% @doc this function is called whenever u are writing a new record in the table 
%% by giving its result to the counter field in your record.
%% @end
%%
%% @spec auto_increment (TableName :: atom ()) -> integer () | exit (Reason)
auto_increment (TableName) -> case lists: member (counter, table_info (TableName, attributes)) of false -> erlang: exit ({counter, field, not_found, in_table, TableName}); true -> table_info (TableName, size) + 1 end.
table_info (Tab, Item) -> F = fun ({X, Y}) -> mnesia: table_info (X, Y) end, mnesia: activity (transaction, F, [{Tab, Item}], mnesia_frag).

, i, , , , , try... catch (...) , , . , , mnesia:is_transaction(), true, mnesia:abort/1, false, . mnesia_frag mnesia . mnesia:transaction(Fun), , ( ).
, , . , . , , = 5, = 6 = 5 .. . , , , . , , , mnesia:foldl/3 or mnesia:foldr/3 , the difference between these two comes in only with ordered table types
, :


auto_decrement(Counter_deleted,TableName)->
    Attrs = table_info(TableName,attributes),
    case lists:member(counter,Attrs) of
        false -> erlang:exit({counter,field,not_found,in_table,TableName});
        true -> 
            Counter_position = position(counter,Attrs) + 1,         
            Iterator =  fun(Rec,_) when element(Counter_position,Rec) > Counter_deleted -> 
                            Count = element(Counter_position,Rec),
                            New_rec = erlang:setelement(Counter_position,Rec,Count - 1),
                            mnesia:write(TableName,New_rec,read),
                            [];
                        (_,_) -> []
                        end,
            Find = fun({Fun,Table}) -> mnesia:foldl(Fun, [],Table) end,
            mnesia:activity(transaction,Find,[{Iterator,TableName}],mnesia_frag)
    end.


, , . , , :


position(_,[]) -> -1;
position(Value,List)->
    find(lists:member(Value,List),Value,List,1).

find (false, _, _, _) -> -1;
find (true, V, [V | _], N) -> N;
find (true, V, [_ | X], N) ->
    find (V, X, N + 1).

find (V, [V | _], N) -> N;
find (V, [_ | X], N) -> find (V, X, N + 1).


This is because this module does not need to know any of the programmers' entries to help it with the help of counters. In order to access the counter value from a record using dialing functions such as element(N::integer(),Tuple::tuple()), I have to dynamically calculate its position in the tuple representation of the record.

These two functions have worked for me and are still working till auto_increment 
is implemented in mnesia.

For example, using qlc (query list comprehension) to query tables with dynamic constraints, consider the following code snippets:

select (Q) ->
    F = fun (QH) -> qlc: e (QH) end,
    mnesia:activity(transaction,F,[Q],mnesia_frag).

read_by_custom_validation(Validation_fun,From_table)->
    select(qlc:q([X || X <- mnesia:table(From_table),Validation_fun(X) == true])).

%% Applying the two functions....
find_records_with_counter(From_this,To_that) when 
is_integer(From_this),is_integer(To_that),To_that > From_this -> F = fun(#recordName{counter = N}) when N >= From_this,N =< To_That -> true; (_) -> false end, read_by_custom_validation(F,TableName).

...



(stock_project@127.0.0.1)6> stock:get_items_in_range(1,4).
[#item{item_id = "D694",name = "cement",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 500,units = "bags",unit_cost = 20000,
       state = available,last_modified = undefined,
       category = "building material",counter = 1},
 #item{item_id = "131B",name = "nails",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 20000,units = "kgs",unit_cost = 1000,
       state = available,last_modified = undefined,
       category = "building material",counter = 2},
 #item{item_id = "FDD9",name = "iron sheets",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 20,units = "bars",unit_cost = 50000,
       state = available,last_modified = undefined,
       category = "building material",counter = 3},
 #item{item_id = "09D4",name = "paint",
       time_stamp = {"30/12/2010","11:29:10 am"},
       min_stock = 30000,units = "tins",unit_cost = 5000,
       state = available,last_modified = undefined,
       category = "building material",counter = 4}]
(stock_project@127.0.0.1)7>

. , , . , .

+3
1

? , , , , , , .

  • . {counts, TableName, Count} dirty_update_counter .
  • (, , ), OrderedBy, .
  • , ( item_id, , ) , - .

, , , . , MySQL .

+4

Source: https://habr.com/ru/post/1783248/


All Articles