Изменение таблиц Mnesia/типов записей

Я не совсем понимаю, как записи работают с Mnesia и каков эффект изменения типов записей. Вот в примере:

Erlang/OTP 17 [erts-6.2] [source] [64-bit] [async-threads:10] [kernel-poll:false]
Eshell V6.2  (abort with ^G)

1> c(test).
{ok,test}
2> mnesia:start().
ok
3> test:reset_db().
{atomic,ok}
4> test:add_sensor("1a0",12,erlang:now()).
{atomic,ok}
5> test:add_sensor("1a1",10,erlang:now()).
{atomic,ok}
6> test:list_sensors().
[{sensors,"1a0",12,{1484,392274,122051}},
 {sensors,"1a1",10,{1484,392280,673175}}]
7> test:list_sensors_id().
["1a0","1a1"]

Так что все это имеет смысл — мы создали таблицу (test:reset_db) с записями типа «датчики» и добавили два датчика (test:add_sensor). Мы видим обе записи типа «датчики». Теперь изменим таблицу:

8> test:update_db().
{atomic,ok}
9> test:list_sensors().
[{sensors_new,"1a0",12,{1484,392274,122051},0},
 {sensors_new,"1a1",10,{1484,392280,673175},0}]
10> test:list_sensors_id().
["1a0","1a1"]
11> q().
ok

Так что это часть, которую я не понимаю — мы обновили нашу таблицу (test:update_db), так что теперь у нас есть таблицы «sensors» с типами записей «sensors_new» — это согласуется с тем, что мы видим в test:list_sensors, но что я не понимаю, почему test:list_sensors_id() все еще работает? Есть ли соответствие между #sensors_new.name и #sensors.id? Как Erlang/Mnesia узнает, что "X#sensors.id" и "X#sensors_new.name" после перевода являются одним и тем же полем? Или что мне не хватает?

-include_lib("stdlib/include/qlc.hrl").
-module(test).
-compile(export_all).

-record(sensors,{id,val,update}).
-record(sensors_new,{name,val,update,type}).

reset_db() ->
  mnesia:delete_table(sensors),
  mnesia:create_table(sensors, [{attributes, record_info(fields, sensors)}]).

update_db() ->
  Transformer = fun(X) when is_record(X, sensors) ->
  #sensors_new{name = X#sensors.id,
              val = X#sensors.val,
              update = X#sensors.update,
              type = 0} end,
  {atomic, ok} = mnesia:transform_table(sensors,Transformer,record_info(fields, sensors_new),sensors_new).


add_sensor(Id, Val, Update) ->
  Row = #sensors{id=Id, val=Val, update=Update},
  F = fun() ->
    mnesia:write(Row)
    end,
  mnesia:transaction(F).

list_sensors() ->
  do(qlc:q([X || X <-mnesia:table(sensors)])).

list_sensors_id() ->
  do(qlc:q([X#sensors.id || X <-mnesia:table(sensors)])).

do(Q) ->
    F = fun() -> qlc:e(Q) end,
    {atomic, Val} = mnesia:transaction(F),
    Val.

person w1nter    schedule 14.01.2017    source источник


Ответы (1)


Сначала краткий обзор: записи в Erlang — это просто синтаксический сахар над кортежами с именем записи, добавленным в качестве первого элемента. Когда вы обращаетесь к Nth полю в записи R, Erlang фактически преобразует его для использования element(N+1, R). В этом примере #sensors.id и #sensors_new.name дают одно и то же значение 2, поэтому доступ к любому из них равно element(2, R).

Теперь по умолчанию доступ к полям записи через неправильный тип должен завершиться ошибкой (за исключением чего-то вроде двух записей с одинаковым именем). Если переменная Foo связана с записью типа #sensors, но вы пытаетесь получить доступ к чему-то через #sensors_new, например, Foo#sensors_new.name, это обычно вызывает ошибку badrecord, например:

4> Foo = #sensors{id="bar"}.
#sensors{id = "bar"}
5> Foo#sensors_new.name.
** exception error: {badrecord,sensors_new}

Однако кажется, что проверка обойден при использовании QLC (преобразование синтаксического анализа QLC проходит в no_strict_record_tests, отключая этот «строгий» тест доступа к полю записи). Если бы мне пришлось угадывать, я бы сказал, что это была оптимизация в сочетании с предположением, что вы не будете использовать тот же QLC, если поменяете резервную запись.

person ECrownofFire    schedule 18.01.2017