diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /lib/mnesia/doc/src/Mnesia_chap2.xmlsrc | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'lib/mnesia/doc/src/Mnesia_chap2.xmlsrc')
-rw-r--r-- | lib/mnesia/doc/src/Mnesia_chap2.xmlsrc | 647 |
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öm, Hans Nilsson and Hå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> + |