Tutorial: Mnesia
3 Structure
3.1 Starting Again
We've had a quick taste of Mnesia, and what some of the calls look like in LFE. Next we're going to tackle a bit more heady stuff: tables and relationships.
Go ahead and re-start the REPL, using a different database name this time:
$ DB=./Company.DB make mnesia-shell
> (mnesia:create_schema (list (node)))
ok
> (mnesia:start)
ok
>
Then pull in the code that will let us define our tables, adding them to the Mnesia schema we just created:
> (slurp '"src/structure.lfe")
#(ok structure)
>
3.2 Records as Tables
The structure
module includes LFE records that act as our table definitions,
as well as a macro that lets us create Mnesia tables with almost no boilerplate.
The following records are defined in include/tables.lfe
:
(defrecord employee
id
name
salary
gender
phone
room-number)
(defrecord department
id
name)
(defrecord project
name
number)
(defrecord manager
employee-id
department-id)
(defrecord in-department
employee-id
department-id)
(defrecord in-project
employee-id
project-name)
These records (tables) are taken from the example given in the Erlang Mnesia tutorial which also gives this entity diagram for their proposed "Company" database:
3.3 Creating Our Tables
In the src/structure.lfe
module which we have just imported, some utility
functions are defined which will lets us easily create tables in Mnesia based
on the records defined in the include/tables.lfe
file. Of particular
interest right now is the (init)
function; let's call it:
> (init)
#(ok
(#(create-set-tables (#(atomic ok) #(atomic ok) #(atomic ok) #(atomic ok)))
#(create-bag-tables (#(atomic ok) #(atomic ok)))))
>
This just created all our Mnesia tables for us. If we run it again, we'll see errors indicating that the tables have already been created:
> (init)
#(error
(#(create-set-tables
(#(aborted #(already_exists employee))
#(aborted #(already_exists department))
#(aborted #(already_exists project))
#(aborted #(already_exists in-department))))
#(create-bag-tables
(#(aborted #(already_exists manager))
#(aborted #(already_exists in-project))))))
>
As you may guess from the output of that second call, under the covers, the
init
function calls a couple of utility functions:
(structure:create-set-tables)
, and(structure:create-bag-tables)
These, in turn, call a macro we created to make table-creation much easier. The custom macro alleviates the dev from having to write tedious and repetitive boilerplate code. This macro actually calls another macro that is generated -- by LFE -- for each record (one that gets a list of all the fields for a given record).
Next, let's re-run that info
function we saw in the previous section:
> (mnesia:info)
The output of that function will be very similar to what we saw in the previous section. However, do note that our new tables are reported in the "Active tables" section:
...
---> Active tables <---
in-project : with 0 records occupying 305 words of mem
in-department : with 0 records occupying 305 words of mem
manager : with 0 records occupying 305 words of mem
project : with 0 records occupying 305 words of mem
department : with 0 records occupying 305 words of mem
employee : with 0 records occupying 305 words of mem
...
If you would like to check up on the tables created above, you can use the
table_info
function to pull out certain data. For instance, here's how
you find what backend type is being used for any given table:
> (mnesia:table_info 'employee 'type)
set
> (mnesia:table_info 'in-project 'type)
bag
>
If you're interested in seeing all the details of any given table, you can
do so with the 'all
parameter:
> (mnesia:table_info 'employee 'all)
(#(access_mode read_write)
#(active_replicas (nonode@nohost))
#(all_nodes (nonode@nohost))
#(arity 7)
#(attributes (id name salary gender phone room-number))
#(checkpoints ())
#(commit_work ())
#(cookie #(#(1396 680215 616649) nonode@nohost))
#(cstruct
#(cstruct
employee
set
(nonode@nohost)
()
()
0
read_write
false
()
()
false
employee
(id name salary gender ...)
()
()
()
#(...)...))
#(disc_copies ())
#(disc_only_copies ())
#(frag_properties ())
#(index ())
#(load_by_force false)
#(load_node nonode@nohost)
#(load_order 0)
#(load_reason #(dumper create_table))
#(local_content false)
#(majority false)
#(master_nodes ())
#(memory 317)
#(ram_copies (nonode@nohost))
#(record_name employee)
#(record_validation #(employee 7 set))
#(type set)
#(size 0)
#(snmp ())
#(storage_properties ...)
#(...)...)
>
Next up, we'll start inserting some data.