Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

erlang mnesia - illegal record info

I am trying to have a function that ensures the table I need is already created and if not to create it. Here's the sample:

ensure_table_exists(Table, MnesiaTables, Nodes) ->
case lists:member(Table, MnesiaTables) of
    true ->
        throw({error, db_might_have_already_been_created});
    false ->
        mnesia:create_table(Table, [{disc_copies, Nodes},
                {attributes, record_info(fields, Table)}]), 
        ok  
end.

The issue is that when compiling I get the error: illegal record info. It might have to do that record_info is resolved at compile time or that the second argument to record info should actually be a record that can be retrieved from the source code ?

like image 993
hyperboreean Avatar asked Sep 10 '10 13:09

hyperboreean


2 Answers

Yes, all record related things including record_info/2 are resolved at compile time. This means that record and field names must be known at compile time. This is the reason for the compiler error.

I don't think your function is really too defensive in that what you are doing is signaling a more specific error. It would be another matter if you were to return {error, ...}.

One last point is that if you mean to raise an exception you should not use throw/1 but instead use erlang:error/1. throw is intended for non-local return (caught with a catch) while erlang:error is intended for raising an exception. In many cases the result may be the same but the actual error value may be misleading (nocatch). It is always better the clearer you can show your intention, which in this case is signaling an error.

P.S. Yes, I know that catch also catches errors/exits as well. This was intentional. In a perfect world maybe catch should only catch throws and try only errors/exits.

like image 183
rvirding Avatar answered Sep 24 '22 09:09

rvirding


Unfortunately record_info is not really a function even if it looks like one.

You can verify that by testing the following. Create a file:

 -module(something).
 -record(a, {}).

Start the Erlang shell:

 > rr(something).
 [a]
 > record_info(fields, a).
 []
 > A = a.
 > record_info(fields, A).
 * 2: illegal record info

So my recommendation would be to either use a macro or a specialised function for the record_info part.

To answer your original question. Use something like:

 tables() ->
   [?TABLE_MACRO(tablename),
    ?TABLE_MACRO(tablename2),
    ...].

where TABLE_MACRO is something like:

 -define(TABLE_MACRO(Table), fun() ->
      mnesia:create_table(Table, [{disc_copies, Nodes},
               {attributes, record_info(fields, Table)}])
      end).

and then have a function using something like the below.

 [case CreateTable of
   {aborted, {already_exists, _}} -> ok;
   {atomic, ok} -> ok
  end || CreateTable <- tables()].

Yuck! Can be cleaned up quite a bit, but hopefully you understand the general idea.

  • Macro instead of variable use.
  • Match on both {atomic, ok} and {aborted, {already_exists, _TableName}}
like image 36
Daniel Luna Avatar answered Sep 23 '22 09:09

Daniel Luna