改变 Mnesia Tables/Record 类型

Altering Mnesia Tables/Record Types

我不完全理解记录如何与 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"]

所以这一切都是有道理的 - 我们创建了一个 table (test:reset_db) 类型的记录 "sensors",并添加了两个传感器 (test:add_sensor ).我们看到两条 "sensors" 类型的记录。现在让我们修改 table:

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

所以这是我没有得到的部分 - 我们更新了我们的 table(测试:update_db)所以现在我们有 "sensors" tables "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.

首先快速回顾一下:Erlang 中的记录只是元组的语法糖,记录名称作为第一个元素添加。当您访问记录 R 中的第 N 字段时,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}

但是,似乎that check is bypassed 使用 QLCs(QLC 解析转换传入 no_strict_record_tests,禁用 "strict" 记录字段访问测试)。如果非要我猜的话,我会说这是一种优化,并假设您交换了支持记录就不会使用相同的 QLC。