aboutsummaryrefslogtreecommitdiffstats
path: root/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mnesia/doc/src/Mnesia_chap2.xmlsrc')
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap2.xmlsrc647
1 files changed, 647 insertions, 0 deletions
diff --git a/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc
new file mode 100644
index 0000000000..0714c7b645
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_chap2.xmlsrc
@@ -0,0 +1,647 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1997</year><year>2009</year>
+ <holder>Ericsson AB. All Rights Reserved.</holder>
+ </copyright>
+ <legalnotice>
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ </legalnotice>
+
+ <title>Getting Started with Mnesia</title>
+ <prepared>Claes Wikstr&ouml;m, Hans Nilsson and H&aring;kan Mattsson</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev>C</rev>
+ <file>Mnesia_chap2.xml</file>
+ </header>
+ <p>This chapter introduces Mnesia. Following a brief discussion
+ about the first initial setup, a Mnesia database example is
+ demonstrated. This database example will be referenced in the
+ following chapters, where this example is modified in order to
+ illustrate various program constructs. In this chapter, the
+ following mandatory procedures are illustrated by examples:
+ </p>
+ <list type="bulleted">
+ <item>Starting an Erlang session and specifying a directory for the
+ Mnesia database.
+ </item>
+ <item>Initializing a database schema.
+ </item>
+ <item>Starting Mnesia and creating the required tables.</item>
+ </list>
+
+ <section>
+ <title>Starting Mnesia for the first time</title>
+ <p>Following is a simplified demonstration of a Mnesia system startup. This is the dialogue from the Erlang
+ shell:
+ </p>
+ <pre><![CDATA[
+ unix> erl -mnesia dir '"/tmp/funky"'
+ Erlang (BEAM) emulator version 4.9
+
+ Eshell V4.9 (abort with ^G)
+ 1>
+ 1> mnesia:create_schema([node()]).
+ ok
+ 2> mnesia:start().
+ ok
+ 3> mnesia:create_table(funky, []).
+ {atomic,ok}
+ 4> mnesia:info().
+ ---> Processes holding locks <---
+ ---> Processes waiting for locks <---
+ ---> Pending (remote) transactions <---
+ ---> Active (local) transactions <---
+ ---> Uncertain transactions <---
+ ---> Active tables <---
+ funky : with 0 records occupying 269 words of mem
+ schema : with 2 records occupying 353 words of mem
+ ===> System info in version "1.0", debug level = none <===
+ opt_disc. Directory "/tmp/funky" is used.
+ use fall-back at restart = false
+ running db nodes = [nonode@nohost]
+ stopped db nodes = []
+ remote = []
+ ram_copies = [funky]
+ disc_copies = [schema]
+ disc_only_copies = []
+ [{nonode@nohost,disc_copies}] = [schema]
+ [{nonode@nohost,ram_copies}] = [funky]
+ 1 transactions committed, 0 aborted, 0 restarted, 1 logged to disc
+ 0 held locks, 0 in queue; 0 local transactions, 0 remote
+ 0 transactions waits for other nodes: []
+ ok
+ ]]></pre>
+ <p>In the example above the following actions were performed:
+ </p>
+ <list type="bulleted">
+ <item>The Erlang system was started from the UNIX prompt
+ with a flag <c>-mnesia dir '"/tmp/funky"'</c>. This flag indicates
+ to Mnesia which directory will store the data.
+ </item>
+ <item>A new empty schema was initialized on the local node by evaluating
+ <c>mnesia:create_schema([node()]).</c> The schema contains
+ information about the database in general. This will be
+ thoroughly explained later on.
+ </item>
+ <item>The DBMS was started by evaluating <c>mnesia:start()</c>.
+ </item>
+ <item>A first table was created, called <c>funky</c> by evaluating
+ the expression <c>mnesia:create_table(funky, [])</c>. The table
+ was given default properties.
+ </item>
+ <item><c>mnesia:info()</c> was evaluated and subsequently displayed
+ information regarding the status of the database on the terminal.
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>An Introductory Example</title>
+ <p>A Mnesia database is organized as a set of tables.
+ Each table is populated with instances (Erlang records).
+ A table also has a number of properties, such as location and
+ persistence.
+ </p>
+ <p>In this example we shall:
+ </p>
+ <list type="bulleted">
+ <item>Start an Erlang system, and specify the directory where
+ the database will be located.
+ </item>
+ <item>Initiate a new schema with an attribute that specifies
+ on which node, or nodes, the database will operate.
+ </item>
+ <item>Start Mnesia itself.
+ </item>
+ <item>Create and populate the database tables.
+ </item>
+ </list>
+
+ <section>
+ <title>The Example Database</title>
+ </section>
+ <p>In this database example, we will create the database and
+ relationships depicted in the following diagram. We will call this
+ database the <em>Company</em> database.
+ </p>
+ <image file="company.gif">
+ <icaption>Company Entity-Relation Diagram</icaption>
+ </image>
+ <p>The database model looks as follows:
+ </p>
+ <list type="bulleted">
+ <item>There are three entities: employee, project, and
+ department.
+ </item>
+ <item>
+ <p>There are three relationships between these entities:</p>
+ <list type="ordered">
+ <item>A department is managed by an employee, hence the
+ <em>manager</em> relationship.
+ </item>
+ <item>An employee works at a department, hence the
+ <em>at_dep</em> relationship.
+ </item>
+ <item>Each employee works on a number of projects, hence
+ the <em>in_proj</em> relationship.
+ </item>
+ </list>
+ </item>
+ </list>
+
+ <section>
+ <title>Defining Structure and Content</title>
+ <p>We first enter our record definitions into a text file
+ named <c>company.hrl</c>. This file defines the following
+ structure for our sample database:
+ </p>
+ <codeinclude file="company.hrl" tag="%0" type="erl"></codeinclude>
+ <p>The structure defines six tables in our database. In Mnesia,
+ the function <c>mnesia:create_table(Name, ArgList)</c> is
+ used to create tables. <c>Name</c> is the table
+ name <em>Note:</em> The current version of Mnesia does
+ not require that the name of the table is the same as the record
+ name, See Chapter 4:
+ <seealso marker="Mnesia_chap4#recordnames_tablenames">Record Names Versus Table Names.</seealso></p>
+ <p>For example, the table
+ for employees will be created with the function
+ <c>mnesia:create_table(employee, [{attributes, record_info(fields, employee)}]).</c> The table
+ name <c>employee</c> matches the name for records specified
+ in <c>ArgList</c>. The expression <c>record_info(fields, RecordName)</c> is processed by the Erlang preprocessor and
+ evaluates to a list containing the names of the different
+ fields for a record.
+ </p>
+ </section>
+
+ <section>
+ <title>The Program</title>
+ <p>The following shell interaction starts Mnesia and
+ initializes the schema for our <c>company</c> database:
+ </p>
+ <pre>
+
+ % <input>erl -mnesia dir '"/ldisc/scratch/Mnesia.Company"'</input>
+ Erlang (BEAM) emulator version 4.9
+
+ Eshell V4.9 (abort with ^G)
+ 1> mnesia:create_schema([node()]).
+ ok
+ 2> mnesia:start().
+ ok
+ </pre>
+ <p>The following program module creates and populates previously defined tables:
+ </p>
+ <codeinclude file="company.erl" tag="%0" type="erl"></codeinclude>
+ </section>
+
+ <section>
+ <title>The Program Explained</title>
+ <p>The following commands and functions were used to initiate the
+ Company database:
+ </p>
+ <list type="bulleted">
+ <item><c>% erl -mnesia dir '"/ldisc/scratch/Mnesia.Company"'.</c> This is a UNIX
+ command line entry which starts the Erlang system. The flag
+ <c>-mnesia dir Dir</c> specifies the location of the
+ database directory. The system responds and waits for
+ further input with the prompt <em>1></em>.
+ </item>
+ <item><c>mnesia:create_schema([node()]).</c> This function
+ has the format <c>mnesia:create_schema(DiscNodeList)</c> and
+ initiates a new schema. In this example, we have created a
+ non-distributed system using only one node. Schemas are fully
+ explained in Chapter 3:<seealso marker="Mnesia_chap3#def_schema">Defining a Schema</seealso>.
+ </item>
+ <item><c>mnesia:start().</c> This function starts
+ Mnesia. This function is fully explained in Chapter 3:
+ <seealso marker="Mnesia_chap3#start_mnesia">Starting Mnesia</seealso>.
+ </item>
+ </list>
+ <p>Continuing the dialogue with the Erlang shell will produce the following
+ the following:
+ </p>
+ <pre><![CDATA[
+ 3> company:init().
+ {atomic,ok}
+ 4> mnesia:info().
+ ---> Processes holding locks <---
+ ---> Processes waiting for locks <---
+ ---> Pending (remote) transactions <---
+ ---> Active (local) transactions <---
+ ---> Uncertain transactions <---
+ ---> Active tables <---
+ in_proj : with 0 records occuping 269 words of mem
+ at_dep : with 0 records occuping 269 words of mem
+ manager : with 0 records occuping 269 words of mem
+ project : with 0 records occuping 269 words of mem
+ dept : with 0 records occuping 269 words of mem
+ employee : with 0 records occuping 269 words of mem
+ schema : with 7 records occuping 571 words of mem
+ ===> System info in version "1.0", debug level = none <===
+ opt_disc. Directory "/ldisc/scratch/Mnesia.Company" is used.
+ use fall-back at restart = false
+ running db nodes = [nonode@nohost]
+ stopped db nodes = []
+ remote = []
+ ram_copies =
+ [at_dep,dept,employee,in_proj,manager,project]
+ disc_copies = [schema]
+ disc_only_copies = []
+ [{nonode@nohost,disc_copies}] = [schema]
+ [{nonode@nohost,ram_copies}] =
+ [employee,dept,project,manager,at_dep,in_proj]
+ 6 transactions committed, 0 aborted, 0 restarted, 6 logged to disc
+ 0 held locks, 0 in queue; 0 local transactions, 0 remote
+ 0 transactions waits for other nodes: []
+ ok
+ ]]></pre>
+ <p>A set of tables is created:
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:create_table(Name,ArgList)</c>. This
+ function is used to create the required database tables. The
+ options available with <c>ArgList</c> are explained in
+ Chapter 3: <seealso marker="Mnesia_chap3#create_tables">Creating New Tables</seealso>. </item>
+ </list>
+ <p>The <c>company:init/0</c> function creates our tables. Two tables are
+ of type <c>bag</c>. This is the <c>manager</c> relation as well
+ the <c>in_proj</c> relation. This shall be interpreted as: An
+ employee can be manager over several departments, and an employee
+ can participate in several projects. However, the <c>at_dep</c>
+ relation is <c>set</c> because an employee can only work in one department.
+ In this data model we have examples of relations that are one-to-one (<c>set</c>),
+ as well as one-to-many (<c>bag</c>).
+ </p>
+ <p><c>mnesia:info()</c> now indicates that a database
+ which has seven local tables, of which, six are our
+ user defined tables and one is the schema.
+ Six transactions have been committed, as six successful transactions were run when
+ creating the tables.
+ </p>
+ <p>To write a function which inserts an employee record into the database, there must be an
+ <c>at_dep</c> record and a set of <c>in_proj</c> records inserted. Examine the following
+ code used to complete this action:
+ </p>
+ <codeinclude file="company.erl" tag="%1" type="erl"></codeinclude>
+ <list type="bulleted">
+ <item>
+ <p><c>insert_emp(Emp, DeptId, ProjNames) -></c>. The
+ <c>insert_emp/3</c> arguments are:</p>
+ <list type="ordered">
+ <item><c>Emp</c> is an employee record.
+ </item>
+ <item><c>DeptId</c> is the identity of the department where the employee is working.
+ </item>
+ <item><c>ProjNames</c> is a list of the names of the projects where the employee are working.</item>
+ </list>
+ </item>
+ </list>
+ <p>The <c>insert_emp(Emp, DeptId, ProjNames) -></c> function
+ creates a <em>functional object</em>. Functional objects
+ are identified by the term <c>Fun</c>. The Fun is passed
+ as a single argument to the function
+ <c>mnesia:transaction(Fun)</c>. This means that Fun is
+ run as a transaction with the following properties:
+ </p>
+ <list type="bulleted">
+ <item>Fun either succeeds or fails completely.
+ </item>
+ <item>Code which manipulates the same data records can be
+ run concurrently without the different processes interfering
+ with each other.
+ </item>
+ </list>
+ <p>The function can be used as:</p>
+ <code type="none">
+ Emp = #employee{emp_no= 104732,
+ name = klacke,
+ salary = 7,
+ sex = male,
+ phone = 98108,
+ room_no = {221, 015}},
+ insert_emp(Me, 'B/SFR', [Erlang, mnesia, otp]).
+ </code>
+ <note>
+ <p>Functional Objects (Funs) are described in the
+ Erlang Reference Manual, "Fun Expressions".
+ </p>
+ </note>
+ </section>
+
+ <section>
+ <title>Initial Database Content</title>
+ <p>After the insertion of the employee named <c>klacke</c>
+ we have the following records in the database:
+ </p>
+ <marker id="table2_1"></marker>
+ <table>
+ <row>
+ <cell align="left" valign="middle">emp_no</cell>
+ <cell align="left" valign="middle">name</cell>
+ <cell align="left" valign="middle">salary</cell>
+ <cell align="left" valign="middle">sex</cell>
+ <cell align="left" valign="middle">phone</cell>
+ <cell align="left" valign="middle">room_no</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">104732</cell>
+ <cell align="left" valign="middle">klacke</cell>
+ <cell align="left" valign="middle">7</cell>
+ <cell align="left" valign="middle">male</cell>
+ <cell align="left" valign="middle">99586</cell>
+ <cell align="left" valign="middle">{221, 015}</cell>
+ </row>
+ <tcaption>
+Employee</tcaption>
+ </table>
+ <p>An employee record has the following Erlang record/tuple
+ representation: <c>{employee, 104732, klacke, 7, male, 98108, {221, 015}}</c></p>
+ <marker id="table2_2"></marker>
+ <table>
+ <row>
+ <cell align="left" valign="middle">emp</cell>
+ <cell align="left" valign="middle">dept_name</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">klacke</cell>
+ <cell align="left" valign="middle">B/SFR</cell>
+ </row>
+ <tcaption>
+At_dep</tcaption>
+ </table>
+ <p>At_dep has the following Erlang tuple representation:
+ <c>{at_dep, klacke, 'B/SFR'}</c>.
+ </p>
+ <marker id="table3_3"></marker>
+ <table>
+ <row>
+ <cell align="left" valign="middle">emp</cell>
+ <cell align="left" valign="middle">proj_name</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">klacke</cell>
+ <cell align="left" valign="middle">Erlang</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">klacke</cell>
+ <cell align="left" valign="middle">otp</cell>
+ </row>
+ <row>
+ <cell align="left" valign="middle">klacke</cell>
+ <cell align="left" valign="middle">mnesia</cell>
+ </row>
+ <tcaption>
+In_proj</tcaption>
+ </table>
+ <p>In_proj has the following Erlang tuple representation:
+ <c>{in_proj, klacke, 'Erlang', klacke, 'otp', klacke, 'mnesia'}</c></p>
+ <p>There is no difference between rows in a table and Mnesia
+ records. Both concepts are the same and will be used
+ interchangeably throughout this book.
+ </p>
+ <p>A Mnesia table is populated by Mnesia records. For example,
+ the tuple <c>{boss, klacke, bjarne}</c> is an record. The
+ second element in this tuple is the key. In order to uniquely
+ identify a table row both the key and the table name is
+ needed. The term <em>object identifier</em>,
+ (oid) is sometimes used for the arity two tuple {Tab, Key}. The oid for
+ the <c>{boss, klacke, bjarne}</c> record is the arity two
+ tuple <c>{boss, klacke}</c>. The first element of the tuple is
+ the type of the record and the second element is the key. An
+ oid can lead to zero, one, or more records depending on
+ whether the table type is <c>set</c> or <c>bag</c>.
+ </p>
+ <p>We were also able to insert the <c>{boss, klacke, bjarne}</c> record which contains an implicit reference to
+ another employee which does not yet exist in the
+ database. Mnesia does not enforce this.
+ </p>
+ </section>
+
+ <section>
+ <title>Adding Records and Relationships to the Database</title>
+ <p>After adding additional record to the Company database, we
+ may end up with the following records:
+ </p>
+ <p><em>Employees</em></p>
+ <code type="none">
+ {employee, 104465, "Johnson Torbjorn", 1, male, 99184, {242,038}}.
+ {employee, 107912, "Carlsson Tuula", 2, female,94556, {242,056}}.
+ {employee, 114872, "Dacker Bjarne", 3, male, 99415, {221,035}}.
+ {employee, 104531, "Nilsson Hans", 3, male, 99495, {222,026}}.
+ {employee, 104659, "Tornkvist Torbjorn", 2, male, 99514, {222,022}}.
+ {employee, 104732, "Wikstrom Claes", 2, male, 99586, {221,015}}.
+ {employee, 117716, "Fedoriw Anna", 1, female,99143, {221,031}}.
+ {employee, 115018, "Mattsson Hakan", 3, male, 99251, {203,348}}.
+ </code>
+ <p><em>Dept</em></p>
+ <code type="none">
+
+ {dept, 'B/SF', "Open Telecom Platform"}.
+ {dept, 'B/SFP', "OTP - Product Development"}.
+ {dept, 'B/SFR', "Computer Science Laboratory"}.
+ </code>
+ <p><em>Projects</em></p>
+ <code type="none">
+ %% projects
+ {project, erlang, 1}.
+ {project, otp, 2}.
+ {project, beam, 3}.
+ {project, mnesia, 5}.
+ {project, wolf, 6}.
+ {project, documentation, 7}.
+ {project, www, 8}.
+ </code>
+ <p>The above three tables, titled <c>employees</c>,
+ <c>dept</c>, and <c>projects</c>, are the tables which are
+ made up of real records. The following database content is
+ stored in the tables which is built on
+ relationships. These tables are titled <c>manager</c>,
+ <c>at_dep</c>, and <c>in_proj</c>.
+ </p>
+ <p><em>Manager</em></p>
+ <code type="none">
+
+ {manager, 104465, 'B/SF'}.
+ {manager, 104465, 'B/SFP'}.
+ {manager, 114872, 'B/SFR'}.
+ </code>
+ <p><em>At_dep</em></p>
+ <code type="none">
+ {at_dep, 104465, 'B/SF'}.
+ {at_dep, 107912, 'B/SF'}.
+ {at_dep, 114872, 'B/SFR'}.
+ {at_dep, 104531, 'B/SFR'}.
+ {at_dep, 104659, 'B/SFR'}.
+ {at_dep, 104732, 'B/SFR'}.
+ {at_dep, 117716, 'B/SFP'}.
+ {at_dep, 115018, 'B/SFP'}.
+ </code>
+ <p><em>In_proj</em></p>
+ <code type="none">
+ {in_proj, 104465, otp}.
+ {in_proj, 107912, otp}.
+ {in_proj, 114872, otp}.
+ {in_proj, 104531, otp}.
+ {in_proj, 104531, mnesia}.
+ {in_proj, 104545, wolf}.
+ {in_proj, 104659, otp}.
+ {in_proj, 104659, wolf}.
+ {in_proj, 104732, otp}.
+ {in_proj, 104732, mnesia}.
+ {in_proj, 104732, erlang}.
+ {in_proj, 117716, otp}.
+ {in_proj, 117716, documentation}.
+ {in_proj, 115018, otp}.
+ {in_proj, 115018, mnesia}.
+ </code>
+ <p>The room number is an attribute of the employee
+ record. This is a structured attribute which consists of a
+ tuple. The first element of the tuple identifies a corridor,
+ and the second element identifies the actual room in the
+ corridor. We could have chosen to represent this as a record
+ <c>-record(room, {corr, no}).</c> instead of an anonymous
+ tuple representation.
+ </p>
+ <p>The Company database is now initialized and contains
+ data. </p>
+ </section>
+
+ <section>
+ <title>Writing Queries</title>
+ <p>Retrieving data from DBMS should usually be done with <c>mnesia:read/3</c> or
+ <c>mnesia:read/1</c> functions. The following function raises the salary:</p>
+ <codeinclude file="company.erl" tag="%5" type="erl"></codeinclude>
+ <p>Since we want to update the record using <c>mnesia:write/1</c> after we have
+ increased the salary we acquire a write lock (third argument to read) when we read the
+ record from the table.
+ </p>
+ <p>It is not always the case that we can directly read the values from the table,
+ we might need to search the table or several tables to get the data we want, this
+ is done by writing database queries. Queries are always more expensive operations
+ than direct lookups done with <c>mnesia:read</c> and should be avoided in performance
+ critical code.</p>
+ <p>There are two methods for writing database queries:
+ </p>
+ <list type="bulleted">
+ <item>Mnesia functions
+ </item>
+ <item>QLC</item>
+ </list>
+
+ <section>
+ <title>Mnesia functions </title>
+ <p></p>
+ <p>The following function extracts the names of the female employees
+ stored in the database:
+ </p>
+ <pre>
+\011 mnesia:select(employee, [{#employee{sex = female, name = '$1', _ = '_'},[], ['$1']}]).
+ </pre>
+ <p>Select must always run within an activity such as a
+ transaction. To be able to call from the shell we might
+ construct a function as:
+ </p>
+ <codeinclude file="company.erl" tag="%20" type="erl"></codeinclude>
+ <p>The select expression matches all entries in table employee with
+ the field sex set to female.
+ </p>
+ <p>This function can be called from the shell as follows:
+ </p>
+ <pre>
+ (klacke@gin)1> <input>company:all_females().</input>
+ {atomic, ["Carlsson Tuula", "Fedoriw Anna"]}
+ </pre>
+ <p>See also the <seealso marker="Mnesia_chap4#matching">Pattern Matching </seealso>
+ chapter for a description of select and its syntax.
+ </p>
+ </section>
+
+ <section>
+ <title>Using QLC </title>
+ <p>This section contains simple introductory examples
+ only. Refer to <em>QLC reference manual</em> for a
+ full description of the QLC query language. Using QLC
+ might be more expensive than using Mnesia functions directly but
+ offers a nice syntax.
+ </p>
+ <p>The following function extracts a list of female employees
+ from the database:
+ </p>
+ <pre>
+ Q = qlc:q([E#employee.name || E <![CDATA[<-]]> mnesia:table(employee),
+\011 E#employee.sex == female]),
+\011 qlc:e(Q),
+ </pre>
+ <p>Accessing mnesia tables from a QLC list comprehension must
+ always be done within a transaction. Consider the following
+ function:
+ </p>
+ <codeinclude file="company.erl" tag="%2" type="erl"></codeinclude>
+ <p>This function can be called from the shell as follows:
+ </p>
+ <pre>
+ (klacke@gin)1> <input>company:females().</input>
+ {atomic, ["Carlsson Tuula", "Fedoriw Anna"]}
+ </pre>
+ <p>In traditional relational database terminology, the above
+ operation would be called a selection, followed by a projection.
+ </p>
+ <p>The list comprehension expression shown above contains a
+ number of syntactical elements.
+ </p>
+ <list type="bulleted">
+ <item>the first <c>[</c> bracket should be read as "build the
+ list"
+ </item>
+ <item>the <c>||</c> "such that" and the arrow <c><![CDATA[<-]]></c> should
+ be read as "taken from"
+ </item>
+ </list>
+ <p>Hence, the above list comprehension demonstrates the
+ formation of the list <c>E#employee.name</c> such that <c>E</c> is
+ taken from the table of employees and the <c>sex</c> attribute
+ of each records is equal with the atom <c>female</c>.
+ </p>
+ <p>The whole list comprehension must be given to the
+ <c>qlc:q/1</c> function.
+ </p>
+ <p>It is possible to combine list comprehensions with low
+ level Mnesia functions in the same transaction. If we want to
+ raise the salary of all female employees we execute:
+ </p>
+ <codeinclude file="company.erl" tag="%4" type="erl"></codeinclude>
+ <p>The function <c>raise_females/1</c> returns the tuple
+ <c>{atomic, Number}</c>, where <c>Number</c> is the number of
+ female employees who received a salary increase. Should an error
+ occur, the value <c>{aborted, Reason}</c> is returned. In the
+ case of an error, Mnesia guarantees that the salary is not
+ raised for any employees at all.
+ </p>
+ <pre>
+
+ 33><input>company:raise_females(33).</input>
+ {atomic,2}
+ </pre>
+ </section>
+ </section>
+ </section>
+</chapter>
+