aboutsummaryrefslogtreecommitdiffstats
path: root/lib/mnesia/doc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/mnesia/doc')
-rw-r--r--lib/mnesia/doc/html/.gitignore0
-rw-r--r--lib/mnesia/doc/man3/.gitignore0
-rw-r--r--lib/mnesia/doc/man6/.gitignore0
-rw-r--r--lib/mnesia/doc/misc/Makefile64
-rw-r--r--lib/mnesia/doc/misc/implementation.txt375
-rw-r--r--lib/mnesia/doc/pdf/.gitignore0
-rw-r--r--lib/mnesia/doc/src/DATA100
-rw-r--r--lib/mnesia/doc/src/DATA217
-rw-r--r--lib/mnesia/doc/src/FRUITS12
-rw-r--r--lib/mnesia/doc/src/Makefile240
-rw-r--r--lib/mnesia/doc/src/Mnesia_App_A.xml87
-rw-r--r--lib/mnesia/doc/src/Mnesia_App_B.xmlsrc41
-rw-r--r--lib/mnesia/doc/src/Mnesia_App_C.xmlsrc43
-rw-r--r--lib/mnesia/doc/src/Mnesia_App_D.xmlsrc43
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap1.xml265
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap2.xmlsrc647
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap3.xml556
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap4.xmlsrc1171
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap5.xmlsrc1398
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap7.xmlsrc890
-rw-r--r--lib/mnesia/doc/src/Mnesia_chap8.xml64
-rw-r--r--lib/mnesia/doc/src/book.gifbin0 -> 1081 bytes
-rw-r--r--lib/mnesia/doc/src/book.xml48
-rw-r--r--lib/mnesia/doc/src/bup.erl239
-rw-r--r--lib/mnesia/doc/src/company.erl373
-rw-r--r--lib/mnesia/doc/src/company.fig88
-rw-r--r--lib/mnesia/doc/src/company.gifbin0 -> 2781 bytes
-rw-r--r--lib/mnesia/doc/src/company.hrl50
-rw-r--r--lib/mnesia/doc/src/company.ps213
-rw-r--r--lib/mnesia/doc/src/company_o.erl144
-rw-r--r--lib/mnesia/doc/src/company_o.hrl38
-rw-r--r--lib/mnesia/doc/src/fascicules.xml18
-rw-r--r--lib/mnesia/doc/src/make.dep46
-rw-r--r--lib/mnesia/doc/src/mnesia.gifbin0 -> 15184 bytes
-rw-r--r--lib/mnesia/doc/src/mnesia.xml3100
-rw-r--r--lib/mnesia/doc/src/mnesia_frag_hash.xml166
-rw-r--r--lib/mnesia/doc/src/mnesia_registry.xml104
-rw-r--r--lib/mnesia/doc/src/note.gifbin0 -> 1539 bytes
-rw-r--r--lib/mnesia/doc/src/notes.gifbin0 -> 2005 bytes
-rw-r--r--lib/mnesia/doc/src/notes.xml383
-rw-r--r--lib/mnesia/doc/src/notes_history.xml322
-rw-r--r--lib/mnesia/doc/src/part.xml49
-rw-r--r--lib/mnesia/doc/src/part_notes.xml41
-rw-r--r--lib/mnesia/doc/src/part_notes_history.xml41
-rw-r--r--lib/mnesia/doc/src/ref_man.gifbin0 -> 1530 bytes
-rw-r--r--lib/mnesia/doc/src/ref_man.xml44
-rw-r--r--lib/mnesia/doc/src/summary.html.src1
-rw-r--r--lib/mnesia/doc/src/user_guide.gifbin0 -> 1581 bytes
-rw-r--r--lib/mnesia/doc/src/warning.gifbin0 -> 1498 bytes
49 files changed, 11521 insertions, 0 deletions
diff --git a/lib/mnesia/doc/html/.gitignore b/lib/mnesia/doc/html/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/mnesia/doc/html/.gitignore
diff --git a/lib/mnesia/doc/man3/.gitignore b/lib/mnesia/doc/man3/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/mnesia/doc/man3/.gitignore
diff --git a/lib/mnesia/doc/man6/.gitignore b/lib/mnesia/doc/man6/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/mnesia/doc/man6/.gitignore
diff --git a/lib/mnesia/doc/misc/Makefile b/lib/mnesia/doc/misc/Makefile
new file mode 100644
index 0000000000..e5fa327f5b
--- /dev/null
+++ b/lib/mnesia/doc/misc/Makefile
@@ -0,0 +1,64 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+#
+# 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.
+#
+# %CopyrightEnd%
+#
+
+#
+OTP_MAKE_ROOT=/home/super/otp/otp_make
+include $(OTP_MAKE_ROOT)/otp.mk
+
+#
+# Release Macros
+#
+
+#
+# Common macros
+#
+
+
+SGML_FILES= release_notes.sgml
+
+FIG_FILES =
+HTML_FILES= $(SGML_FILES:.sgml=.html)
+TEX_FILES= $(SGML_FILES:.sgml=.tex)
+DVI_FILES= $(SGML_FILES:.sgml=.dvi)
+PSFIG_FILES= $(FIG_FILES:.fig=.ps)
+PS_FILES= $(SGML_FILES:.sgml=.ps)
+GIF_FILES= min_head.gif
+ERL_FILES=
+HRL_FILES=
+DATA_FILES=
+
+
+#
+# Make Rules
+#
+
+all $(DEFAULT_OPT_TARGETS) $(DEFAULT_DEBUG_TARGETS): $(HTML_FILES) $(TEX_FILES) $(PSFIG_FILES) $(DVI_FILES) $(PS_FILES)
+
+clean:
+ @rm -f *.toc *.log *.aux *.tex sgmls_output sgmls_errs $(HTML_FILES) $(TEX_FILES) $(DVI_FILES) $(PSFIG_FILES) $(PS_FILES)
+
+#
+# Release Targets
+#
+include $(OTP_MAKE_ROOT)/otp_release_targets.mk
+
+release_variant: opt.$(TARGET)
+ $(MKPATH.$(TARGET)) $(RELEASE_PATH)/$(TARGET)/lib/mnesia-$(VSN)/doc/misc
+ $(INSTALLFILES.$(TARGET)) $(HTML_FILES) $(GIF_FILES) $(ERL_FILES) $(HRL_FILES) $(DATA_FILES) $(PS_FILES) $(RELEASE_PATH)/$(TARGET)/lib/mnesia-$(VSN)/doc/misc
+
diff --git a/lib/mnesia/doc/misc/implementation.txt b/lib/mnesia/doc/misc/implementation.txt
new file mode 100644
index 0000000000..1b8369e466
--- /dev/null
+++ b/lib/mnesia/doc/misc/implementation.txt
@@ -0,0 +1,375 @@
+
+Mnesia
+
+1 Introduction
+
+This document aims to give a brief introduction of the implementation
+of mnesia, it's data and functions.
+
+H�kan has written other mnesia papers of interest, (see ~hakan/public_html/):
+o Resource consumption (mnesia_consumption.txt)
+o What to think about when changing mnesia (mnesia_upgrade_policy.txt)
+o Mnesia internals course (mnesia_internals_slides.pdf)
+o Mnesia overview (mnesia_overview.pdf)
+
+1.1. Basic concepts
+
+In a mnesia cluster all nodes are equal, there is no concept off
+master or backup nodes. That said when mixing disc based (uses the
+disc to store meta information) nodes and ram based (do not use disc
+at all) nodes the disc based ones sometimes have precedence over ram
+based nodes.
+
+2 Meta Data
+
+Mnesia has two types of global meta data, static and dynamic.
+All the meta data is stored in the ets table mnesia_gvar.
+
+2.1 Static Meta Data
+The static data is the schema information, usually kept in
+'schema.DAT' file, the data is created with
+mnesia:create_schema(Nodes) for disc nodes (i.e. nodes which uses the
+disc). Ram based mnesia nodes create an empty schema at startup.
+
+The static data i.e. schema, contains information about which nodes
+are involved in the cluster and which type (ram or disc) they have. It
+also contains information about which tables exist on which node and
+so on.
+
+The schema information (static data) must always be the same on all
+active nodes in the mnesia cluster. Schema information is updated via
+schema functions, e.g. mnesia:add_table_copy/3,
+mnesia:change_table_copy/3...
+
+2.2 Dynamic Meta Data
+
+The dynamic data is transient and is local to each mnesia node
+in the cluster. Examples of dynamic data is: currently active mnesia
+nodes, which tables are currently available and where are they
+located. Dynamic data is updated internally by each mnesia during the
+nodes lifetime, i.e. when nodes goes up and down or are added to or
+deleted from the mnesia cluster.
+
+3 Processes and Files
+
+The most important processes in mnesia are mnesia_monitor,
+mnesia_controller, mnesia_tm and mnesia_locker.
+
+Mnesia_monitor acts as supervisor and monitors all resources. It
+listens for nodeup and nodedown and keeps links to other mnesia nodes,
+if a node goes down it forwards the information to all the necessary
+processes, e.g. mnesia_controller, mnesia_locker, mnesia_tm and all
+transactions. During start it negotiates the protocol version with
+the other nodes and keep track of which nodes uses which version. The
+monitor process also detects and warns about partioned networks, it is
+then up to the user to deal with them. It is the owner of all open
+files, ets tables and so on.
+
+The mnesia_controller process is responsible for loading tables,
+keeping the dynamic meta data updated, synchronize dangerous work such
+as schema transactions vs dump log operation vs table loading/sending.
+
+The last two processes are involved in all transactions, the
+mnesia_locker process manages transaction locks, and mnesia_tm manages
+all transaction work.
+
+4 Startup and table Loading
+
+The early startup is mostly driven by the mnesia_tm process/module,
+logs are dumped (see log dumping), node-names of other nodes in the
+cluster are retrieved from the static meta data or from environment
+parameters and initial connections are made to the other mnesia
+nodes.
+
+The rest of start up is driven by the mnesia_controller process where
+the schema (static meta data) is merged between each node, this is
+done to keep the schema consistent between all nodes in the
+cluster. When the schema is merged all local tables are put in a
+loading queue, tables which are only available or have local content
+is loaded directly from disc or created if they are type ram_copies.
+
+The other tables are kept in the queue until mnesia decides whether to
+load them from disk or from another node. If another mnesia node has
+already loaded the table, i.e. got a copy in ram or an open dets file,
+the table is always loaded from that node to keep the data consistent.
+If no other node has a loaded copy of the table, some mnesia node has
+to load it first, and the other nodes can copy the table from the
+first node. Mnesia keeps information about when other nodes went down,
+a starting mnesia will check which nodes have been down, if some of
+the nodes have not been down the starting node will let those nodes
+load the table first. If all other nodes have been down then the
+starting mnesia will load the table. The node that is allowed to load
+the table will load it and the other nodes will copy it from that node.
+
+If a node, which the starter node has not a 'mnesia_down' note from,
+is down the starter node will have to wait until that node comes up
+and decision can be taken, this behavior can be overruled by user
+settings. The order of table loading could be described as:
+
+1. Mnesia downs, Normally decides from where mnesia should load tables.
+2. Master nodes (overrides mnesia downs).
+3. Force load (overrides Master nodes).
+ 1) If possible, load table from active master nodes
+ 2) if no master nodes is active load from any active nodes,
+ 3) if no active node has an active table get local copy
+ (if ram create empty one)
+
+Currently mnesia can handle one download and one upload at the same
+time. Dumping and loading/sending may run simultaneously but neither
+of them may run during schema commit. Loaders/senders may not start if
+a schema commit is enqueued. That synchronization is made to prohibit
+that the schema transaction modifies the meta data and the
+prerequisites of the table loading changes.
+
+The actual loading of a table is implemented in 'mnesia_loader.erl'.
+It currently works as follows:
+
+Receiver Sender
+-------- ------
+Spawned
+Find sender node
+Queue sender request ---->
+ Spawned
+*)Spawn real receiver <---- Send init info
+*)Grab schema lock for Grab write table lock
+ that table to avoid Subscribe receiver
+ deadlock with schema transactions to table updates
+Create table (ets or dets) Release Lock
+
+Get data (with ack ---->
+ as flow control) <---- Burst data to receiver
+ Send no_more
+Apply subscription messages
+Store copy on disc Grab read lock
+Create index, snmp data Update meta data info
+and checkpoints if needed cleanup
+no_more ---->
+ Release lock
+
+*) Don't spawn or grab schema lock if operation is add_table_copy,
+ it's already a schema operation.
+
+
+5 Transaction
+
+Transaction are normally driven from the client process, i.e. the
+process that call 'mnesia:transaction'. The client first acquires a
+globally unique transaction id (tid) and temporary transaction storage
+(ts an ets table) from mnesia_tm and then executes the transaction
+fun. Mnesia-api calls such as 'mnesia:write/1' and 'mnesia:read'
+contains code for acquiring the needed locks. Intermediate database
+states and acquired locks are kept in the transaction storage, and all
+mnesia operations has to be "patched" against that store. I.e. a write
+operation in a transaction should be seen within (and only within)
+that transaction, if the same key is read after the write.
+After the transaction fun is completed the ts is analyzed to see which
+nodes are involved in the transaction, and what type of commit protocol
+shall be used. Then the result is committed and additional work such as
+snmp, checkpoints and index updates are performed. The transaction is
+finish by releasing all resources.
+
+An example:
+
+Example = fun(X) ->
+ {table1, key, Value} = mnesia:read(table1, key),
+ ok = mnesia:write(table1, {table1, key, Value+X}),
+ {table1, key, Updated} = mnesia:read(table1, key),
+ Updated
+ end,
+mnesia:transaction(Example, [10]).
+
+A message overview of a simple successful asynchronous transaction
+ non local
+Client Process mnesia_tm(local) mnesia_locker mnesia_tm
+------------------------------------------------------------------------
+Get tid ---->
+ <--- Tid and ts
+Get read lock
+from available node ------------------------------->
+Value <----------Value or restart trans---
+Patch value against ts
+
+Get write lock
+from all nodes ------------------------------->
+ ------------------------------->
+ok's <<---------ok's or restart trans---
+write data in ts
+
+Get read lock,already done.
+Read data Value
+'Patch' data with ts
+Fun return Value+X.
+
+If everything is ok
+commit transaction
+
+Find the nodes that the transaction
+needs to be committed on and
+collect every update from ts.
+
+Ask for commit ----------->
+ ----------------------------------------------->
+
+Ok's <<--------- ------------------------------
+Commit ----------------------------------------------->
+log commit decision on disk
+Commit locally
+ update snmp
+ update checkpoints
+ notify subscribers
+ update index
+
+Release locks ------------------------------->
+Release transaction ----->
+
+Return trans result
+----------------------
+
+If all needed resources are available, i.e. the needed tables are
+loaded somewhere in the cluster during the transaction, and the user
+code doesn't crash, a transaction in mnesia won't fail. If something
+happens in the mnesia cluster such as node down from the replica the
+transaction was about to read from, or that a lock couldn't be
+acquired and the transaction was not allowed to be queued on that
+lock, the transaction is restarted, i.e. all resources are released
+and the fun is called again. By default a transaction can be
+restarted is infinity many times, but the user may choose to limit
+the number of restarts.
+
+The dirty operations don't do any of the above they just finds out
+where to write the data, logs the operation to disk and casts (or call
+in case of sync_dirty operation) the data to those nodes. Therefore
+the dirty operations have the drawback that each write or delete sends
+a message per operation to the involved nodes.
+
+There is also a synchronous variant of 2-phase commit protocol which
+waits on an additional ack message after the transaction is committed
+on every node. The intention is to provide the user with a way to
+solve overloading problems.
+
+A 3-phase commit protocol is used for schema transaction or if the
+transaction result is going to be committed in a asymmetrical way,
+i.e. a transaction that writes to table a and b where table a and b
+have replicas on different nodes. The outcome of the transactions are
+stored temporary in an ets table and in the log file.
+
+6 Schema transactions
+
+Schema transactions are handled differently than ordinary
+transactions, they are implemented in mnesia_schema (and in
+mnesia_dumper). The schema operation is always spawned to protect from
+that the client process dies during the transaction.
+
+The actual transaction fun checks the pre-conditions and acquires the
+needed locks and notes the operation in the transaction store. During
+the commit, the schema transaction runs a schema prepare operation (on
+every node) that does the needed prerequisite job. Then the operation
+is logged to disc, and the actual commit work is done by dumping the
+log. Every schema operation has special clause in mnesia_dumper to
+handle the finishing work. Every schema prepare operation has a
+matching undo_prepare operation which needs to be invoked if the
+transaction is aborted.
+
+7 Locks
+
+"The locking algorithm is a traditional 'two-phase locking'* and the
+deadlock prevention is 'wait-die'*, time stamps for the wait-die algorithm
+is 'Lamport clock'* maintained by mnesia_tm. The Lamport clock is kept
+when the transaction is restarted to avoid starving."
+
+* References can be found in the paper mnesia_overview.pdf
+ Klacke, H�kan and Hans wrote about mnesia.
+
+What the quote above means is that read locks are acquired on the
+replica that mnesia read from, write locks are acquired on all nodes
+which have a replica. Several read lock can lock the same object, but
+write locks are exclusive. The transaction identifier (tid) is a ever
+increasing system uniq counter which have the same sort order on every
+node (a Lamport clock), which enables mnesia_locker to order the lock
+requests. When a lock request arrives, mnesia_locker checks whether
+the lock is available, if it is a 'granted' is sent back to the client
+and the lock is noted as taken in an ets table. If the lock is already
+occupied, it's tid is compared with tid of the transaction holding the
+lock. If the tid of holding transaction is greater than the tid of
+asking transaction it's allowed to be put in the lock queue (another
+ets table) and no response is sent back until the lock is released, if
+not the transaction will get a negative response and mnesia_tm will
+restart the transaction after it has slept for a random time.
+
+Sticky locks works almost as a write lock, the first time a sticky
+lock is acquired a request is sent to all nodes. The lock is marked as
+taken by the requesting node (not transaction), when the lock is later
+released it's only released on the node that has the sticky lock,
+thus the next time a transaction is requesting the lock it don't need
+to ask the others nodes. If another node wants the lock it has to request
+a lock release first, before it can acquire the lock.
+
+8 Fragmented tables
+
+Fragmented tables are used to split a large table in smaller parts.
+It is implemented as a layer between the client and mnesia which
+extends the meta data with additional properties and maps a {table,
+key} tuple to a table_fragment.
+
+The default mapping is erlang:phash() but the user may provide his own
+mapping function to be able to predict which records is stored in
+which table fragment, e.g. the client may want to steer where a
+record generated from a certain device is placed.
+
+The foreign key is used to co-locate other tables to the same node.
+The other additinal table attributes are also used to distribute the
+table fragments.
+
+9 Log Dumping
+
+All operations on disk tables are stored on a log 'LATEST.LOG' on
+disk, so mnesia can redo the transactions if the node goes down.
+Dumping the log means that mnesia moves the committed data from the
+general log to the table specific disk storage. To avoid that the log
+grows to large and uses a lot of disk space and makes the startup slow,
+mnesia dumps the log during it's uptime. There are two triggers that
+start the log dumping, timeouts and the number of commits since last
+dump, both are user configurable.
+
+Disc copies tables are implemented with two disk_log files, one
+'table.DCD' (disc copies data) and one 'table.DCL' (disc copies log).
+The dcd contains raw records, and the dcl contains operations on that
+table, i.e. '{write, {table, key, value}}' or '{delete, {table,
+key}}'. First time a record for a specific table is found when
+dumping the table, the size of both the dcd and the dcl files are
+checked. And if the sizeof(dcl)/sizeof(dcd) is greater than a
+threshold, the current ram table is dumped to file 'table.DCD' and the
+corresponding dcl file is deleted, and all other records in the
+general log that belongs to that table are ignored. If the threshold
+is not meet than the operations in the general log to that table are
+appended to the dcl file. On start up both files are read, first the
+contents of the dcd are loaded to an ets table, then it's modified by
+the operations stored in the corresponding dcl file.
+
+Disc only copies tables updates the 'dets' file directly when
+committing the data so those entries can be ignored during normal log
+dumping, they are only added to the 'dets' file during startup when
+mnesia don't know the state of the disk table.
+
+10 Checkpoints and backups
+
+Checkpoints are created to be able to take snapshots of the database,
+which is pretty good when you want consistent backups, i.e. you don't
+want half of a transaction in the backup. The checkpoint creates a
+shadow table (called retainer) for each table involved in the
+checkpoint. When a checkpoint is requested it will not start until all
+ongoing transactions are completed. The new transactions will update
+both the real table and update the shadow table with operations to
+undo the changes on the real table, when a key is modified the first
+time. I.e. when write operation '{table, a, 14}' is made, the shadow
+table is checked if key 'a' has a undo operation, if it has, nothing
+more is done. If not a {write, {table, a, OLD_VALUE}} is added to the
+shadow table if the real table had an old value, if not a {delete,
+{table, a}} operation is added to the shadow table.
+
+The backup is taken by copying every record in the real table and then
+appending every operation in the shadow table to the backup, thus
+undoing the changes that where made since the checkpoint where
+started.
+
+
diff --git a/lib/mnesia/doc/pdf/.gitignore b/lib/mnesia/doc/pdf/.gitignore
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/lib/mnesia/doc/pdf/.gitignore
diff --git a/lib/mnesia/doc/src/DATA b/lib/mnesia/doc/src/DATA
new file mode 100644
index 0000000000..5b4bedacaf
--- /dev/null
+++ b/lib/mnesia/doc/src/DATA
@@ -0,0 +1,100 @@
+%S0
+{tables,
+ [{employee, [{attributes, [emp_no,name,salary,sex, phone,room_no]}]},
+ {dept, [{attributes, [id, name]}]},
+ {project, [{attributes, [name, number]}]},
+ {manager, [{attributes, [emp, dept]},
+ {type, bag}]},
+ {at_dep, [{attributes, [emp, dept_id]}]},
+ {in_proj, [{attributes, [emp, proj_name]},
+ {type, bag}]}
+ ]
+}.
+
+%E0
+
+
+%S1
+{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, 114849, "Armstrong Josef", 3, male, 99452, {221,023}}.
+{employee, 114952, "Froberg Magnus", 5, male, 99469, {222,018}}.
+{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, 115020, "Hansson Catrin", 6, female, 99129, {222,072}}.
+{employee, 115018, "Mattsson Hakan", 3, male, 99251, {203,348}}.
+{employee, 113069, "Eriksson Morgan", 6, male, 99186, {241,543}}.
+%E1
+
+%S2
+%% departments
+{dept, 'B/SF', "Open Telecom Platform"}.
+{dept, 'B/SFP', "OTP - Product Development"}.
+{dept, 'B/SFR', "Computer Science Laboratory"}.
+%E2
+
+
+%% projects
+%S3
+{project, erlang, 1}.
+{project, otp, 2}.
+{project, beam, 3}.
+{project, mnesia, 5}.
+{project, wolf, 6}.
+{project, documentation, 7}.
+{project, www, 8}.
+
+%E3
+
+
+
+%% manager
+%S4
+{manager, 104465, 'B/SF'}.
+{manager, 104465, 'B/SFP'}.
+{manager, 114872, 'B/SFR'}.
+
+%E4
+%S5
+{at_dep, 104465, 'B/SF'}.
+{at_dep, 107912, 'B/SF'}.
+{at_dep, 114872, 'B/SFR'}.
+{at_dep, 114849, 'B/SFR'}.
+{at_dep, 114952, 'B/SFR'}.
+{at_dep, 104531, 'B/SFR'}.
+{at_dep, 104659, 'B/SFR'}.
+{at_dep, 104732, 'B/SFR'}.
+{at_dep, 117716, 'B/SFP'}.
+{at_dep, 115020, 'B/SFP'}.
+{at_dep, 115018, 'B/SFP'}.
+{at_dep, 113069, 'B/SFP'}.
+
+
+%E5
+%S6
+{in_proj, 104465, otp}.
+{in_proj, 107912, otp}.
+{in_proj, 114872, otp}.
+{in_proj, 114849, otp}.
+{in_proj, 114849, erlang}.
+{in_proj, 114952, 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, 115020, otp}.
+{in_proj, 115018, otp}.
+{in_proj, 115018, mnesia}.
+{in_proj, 113069, otp}.
+
+%E6
+
diff --git a/lib/mnesia/doc/src/DATA2 b/lib/mnesia/doc/src/DATA2
new file mode 100644
index 0000000000..e547e84d99
--- /dev/null
+++ b/lib/mnesia/doc/src/DATA2
@@ -0,0 +1,17 @@
+
+
+%S0
+{tables
+ [{foo, [{attributes, [x,y,z]}]}]}.
+%E0
+
+
+%S1
+{foo, a, benny, 18}.
+{foo, b, elvis, 19}.
+{foo, c, benny, 20}.
+{foo, d, elvis, 21}.
+{foo, e, klacke, 22}.
+{foo, f, hans, 23}.
+%E1
+
diff --git a/lib/mnesia/doc/src/FRUITS b/lib/mnesia/doc/src/FRUITS
new file mode 100644
index 0000000000..43d64f9d8c
--- /dev/null
+++ b/lib/mnesia/doc/src/FRUITS
@@ -0,0 +1,12 @@
+%0
+{tables,
+ [{fruit, [{attributes, [name, color, taste]}]},
+ {vegetable, [{attributes, [name, color, taste, price]}]}]}.
+
+
+{fruit, orange, orange, sweet}.
+{fruit, apple, green, sweet}.
+{vegetable, carrot, orange, carrotish, 2.55}.
+{vegetable, potato, yellow, none, 0.45}.
+%0
+
diff --git a/lib/mnesia/doc/src/Makefile b/lib/mnesia/doc/src/Makefile
new file mode 100644
index 0000000000..f45b5137a3
--- /dev/null
+++ b/lib/mnesia/doc/src/Makefile
@@ -0,0 +1,240 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+#
+# 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.
+#
+# %CopyrightEnd%
+#
+
+#
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+# ----------------------------------------------------
+# Application version
+# ----------------------------------------------------
+include ../../vsn.mk
+VSN=$(MNESIA_VSN)
+APPLICATION=mnesia
+
+# ----------------------------------------------------
+# Include dependency
+# ----------------------------------------------------
+
+ifndef DOCSUPPORT
+include make.dep
+endif
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN)
+
+# ----------------------------------------------------
+# Target Specs
+# ----------------------------------------------------
+XML_APPLICATION_FILES = ref_man.xml
+XML_REF3_FILES = \
+ mnesia.xml \
+ mnesia_frag_hash.xml \
+ mnesia_registry.xml
+
+XML_PART_FILES = \
+ part.xml \
+ part_notes.xml \
+ part_notes_history.xml
+
+XML_CHAPTER_FILES = \
+ Mnesia_chap1.xml \
+ Mnesia_chap2.xml \
+ Mnesia_chap3.xml \
+ Mnesia_chap4.xml \
+ Mnesia_chap5.xml \
+ Mnesia_chap7.xml \
+ Mnesia_chap8.xml \
+ Mnesia_App_A.xml \
+ Mnesia_App_B.xml \
+ Mnesia_App_C.xml \
+ Mnesia_App_D.xml \
+ notes.xml
+
+BOOK_FILES = book.xml
+
+
+XML_FILES = \
+ $(BOOK_FILES) $(XML_CHAPTER_FILES) \
+ $(XML_PART_FILES) $(XML_REF3_FILES) $(XML_APPLICATION_FILES)
+
+GIF_FILES = \
+ book.gif \
+ company.gif \
+ mnesia.gif \
+ note.gif \
+ notes.gif \
+ ref_man.gif \
+ user_guide.gif \
+ warning.gif
+
+XML_HTML_FILES = \
+ notes_history.xml
+
+
+# ----------------------------------------------------
+
+HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_HTML_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html)
+
+INFO_FILE = ../../info
+EXTRA_FILES = summary.html.src \
+ $(DEFAULT_GIF_FILES) \
+ $(DEFAULT_HTML_FILES) \
+ $(XML_REF3_FILES:%.xml=$(HTMLDIR)/%.html) \
+ $(XML_CHAPTER_FILES:%.xml=$(HTMLDIR)/%.html)
+
+MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3)
+
+ifdef DOCSUPPORT
+
+HTML_REF_MAN_FILE = $(HTMLDIR)/index.html
+
+TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf
+
+else
+TEX_FILES_BOOK = \
+ $(BOOK_FILES:%.xml=%.tex)
+TEX_FILES_REF_MAN = $(XML_REF3_FILES:%.xml=%.tex) \
+ $(XML_APPLICATION_FILES:%.xml=%.tex)
+TEX_FILES_USERS_GUIDE = \
+ $(XML_CHAPTER_FILES:%.xml=%.tex)
+
+TOP_PDF_FILE = $(APPLICATION)-$(VSN).pdf
+TOP_PS_FILE = $(APPLICATION)-$(VSN).ps
+
+$(TOP_PDF_FILE): book.dvi ../../vsn.mk
+ $(DVI2PS) $(DVIPS_FLAGS) -f $< | $(DISTILL) $(DISTILL_FLAGS) > $@
+
+$(TOP_PS_FILE): book.dvi ../../vsn.mk
+ $(DVI2PS) $(DVIPS_FLAGS) -f $< > $@
+
+endif
+
+# ----------------------------------------------------
+# FLAGS
+# ----------------------------------------------------
+XML_FLAGS +=
+DVIPS_FLAGS +=
+
+# ----------------------------------------------------
+# Targets
+# ----------------------------------------------------
+$(HTMLDIR)/%.gif: %.gif
+ $(INSTALL_DATA) $< $@
+
+ifdef DOCSUPPORT
+
+docs: pdf html man
+
+$(TOP_PDF_FILE): $(XML_FILES)
+
+pdf: $(TOP_PDF_FILE)
+
+html: gifs $(HTML_REF_MAN_FILE)
+
+clean clean_docs:
+ rm -rf $(HTMLDIR)/*
+ rm -f $(MAN3DIR)/*
+ rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo)
+ rm -f errs core *~
+
+else
+
+ifeq ($(DOCTYPE),pdf)
+docs: pdf
+else
+ifeq ($(DOCTYPE),ps)
+docs: ps
+else
+docs: html gifs man
+endif
+endif
+
+pdf: $(TOP_PDF_FILE)
+
+ps: $(TOP_PS_FILE)
+
+html: $(HTML_FILES)
+
+
+clean clean_docs clean_tex:
+ rm -f $(TEX_FILES_USERS_GUIDE) $(TEX_FILES_REF_MAN) $(TEX_FILES_BOOK)
+ rm -f $(HTML_FILES) $(MAN3_FILES)
+ rm -f $(TOP_PDF_FILE) $(TOP_PS_FILE)
+ rm -f errs core *~ *xmls_output *xmls_errs $(LATEX_CLEAN)
+
+endif
+
+man: $(MAN3_FILES)
+
+gifs: $(GIF_FILES:%=$(HTMLDIR)/%)
+
+$(INDEX_TARGET): $(INDEX_SRC) ../../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' $< > $@
+
+debug opt:
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+ifdef DOCSUPPORT
+
+release_docs_spec: docs
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(HTMLDIR)/* \
+ $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
+ $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
+
+else
+
+ifeq ($(DOCTYPE),pdf)
+release_docs_spec: pdf
+ $(INSTALL_DIR) $(RELEASE_PATH)/pdf
+ $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELEASE_PATH)/pdf
+else
+ifeq ($(DOCTYPE),ps)
+release_docs_spec: ps
+ $(INSTALL_DIR) $(RELEASE_PATH)/ps
+ $(INSTALL_DATA) $(TOP_PS_FILE) $(RELEASE_PATH)/ps
+else
+release_docs_spec: docs
+ $(INSTALL_DIR) $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \
+ $(RELSYSDIR)/doc/html
+ $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR)
+ $(INSTALL_DIR) $(RELEASE_PATH)/man/man3
+ $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3
+endif
+endif
+
+endif
+
+
+release_spec:
+
diff --git a/lib/mnesia/doc/src/Mnesia_App_A.xml b/lib/mnesia/doc/src/Mnesia_App_A.xml
new file mode 100644
index 0000000000..86e5b7d03c
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_App_A.xml
@@ -0,0 +1,87 @@
+<?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>Appendix A: Mnesia Error Messages</title>
+ <prepared>Claes Wikstr&ouml;m, Hans Nilsson and H&aring;kan Mattsson</prepared>
+ <responsible>Bjarne D&auml;cker</responsible>
+ <docno></docno>
+ <approved>Bjarne D&auml;cker</approved>
+ <checked>Bjarne D&auml;cker</checked>
+ <date>96-11-20</date>
+ <rev>B</rev>
+ <file>Mnesia_App_A.xml</file>
+ </header>
+ <p>Whenever an operation returns an error in Mnesia, a description
+ of the error is available. For example, the functions
+ <c>mnesia:transaction(Fun)</c>, or <c>mnesia:create_table(N,L)</c>
+ may return the tuple <c>{aborted, Reason}</c>, where <c>Reason</c>
+ is a term describing the error. The following function is used to
+ retrieve more detailed information about the error:
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:error_description(Error)</c></item>
+ </list>
+
+ <section>
+ <title>Errors in Mnesia</title>
+ <p>The following is a list of valid errors in Mnesia.</p>
+ <list type="bulleted">
+ <item><c>badarg</c>. Bad or invalid argument, possibly bad type.
+ </item>
+ <item><c>no_transaction</c>. Operation not allowed outside transactions.
+ </item>
+ <item><c>combine_error</c>. Table options were illegally combined.
+ </item>
+ <item><c>bad_index</c>. Index already exists, or was out of bounds.
+ </item>
+ <item><c>already_exists</c>. Schema option to be activated is already on.
+ </item>
+ <item><c>index_exists</c>. Some operations cannot be performed on tables with an index.
+ </item>
+ <item><c>no_exists</c>.; Tried to perform operation on non-existing (non-alive) item.
+ </item>
+ <item><c>system_limit</c>.; A system limit was exhausted.
+ </item>
+ <item><c>mnesia_down</c>. A transaction involves records on a
+ remote node which became unavailable before the transaction
+ was completed. Record(s) are no longer available elsewhere in
+ the network.</item>
+ <item><c>not_a_db_node</c>. A node was mentioned which does not exist in the schema.</item>
+ <item><c>bad_type</c>.; Bad type specified in argument.</item>
+ <item><c>node_not_running</c>. Node is not running.</item>
+ <item><c>truncated_binary_file</c>. Truncated binary in file.</item>
+ <item><c>active</c>. Some delete operations require that all active records are removed.</item>
+ <item><c>illegal</c>. Operation not supported on this record.</item>
+ </list>
+ <p>The following example illustrates a function which returns an error, and the method to retrieve more detailed error information.
+ </p>
+ <p>The function <c>mnesia:create_table(bar, [{attributes, 3.14}])</c> will return the tuple <c>{aborted,Reason}</c>, where <c>Reason</c> is the tuple
+ <c>{bad_type,bar,3.14000}</c>.
+ </p>
+ <p>The function <c>mnesia:error_description(Reason)</c>, returns the term
+ <c>{"Bad type on some provided arguments",bar,3.14000}</c> which is an error
+ description suitable
+ for display.</p>
+ </section>
+</chapter>
+
diff --git a/lib/mnesia/doc/src/Mnesia_App_B.xmlsrc b/lib/mnesia/doc/src/Mnesia_App_B.xmlsrc
new file mode 100644
index 0000000000..52f5e06d83
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_App_B.xmlsrc
@@ -0,0 +1,41 @@
+<?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>Appendix B: The Backup Call Back Interface</title>
+ <prepared>Claes Wikstr&ouml;m, Hans Nilsson and H&aring;kan Mattsson</prepared>
+ <responsible>Bjarne D&auml;cker</responsible>
+ <docno></docno>
+ <approved>Bjarne D&auml;cker</approved>
+ <checked>Bjarne D&auml;cker</checked>
+ <date>97-05-27</date>
+ <rev>C</rev>
+ <file>Mnesia_App_B.xml</file>
+ </header>
+
+ <section>
+ <title>mnesia_backup callback behavior</title>
+ <p></p>
+ <codeinclude file="../../src/mnesia_backup.erl" tag="%0" type="erl"></codeinclude>
+ </section>
+</chapter>
+
diff --git a/lib/mnesia/doc/src/Mnesia_App_C.xmlsrc b/lib/mnesia/doc/src/Mnesia_App_C.xmlsrc
new file mode 100644
index 0000000000..d8916f25cb
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_App_C.xmlsrc
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1998</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>Appendix C: The Activity Access Call Back Interface</title>
+ <prepared>H&aring;kan Mattsson</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
+ <file>Mnesia_App_C.xml</file>
+ </header>
+
+ <section>
+ <title>mnesia_access callback behavior</title>
+ <p></p>
+ <codeinclude file="../../src/mnesia_frag.erl" tag="%header_doc_include" type="erl"></codeinclude>
+ <p></p>
+ <codeinclude file="../../src/mnesia_frag.erl" tag="%impl_doc_include" type="erl"></codeinclude>
+ </section>
+</chapter>
+
diff --git a/lib/mnesia/doc/src/Mnesia_App_D.xmlsrc b/lib/mnesia/doc/src/Mnesia_App_D.xmlsrc
new file mode 100644
index 0000000000..d98680640d
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_App_D.xmlsrc
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2002</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>Appendix D: The Fragmented Table Hashing Call Back Interface</title>
+ <prepared>H&aring;kan Mattsson</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
+ <file>Mnesia_App_D.xml</file>
+ </header>
+
+ <section>
+ <title>mnesia_frag_hash callback behavior</title>
+ <p></p>
+ <codeinclude file="../../src/mnesia_frag_hash.erl" tag="%header_doc_include" type="erl"></codeinclude>
+ <p></p>
+ <codeinclude file="../../src/mnesia_frag_hash.erl" tag="%impl_doc_include" type="erl"></codeinclude>
+ </section>
+</chapter>
+
diff --git a/lib/mnesia/doc/src/Mnesia_chap1.xml b/lib/mnesia/doc/src/Mnesia_chap1.xml
new file mode 100644
index 0000000000..9af81c85cb
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_chap1.xml
@@ -0,0 +1,265 @@
+<?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>Introduction</title>
+ <prepared>Claes Wikstr&ouml;m, Hans Nilsson and H&aring;kan Mattsson</prepared>
+ <responsible>Bjarne D&auml;cker</responsible>
+ <docno></docno>
+ <approved>Bjarne D&auml;cker</approved>
+ <checked>Bjarne D&auml;cker</checked>
+ <date></date>
+ <rev>C</rev>
+ <file>Mnesia_chap1.xml</file>
+ </header>
+ <p>This book describes the Mnesia DataBase Management
+ System (DBMS). <em>Mnesia</em> is a distributed Database Management
+ System, appropriate for telecommunications applications and other
+ Erlang applications which require continuous operation and soft
+ real-time properties. It is one section of the Open Telecom Platform
+ (OTP), which is a control system platform for building
+ telecommunications applications.</p>
+
+ <section>
+ <title>About Mnesia</title>
+ <p>The management of data in telecommunications system has many
+ aspects whereof some, but not all, are addressed by traditional
+ commercial DBMSs (Data Base Management Systems). In particular the
+ very high level of fault tolerance which is required in many nonstop
+ systems, combined with requirements on the DBMS to run in the same
+ address space as the application, have led us to implement a brand new
+ DBMS. called Mnesia. Mnesia is implemented in, and very tightly
+ connected to, the programming language Erlang and it provides the
+ functionality that is necessary for the implementation of fault
+ tolerant telecommunications systems. Mnesia is a multiuser Distributed
+ DBMS specially made for industrial telecommunications applications
+ written in the symbolic programming language Erlang, which is also
+ the intended target language. Mnesia tries to address all of the data
+ management issues required for typical telecommunications systems and
+ it has a number of features that are not normally found in traditional
+ databases. <br></br>
+
+ In telecommunications applications there are different needs
+ from the features provided by traditional DBMSs. The applications now
+ implemented in the Erlang language need a mixture of a broad range
+ of features, which generally are not satisfied by traditional DBMSs.
+ Mnesia is designed with requirements like the following in mind:</p>
+ <list type="ordered">
+ <item>Fast real-time key/value lookup</item>
+ <item>Complicated non real-time queries mainly for
+ operation and maintenance</item>
+ <item>Distributed data due to distributed
+ applications</item>
+ <item>High fault tolerance</item>
+ <item>Dynamic re-configuration</item>
+ <item>Complex objects</item>
+ </list>
+ <p>What
+ sets Mnesia apart from most other DBMSs is that it is designed with
+ the typical data management problems of telecommunications applications
+ in mind. Hence Mnesia combines many concepts found in traditional
+ databases, such as transactions and queries with concepts found in data
+ management systems for telecommunications applications, such as very
+ fast real-time operations, configurable degree of fault tolerance (by
+ means of replication) and the ability to reconfigure the system without
+ stopping or suspending it. Mnesia is also interesting due to its tight
+ coupling to the programming language Erlang, thus almost turning Erlang
+ into a database programming language. This has many benefits, the
+ foremost is that
+ the impedance mismatch between data format used by the
+ DBMS and data format used by the programming language, which is used
+ to manipulate the data, completely disappears. <br></br>
+</p>
+ </section>
+
+ <section>
+ <title>The Mnesia DataBase Management System (DBMS)</title>
+ <p></p>
+
+ <section>
+ <title>Features</title>
+ <p>Mnesia contains the following features which combine to produce a fault-tolerant,
+ distributed database management system written in Erlang:
+ </p>
+ <list type="bulleted">
+ <item>Database schema can be dynamically reconfigured at runtime.
+ </item>
+ <item>Tables can be declared to have properties such as location,
+ replication, and persistence.
+ </item>
+ <item>Tables can be moved or replicated to several nodes to improve
+ fault tolerance. The rest of the system can still access the tables
+ to read, write, and delete records.
+ </item>
+ <item>Table locations are transparent to the programmer.
+ Programs address table names and the system itself keeps track of
+ table locations.
+ </item>
+ <item>Database transactions can be distributed, and a large number of
+ functions can be called within one transaction.
+ </item>
+ <item>Several transactions can run concurrently, and their execution is
+ fully synchronized by the database management system.
+ Mnesia ensures that no two processes manipulate data simultaneously.
+ </item>
+ <item>Transactions can be assigned the property of being executed on
+ all nodes in the system, or on none. Transactions can also be bypassed
+ in favor of running so called "dirty operations", which reduce
+ overheads and run very fast.
+ </item>
+ </list>
+ <p>Details of these features are described in the following sections.</p>
+ </section>
+ <p></p>
+
+ <section>
+ <title>Add-on Applications</title>
+ <p>QLC and Mnesia Session can be used in conjunction with Mnesia to produce
+ specialized functions which enhance the operational ability of Mnesia.
+ Both Mnesia Session and QLC have their own documentation as part
+ of the OTP documentation set. Below are the main features of Mnesia Session
+ and QLC when used in conjunction with Mnesia:</p>
+ <list type="bulleted">
+ <item><em>QLC</em> has the ability to optimize the query
+ compiler for the Mnesia Database Management System, essentially making
+ the DBMS more efficient.</item>
+ <item><em>QLC</em>, can be used as a database programming
+ language for Mnesia. It includes a notation called "list
+ comprehensions" and can be used to make complex database
+ queries over a set of tables.</item>
+ <item><em>Mnesia Session</em> is an interface for the Mnesia Database
+ Management System</item>
+ <item><em>Mnesia Session</em> enables access to the
+ Mnesia DBMS from foreign programming languages (i.e. other
+ languages than Erlang).</item>
+ </list>
+ <p></p>
+
+ <section>
+ <title>When to Use Mnesia</title>
+ <p>Use Mnesia with the following types of applications:
+ </p>
+ <list type="bulleted">
+ <item>Applications that need to replicate data.
+ </item>
+ <item>Applications that perform complicated searches on data.
+ </item>
+ <item>Applications that need to use atomic transactions to
+ update several records simultaneously.
+ </item>
+ <item>Applications that use soft real-time characteristics.
+ </item>
+ </list>
+ <p>On the other hand, Mnesia may not be appropriate with the
+ following types of applications:
+ </p>
+ <list type="bulleted">
+ <item>Programs that process plain text or binary data files
+ </item>
+ <item>Applications that merely need a look-up dictionary
+ which can be stored to disc can utilize the standard
+ library module <c>dets</c>, which is a disc based version
+ of the module <c>ets</c>.
+ </item>
+ <item>Applications which need disc logging facilities can
+ utilize the module <c>disc_log</c> by preference.
+ </item>
+ <item>Not suitable for hard real time systems.
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Scope and Purpose</title>
+ <p>This manual is included in the OTP document set. It describes
+ how to build Mnesia database applications, and how to integrate
+ and utilize the Mnesia database management system with
+ OTP. Programming constructs are described, and numerous
+ programming examples are included to illustrate the use of
+ Mnesia.
+ </p>
+ </section>
+
+ <section>
+ <title>Prerequisites</title>
+ <p>Readers of this manual are assumed to be familiar with system
+ development principles and database management systems. Readers
+ are also assumed to be familiar with the Erlang programming
+ language.</p>
+ </section>
+
+ <section>
+ <title>About This Book</title>
+ <p>This book contains the following chapters:
+ </p>
+ <list type="bulleted">
+ <item>Chapter 2, "Getting Started with Mnesia", introduces
+ Mnesia with an example database. Examples are shown of how to
+ start an Erlang session, specify a Mnesia database directory,
+ initialize a database schema, start Mnesia, and create
+ tables. Initial prototyping of record definitions is also
+ discussed.
+ </item>
+ <item>Chapter 3, "Building a Mnesia Database", more formally
+ describes the steps introduced in Chapter 2, namely the Mnesia
+ functions which define a database schema, start Mnesia, and
+ create the required tables.
+ </item>
+ <item>Chapter 4, "Transactions and other access contexts",
+ describes the transactions properties which make Mnesia into a
+ fault tolerant, real-time distributed database management
+ system. This chapter also describes the concept of locking in
+ order to ensure consistency in tables, and so called "dirty
+ operations", or short cuts which bypass the transaction system
+ to improve speed and reduce overheads.
+ </item>
+ <item>Chapter 5, "Miscellaneous Mnesia Features", describes
+ features which enable the construction of more complex
+ database applications. These features includes indexing,
+ checkpoints, distribution and fault tolerance, disc-less
+ nodes, replication manipulation, local content tables, concurrency,
+ and object based programming in Mnesia.
+ </item>
+ <item>Chapter 6, "Mnesia System Information", describes the
+ files contained in the Mnesia database directory, database
+ configuration data, core and table dumps, as well as the
+ important subject of backup, fall-back, and disaster recovery
+ principles.
+ </item>
+ <item>Chapter 7, "Combining Mnesia with SNMP", is a short
+ chapter which outlines Mnesia integrated with SNMP.
+ </item>
+ <item>Appendix A, "Mnesia Errors Messages", lists Mnesia error
+ messages and their meanings.
+ </item>
+ <item>Appendix B, "The Backup Call Back Interface", is a
+ program listing of the default implementation of this facility.
+ </item>
+ <item>Appendix C, "The Activity Access Call Back Interface",
+ is a program outlining of one possible implementations of this facility.
+ </item>
+ </list>
+ </section>
+ </section>
+</chapter>
+
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>
+
diff --git a/lib/mnesia/doc/src/Mnesia_chap3.xml b/lib/mnesia/doc/src/Mnesia_chap3.xml
new file mode 100644
index 0000000000..9a382bcb5a
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_chap3.xml
@@ -0,0 +1,556 @@
+<?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>Building A Mnesia Database</title>
+ <prepared></prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
+ <file>Mnesia_chap3.xml</file>
+ </header>
+ <p>This chapter details the basic steps involved when designing
+ a Mnesia database and the programming constructs which make different
+ solutions available to the programmer. The chapter includes the following
+ sections:
+ </p>
+ <list type="bulleted">
+ <item>defining a schema</item>
+ <item>the datamodel</item>
+ <item>starting Mnesia</item>
+ <item>creating new tables.</item>
+ </list>
+
+ <section>
+ <marker id="def_schema"></marker>
+ <title>Defining a Schema</title>
+ <p>The configuration of a Mnesia system is described in the
+ schema. The schema is a special table which contains information
+ such as the table names and each table's
+ storage type, (i.e. whether a table should be stored in RAM,
+ on disc or possibly on both, as well as its location).
+ </p>
+ <p>Unlike data tables, information contained in schema tables can only be
+ accessed and modified by using the schema related functions
+ described in this section.
+ </p>
+ <p>Mnesia has various functions for defining the
+ database schema. It is possible to move tables, delete tables,
+ or reconfigure the layout of tables.
+ </p>
+ <p>An important aspect of these functions is that the system can access a
+ table while it is being reconfigured. For example, it is possible to move a
+ table and simultaneously perform write operations to the same
+ table. This feature is essential for applications that require
+ continuous service.
+ </p>
+ <p>The following section describes the functions available for schema management,
+ all of which return a tuple:
+ </p>
+ <list type="bulleted">
+ <item><c>{atomic, ok}</c>; or,
+ </item>
+ <item><c>{aborted, Reason}</c> if unsuccessful.</item>
+ </list>
+
+ <section>
+ <title>Schema Functions</title>
+ <list type="bulleted">
+ <item><c>mnesia:create_schema(NodeList)</c>. This function is
+ used to initialize a new, empty schema. This is a mandatory
+ requirement before Mnesia can be started. Mnesia is a truly
+ distributed DBMS and the schema is a system table that is
+ replicated on all nodes in a Mnesia system.
+ The function will fail if a schema is already present on any of
+ the nodes in <c>NodeList</c>. This function requires Mnesia
+ to be stopped on the all
+ <c>db_nodes</c> contained in the parameter <c>NodeList</c>.
+ Applications call this function only once,
+ since it is usually a one-time activity to initialize a new
+ database.
+ </item>
+ <item><c>mnesia:delete_schema(DiscNodeList)</c>. This function
+ erases any old schemas on the nodes in
+ <c>DiscNodeList</c>. It also removes all old tables together
+ with all data. This function requires Mnesia to be stopped
+ on all <c>db_nodes</c>.
+ </item>
+ <item><c>mnesia:delete_table(Tab)</c>. This function
+ permanently deletes all replicas of table <c>Tab</c>.
+ </item>
+ <item><c>mnesia:clear_table(Tab)</c>. This function
+ permanently deletes all entries in table <c>Tab</c>.
+ </item>
+ <item><c>mnesia:move_table_copy(Tab, From, To)</c>. This
+ function moves the copy of table <c>Tab</c> from node
+ <c>From</c> to node <c>To</c>. The table storage type,
+ <c>{type}</c> is preserved, so if a RAM table is moved from
+ one node to another node, it remains a RAM table on the new
+ node. It is still possible for other transactions to perform
+ read and write operation to the table while it is being
+ moved.
+ </item>
+ <item><c>mnesia:add_table_copy(Tab, Node, Type)</c>. This
+ function creates a replica of the table <c>Tab</c> at node
+ <c>Node</c>. The <c>Type</c> argument must be either of the
+ atoms <c>ram_copies</c>, <c>disc_copies</c>, or
+ <c>disc_only_copies</c>. If we add a copy of the system
+ table <c>schema</c> to a node, this means that we want the
+ Mnesia schema to reside there as well. This action then
+ extends the set of nodes that comprise this particular
+ Mnesia system.
+ </item>
+ <item><c>mnesia:del_table_copy(Tab, Node)</c>. This function
+ deletes the replica of table <c>Tab</c> at node <c>Node</c>.
+ When the last replica of a table is removed, the table is
+ deleted.
+ </item>
+ <item>
+ <p><c>mnesia:transform_table(Tab, Fun, NewAttributeList, NewRecordName)</c>. This
+ function changes the format on all records in table
+ <c>Tab</c>. It applies the argument <c>Fun</c> to all
+ records in the table. <c>Fun</c> shall be a function which
+ takes an record of the old type, and returns the record of the new
+ type. The table key may not be changed.</p>
+ <code type="none">
+-record(old, {key, val}).
+-record(new, {key, val, extra}).
+
+Transformer =
+ fun(X) when record(X, old) ->
+ #new{key = X#old.key,
+ val = X#old.val,
+ extra = 42}
+ end,
+{atomic, ok} = mnesia:transform_table(foo, Transformer,
+ record_info(fields, new),
+ new),
+ </code>
+ <p>The <c>Fun</c> argument can also be the atom
+ <c>ignore</c>, it indicates that only the meta data about the table will
+ be updated. Usage of <c>ignore</c> is not recommended (since it creates
+ inconsistencies between the meta data and the actual data) but included
+ as a possibility for the user do to his own (off-line) transform.</p>
+ </item>
+ <item><c>change_table_copy_type(Tab, Node, ToType)</c>. This
+ function changes the storage type of a table. For example, a
+ RAM table is changed to a disc_table at the node specified
+ as <c>Node</c>.</item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>The Data Model</title>
+ <p>The data model employed by Mnesia is an extended
+ relational data model. Data is organized as a set of
+ tables and relations between different data records can
+ be modeled as additional tables describing the actual
+ relationships.
+ Each table contains instances of Erlang records
+ and records are represented as Erlang tuples.
+ </p>
+ <p>Object identifiers, also known as oid, are made up of a table name and a key.
+ For example, if we have an employee record represented by the tuple
+ <c>{employee, 104732, klacke, 7, male, 98108, {221, 015}}</c>.
+ This record has an object id, (Oid) which is the tuple
+ <c>{employee, 104732}</c>.
+ </p>
+ <p>Thus, each table is made up of records, where the first element
+ is a record name and the second element of the table is a key
+ which identifies the particular record in that table. The
+ combination of the table name and a key, is an arity two tuple
+ <c>{Tab, Key}</c> called the Oid. See Chapter 4:<seealso marker="Mnesia_chap4#recordnames_tablenames">Record Names Versus Table Names</seealso>, for more information
+ regarding the relationship between the record name and the table
+ name.
+ </p>
+ <p>What makes the Mnesia data model an extended relational model
+ is the ability to store arbitrary Erlang terms in the attribute
+ fields. One attribute value could for example be a whole tree of
+ oids leading to other terms in other tables. This
+ type of record is hard to model in traditional relational
+ DBMSs.</p>
+ </section>
+
+ <section>
+ <marker id="start_mnesia"></marker>
+ <title>Starting Mnesia</title>
+ <p>Before we can start Mnesia, we must initialize an empty schema
+ on all the participating nodes.
+ </p>
+ <list type="bulleted">
+ <item>The Erlang system must be started.
+ </item>
+ <item>Nodes with disc database schema must be defined and
+ implemented with the function <c>create_schema(NodeList).</c></item>
+ </list>
+ <p>When running a distributed system, with two or more
+ participating nodes, then the <c>mnesia:start( ).</c> function
+ must be executed on each participating node. Typically this would
+ be part of the boot script in an embedded environment.
+ In a test environment or an interactive environment,
+ <c>mnesia:start()</c> can also be used either from the
+ Erlang shell, or another program.
+ </p>
+
+ <section>
+ <title>Initializing a Schema and Starting Mnesia</title>
+ <p>To use a known example, we illustrate how to run the
+ Company database described in Chapter 2 on two separate nodes,
+ which we call <c>a@gin</c> and <c>b@skeppet</c>. Each of these
+ nodes must have have a Mnesia directory as well as an
+ initialized schema before Mnesia can be started. There are two
+ ways to specify the Mnesia directory to be used:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p>Specify the Mnesia directory by providing an application
+ parameter either when starting the Erlang shell or in the
+ application script. Previously the following example was used
+ to create the directory for our Company database:</p>
+ <pre>
+%<input>erl -mnesia dir '"/ldisc/scratch/Mnesia.Company"'</input>
+ </pre>
+ </item>
+ <item>If no command line flag is entered, then the Mnesia
+ directory will be the current working directory on the node
+ where the Erlang shell is started.</item>
+ </list>
+ <p>To start our Company database and get it running on the two
+ specified nodes, we enter the following commands:
+ </p>
+ <list type="ordered">
+ <item>
+ <p>On the node called gin:</p>
+ <pre>
+ gin %<input>erl -sname a -mnesia dir '"/ldisc/scratch/Mnesia.company"'</input>
+ </pre>
+ </item>
+ <item>
+ <p>On the node called skeppet:</p>
+ <pre>
+skeppet %<input>erl -sname b -mnesia dir '"/ldisc/scratch/Mnesia.company"'</input>
+ </pre>
+ </item>
+ <item>
+ <p>On one of the two nodes:</p>
+ <pre>
+(a@gin1)><input>mnesia:create_schema([a@gin, b@skeppet]).</input>
+ </pre>
+ </item>
+ <item>The function <c>mnesia:start()</c> is called on both
+ nodes.
+ </item>
+ <item>
+ <p>To initialize the database, execute the following
+ code on one of the two nodes.</p>
+ <codeinclude file="company.erl" tag="%12" type="erl"></codeinclude>
+ </item>
+ </list>
+ <p>As illustrated above, the two directories reside on different nodes, because the
+ <c>/ldisc/scratch</c> (the "local" disc) exists on the two different
+ nodes.
+ </p>
+ <p>By executing these commands we have configured two Erlang
+ nodes to run the Company database, and therefore, initialize the
+ database. This is required only once when setting up, the next time the
+ system is started <c>mnesia:start()</c> is called
+ on both nodes, to initialize the system from disc.
+ </p>
+ <p>In a system of Mnesia nodes, every node is aware of the
+ current location of all tables. In this example, data is
+ replicated on both nodes and functions which manipulate the
+ data in our tables can be executed on either of the two nodes.
+ Code which manipulate Mnesia data behaves identically
+ regardless of where the data resides.
+ </p>
+ <p>The function <c>mnesia:stop()</c> stops Mnesia on the node
+ where the function is executed. Both the <c>start/0</c> and
+ the <c>stop/0</c> functions work on the "local" Mnesia system,
+ and there are no functions which start or stop a set of nodes.
+ </p>
+ </section>
+
+ <section>
+ <title>The Start-Up Procedure</title>
+ <p>Mnesia is started by calling the following function:
+ </p>
+ <code type="none">
+ mnesia:start().
+ </code>
+ <p>This function initiates the DBMS locally. </p>
+ <p>The choice of configuration will alter the location and load
+ order of the tables. The alternatives are listed below: <br></br>
+</p>
+ <list type="ordered">
+ <item>Tables that are stored locally only, are initialized
+ from the local Mnesia directory.
+ </item>
+ <item>Replicated tables that reside locally
+ as well as somewhere else are either initiated from disc or
+ by copying the entire table from the other node depending on
+ which of the different replicas is the most recent. Mnesia
+ determines which of the tables is the most recent.
+ </item>
+ <item>Tables that reside on remote nodes are available to other nodes as soon
+ as they are loaded.</item>
+ </list>
+ <p>Table initialization is asynchronous, the function
+ call <c>mnesia:start()</c> returns the atom <c>ok</c> and
+ then starts to initialize the different tables. Depending on
+ the size of the database, this may take some time, and the
+ application programmer must wait for the tables that the
+ application needs before they can be used. This achieved by using
+ the function:</p>
+ <list type="bulleted">
+ <item><c>mnesia:wait_for_tables(TabList, Timeout)</c></item>
+ </list>
+ <p>This function suspends the caller until all tables
+ specified in <c>TabList</c> are properly initiated.
+ </p>
+ <p>A problem can arise if a replicated table on one node is
+ initiated, but Mnesia deduces that another (remote)
+ replica is more recent than the replica existing on
+ the local node, the initialization procedure will not proceed.
+ In this situation, a call to to
+ <c>mnesia:wait_for_tables/2</c> suspends the caller until the
+ remote node has initiated the table from its local disc and
+ the node has copied the table over the network to the local node.
+ </p>
+ <p>This procedure can be time consuming however, the shortcut function
+ shown below will load all the tables from disc at a faster rate:
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:force_load_table(Tab)</c>. This function forces
+ tables to be loaded from disc regardless of the network
+ situation.</item>
+ </list>
+ <p>Thus, we can assume that if an application
+ wishes to use tables <c>a</c> and <c>b</c>, then the
+ application must perform some action similar to the below code before it can utilize the tables.
+ </p>
+ <pre>
+ case mnesia:wait_for_tables([a, b], 20000) of
+ {timeout, RemainingTabs} ->
+ panic(RemainingTabs);
+ ok ->
+ synced
+ end.
+ </pre>
+ <warning>
+ <p>When tables are forcefully loaded from the local disc,
+ all operations that were performed on the replicated table
+ while the local node was down, and the remote replica was
+ alive, are lost. This can cause the database to become
+ inconsistent.</p>
+ </warning>
+ <p>If the start-up procedure fails, the
+ <c>mnesia:start()</c> function returns the cryptic tuple
+ <c>{error,{shutdown, {mnesia_sup,start,[normal,[]]}}}</c>.
+ Use command line arguments -boot start_sasl as argument to
+ the erl script in order to get more information
+ about the start failure.
+ </p>
+ </section>
+ </section>
+
+ <section>
+ <marker id="create_tables"></marker>
+ <title>Creating New Tables</title>
+ <p>Mnesia provides one function to create new tables. This
+ function is: <c>mnesia:create_table(Name, ArgList).</c></p>
+ <p>When executing this function, it returns one of the following
+ responses:
+ </p>
+ <list type="bulleted">
+ <item><c>{atomic, ok}</c> if the function executes
+ successfully
+ </item>
+ <item><c>{aborted, Reason}</c> if the function fails.
+ </item>
+ </list>
+ <p>The function arguments are:
+ </p>
+ <list type="bulleted">
+ <item><c>Name</c> is the atomic name of the table. It is
+ usually the same name as the name of the records that
+ constitute the table. (See <c>record_name</c> for more
+ details.)
+ </item>
+ <item>
+ <p><c>ArgList</c> is a list of <c>{Key,Value}</c> tuples.
+ The following arguments are valid:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>{type, Type}</c> where <c>Type</c> must be either of the
+ atoms <c>set</c>, <c>ordered_set</c> or <c>bag</c>.
+ The default value is
+ <c>set</c>. Note: currently 'ordered_set'
+ is not supported for 'disc_only_copies' tables.
+ A table of type <c>set</c> or <c>ordered_set</c> has either zero or
+ one record per key. Whereas a table of type <c>bag</c> can
+ have an arbitrary number of records per key. The key for
+ each record is always the first attribute of the record.</p>
+ <p>The following example illustrates the difference between
+ type <c>set</c> and <c>bag</c>: </p>
+ <pre>
+ f() -> F = fun() ->
+\011 mnesia:write({foo, 1, 2}), mnesia:write({foo, 1, 3}),
+\011 mnesia:read({foo, 1}) end, mnesia:transaction(F). </pre>
+ <p>This transaction will return the list <c>[{foo,1,3}]</c> if
+ the <c>foo</c> table is of type <c>set</c>. However, list
+ <c>[{foo,1,2}, {foo,1,3}]</c> will return if the table is
+ of type <c>bag</c>. Note the use of <c>bag</c> and
+ <c>set</c> table types. </p>
+ <p>Mnesia tables can never contain
+ duplicates of the same record in the same table. Duplicate
+ records have attributes with the same contents and key.
+ </p>
+ </item>
+ <item>
+ <p><c>{disc_copies, NodeList}</c>, where <c>NodeList</c> is a
+ list of the nodes where this table will reside on disc.</p>
+ <p>Write operations to a table replica of type
+ <c>disc_copies</c> will write data to the disc copy as well
+ as to the RAM copy of the table. </p>
+ <p>It is possible to have a
+ replicated table of type <c>disc_copies</c> on one node, and
+ the same table stored as a different type on another node.
+ The default value is <c>[]</c>. This arrangement is
+ desirable if we want the following operational
+ characteristics are required:</p>
+ <list type="ordered">
+ <item>read operations must be very fast and performed in RAM
+ </item>
+ <item>all write operations must be written to persistent
+ storage.</item>
+ </list>
+ <p>A write operation on a <c>disc_copies</c> table
+ replica will be performed in two steps. First the write
+ operation is appended to a log file, then the actual
+ operation is performed in RAM.
+ </p>
+ </item>
+ <item>
+ <p><c>{ram_copies, NodeList}</c>, where <c>NodeList</c> is a
+ list of the nodes where this table is stored in RAM. The
+ default value for <c>NodeList</c> is <c>[node()]</c>. If the
+ default value is used to create a new table, it will be
+ located on the local node only. </p>
+ <p>Table replicas of type
+ <c>ram_copies</c> can be dumped to disc with the function
+ <c>mnesia:dump_tables(TabList)</c>.
+ </p>
+ </item>
+ <item><c>{disc_only_copies, NodeList}</c>. These table
+ replicas are stored on disc only and are therefore slower to
+ access. However, a disc only replica consumes less memory than
+ a table replica of the other two storage types.
+ </item>
+ <item><c>{index, AttributeNameList}</c>, where
+ <c>AttributeNameList</c> is a list of atoms specifying the
+ names of the attributes Mnesia shall build and maintain. An
+ index table will exist for every element in the list. The
+ first field of a Mnesia record is the key and thus need no
+ extra index.
+ <br></br>
+The first field of a record is the second element of the
+ tuple, which is the representation of the record.
+ </item>
+ <item><c>{snmp, SnmpStruct}</c>. <c>SnmpStruct</c> is
+ described in the SNMP User Guide. Basically, if this attribute
+ is present in <c>ArgList</c> of <c>mnesia:create_table/2</c>,
+ the table is immediately accessible by means of the Simple
+ Network Management Protocol (SNMP).
+ <br></br>
+It is easy to design applications which use SNMP to
+ manipulate and control the system. Mnesia provides a direct
+ mapping between the logical tables that make up an SNMP
+ control application and the physical data which make up a
+ Mnesia table. <c>[]</c>
+ is default.
+ </item>
+ <item><c>{local_content, true}</c> When an application needs a
+ table whose contents should be locally unique on each
+ node,
+ <c>local_content</c> tables may be used. The name of the
+ table is known to all Mnesia nodes, but its contents is
+ unique for each node. Access to this type of table must be
+ done locally. </item>
+ <item>
+ <p><c>{attributes, AtomList}</c> is a list of the attribute
+ names for the records that are supposed to populate the
+ table. The default value is the list <c>[key, val]</c>. The
+ table must at least have one extra attribute besides the
+ key. When accessing single attributes in a record, it is not
+ recommended to hard code the attribute names as atoms. Use
+ the construct <c>record_info(fields,record_name)</c>
+ instead. The expression
+ <c>record_info(fields,record_name)</c> is processed by the
+ Erlang macro pre-processor and returns a list of the
+ record's field names. With the record definition
+ <c>-record(foo, {x,y,z}).</c> the expression
+ <c>record_info(fields,foo)</c> is expanded to the list
+ <c>[x,y,z]</c>. Accordingly, it is possible to provide the
+ attribute names yourself, or to use the <c>record_info/2</c>
+ notation. </p>
+ <p>It is recommended that
+ the <c>record_info/2</c> notation be used as it is easier to
+ maintain the program and it will be more robust with regards
+ to future record changes.
+ </p>
+ </item>
+ <item>
+ <p><c>{record_name, Atom}</c> specifies the common name of
+ all records stored in the table. All records, stored in
+ the table, must have this name as their first element.
+ The <c>record_name</c> defaults to the name of the
+ table. For more information see Chapter 4:<seealso marker="Mnesia_chap4#recordnames_tablenames">Record Names Versus Table Names</seealso>.</p>
+ </item>
+ </list>
+ </item>
+ </list>
+ <p>As an example, assume we have the record definition:</p>
+ <pre>
+ -record(funky, {x, y}).
+ </pre>
+ <p>The below call would create a table which is replicated on two
+ nodes, has an additional index on the <c>y</c> attribute, and is
+ of type
+ <c>bag</c>.</p>
+ <pre>
+ mnesia:create_table(funky, [{disc_copies, [N1, N2]}, {index,
+ [y]}, {type, bag}, {attributes, record_info(fields, funky)}]).
+ </pre>
+ <p>Whereas a call to the below default code values: </p>
+ <pre>
+mnesia:create_table(stuff, []) </pre>
+ <p>would return a table with a RAM copy on the
+ local node, no additional indexes and the attributes defaulted to
+ the list <c>[key,val]</c>.</p>
+ </section>
+</chapter>
+
diff --git a/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc
new file mode 100644
index 0000000000..7d89c1b0dd
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_chap4.xmlsrc
@@ -0,0 +1,1171 @@
+<?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>Transactions and Other Access Contexts</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></rev>
+ <file>Mnesia_chap4.xml</file>
+ </header>
+ <p>This chapter describes the Mnesia transaction system and the
+ transaction properties which make Mnesia a fault tolerant,
+ distributed database management system.
+ </p>
+ <p>Also covered in this chapter are the locking functions,
+ including table locks and sticky locks, as well as alternative
+ functions which bypass the transaction system in favor of improved
+ speed and reduced overheads. These functions are called "dirty
+ operations". We also describe the usage of nested transactions.
+ This chapter contains the following sections:
+ </p>
+ <list type="bulleted">
+ <item>transaction properties, which include atomicity,
+ consistency, isolation, and durability
+ </item>
+ <item>Locking
+ </item>
+ <item>Dirty operations
+ </item>
+ <item>Record names vs table names
+ </item>
+ <item>Activity concept and various access contexts
+ </item>
+ <item>Nested transactions
+ </item>
+ <item>Pattern matching
+ </item>
+ <item>Iteration
+ </item>
+ </list>
+
+ <section>
+ <marker id="trans_prop"></marker>
+ <title>Transaction Properties</title>
+ <p>Transactions are an important tool when designing fault
+ tolerant, distributed systems. A Mnesia transaction is a mechanism
+ by which a series of database operations can be executed as one
+ functional block. The functional block which is run as a
+ transaction is called a Functional Object (Fun), and this code can
+ read, write, or delete Mnesia records. The Fun is evaluated as a
+ transaction which either commits, or aborts. If a transaction
+ succeeds in executing Fun it will replicate the action on all nodes
+ involved, or abort if an error occurs.
+ </p>
+ <p>The following example shows a transaction which raises the
+ salary of certain employee numbers.
+ </p>
+ <codeinclude file="company.erl" tag="%5" type="erl"></codeinclude>
+ <p>The transaction <c>raise(Eno, Raise) - ></c> contains a Fun
+ made up of four lines of code. This Fun is called by the statement
+ <c>mnesia:transaction(F)</c> and returns a value.
+ </p>
+ <p>The Mnesia transaction system facilitates the construction of
+ reliable, distributed systems by providing the following important
+ properties:
+ </p>
+ <list type="bulleted">
+ <item>The transaction handler ensures that a Fun which is placed
+ inside a transaction does not interfere with operations embedded
+ in other transactions when it executes a series of operations on
+ tables.
+ </item>
+ <item>The transaction handler ensures that either all operations
+ in the transaction are performed successfully on all nodes
+ atomically, or the transaction fails without permanent effect on
+ any of the nodes.
+ </item>
+ <item>The Mnesia transactions have four important properties,
+ which we call <em>A</em>tomicity,
+ <em>C</em>onsistency,<em>I</em>solation, and
+ <em>D</em>urability, or ACID for short. These properties are
+ described in the following sub-sections.</item>
+ </list>
+
+ <section>
+ <title>Atomicity</title>
+ <p><em>Atomicity</em> means that database changes which are
+ executed by a transaction take effect on all nodes involved, or
+ on none of the nodes. In other words, the transaction either
+ succeeds entirely, or it fails entirely.
+ </p>
+ <p>Atomicity is particularly important when we want to
+ atomically write more than one record in the same
+ transaction. The <c>raise/2</c> function, shown as an example
+ above, writes one record only. The <c>insert_emp/3</c> function,
+ shown in the program listing in Chapter 2, writes the record
+ <c>employee</c> as well as employee relations such as
+ <c>at_dep</c> and <c>in_proj</c> into the database. If we run
+ this latter code inside a transaction, then the transaction
+ handler ensures that the transaction either succeeds completely,
+ or not at all.
+ </p>
+ <p>Mnesia is a distributed DBMS where data can be replicated on
+ several nodes. In many such applications, it is important that a
+ series of write operations are performed atomically inside a
+ transaction. The atomicity property ensures that a transaction
+ take effect on all nodes, or none at all. </p>
+ </section>
+
+ <section>
+ <title>Consistency</title>
+ <p><em>Consistency</em>. This transaction property ensures that
+ a transaction always leaves the DBMS in a consistent state. For
+ example, Mnesia ensures that inconsistencies will not occur if
+ Erlang, Mnesia or the computer crashes while a write operation
+ is in progress.
+ </p>
+ </section>
+
+ <section>
+ <title>Isolation</title>
+ <p><em>Isolation</em>. This transaction property ensures that
+ transactions which execute on different nodes in a network, and
+ access and manipulate the same data records, will not interfere
+ with each other.
+ </p>
+ <p>The isolation property makes it possible to concurrently execute
+ the <c>raise/2</c> function. A classical problem in concurrency control
+ theory is the so called "lost update problem".
+ </p>
+ <p>The isolation property is extremely useful if the following
+ circumstances occurs where an employee (with an employee number
+ 123) and two processes, (P1 and P2), are concurrently trying to
+ raise the salary for the employee. The initial value of the
+ employees salary is, for example, 5. Process P1 then starts to execute,
+ it reads the employee record and adds 2 to the salary. At this
+ point in time, process P1 is for some reason preempted and
+ process P2 has the opportunity to run. P2 reads the record, adds 3
+ to the salary, and finally writes a new employee record with
+ the salary set to 8. Now, process P1 start to run again and
+ writes its employee record with salary set to 7, thus
+ effectively overwriting and undoing the work performed by
+ process P2. The update performed by P2 is lost.
+ </p>
+ <p>A transaction system makes it possible to concurrently
+ execute two or more processes which manipulate the same
+ record. The programmer does not need to check that the
+ updates are synchronous, this is overseen by the
+ transaction handler. All programs accessing the database through
+ the transaction system may be written as if they had sole access
+ to the data.
+ </p>
+ </section>
+
+ <section>
+ <title>Durability</title>
+ <p><em>Durability</em>. This transaction property ensures that
+ changes made to the DBMS by a transaction are permanent. Once a
+ transaction has been committed, all changes made to the database
+ are durable - i.e. they are written safely to disc and will not
+ be corrupted or disappear.
+ </p>
+ <note>
+ <p>The durability feature described does not entirely apply to
+ situations where Mnesia is configured as a "pure" primary memory
+ database.
+ </p>
+ </note>
+ </section>
+ </section>
+
+ <section>
+ <title>Locking</title>
+ <p>Different transaction managers employ different strategies to
+ satisfy the isolation property. Mnesia uses the standard technique
+ of two-phase locking. This means that locks are set on records
+ before they are read or written. Mnesia uses five different kinds
+ of locks.
+ </p>
+ <list type="bulleted">
+ <item><em>Read locks</em>. A read lock is set on one replica of
+ a record before it can be read.
+ </item>
+ <item><em>Write locks</em>. Whenever a transaction writes to an
+ record, write locks are first set on all replicas of that
+ particular record.
+ </item>
+ <item><em>Read table locks</em>. If a transaction traverses an
+ entire table in search for a record which satisfy some
+ particular property, it is most inefficient to set read locks on
+ the records, one by one. It is also very memory consuming, since
+ the read locks themselves may take up considerable space if the
+ table is very large. For this reason, Mnesia can set a read lock
+ on an entire table.
+ </item>
+ <item><em>Write table locks</em>. If a transaction writes a
+ large number of records to one table, it is possible to set a
+ write lock on the entire table.
+ </item>
+ <item><em>Sticky locks</em>. These are write locks that stay in
+ place at a node after the transaction which initiated the lock
+ has terminated. </item>
+ </list>
+ <p>Mnesia employs a strategy whereby functions such as
+ <c>mnesia:read/1</c> acquire the necessary locks dynamically as
+ the transactions execute. Mnesia automatically sets and releases
+ the locks and the programmer does not have to code these
+ operations.
+ </p>
+ <p>Deadlocks can occur when concurrent processes set and release
+ locks on the same records. Mnesia employs a "wait-die" strategy to
+ resolve these situations. If Mnesia suspects that a deadlock can
+ occur when a transaction tries to set a lock, the transaction is
+ forced to release all its locks and sleep for a while. The
+ Fun in the transaction will be evaluated one more time.
+ </p>
+ <p>For this reason, it is important that the code inside the Fun given to
+ <c>mnesia:transaction/1</c> is pure. Some strange results can
+ occur if, for example, messages are sent by the transaction
+ Fun. The following example illustrates this situation:
+ </p>
+ <codeinclude file="company.erl" tag="%6" type="erl"></codeinclude>
+ <p>This transaction could write the text <c>"Trying to write ... "</c> a thousand times to the terminal. Mnesia does guarantee,
+ however, that each and every transaction will eventually run. As a
+ result, Mnesia is not only deadlock free, but also livelock
+ free.
+ </p>
+ <p>The Mnesia programmer cannot prioritize one particular
+ transaction to execute before other transactions which are waiting
+ to execute. As a result, the Mnesia DBMS transaction system is not
+ suitable for hard real time applications. However, Mnesia contains
+ other features that have real time properties.
+ </p>
+ <p>Mnesia dynamically sets and releases locks as
+ transactions execute, therefore, it is very dangerous to execute code with
+ transaction side-effects. In particular, a <c>receive</c>
+ statement inside a transaction can lead to a situation where the
+ transaction hangs and never returns, which in turn can cause locks
+ not to release. This situation could bring the whole system to a
+ standstill since other transactions which execute in other
+ processes, or on other nodes, are forced to wait for the defective
+ transaction.
+ </p>
+ <p>If a transaction terminates abnormally, Mnesia will
+ automatically release the locks held by the transaction.
+ </p>
+ <p>We have shown examples of a number of functions that can be
+ used inside a transaction. The following list shows the
+ <em>simplest</em> Mnesia functions that work with transactions. It
+ is important to realize that these functions must be embedded in a
+ transaction. If no enclosing transaction (or other enclosing
+ Mnesia activity) exists, they will all fail.
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:transaction(Fun) -> {aborted, Reason} |{atomic, Value}</c>. This function executes one transaction with the
+ functional object <c>Fun</c> as the single parameter.
+ </item>
+ <item><c>mnesia:read({Tab, Key}) -> transaction abort | RecordList</c>. This function reads all records with <c>Key</c>
+ as key from table <c>Tab</c>. This function has the same semantics
+ regardless of the location of <c>Table</c>. If the table is of
+ type <c>bag</c>, the <c>read({Tab, Key})</c> can return an arbitrarily
+ long list. If the table is of type <c>set</c>, the list is
+ either of length one, or <c>[]</c>.
+ </item>
+ <item><c>mnesia:wread({Tab, Key}) -> transaction abort | RecordList</c>. This function behaves the same way as the
+ previously listed <c>read/1</c> function, except that it
+ acquires a write lock instead of a read lock. If we execute a
+ transaction which reads a record, modifies the record, and then
+ writes the record, it is slightly more efficient to set the
+ write lock immediately. In cases where we issue a
+ <c>mnesia:read/1</c>, followed by a <c>mnesia:write/1</c>, the
+ first read lock must be upgraded to a write lock when the write
+ operation is executed.
+ </item>
+ <item><c>mnesia:write(Record) -> transaction abort | ok</c>. This function writes a record into the database. The
+ <c>Record</c> argument is an instance of a record. The function
+ returns <c>ok</c>, or aborts the transaction if an error should
+ occur.
+ </item>
+ <item><c>mnesia:delete({Tab, Key}) -> transaction abort | ok</c>. This
+ function deletes all records with the given key.
+ </item>
+ <item><c>mnesia:delete_object(Record) -> transaction abort | ok</c>. This function deletes records with object id
+ <c>Record</c>. This function is used when we want to delete only
+ some records in a table of type <c>bag</c>. </item>
+ </list>
+
+ <section>
+ <title>Sticky Locks</title>
+ <p>As previously stated, the locking strategy used by Mnesia is
+ to lock one record when we read a record, and lock all replicas
+ of a record when we write a record. However, there are
+ applications which use Mnesia mainly for its fault-tolerant
+ qualities, and these applications may be configured with one
+ node doing all the heavy work, and a standby node which is ready
+ to take over in case the main node fails. Such applications may
+ benefit from using sticky locks instead of the normal locking
+ scheme.
+ </p>
+ <p>A sticky lock is a lock which stays in place at a node after
+ the transaction which first acquired the lock has terminated. To
+ illustrate this, assume that we execute the following
+ transaction:
+ </p>
+ <code type="none">
+ F = fun() ->
+ mnesia:write(#foo{a = kalle})
+ end,
+ mnesia:transaction(F).
+ </code>
+ <p>The <c>foo</c> table is replicated on the two nodes <c>N1</c>
+ and <c>N2</c>.
+ <br></br>
+Normal locking requires:
+ </p>
+ <list type="bulleted">
+ <item>one network rpc (2 messages) to acquire the write lock
+ </item>
+ <item>three network messages to execute the two-phase commit protocol.
+ </item>
+ </list>
+ <p>If we use sticky locks, we must first change the code as follows:
+ </p>
+ <code type="none">
+
+ F = fun() ->
+ mnesia:s_write(#foo{a = kalle})
+ end,
+ mnesia:transaction(F).
+ </code>
+ <p>This code uses the <c>s_write/1</c> function instead of the
+ <c>write/1</c> function. The <c>s_write/1</c> function sets a
+ sticky lock instead of a normal lock. If the table is not
+ replicated, sticky locks have no special effect. If the table is
+ replicated, and we set a sticky lock on node <c>N1</c>, this
+ lock will then stick to node <c>N1</c>. The next time we try to
+ set a sticky lock on the same record at node <c>N1</c>, Mnesia
+ will see that the lock is already set and will not do a network
+ operation in order to acquire the lock.
+ </p>
+ <p>It is much more efficient to set a local lock than it is to set
+ a networked lock, and for this reason sticky locks can benefit
+ application that use a replicated table and perform most of the
+ work on only one of the nodes.
+ </p>
+ <p>If a record is stuck at node <c>N1</c> and we try to set a
+ sticky lock for the record on node <c>N2</c>, the record must be
+ unstuck. This operation is expensive and will reduce performance. The unsticking is
+ done automatically if we issue <c>s_write/1</c> requests at
+ <c>N2</c>.
+ </p>
+ </section>
+
+ <section>
+ <title>Table Locks</title>
+ <p>Mnesia supports read and write locks on whole tables as a
+ complement to the normal locks on single records. As previously
+ stated, Mnesia sets and releases locks automatically, and the
+ programmer does not have to code these operations. However,
+ transactions which read and write a large number of records in a
+ specific table will execute more efficiently if we start the
+ transaction by setting a table lock on this table. This will
+ block other concurrent transactions from the table. The
+ following two function are used to set explicit table locks for
+ read and write operations:
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:read_lock_table(Tab)</c> Sets a read lock on
+ the table <c>Tab</c></item>
+ <item><c>mnesia:write_lock_table(Tab)</c> Sets a write lock on
+ the table <c>Tab</c></item>
+ </list>
+ <p>Alternate syntax for acquisition of table locks is as follows:
+ </p>
+ <code type="none">
+ mnesia:lock({table, Tab}, read)
+ mnesia:lock({table, Tab}, write)
+ </code>
+ <p>The matching operations in Mnesia may either lock the entire
+ table or just a single record (when the key is bound in the
+ pattern).
+ </p>
+ </section>
+
+ <section>
+ <title>Global Locks</title>
+ <p>Write locks are normally acquired on all nodes where a
+ replica of the table resides (and is active). Read locks are
+ acquired on one node (the local one if a local
+ replica exists).
+ </p>
+ <p>The function <c>mnesia:lock/2</c> is intended to support
+ table locks (as mentioned previously)
+ but also for situations when locks need to be
+ acquired regardless of how tables have been replicated:
+ </p>
+ <code type="none">
+ mnesia:lock({global, GlobalKey, Nodes}, LockKind)
+
+ LockKind ::= read | write | ...
+ </code>
+ <p>The lock is acquired on the LockItem on all Nodes in the
+ nodes list.</p>
+ </section>
+ </section>
+
+ <section>
+ <title>Dirty Operations</title>
+ <p>In many applications, the overhead of processing a transaction
+ may result in a loss of performance. Dirty operation are short
+ cuts which bypass much of the processing and increase the speed
+ of the transaction.
+ </p>
+ <p>Dirty operation are useful in many situations, for example in a datagram routing
+ application where Mnesia stores the routing table, and it is time
+ consuming to start a whole transaction every time a packet is
+ received. For this reason, Mnesia has functions which manipulate
+ tables without using transactions. This alternative
+ to processing is known as a dirty operation. However, it is important to
+ realize the trade-off in avoiding the overhead of transaction
+ processing:
+ </p>
+ <list type="bulleted">
+ <item>The atomicity and the isolation properties of Mnesia are lost.
+ </item>
+ <item>The isolation property is compromised, because other
+ Erlang processes, which use transaction to manipulate the data,
+ do not get the benefit of isolation if we simultaneously use
+ dirty operations to read and write records from the same table.
+ </item>
+ </list>
+ <p>The major advantage of dirty operations is that they execute
+ much faster than equivalent operations that are processed as
+ functional objects within a transaction.
+ </p>
+ <p>Dirty operations
+ are written to disc if they are performed on a table of type
+ <c>disc_copies</c>, or type <c>disc_only_copies</c>. Mnesia also
+ ensures that all replicas of a table are updated if a
+ dirty write operation is performed on a table.
+ </p>
+ <p>A dirty operation will ensure a certain level of consistency.
+ For example, it is not possible for dirty operations to return
+ garbled records. Hence, each individual read or write operation
+ is performed in an atomic manner.
+ </p>
+ <p>All dirty functions execute a call to <c>exit({aborted, Reason})</c> on failure. Even if the following functions are
+ executed inside a transaction no locks will be acquired. The
+ following functions are available:
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:dirty_read({Tab, Key})</c>. This function reads
+ record(s) from Mnesia.
+ </item>
+ <item><c>mnesia:dirty_write(Record)</c>. This function writes
+ the record <c>Record</c></item>
+ <item><c>mnesia:dirty_delete({Tab, Key})</c>. This function deletes
+ record(s) with the key <c>Key</c>.
+ </item>
+ <item><c>mnesia:dirty_delete_object(Record)</c> This function is
+ the dirty operation alternative to the function
+ <c>delete_object/1</c></item>
+ <item>
+ <p><c>mnesia:dirty_first(Tab)</c>. This function returns the
+ "first" key in the table <c>Tab</c>. </p>
+ <p>Records in <c>set</c> or <c>bag</c> tables are not sorted.
+ However, there is
+ a record order which is not known to the user.
+ This means that it is possible to traverse a table by means of
+ this function in conjunction with the <c>dirty_next/2</c>
+ function.
+ </p>
+ <p>If there are no records at all in the table, this function
+ will return the atom <c>'$end_of_table'</c>. It is not
+ recommended to use this atom as the key for any user
+ records.
+ </p>
+ </item>
+ <item><c>mnesia:dirty_next(Tab, Key)</c>. This function returns
+ the "next" key in the table <c>Tab</c>. This function makes it
+ possible to traverse a table and perform some operation on all
+ records in the table. When the end of the table is reached the
+ special key <c>'$end_of_table'</c> is returned. Otherwise, the
+ function returns a key which can be used to read the actual
+ record.
+ <br></br>
+The behavior is undefined if any process perform a write
+ operation on the table while we traverse the table with the
+ <c>dirty_next/2</c> function. This is because <c>write</c>
+ operations on a Mnesia table may lead to internal reorganizations
+ of the table itself. This is an implementation detail, but remember
+ the dirty functions are low level functions.
+ </item>
+ <item><c>mnesia:dirty_last(Tab)</c> This function works exactly as
+ <c>mnesia:dirty_first/1</c> but returns the last object in
+ Erlang term order for the <c>ordered_set</c> table type. For
+ all other table types, <c>mnesia:dirty_first/1</c> and
+ <c>mnesia:dirty_last/1</c> are synonyms.
+ </item>
+ <item><c>mnesia:dirty_prev(Tab, Key)</c> This function works exactly as
+ <c>mnesia:dirty_next/2</c> but returns the previous object in
+ Erlang term order for the ordered_set table type. For
+ all other table types, <c>mnesia:dirty_next/2</c> and
+ <c>mnesia:dirty_prev/2</c> are synonyms.
+ </item>
+ <item>
+ <p><c>mnesia:dirty_slot(Tab, Slot)</c></p>
+ <p>Returns the list of records that are associated with Slot
+ in a table. It can be used to traverse a table in a manner
+ similar to the <c>dirty_next/2</c> function. A table has a
+ number of slots that range from zero to some unknown upper
+ bound. The function <c>dirty_slot/2</c> returns the special
+ atom <c>'$end_of_table'</c> when the end of the table is
+ reached.
+ <br></br>
+The behavior of this function is undefined if the
+ table is written on while being
+ traversed. <c>mnesia:read_lock_table(Tab)</c> may be used to
+ ensure that no transaction protected writes are performed
+ during the iteration.
+ </p>
+ </item>
+ <item>
+ <p><c>mnesia:dirty_update_counter({Tab,Key}, Val)</c>. </p>
+ <p>Counters are positive integers with a value greater than or
+ equal to zero. Updating a counter will add the <c>Val</c> and
+ the counter where <c>Val</c> is a positive or negative integer.
+ <br></br>
+ There exists no special counter records in
+ Mnesia. However, records on the form of <c>{TabName, Key, Integer}</c> can be used as counters, and can be
+ persistent.
+ </p>
+ <p>It is not possible to have transaction protected updates of
+ counter records.
+ </p>
+ <p>There are two significant differences when using this
+ function instead of reading the record, performing the
+ arithmetic, and writing the record:
+ </p>
+ <list type="ordered">
+ <item>it is much more efficient
+ </item>
+ <item>the <c>dirty_update_counter/2</c> function is
+ performed as an atomic operation although it is not protected by
+ a transaction. Accordingly, no table update is lost if two
+ processes simultaneously execute the
+ <c>dirty_update_counter/2</c> function.
+ </item>
+ </list>
+ </item>
+ <item><c>mnesia:dirty_match_object(Pat)</c>. This function is
+ the dirty equivalent of <c>mnesia:match_object/1</c>.
+ </item>
+ <item><c>mnesia:dirty_select(Tab, Pat)</c>. This function is
+ the dirty equivalent of <c>mnesia:select/2</c>.
+ </item>
+ <item><c>mnesia:dirty_index_match_object(Pat, Pos)</c>. This
+ function is the dirty equivalent of
+ <c>mnesia:index_match_object/2</c>.
+ </item>
+ <item><c>mnesia:dirty_index_read(Tab, SecondaryKey, Pos)</c>. This
+ function is the dirty equivalent of <c>mnesia:index_read/3</c>.
+ </item>
+ <item><c>mnesia:dirty_all_keys(Tab)</c>. This function is the
+ dirty equivalent of <c>mnesia:all_keys/1</c>.
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <marker id="recordnames_tablenames"></marker>
+ <title>Record Names versus Table Names</title>
+ <p>In Mnesia, all records in a table must have the same name. All
+ the records must be instances of the same
+ record type. The record name does however not necessarily be
+ the same as the table name. Even though that it is the case in
+ the most of the examples in this document. If a table is created
+ without the <c>record_name</c> property the code below will
+ ensure all records in the tables have the same name as the table:
+ </p>
+ <code type="none">
+ mnesia:create_table(subscriber, [])
+ </code>
+ <p>However, if the table is is created with an explicit record name
+ as argument, as shown below, it is possible to store subscriber
+ records in both of the tables regardless of the table names:
+ </p>
+ <code type="none">
+ TabDef = [{record_name, subscriber}],
+ mnesia:create_table(my_subscriber, TabDef),
+ mnesia:create_table(your_subscriber, TabDef).
+ </code>
+ <p>In order to access such
+ tables it is not possible to use the simplified access functions
+ as described earlier in the document. For example,
+ writing a subscriber record into a table requires a
+ <c>mnesia:write/3</c>function instead of the simplified functions
+ <c>mnesia:write/1</c> and <c>mnesia:s_write/1</c>:
+ </p>
+ <code type="none">
+ mnesia:write(subscriber, #subscriber{}, write)
+ mnesia:write(my_subscriber, #subscriber{}, sticky_write)
+ mnesia:write(your_subscriber, #subscriber{}, write)
+ </code>
+ <p>The following simplified piece of code illustrates the
+ relationship between the simplified access functions used in
+ most examples and their more flexible counterparts:
+ </p>
+ <code type="none">
+ mnesia:dirty_write(Record) ->
+ Tab = element(1, Record),
+ mnesia:dirty_write(Tab, Record).
+
+ mnesia:dirty_delete({Tab, Key}) ->
+ mnesia:dirty_delete(Tab, Key).
+
+ mnesia:dirty_delete_object(Record) ->
+ Tab = element(1, Record),
+ mnesia:dirty_delete_object(Tab, Record)
+
+ mnesia:dirty_update_counter({Tab, Key}, Incr) ->
+ mnesia:dirty_update_counter(Tab, Key, Incr).
+
+ mnesia:dirty_read({Tab, Key}) ->
+ Tab = element(1, Record),
+ mnesia:dirty_read(Tab, Key).
+
+ mnesia:dirty_match_object(Pattern) ->
+ Tab = element(1, Pattern),
+ mnesia:dirty_match_object(Tab, Pattern).
+
+ mnesia:dirty_index_match_object(Pattern, Attr)
+ Tab = element(1, Pattern),
+ mnesia:dirty_index_match_object(Tab, Pattern, Attr).
+
+ mnesia:write(Record) ->
+ Tab = element(1, Record),
+ mnesia:write(Tab, Record, write).
+
+ mnesia:s_write(Record) ->
+ Tab = element(1, Record),
+ mnesia:write(Tab, Record, sticky_write).
+
+ mnesia:delete({Tab, Key}) ->
+ mnesia:delete(Tab, Key, write).
+
+ mnesia:s_delete({Tab, Key}) ->
+ mnesia:delete(Tab, Key, sticky_write).
+
+ mnesia:delete_object(Record) ->
+ Tab = element(1, Record),
+ mnesia:delete_object(Tab, Record, write).
+
+ mnesia:s_delete_object(Record) ->
+ Tab = element(1, Record),
+ mnesia:delete_object(Tab, Record. sticky_write).
+
+ mnesia:read({Tab, Key}) ->
+ mnesia:read(Tab, Key, read).
+
+ mnesia:wread({Tab, Key}) ->
+ mnesia:read(Tab, Key, write).
+
+ mnesia:match_object(Pattern) ->
+ Tab = element(1, Pattern),
+ mnesia:match_object(Tab, Pattern, read).
+
+ mnesia:index_match_object(Pattern, Attr) ->
+ Tab = element(1, Pattern),
+ mnesia:index_match_object(Tab, Pattern, Attr, read).
+ </code>
+ </section>
+
+ <section>
+ <title>Activity Concept and Various Access Contexts</title>
+ <p>As previously described, a functional object (Fun) performing
+ table access operations as listed below may be
+ passed on as arguments to the function
+ <c>mnesia:transaction/1,2,3</c>:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p>mnesia:write/3 (write/1, s_write/1)</p>
+ </item>
+ <item>
+ <p>mnesia:delete/3 (delete/1, s_delete/1)</p>
+ </item>
+ <item>
+ <p>mnesia:delete_object/3 (delete_object/1, s_delete_object/1)</p>
+ </item>
+ <item>
+ <p>mnesia:read/3 (read/1, wread/1)</p>
+ </item>
+ <item>
+ <p>mnesia:match_object/2 (match_object/1)</p>
+ </item>
+ <item>
+ <p>mnesia:select/3 (select/2)</p>
+ </item>
+ <item>
+ <p>mnesia:foldl/3 (foldl/4, foldr/3, foldr/4)</p>
+ </item>
+ <item>
+ <p>mnesia:all_keys/1</p>
+ </item>
+ <item>
+ <p>mnesia:index_match_object/4 (index_match_object/2)</p>
+ </item>
+ <item>
+ <p>mnesia:index_read/3</p>
+ </item>
+ <item>
+ <p>mnesia:lock/2 (read_lock_table/1, write_lock_table/1)</p>
+ </item>
+ <item>
+ <p>mnesia:table_info/2</p>
+ </item>
+ </list>
+ <p>These functions will be performed in a
+ transaction context involving mechanisms like locking, logging,
+ replication, checkpoints, subscriptions, commit protocols
+ etc.However, the same function may also be
+ evaluated in other activity contexts.
+ <br></br>
+The following activity access contexts are currently supported:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p>transaction </p>
+ </item>
+ <item>
+ <p>sync_transaction</p>
+ </item>
+ <item>
+ <p>async_dirty</p>
+ </item>
+ <item>
+ <p>sync_dirty</p>
+ </item>
+ <item>
+ <p>ets</p>
+ </item>
+ </list>
+ <p>By passing the same "fun" as argument to the function
+ <c>mnesia:sync_transaction(Fun [, Args])</c> it will be performed
+ in synced transaction context. Synced transactions waits until all
+ active replicas has committed the transaction (to disc) before
+ returning from the mnesia:sync_transaction call. Using
+ sync_transaction is useful for applications that are executing on
+ several nodes and want to be sure that the update is performed on
+ the remote nodes before a remote process is spawned or a message
+ is sent to a remote process, and also when combining transaction
+ writes with dirty_reads. This is also useful in situations where
+ an application performs frequent or voluminous updates which may
+ overload Mnesia on other nodes.
+ </p>
+ <p>By passing the same "fun" as argument to the function
+ <c>mnesia:async_dirty(Fun [, Args])</c> it will be performed in
+ dirty context. The function calls will be mapped to the
+ corresponding dirty functions. This will still involve logging,
+ replication and subscriptions but there will be no locking,
+ local transaction storage or commit protocols involved.
+ Checkpoint retainers will be updated but will be updated
+ "dirty". Thus, they will be updated asynchronously. The
+ functions will wait for the operation to be performed on one
+ node but not the others. If the table resides locally no waiting
+ will occur.
+ </p>
+ <p>By passing the same "fun" as an argument to the function
+ <c>mnesia:sync_dirty(Fun [, Args])</c> it will be performed in
+ almost the same context as <c>mnesia:async_dirty/1,2</c>. The
+ difference is that the operations are performed
+ synchronously. The caller will wait for the updates to be
+ performed on all active replicas. Using sync_dirty is useful for
+ applications that are executing on several nodes and want to be
+ sure that the update is performed on the remote nodes before a remote
+ process is spawned or a message is sent to a remote process. This
+ is also useful in situations where an application performs frequent or
+ voluminous updates which may overload Mnesia on other
+ nodes.
+ </p>
+ <p>You can check if your code is executed within a transaction with
+ <c>mnesia:is_transaction/0</c>, it returns <c>true</c> when called
+ inside a transaction context and false otherwise.</p>
+
+ <p>Mnesia tables with storage type RAM_copies and disc_copies
+ are implemented internally as "ets-tables" and
+ it is possible for applications to access the these tables
+ directly. This is only recommended if all options have been weighed
+ and the possible outcomes are understood. By passing the earlier
+ mentioned "fun" to the function
+ <c>mnesia:ets(Fun [, Args])</c> it will be performed but in a very raw
+ context. The operations will be performed directly on the
+ local ets tables assuming that the local storage type are
+ RAM_copies and that the table is not replicated on other
+ nodes. Subscriptions will not be triggered nor
+ checkpoints updated, but this operation is blindingly fast. Disc resident
+ tables should not be updated with the ets-function since the
+ disc will not be updated.
+ </p>
+ <p>The Fun may also be passed as an argument to the function
+ <c>mnesia:activity/2,3,4</c> which enables usage of customized
+ activity access callback modules. It can either be obtained
+ directly by stating the module name as argument or implicitly
+ by usage of the <c>access_module</c> configuration parameter. A
+ customized callback module may be used for several purposes,
+ such as providing triggers, integrity constraints, run time
+ statistics, or virtual tables.
+ <br></br>
+ The callback module does
+ not have to access real Mnesia tables, it is free to do whatever
+ it likes as long as the callback interface is fulfilled.
+ <br></br>
+ In Appendix C "The Activity Access Call Back Interface" the source
+ code for one alternate implementation is provided
+ (mnesia_frag.erl). The context sensitive function
+ <c>mnesia:table_info/2</c> may be used to provide virtual
+ information about a table. One usage of this is to perform
+ <c>QLC</c> queries within an activity context with a
+ customized callback module. By providing table information about
+ table indices and other <c>QLC</c> requirements,
+ <c>QLC</c> may be used as a generic query language to
+ access virtual tables.
+ </p>
+ <p>QLC queries may be performed in all these activity
+ contexts (transaction, sync_transaction, async_dirty, sync_dirty
+ and ets). The ets activity will only work if the table has no
+ indices.
+ </p>
+ <note>
+ <p>The mnesia:dirty_* function always executes with
+ async_dirty semantics regardless of which activity access contexts
+ are invoked. They may even invoke contexts without any
+ enclosing activity access context.</p>
+ </note>
+ </section>
+
+ <section>
+ <title>Nested transactions</title>
+ <p>Transactions may be nested in an arbitrary fashion. A child transaction
+ must run in the same process as its parent. When a child transaction
+ aborts, the caller of the child transaction will get the
+ return value <c>{aborted, Reason}</c> and any work performed
+ by the child will be erased. If a child transaction commits, the
+ records written by the child will be propagated to the parent.
+ </p>
+ <p>No locks are released when child transactions terminate. Locks
+ created by a sequence of nested transactions are kept until
+ the topmost transaction terminates. Furthermore, any updates
+ performed by a nested transaction are only propagated
+ in such a manner so that the parent of the nested transaction
+ sees the updates. No final commitment will be done until
+ the top level transaction is terminated.
+ So, although a nested transaction returns <c>{atomic, Val}</c>,
+ if the enclosing parent transaction is aborted, the entire
+ nested operation is aborted.
+ </p>
+ <p>The ability to have nested transaction with identical semantics
+ as top level transaction makes it easier to write
+ library functions that manipulate mnesia tables.
+ </p>
+ <p>Say for example that we have a function that adds a
+ new subscriber to a telephony system:</p>
+ <pre>
+ add_subscriber(S) ->
+ mnesia:transaction(fun() ->
+ case mnesia:read( ..........
+ </pre>
+ <p>This function needs to be called as a transaction.
+ Now assume that we wish to write a function that
+ both calls the <c>add_subscriber/1</c> function and
+ is in itself protected by the context of a transaction.
+ By simply calling the <c>add_subscriber/1</c> from within
+ another transaction, a nested transaction is created.
+ </p>
+ <p>It is also possible to mix different activity access contexts while nesting,
+ but the dirty ones (async_dirty,sync_dirty and ets) will inherit the transaction
+ semantics if they are called inside a transaction and thus it will grab locks and
+ use two or three phase commit.
+ </p>
+ <pre>
+ add_subscriber(S) ->
+ mnesia:transaction(fun() ->
+ %% Transaction context
+ mnesia:read({some_tab, some_data}),
+ mnesia:sync_dirty(fun() ->
+ %% Still in a transaction context.
+ case mnesia:read( ..) ..end), end).
+ add_subscriber2(S) ->
+ mnesia:sync_dirty(fun() ->
+ %% In dirty context
+ mnesia:read({some_tab, some_data}),
+ mnesia:transaction(fun() ->
+ %% In a transaction context.
+ case mnesia:read( ..) ..end), end).
+ </pre>
+ </section>
+
+ <section>
+ <title>Pattern Matching</title>
+ <marker id="matching"></marker>
+ <p>When it is not possible to use <c>mnesia:read/3</c> Mnesia
+ provides the programmer with several functions for matching
+ records against a pattern. The most useful functions of these are:
+ </p>
+ <code type="none">
+ mnesia:select(Tab, MatchSpecification, LockKind) ->
+ transaction abort | [ObjectList]
+ mnesia:select(Tab, MatchSpecification, NObjects, Lock) ->
+ transaction abort | {[Object],Continuation} | '$end_of_table'
+ mnesia:select(Cont) ->
+ transaction abort | {[Object],Continuation} | '$end_of_table'
+ mnesia:match_object(Tab, Pattern, LockKind) ->
+ transaction abort | RecordList
+ </code>
+ <p>These functions matches a <c>Pattern</c> against all records in
+ table <c>Tab</c>. In a <c>mnesia:select</c> call <c>Pattern</c> is
+ a part of <c>MatchSpecification</c> described below. It is not
+ necessarily performed as an exhaustive search of the entire
+ table. By utilizing indices and bound values in the key of the
+ pattern, the actual work done by the function may be condensed
+ into a few hash lookups. Using <c>ordered_set</c> tables may reduce the
+ search space if the keys are partially bound.
+ </p>
+ <p>The pattern provided to the functions must be a valid record,
+ and the first element of the provided tuple must be the
+ <c>record_name</c> of the table. The special element <c>'_'</c>
+ matches any data structure in Erlang (also known as an Erlang
+ term). The special elements <c><![CDATA['$<number>']]></c> behaves as Erlang
+ variables i.e. matches anything and binds the first occurrence and
+ matches the coming occurrences of that variable against the bound value.
+ </p>
+ <p>Use the function <c>mnesia:table_info(Tab, wild_pattern)</c>
+ to obtain a basic pattern which matches all records in a table
+ or use the default value in record creation.
+ Do not make the pattern hard coded since it will make your code more
+ vulnerable to future changes of the record definition.
+ </p>
+ <code type="none">
+ Wildpattern = mnesia:table_info(employee, wild_pattern),
+ %% Or use
+ Wildpattern = #employee{_ = '_'},
+ </code>
+ <p>For the employee table the wild pattern will look like:</p>
+ <code type="none">
+ {employee, '_', '_', '_', '_', '_',' _'}.
+ </code>
+ <p>In order to constrain the match you must replace some
+ of the <c>'_'</c> elements. The code for matching out
+ all female employees, looks like:
+ </p>
+ <code type="none">
+ Pat = #employee{sex = female, _ = '_'},
+ F = fun() -> mnesia:match_object(Pat) end,
+ Females = mnesia:transaction(F).
+ </code>
+ <p>It is also possible to use the match function if we want to
+ check the equality of different attributes. Assume that we want
+ to find all employees which happens to have a employee number
+ which is equal to their room number:
+ </p>
+ <code type="none">
+ Pat = #employee{emp_no = '$1', room_no = '$1', _ = '_'},
+ F = fun() -> mnesia:match_object(Pat) end,
+ Odd = mnesia:transaction(F).
+ </code>
+ <p>The function <c>mnesia:match_object/3</c> lacks some important
+ features that <c>mnesia:select/3</c> have. For example
+ <c>mnesia:match_object/3</c> can only return the matching records,
+ and it can not express constraints other then equality.
+ If we want to find the names of the male employees on the second floor
+ we could write:
+ </p>
+ <codeinclude file="company.erl" tag="%21" type="erl"></codeinclude>
+ <p>Select can be used to add additional constraints and create
+ output which can not be done with <c>mnesia:match_object/3</c>. </p>
+ <p>The second argument to select is a <c>MatchSpecification</c>.
+ A <c>MatchSpecification</c> is list of <c>MatchFunctions</c>, where
+ each <c>MatchFunction</c> consists of a tuple containing
+ <c>{MatchHead, MatchCondition, MatchBody}</c>. <c>MatchHead</c>
+ is the same pattern used in <c>mnesia:match_object/3</c>
+ described above. <c>MatchCondition</c> is a list of additional
+ constraints applied to each record, and <c>MatchBody</c> is used
+ to construct the return values.
+ </p>
+ <p>A detailed explanation of match specifications can be found in
+ the <em>Erts users guide: Match specifications in Erlang </em>,
+ and the ets/dets documentations may provide some additional
+ information.
+ </p>
+ <p>The functions <c>select/4</c> and <c>select/1</c> are used to
+ get a limited number of results, where the <c>Continuation</c>
+ are used to get the next chunk of results. Mnesia uses the
+ <c>NObjects</c> as an recommendation only, thus more or less
+ results then specified with <c>NObjects</c> may be returned in
+ the result list, even the empty list may be returned despite there
+ are more results to collect.
+ </p>
+ <warning>
+ <p>There is a severe performance penalty in using
+ <c>mnesia:select/[1|2|3|4]</c> after any modifying operations
+ are done on that table in the same transaction, i.e. avoid using
+ <c>mnesia:write/1</c> or <c>mnesia:delete/1</c> before a
+ <c>mnesia:select</c> in the same transaction.</p>
+ </warning>
+ <p>If the key attribute is bound in a pattern, the match operation
+ is very efficient. However, if the key attribute in a pattern is
+ given as <c>'_'</c>, or <c>'$1'</c>, the whole <c>employee</c>
+ table must be searched for records that match. Hence if the table is
+ large, this can become a time consuming operation, but it can be
+ remedied with indices (refer to Chapter 5: <seealso marker="Mnesia_chap5#indexing">Indexing</seealso>) if
+ <c>mnesia:match_object</c> is used.
+ </p>
+ <p>QLC queries can also be used to search Mnesia tables. By
+ using <c>mnesia:table/[1|2]</c> as the generator inside a QLC
+ query you let the query operate on a mnesia table. Mnesia
+ specific options to <c>mnesia:table/2</c> are {lock, Lock},
+ {n_objects,Integer} and {traverse, SelMethod}. The <c>lock</c>
+ option specifies whether mnesia should acquire a read or write
+ lock on the table, and <c>n_objects</c> specifies how many
+ results should be returned in each chunk to QLC. The last option is
+ <c>traverse</c> and it specifies which function mnesia should
+ use to traverse the table. Default <c>select</c> is used, but by using
+ <c>{traverse, {select, MatchSpecification}}</c> as an option to
+ <c>mnesia:table/2</c> the user can specify it's own view of the
+ table.
+ </p>
+ <p>If no options are specified a read lock will acquired and 100
+ results will be returned in each chunk, and select will be used
+ to traverse the table, i.e.:
+ </p>
+ <code type="none">
+ mnesia:table(Tab) ->
+ mnesia:table(Tab, [{n_objects,100},{lock, read}, {traverse, select}]).
+ </code>
+ <p>The function <c>mnesia:all_keys(Tab)</c> returns all keys in a
+ table.</p>
+ </section>
+
+ <section>
+ <title>Iteration</title>
+ <marker id="iteration"></marker>
+ <p>Mnesia provides a couple of functions which iterates over all
+ the records in a table.
+ </p>
+ <code type="none">
+ mnesia:foldl(Fun, Acc0, Tab) -> NewAcc | transaction abort
+ mnesia:foldr(Fun, Acc0, Tab) -> NewAcc | transaction abort
+ mnesia:foldl(Fun, Acc0, Tab, LockType) -> NewAcc | transaction abort
+ mnesia:foldr(Fun, Acc0, Tab, LockType) -> NewAcc | transaction abort
+ </code>
+ <p>These functions iterate over the mnesia table <c>Tab</c> and
+ apply the function <c>Fun</c> to each record. The <c>Fun</c>
+ takes two arguments, the first argument is a record from the
+ table and the second argument is the accumulator. The
+ <c>Fun</c> return a new accumulator. </p>
+ <p>The first time the <c>Fun</c> is applied <c>Acc0</c> will
+ be the second argument. The next time the <c>Fun</c> is called
+ the return value from the previous call, will be used as the
+ second argument. The term the last call to the Fun returns
+ will be the return value of the <c>fold[lr]</c> function.
+ </p>
+ <p>The difference between <c>foldl</c> and <c>foldr</c> is the
+ order the table is accessed for <c>ordered_set</c> tables,
+ for every other table type the functions are equivalent.
+ </p>
+ <p><c>LockType</c> specifies what type of lock that shall be
+ acquired for the iteration, default is <c>read</c>. If
+ records are written or deleted during the iteration a write
+ lock should be acquired. </p>
+ <p>These functions might be used to find records in a table
+ when it is impossible to write constraints for
+ <c>mnesia:match_object/3</c>, or when you want to perform
+ some action on certain records.
+ </p>
+ <p>For example finding all the employees who has a salary
+ below 10 could look like:</p>
+ <code type="none"><![CDATA[
+ find_low_salaries() ->
+ Constraint =
+ fun(Emp, Acc) when Emp#employee.salary < 10 ->
+ [Emp | Acc];
+ (_, Acc) ->
+ Acc
+ end,
+ Find = fun() -> mnesia:foldl(Constraint, [], employee) end,
+ mnesia:transaction(Find).
+ ]]></code>
+ <p>Raising the salary to 10 for everyone with a salary below 10
+ and return the sum of all raises:</p>
+ <code type="none"><![CDATA[
+ increase_low_salaries() ->
+ Increase =
+ fun(Emp, Acc) when Emp#employee.salary < 10 ->
+ OldS = Emp#employee.salary,
+ ok = mnesia:write(Emp#employee{salary = 10}),
+ Acc + 10 - OldS;
+ (_, Acc) ->
+ Acc
+ end,
+ IncLow = fun() -> mnesia:foldl(Increase, 0, employee, write) end,
+ mnesia:transaction(IncLow).
+ ]]></code>
+ <p>A lot of nice things can be done with the iterator functions
+ but some caution should be taken about performance and memory
+ utilization for large tables. </p>
+ <p>Call these iteration functions on nodes that contain a replica of the
+ table. Each call to the function <c>Fun</c> access the table and if the table
+ resides on another node it will generate a lot of unnecessary
+ network traffic. </p>
+ <p>Mnesia also provides some functions that make it possible for
+ the user to iterate over the table. The order of the
+ iteration is unspecified if the table is not of the <c>ordered_set</c>
+ type. </p>
+ <code type="none">
+ mnesia:first(Tab) -> Key | transaction abort
+ mnesia:last(Tab) -> Key | transaction abort
+ mnesia:next(Tab,Key) -> Key | transaction abort
+ mnesia:prev(Tab,Key) -> Key | transaction abort
+ mnesia:snmp_get_next_index(Tab,Index) -> {ok, NextIndex} | endOfTable
+ </code>
+ <p>The order of first/last and next/prev are only valid for
+ <c>ordered_set</c> tables, for all other tables, they are synonyms.
+ When the end of the table is reached the special key
+ <c>'$end_of_table'</c> is returned.</p>
+ <p>If records are written and deleted during the traversal, use
+ <c>mnesia:fold[lr]/4</c> with a <c>write</c> lock. Or
+ <c>mnesia:write_lock_table/1</c> when using first and next.</p>
+ <p>Writing or deleting in transaction context creates a local copy
+ of each modified record, so modifying each record in a large
+ table uses a lot of memory. Mnesia will compensate for every
+ written or deleted record during the iteration in a transaction
+ context, which may reduce the performance. If possible avoid writing
+ or deleting records in the same transaction before iterating over the
+ table.</p>
+ <p>In dirty context, i.e. <c>sync_dirty</c> or <c>async_dirty</c>,
+ the modified records are not stored in a local copy; instead,
+ each record is updated separately. This generates a lot of
+ network traffic if the table has a replica on another node and
+ has all the other drawbacks that dirty operations
+ have. Especially for the <c>mnesia:first/1</c> and
+ <c>mnesia:next/2</c> commands, the same drawbacks as described
+ above for <c>dirty_first</c> and <c>dirty_next</c> applies, i.e.
+ no writes to the table should be done during iteration.</p>
+ <p></p>
+ </section>
+</chapter>
+
diff --git a/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc
new file mode 100644
index 0000000000..3ec0aa37f5
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_chap5.xmlsrc
@@ -0,0 +1,1398 @@
+<?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>Miscellaneous Mnesia Features</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></rev>
+ <file>Mnesia_chap5.xml</file>
+ </header>
+ <p>The earlier chapters of this User Guide described how to get
+ started with Mnesia, and how to build a Mnesia database. In this
+ chapter, we will describe the more advanced features available
+ when building a distributed, fault tolerant Mnesia database. This
+ chapter contains the following sections:
+ </p>
+ <list type="bulleted">
+ <item>Indexing
+ </item>
+ <item>Distribution and Fault Tolerance
+ </item>
+ <item>Table fragmentation.
+ </item>
+ <item>Local content tables.
+ </item>
+ <item>Disc-less nodes.
+ </item>
+ <item>More about schema management
+ </item>
+ <item>Debugging a Mnesia application
+ </item>
+ <item>Concurrent Processes in Mnesia
+ </item>
+ <item>Prototyping
+ </item>
+ <item>Object Based Programming with Mnesia.
+ </item>
+ </list>
+
+ <section>
+ <marker id="indexing"></marker>
+ <title>Indexing</title>
+ <p>Data retrieval and matching can be performed very efficiently
+ if we know the key for the record. Conversely, if the key is not
+ known, all records in a table must be searched. The larger the
+ table the more time consuming it will become. To remedy this
+ problem Mnesia's indexing capabilities are used to improve data
+ retrieval and matching of records.
+ </p>
+ <p>The following two functions manipulate indexes on existing tables:
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:add_table_index(Tab, AttributeName) -> {aborted, R} |{atomic, ok}</c></item>
+ <item><c>mnesia:del_table_index(Tab, AttributeName) -> {aborted, R} |{atomic, ok}</c></item>
+ </list>
+ <p>These functions create or delete a table index on field
+ defined by <c>AttributeName</c>. To illustrate this, add an
+ index to the table definition <c>(employee, {emp_no, name, salary, sex, phone, room_no}</c>, which is the example table
+ from the Company database. The function
+ which adds an index on the element <c>salary</c> can be expressed in
+ the following way:
+ </p>
+ <list type="ordered">
+ <item><c>mnesia:add_table_index(employee, salary)</c></item>
+ </list>
+ <p>The indexing capabilities of Mnesia are utilized with the
+ following three functions, which retrieve and match records on the
+ basis of index entries in the database.
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:index_read(Tab, SecondaryKey, AttributeName) -> transaction abort | RecordList</c>.
+ Avoids an exhaustive search of the entire table, by looking up
+ the <c>SecondaryKey</c> in the index to find the primary keys.
+ </item>
+ <item><c>mnesia:index_match_object(Pattern, AttributeName) -> transaction abort | RecordList</c>
+ Avoids an exhaustive search of the entire table, by looking up
+ the secondary key in the index to find the primary keys.
+ The secondary key is found in the <c>AttributeName</c> field of
+ the <c>Pattern</c>. The secondary key must be bound.
+ </item>
+ <item><c>mnesia:match_object(Pattern) -> transaction abort | RecordList</c>
+ Uses indices to avoid exhaustive search of the entire table.
+ Unlike the other functions above, this function may utilize
+ any index as long as the secondary key is bound.</item>
+ </list>
+ <p>These functions are further described and exemplified in
+ Chapter 4: <seealso marker="Mnesia_chap4#matching">Pattern matching</seealso>.
+ </p>
+ </section>
+
+ <section>
+ <title>Distribution and Fault Tolerance</title>
+ <p>Mnesia is a distributed, fault tolerant DBMS. It is possible
+ to replicate tables on different Erlang nodes in a variety of
+ ways. The Mnesia programmer does not have to state
+ where the different tables reside, only the names of the
+ different tables are specified in the program code. This is
+ known as "location transparency" and it is an important
+ concept. In particular:
+ </p>
+ <list type="bulleted">
+ <item>A program will work regardless of the
+ location of the data. It makes no difference whether the data
+ resides on the local node, or on a remote node. <em>Note:</em> The program
+ will run slower if the data is located on a remote node.
+ </item>
+ <item>The database can be reconfigured, and tables can be
+ moved between nodes. These operations do not effect the user
+ programs.
+ </item>
+ </list>
+ <p>We have previously seen that each table has a number of
+ system attributes, such as <c>index</c> and
+ <c>type</c>.
+ </p>
+ <p>Table attributes are specified when the table is created. For
+ example, the following function will create a new table with two
+ RAM replicas:
+ </p>
+ <pre>
+ mnesia:create_table(foo,
+ [{ram_copies, [N1, N2]},
+ {attributes, record_info(fields, foo)}]).
+ </pre>
+ <p>Tables can also have the following properties,
+ where each attribute has a list of Erlang nodes as its value.
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>ram_copies</c>. The value of the node list is a list of
+ Erlang nodes, and a RAM replica of the table will reside on
+ each node in the list. This is a RAM replica, and it is
+ important to realize that no disc operations are performed when
+ a program executes write operations to these replicas. However,
+ should permanent RAM replicas be a requirement, then the
+ following alternatives are available:</p>
+ <list type="ordered">
+ <item>The <c>mnesia:dump_tables/1</c> function can be used
+ to dump RAM table replicas to disc.
+ </item>
+ <item>The table replicas can be backed up; either from
+ RAM, or from disc if dumped there with the above
+ function.
+ </item>
+ </list>
+ </item>
+ <item><c>disc_copies</c>. The value of the attribute is a list
+ of Erlang nodes, and a replica of the table will reside both
+ in RAM and on disc on each node in the list. Write operations
+ addressed to the table will address both the RAM and the disc
+ copy of the table.
+ </item>
+ <item><c>disc_only_copies</c>. The value of the attribute is a
+ list of Erlang nodes, and a replica of the table will reside
+ only as a disc copy on each node in the list. The major
+ disadvantage of this type of table replica is the access
+ speed. The major advantage is that the table does not occupy
+ space in memory.
+ </item>
+ </list>
+ <p>It is also possible to set and change table properties on
+ existing tables. Refer to Chapter 3: <seealso marker="Mnesia_chap3#def_schema">Defining the Schema</seealso> for full
+ details.
+ </p>
+ <p>There are basically two reasons for using more than one table
+ replica: fault tolerance, or speed. It is worthwhile to note
+ that table replication provides a solution to both of these
+ system requirements.
+ </p>
+ <p>If we have two active table replicas, all information is
+ still available if one of the replicas fail. This can be a very
+ important property in many applications. Furthermore, if a table
+ replica exists at two specific nodes, applications which execute
+ at either of these nodes can read data from the table without
+ accessing the network. Network operations are considerably
+ slower and consume more resources than local operations.
+ </p>
+ <p>It can be advantageous to create table replicas for a
+ distributed application which reads data often, but writes data
+ seldom, in order to achieve fast read operations on the local
+ node. The major disadvantage with replication is the increased
+ time to write data. If a table has two replicas, every write
+ operation must access both table replicas. Since one of these
+ write operations must be a network operation, it is considerably
+ more expensive to perform a write operation to a replicated
+ table than to a non-replicated table.
+ </p>
+ </section>
+
+ <section>
+ <title>Table Fragmentation</title>
+
+ <section>
+ <title>The Concept</title>
+ <p>A concept of table fragmentation has been introduced in
+ order to cope with very large tables. The idea is to split a
+ table into several more manageable fragments. Each fragment
+ is implemented as a first class Mnesia table and may be
+ replicated, have indices etc. as any other table. But the
+ tables may neither have <c>local_content</c> nor have the
+ <c>snmp</c> connection activated.
+ </p>
+ <p>In order to be able to access a record in a fragmented
+ table, Mnesia must determine to which fragment the
+ actual record belongs. This is done by the
+ <c>mnesia_frag</c> module, which implements the
+ <c>mnesia_access</c> callback behaviour. Please, read the
+ documentation about <c>mnesia:activity/4</c> to see how
+ <c>mnesia_frag</c> can be used as a <c>mnesia_access</c>
+ callback module.
+ </p>
+ <p>At each record access <c>mnesia_frag</c> first computes
+ a hash value from the record key. Secondly the name of the
+ table fragment is determined from the hash value. And
+ finally the actual table access is performed by the same
+ functions as for non-fragmented tables. When the key is
+ not known beforehand, all fragments are searched for
+ matching records. Note: In <c>ordered_set</c> tables
+ the records will be ordered per fragment, and the
+ the order is undefined in results returned by select and
+ match_object.
+ </p>
+ <p>The following piece of code illustrates
+ how an existing Mnesia table is converted to be a
+ fragmented table and how more fragments are added later on.
+ </p>
+ <code type="none"><![CDATA[
+Eshell V4.7.3.3 (abort with ^G)
+(a@sam)1> mnesia:start().
+ok
+(a@sam)2> mnesia:system_info(running_db_nodes).
+[b@sam,c@sam,a@sam]
+(a@sam)3> Tab = dictionary.
+dictionary
+(a@sam)4> mnesia:create_table(Tab, [{ram_copies, [a@sam, b@sam]}]).
+{atomic,ok}
+(a@sam)5> Write = fun(Keys) -> [mnesia:write({Tab,K,-K}) || K <- Keys], ok end.
+#Fun<erl_eval>
+(a@sam)6> mnesia:activity(sync_dirty, Write, [lists:seq(1, 256)], mnesia_frag).
+ok
+(a@sam)7> mnesia:change_table_frag(Tab, {activate, []}).
+{atomic,ok}
+(a@sam)8> mnesia:table_info(Tab, frag_properties).
+[{base_table,dictionary},
+ {foreign_key,undefined},
+ {n_doubles,0},
+ {n_fragments,1},
+ {next_n_to_split,1},
+ {node_pool,[a@sam,b@sam,c@sam]}]
+(a@sam)9> Info = fun(Item) -> mnesia:table_info(Tab, Item) end.
+#Fun<erl_eval>
+(a@sam)10> Dist = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
+[{c@sam,0},{a@sam,1},{b@sam,1}]
+(a@sam)11> mnesia:change_table_frag(Tab, {add_frag, Dist}).
+{atomic,ok}
+(a@sam)12> Dist2 = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
+[{b@sam,1},{c@sam,1},{a@sam,2}]
+(a@sam)13> mnesia:change_table_frag(Tab, {add_frag, Dist2}).
+{atomic,ok}
+(a@sam)14> Dist3 = mnesia:activity(sync_dirty, Info, [frag_dist], mnesia_frag).
+[{a@sam,2},{b@sam,2},{c@sam,2}]
+(a@sam)15> mnesia:change_table_frag(Tab, {add_frag, Dist3}).
+{atomic,ok}
+(a@sam)16> Read = fun(Key) -> mnesia:read({Tab, Key}) end.
+#Fun<erl_eval>
+(a@sam)17> mnesia:activity(transaction, Read, [12], mnesia_frag).
+[{dictionary,12,-12}]
+(a@sam)18> mnesia:activity(sync_dirty, Info, [frag_size], mnesia_frag).
+[{dictionary,64},
+ {dictionary_frag2,64},
+ {dictionary_frag3,64},
+ {dictionary_frag4,64}]
+(a@sam)19>
+ ]]></code>
+ </section>
+
+ <section>
+ <title>Fragmentation Properties</title>
+ <p>There is a table property called
+ <c>frag_properties</c> and may be read with
+ <c>mnesia:table_info(Tab, frag_properties)</c>. The
+ fragmentation properties is a list of tagged tuples with
+ the arity 2. By default the list is empty, but when it is
+ non-empty it triggers Mnesia to regard the table as
+ fragmented. The fragmentation properties are:
+ </p>
+ <taglist>
+ <tag><c>{n_fragments, Int}</c></tag>
+ <item>
+ <p><c>n_fragments</c> regulates how many fragments
+ that the table currently has. This property may explicitly
+ be set at table creation and later be changed with
+ <c>{add_frag, NodesOrDist}</c> or
+ <c>del_frag</c>. <c>n_fragment</c>s defaults to <c>1</c>.
+ </p>
+ </item>
+ <tag><c>{node_pool, List}</c></tag>
+ <item>
+ <p>The node pool contains a list of nodes and may
+ explicitly be set at table creation and later be changed
+ with <c>{add_node, Node}</c> or <c>{del_node, Node}</c>. At table creation Mnesia tries to distribute
+ the replicas of each fragment evenly over all the nodes in
+ the node pool. Hopefully all nodes will end up with the
+ same number of replicas. <c>node_pool</c> defaults to the
+ return value from <c>mnesia:system_info(db_nodes)</c>.
+ </p>
+ </item>
+ <tag><c>{n_ram_copies, Int}</c></tag>
+ <item>
+ <p>Regulates how many <c>ram_copies</c> replicas
+ that each fragment should have. This property may
+ explicitly be set at table creation. The default is
+ <c>0</c>, but if <c>n_disc_copies</c> and
+ <c>n_disc_only_copies</c> also are <c>0</c>,
+ <c>n_ram_copies</c>\011will default be set to <c>1</c>.
+ </p>
+ </item>
+ <tag><c>{n_disc_copies, Int}</c></tag>
+ <item>
+ <p>Regulates how many <c>disc_copies</c> replicas
+ that each fragment should have. This property may
+ explicitly be set at table creation. The default is <c>0</c>.
+ </p>
+ </item>
+ <tag><c>{n_disc_only_copies, Int}</c></tag>
+ <item>
+ <p>Regulates how many <c>disc_only_copies</c> replicas
+ that each fragment should have. This property may
+ explicitly be set at table creation. The default is <c>0</c>.
+ </p>
+ </item>
+ <tag><c>{foreign_key, ForeignKey}</c></tag>
+ <item>
+ <p><c>ForeignKey</c> may either be the atom
+ <c>undefined</c> or the tuple <c>{ForeignTab, Attr}</c>,
+ where <c>Attr</c> denotes an attribute which should be
+ interpreted as a key in another fragmented table named
+ <c>ForeignTab</c>. Mnesia will ensure that the number of
+ fragments in this table and in the foreign table are
+ always the same. When fragments are added or deleted
+ Mnesia will automatically propagate the operation to all
+ fragmented tables that has a foreign key referring to this
+ table. Instead of using the record key to determine which
+ fragment to access, the value of the <c>Attr</c> field is
+ used. This feature makes it possible to automatically
+ co-locate records in different tables to the same
+ node. <c>foreign_key</c> defaults to
+ <c>undefined</c>. However if the foreign key is set to
+ something else it will cause the default values of the
+ other fragmentation properties to be the same values as
+ the actual fragmentation properties of the foreign table.
+ </p>
+ </item>
+ <tag><c>{hash_module, Atom}</c></tag>
+ <item>
+ <p>Enables definition of an alternate hashing scheme.
+ The module must implement the <c>mnesia_frag_hash</c>
+ callback behaviour (see the reference manual). This
+ property may explicitly be set at table creation.
+ The default is <c>mnesia_frag_hash</c>.</p>
+ <p>Older tables that was created before the concept of
+ user defined hash modules was introduced, uses
+ the <c>mnesia_frag_old_hash</c> module in order to
+ be backwards compatible. The <c>mnesia_frag_old_hash</c>
+ is still using the poor deprecated <c>erlang:hash/1</c>
+ function.
+ </p>
+ </item>
+ <tag><c>{hash_state, Term}</c></tag>
+ <item>
+ <p>Enables a table specific parameterization
+ of a generic hash module. This property may explicitly
+ be set at table creation.
+ The default is <c>undefined</c>.</p>
+ <code type="none"><![CDATA[
+Eshell V4.7.3.3 (abort with ^G)
+(a@sam)1> mnesia:start().
+ok
+(a@sam)2> PrimProps = [{n_fragments, 7}, {node_pool, [node()]}].
+[{n_fragments,7},{node_pool,[a@sam]}]
+(a@sam)3> mnesia:create_table(prim_dict,
+ [{frag_properties, PrimProps},
+ {attributes,[prim_key,prim_val]}]).
+{atomic,ok}
+(a@sam)4> SecProps = [{foreign_key, {prim_dict, sec_val}}].
+[{foreign_key,{prim_dict,sec_val}}]
+(a@sam)5> mnesia:create_table(sec_dict,
+\011 [{frag_properties, SecProps},
+(a@sam)5> {attributes, [sec_key, sec_val]}]).
+{atomic,ok}
+(a@sam)6> Write = fun(Rec) -> mnesia:write(Rec) end.
+#Fun<erl_eval>
+(a@sam)7> PrimKey = 11.
+11
+(a@sam)8> SecKey = 42.
+42
+(a@sam)9> mnesia:activity(sync_dirty, Write,
+\011\011 [{prim_dict, PrimKey, -11}], mnesia_frag).
+ok
+(a@sam)10> mnesia:activity(sync_dirty, Write,
+\011\011 [{sec_dict, SecKey, PrimKey}], mnesia_frag).
+ok
+(a@sam)11> mnesia:change_table_frag(prim_dict, {add_frag, [node()]}).
+{atomic,ok}
+(a@sam)12> SecRead = fun(PrimKey, SecKey) ->
+\011\011 mnesia:read({sec_dict, PrimKey}, SecKey, read) end.
+#Fun<erl_eval>
+(a@sam)13> mnesia:activity(transaction, SecRead,
+\011\011 [PrimKey, SecKey], mnesia_frag).
+[{sec_dict,42,11}]
+(a@sam)14> Info = fun(Tab, Item) -> mnesia:table_info(Tab, Item) end.
+#Fun<erl_eval>
+(a@sam)15> mnesia:activity(sync_dirty, Info,
+\011\011 [prim_dict, frag_size], mnesia_frag).
+[{prim_dict,0},
+ {prim_dict_frag2,0},
+ {prim_dict_frag3,0},
+ {prim_dict_frag4,1},
+ {prim_dict_frag5,0},
+ {prim_dict_frag6,0},
+ {prim_dict_frag7,0},
+ {prim_dict_frag8,0}]
+(a@sam)16> mnesia:activity(sync_dirty, Info,
+\011\011 [sec_dict, frag_size], mnesia_frag).
+[{sec_dict,0},
+ {sec_dict_frag2,0},
+ {sec_dict_frag3,0},
+ {sec_dict_frag4,1},
+ {sec_dict_frag5,0},
+ {sec_dict_frag6,0},
+ {sec_dict_frag7,0},
+ {sec_dict_frag8,0}]
+(a@sam)17>
+ ]]></code>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Management of Fragmented Tables</title>
+ <p>The function <c>mnesia:change_table_frag(Tab, Change)</c>
+ is intended to be used for reconfiguration of fragmented
+ tables. The <c>Change</c> argument should have one of the
+ following values:
+ </p>
+ <taglist>
+ <tag><c>{activate, FragProps}</c></tag>
+ <item>
+ <p>Activates the fragmentation properties of an
+ existing table. <c>FragProps</c> should either contain
+ <c>{node_pool, Nodes}</c> or be empty.
+ </p>
+ </item>
+ <tag><c>deactivate</c></tag>
+ <item>
+ <p>Deactivates the fragmentation properties of a
+ table. The number of fragments must be <c>1</c>. No other
+ tables may refer to this table in its foreign key.
+ </p>
+ </item>
+ <tag><c>{add_frag, NodesOrDist}</c></tag>
+ <item>
+ <p>Adds one new fragment to a fragmented table. All
+ records in one of the old fragments will be rehashed and
+ about half of them will be moved to the new (last)
+ fragment. All other fragmented tables, which refers to this
+ table in their foreign key, will automatically get a new
+ fragment, and their records will also be dynamically
+ rehashed in the same manner as for the main table.
+ </p>
+ <p>The <c>NodesOrDist</c> argument may either be a list
+ of nodes or the result from <c>mnesia:table_info(Tab, frag_dist)</c>. The <c>NodesOrDist</c> argument is
+ assumed to be a sorted list with the best nodes to
+ host new replicas first in the list. The new fragment
+ will get the same number of replicas as the first
+ fragment (see <c>n_ram_copies</c>, <c>n_disc_copies</c>
+ and <c>n_disc_only_copies</c>). The <c>NodesOrDist</c>
+ list must at least contain one element for each
+ replica that needs to be allocated.
+ </p>
+ </item>
+ <tag><c>del_frag</c></tag>
+ <item>
+ <p>Deletes one fragment from a fragmented table. All
+ records in the last fragment will be moved to one of the other
+ fragments. All other fragmented tables which refers to
+ this table in their foreign key, will automatically lose
+ their last fragment and their records will also be
+ dynamically rehashed in the same manner as for the main
+ table.
+ </p>
+ </item>
+ <tag><c>{add_node, Node}</c></tag>
+ <item>
+ <p>Adds a new node to the <c>node_pool</c>. The new
+ node pool will affect the list returned from
+ <c>mnesia:table_info(Tab, frag_dist)</c>.
+ </p>
+ </item>
+ <tag><c>{del_node, Node}</c></tag>
+ <item>
+ <p>Deletes a new node from the <c>node_pool</c>. The
+ new node pool will affect the list returned from
+ <c>mnesia:table_info(Tab, frag_dist)</c>.</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Extensions of Existing Functions</title>
+ <p>The function <c>mnesia:create_table/2</c> is used to
+ create a brand new fragmented table, by setting the table
+ property <c>frag_properties</c> to some proper values.
+ </p>
+ <p>The function <c>mnesia:delete_table/1</c> is used to
+ delete a fragmented table including all its
+ fragments. There must however not exist any other
+ fragmented tables which refers to this table in their foreign key.
+ </p>
+ <p>The function <c>mnesia:table_info/2</c> now understands
+ the <c>frag_properties</c> item.
+ </p>
+ <p>If the function <c>mnesia:table_info/2</c> is invoked in
+ the activity context of the <c>mnesia_frag</c> module,
+ information of several new items may be obtained:
+ </p>
+ <taglist>
+ <tag><c>base_table</c></tag>
+ <item>
+ <p>the name of the fragmented table
+ </p>
+ </item>
+ <tag><c>n_fragments</c></tag>
+ <item>
+ <p>the actual number of fragments
+ </p>
+ </item>
+ <tag><c>node_pool</c></tag>
+ <item>
+ <p>the pool of nodes
+ </p>
+ </item>
+ <tag><c>n_ram_copies</c></tag>
+ <item></item>
+ <tag><c>n_disc_copies</c></tag>
+ <item></item>
+ <tag><c>n_disc_only_copies</c></tag>
+ <item>
+ <p>the number of replicas with storage type
+ <c>ram_copies</c>, <c>disc_copies</c> and <c>disc_only_copies</c>
+ respectively. The actual values are dynamically derived
+ from the first fragment. The first fragment serves as a
+ pro-type and when the actual values needs to be computed
+ (e.g. when adding new fragments) they are simply
+ determined by counting the number of each replicas for
+ each storage type. This means, when the functions
+ <c>mnesia:add_table_copy/3</c>,
+ <c>mnesia:del_table_copy/2</c> and<c>mnesia:change_table_copy_type/2</c> are applied on the
+ first fragment, it will affect the settings on
+ <c>n_ram_copies</c>, <c>n_disc_copies</c>, and
+ <c>n_disc_only_copies</c>.
+ </p>
+ </item>
+ <tag><c>foreign_key</c></tag>
+ <item>
+ <p>the foreign key.
+ </p>
+ </item>
+ <tag><c>foreigners</c></tag>
+ <item>
+ <p>all other tables that refers to this table in
+ their foreign key.
+ </p>
+ </item>
+ <tag><c>frag_names</c></tag>
+ <item>
+ <p>the names of all fragments.
+ </p>
+ </item>
+ <tag><c>frag_dist</c></tag>
+ <item>
+ <p>a sorted list of <c>{Node, Count}</c> tuples
+ which is sorted in increasing <c>Count</c> order. The
+ <c>Count</c> is the total number of replicas that this
+ fragmented table hosts on each <c>Node</c>. The list
+ always contains at least all nodes in the
+ <c>node_pool</c>. The nodes which not belongs to the
+ <c>node_pool</c> will be put last in the list even if
+ their <c>Count</c> is lower.
+ </p>
+ </item>
+ <tag><c>frag_size</c></tag>
+ <item>
+ <p>a list of <c>{Name, Size}</c> tuples where
+ <c>Name</c> is a fragment <c>Name</c> and <c>Size</c> is
+ how many records it contains.
+ </p>
+ </item>
+ <tag><c>frag_memory</c></tag>
+ <item>
+ <p>a list of <c>{Name, Memory}</c> tuples where
+ <c>Name</c> is a fragment <c>Name</c> and <c>Memory</c> is
+ how much memory it occupies.
+ </p>
+ </item>
+ <tag><c>size</c></tag>
+ <item>
+ <p>total size of all fragments
+ </p>
+ </item>
+ <tag><c>memory</c></tag>
+ <item>
+ <p>the total memory of all fragments</p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Load Balancing</title>
+ <p>There are several algorithms for distributing records
+ in a fragmented table evenly over a
+ pool of nodes. No one is best, it simply depends of the
+ application needs. Here follows some examples of
+ situations which may need some attention:
+ </p>
+ <p><c>permanent change of nodes</c> when a new permanent
+ <c>db_node</c> is introduced or dropped, it may be time to
+ change the pool of nodes and re-distribute the replicas
+ evenly over the new pool of nodes. It may also be time to
+ add or delete a fragment before the replicas are re-distributed.
+ </p>
+ <p><c>size/memory threshold</c> when the total size or
+ total memory of a fragmented table (or a single
+ fragment) exceeds some application specific threshold, it
+ may be time to dynamically add a new fragment in order
+ obtain a better distribution of records.
+ </p>
+ <p><c>temporary node down</c> when a node temporarily goes
+ down it may be time to compensate some fragments with new
+ replicas in order to keep the desired level of
+ redundancy. When the node comes up again it may be time to
+ remove the superfluous replica.
+ </p>
+ <p><c>overload threshold</c> when the load on some node is
+ exceeds some application specific threshold, it may be time to
+ either add or move some fragment replicas to nodes with lesser
+ load. Extra care should be taken if the table has a foreign
+ key relation to some other table. In order to avoid severe
+ performance penalties, the same re-distribution must be
+ performed for all of the related tables.
+ </p>
+ <p>Use <c>mnesia:change_table_frag/2</c> to add new fragments
+ and apply the usual schema manipulation functions (such as
+ <c>mnesia:add_table_copy/3</c>, <c>mnesia:del_table_copy/2</c>
+ and <c>mnesia:change_table_copy_type/2</c>) on each fragment
+ to perform the actual re-distribution.
+ </p>
+ </section>
+ </section>
+
+ <section>
+ <title>Local Content Tables</title>
+ <p>Replicated tables have the same content on all nodes where
+ they are replicated. However, it is sometimes advantageous to
+ have tables but different content on different nodes.
+ </p>
+ <p>If we specify the attribute <c>{local_content, true}</c> when
+ we create the table, the table will reside on the nodes where
+ we specify that the table shall exist, but the write operations on the
+ table will only be performed on the local copy.
+ </p>
+ <p>Furthermore, when the table is initialized at start-up, the
+ table will only be initialized locally, and the table
+ content will not be copied from another node.
+ </p>
+ </section>
+
+ <section>
+ <title>Disc-less Nodes</title>
+ <p>It is possible to run Mnesia on nodes that do not have a
+ disc. It is of course not possible to have replicas
+ of neither <c>disc_copies</c>, nor <c>disc_only_copies</c>
+ on such nodes. This especially troublesome for the
+ <c>schema</c> table since Mnesia need the schema in order
+ to initialize itself.
+ </p>
+ <p>The schema table may, as other tables, reside on one or
+ more nodes. The storage type of the schema table may either
+ be <c>disc_copies</c> or <c>ram_copies</c>
+ (not <c>disc_only_copies</c>). At
+ start-up Mnesia uses its schema to determine with which
+ nodes it should try to establish contact. If any
+ of the other nodes are already started, the starting node
+ merges its table definitions with the table definitions
+ brought from the other nodes. This also applies to the
+ definition of the schema table itself. The application
+ parameter <c>extra_db_nodes</c> contains a list of nodes which
+ Mnesia also should establish contact with besides the ones
+ found in the schema. The default value is the empty list
+ <c>[]</c>.
+ </p>
+ <p>Hence, when a disc-less node needs to find the schema
+ definitions from a remote node on the network, we need to supply
+ this information through the application parameter <c>-mnesia extra_db_nodes NodeList</c>. Without this
+ configuration parameter set, Mnesia will start as a single node
+ system. It is also possible to use <c>mnesia:change_config/2</c>
+ to assign a value to 'extra_db_nodes' and force a connection
+ after mnesia have been started, i.e.
+ mnesia:change_config(extra_db_nodes, NodeList).
+ </p>
+ <p>The application parameter schema_location controls where
+ Mnesia will search for its schema. The parameter may be one of
+ the following atoms:
+ </p>
+ <taglist>
+ <tag><c>disc</c></tag>
+ <item>
+ <p>Mandatory disc. The schema is assumed to be located
+ on the Mnesia directory. And if the schema cannot be found,
+ Mnesia refuses to start.
+ </p>
+ </item>
+ <tag><c>ram</c></tag>
+ <item>
+ <p>Mandatory ram. The schema resides in ram
+ only. At start-up a tiny new schema is generated. This
+ default schema contains just the definition of the schema
+ table and only resides on the local node. Since no other
+ nodes are found in the default schema, the configuration
+ parameter <c>extra_db_nodes</c> must be used in order to let the
+ node share its table definitions with other nodes. (The
+ <c>extra_db_nodes</c> parameter may also be used on disc-full nodes.)
+ </p>
+ </item>
+ <tag><c>opt_disc</c></tag>
+ <item>
+ <p>Optional disc. The schema may reside on either disc
+ or ram. If the schema is found on disc, Mnesia starts as a
+ disc-full node (the storage type of the schema table is
+ disc_copies). If no schema is found on disc, Mnesia starts
+ as a disc-less node (the storage type of the schema table is
+ ram_copies). The default value for the application parameter
+ is
+ <c>opt_disc</c>. </p>
+ </item>
+ </taglist>
+ <p>When the <c>schema_location</c> is set to opt_disc the
+ function <c>mnesia:change_table_copy_type/3</c> may be used to
+ change the storage type of the schema.
+ This is illustrated below:
+ </p>
+ <pre>
+ 1> mnesia:start().
+ ok
+ 2> mnesia:change_table_copy_type(schema, node(), disc_copies).
+ {atomic, ok}
+ </pre>
+ <p>Assuming that the call to <c>mnesia:start</c> did not
+ find any schema to read on the disc, then Mnesia has started
+ as a disc-less node, and then changed it to a node that
+ utilizes the disc to locally store the schema.
+ </p>
+ </section>
+
+ <section>
+ <title>More Schema Management</title>
+ <p>It is possible to add and remove nodes from a Mnesia system.
+ This can be done by adding a copy of the schema to those nodes.
+ </p>
+ <p>The functions <c>mnesia:add_table_copy/3</c> and
+ <c>mnesia:del_table_copy/2</c> may be used to add and delete
+ replicas of the schema table. Adding a node to the list
+ of nodes where the schema is replicated will affect two
+ things. First it allows other tables to be replicated to
+ this node. Secondly it will cause Mnesia to try to contact
+ the node at start-up of disc-full nodes.
+ </p>
+ <p>The function call <c>mnesia:del_table_copy(schema, mynode@host)</c> deletes the node 'mynode@host' from the
+ Mnesia system. The call fails if mnesia is running on
+ 'mynode@host'. The other mnesia nodes will never try to connect
+ to that node again. Note, if there is a disc
+ resident schema on the node 'mynode@host', the entire mnesia
+ directory should be deleted. This can be done with
+ <c>mnesia:delete_schema/1</c>. If
+ mnesia is started again on the the node 'mynode@host' and the
+ directory has not been cleared, mnesia's behaviour is undefined.
+ </p>
+ <p>If the storage type of the schema is ram_copies, i.e, we
+ have disc-less node, Mnesia
+ will not use the disc on that particular node. The disc
+ usage is enabled by changing the storage type of the table
+ <c>schema</c> to disc_copies.
+ </p>
+ <p>New schemas are
+ created explicitly with <c>mnesia:create_schema/1</c> or implicitly
+ by starting Mnesia without a disc resident schema. Whenever
+ a table (including the schema table) is created it is
+ assigned its own unique cookie. The schema table is not created with
+ <c>mnesia:create_table/2</c> as normal tables.
+ </p>
+ <p>At start-up Mnesia connects different nodes to each other,
+ then they exchange table definitions with each other and the
+ table definitions are merged. During the merge procedure Mnesia
+ performs a sanity test to ensure that the table definitions are
+ compatible with each other. If a table exists on several nodes
+ the cookie must be the same, otherwise Mnesia will shutdown one
+ of the nodes. This unfortunate situation will occur if a table
+ has been created on two nodes independently of each other while
+ they were disconnected. To solve the problem, one of the tables
+ must be deleted (as the cookies differ we regard it to be two
+ different tables even if they happen to have the same name).
+ </p>
+ <p>Merging different versions of the schema table, does not
+ always require the cookies to be the same. If the storage
+ type of the schema table is disc_copies, the cookie is
+ immutable, and all other db_nodes must have the same
+ cookie. When the schema is stored as type ram_copies,
+ its cookie can be replaced with a cookie from another node
+ (ram_copies or disc_copies). The cookie replacement (during
+ merge of the schema table definition) is performed each time
+ a RAM node connects to another node.
+ </p>
+ <p><c>mnesia:system_info(schema_location)</c> and
+ <c>mnesia:system_info(extra_db_nodes)</c> may be used to determine
+ the actual values of schema_location and extra_db_nodes
+ respectively. <c>mnesia:system_info(use_dir)</c> may be used to
+ determine whether Mnesia is actually using the Mnesia
+ directory. <c>use_dir</c> may be determined even before
+ Mnesia is started. The function <c>mnesia:info/0</c> may now be
+ used to printout some system information even before Mnesia
+ is started. When Mnesia is started the function prints out
+ more information.
+ </p>
+ <p>Transactions which update the definition of a table,
+ requires that Mnesia is started on all nodes where the
+ storage type of the schema is disc_copies. All replicas of
+ the table on these nodes must also be loaded. There are a
+ few exceptions to these availability rules. Tables may be
+ created and new replicas may be added without starting all
+ of the disc-full nodes. New replicas may be added before all
+ other replicas of the table have been loaded, it will suffice
+ when one other replica is active.
+ </p>
+ </section>
+
+ <section>
+ <title>Mnesia Event Handling</title>
+ <p>System events and table events are the two categories of events
+ that Mnesia will generate in various situations.
+ </p>
+ <p>It is possible for user process to subscribe on the
+ events generated by Mnesia.
+ We have the following two functions:</p>
+ <taglist>
+ <tag><c>mnesia:subscribe(Event-Category)</c></tag>
+ <item>
+ <p>Ensures that a copy of all events of type
+ <c>Event-Category</c> are sent to the calling process.
+ </p>
+ </item>
+ <tag><c>mnesia:unsubscribe(Event-Category)</c></tag>
+ <item>Removes the subscription on events of type
+ <c>Event-Category</c></item>
+ </taglist>
+ <p><c>Event-Category</c> may either be the atom <c>system</c>, or
+ one of the tuples <c>{table, Tab, simple}</c>, <c>{table, Tab, detailed}</c>. The old event-category <c>{table, Tab}</c> is the same
+ event-category as <c>{table, Tab, simple}</c>.
+ The subscribe functions activate a subscription
+ of events. The events are delivered as messages to the process
+ evaluating the <c>mnesia:subscribe/1</c> function. The syntax of
+ system events is <c>{mnesia_system_event, Event}</c> and
+ <c>{mnesia_table_event, Event}</c> for table events. What system
+ events and table events means is described below.
+ </p>
+ <p>All system events are subscribed by Mnesia's
+ gen_event handler. The default gen_event handler is
+ <c>mnesia_event</c>. But it may be changed by using the application
+ parameter <c>event_module</c>. The value of this parameter must be
+ the name of a module implementing a complete handler
+ as specified by the <c>gen_event</c> module in
+ STDLIB. <c>mnesia:system_info(subscribers)</c> and
+ <c>mnesia:table_info(Tab, subscribers)</c> may be used to determine
+ which processes are subscribed to various
+ events.
+ </p>
+
+ <section>
+ <title>System Events</title>
+ <p>The system events are detailed below:</p>
+ <taglist>
+ <tag><c>{mnesia_up, Node}</c></tag>
+ <item>
+ <p>Mnesia has been started on a node.
+ Node is the name of the node. By default this event is ignored.
+ </p>
+ </item>
+ <tag><c>{mnesia_down, Node}</c></tag>
+ <item>
+ <p>Mnesia has been stopped on a node.
+ Node is the name of the node. By default this event is
+ ignored.
+ </p>
+ </item>
+ <tag><c>{mnesia_checkpoint_activated, Checkpoint}</c></tag>
+ <item>
+ <p>a checkpoint with the name
+ <c>Checkpoint</c> has been activated and that the current node is
+ involved in the checkpoint. Checkpoints may be activated
+ explicitly with <c>mnesia:activate_checkpoint/1</c> or implicitly
+ at backup, adding table replicas, internal transfer of data
+ between nodes etc. By default this event is ignored.
+ </p>
+ </item>
+ <tag><c>{mnesia_checkpoint_deactivated, Checkpoint}</c></tag>
+ <item>
+ <p>A checkpoint with the name
+ <c>Checkpoint</c> has been deactivated and that the current node was
+ involved in the checkpoint. Checkpoints may explicitly be
+ deactivated with <c>mnesia:deactivate/1</c> or implicitly when the
+ last replica of a table (involved in the checkpoint)
+ becomes unavailable, e.g. at node down. By default this
+ event is ignored.
+ </p>
+ </item>
+ <tag><c>{mnesia_overload, Details}</c></tag>
+ <item>
+ <p>Mnesia on the current node is
+ overloaded and the subscriber should take action.
+ </p>
+ <p>A typical overload situation occurs when the
+ applications are performing more updates on disc
+ resident tables than Mnesia is able to handle. Ignoring
+ this kind of overload may lead into a situation where
+ the disc space is exhausted (regardless of the size of
+ the tables stored on disc).
+ <br></br>
+ Each update is appended to
+ the transaction log and occasionally(depending of how it
+ is configured) dumped to the tables files. The
+ table file storage is more compact than the transaction
+ log storage, especially if the same record is updated
+ over and over again. If the thresholds for dumping the
+ transaction log have been reached before the previous
+ dump was finished an overload event is triggered.
+ </p>
+ <p>Another typical overload situation is when the
+ transaction manager cannot commit transactions at the
+ same pace as the applications are performing updates of
+ disc resident tables. When this happens the message
+ queue of the transaction manager will continue to grow
+ until the memory is exhausted or the load
+ decreases.
+ </p>
+ <p>The same problem may occur for dirty updates. The overload
+ is detected locally on the current node, but its cause may
+ be on another node. Application processes may cause heavy
+ loads if any table are residing on other nodes (replicated or not). By default this event
+ is reported to the error_logger.
+ </p>
+ </item>
+ <tag><c>{inconsistent_database, Context, Node}</c></tag>
+ <item>
+ <p>Mnesia regards the database as
+ potential inconsistent and gives its applications a chance
+ to recover from the inconsistency, e.g. by installing a
+ consistent backup as fallback and then restart the system
+ or pick a <c>MasterNode</c> from <c>mnesia:system_info(db_nodes)</c>)
+ and invoke <c>mnesia:set_master_node([MasterNode])</c>. By default an
+ error is reported to the error logger.
+ </p>
+ </item>
+ <tag><c>{mnesia_fatal, Format, Args, BinaryCore}</c></tag>
+ <item>
+ <p>Mnesia has encountered a fatal error
+ and will (in a short period of time) be terminated. The reason for
+ the fatal error is explained in Format and Args which may
+ be given as input to <c>io:format/2</c> or sent to the
+ error_logger. By default it will be sent to the
+ error_logger. <c>BinaryCore</c> is a binary containing a summary of
+ Mnesia's internal state at the time the when the fatal error was
+ encountered. By default the binary is written to a
+ unique file name on current directory. On RAM nodes the
+ core is ignored.
+ </p>
+ </item>
+ <tag><c>{mnesia_info, Format, Args}</c></tag>
+ <item>
+ <p>Mnesia has detected something that
+ may be of interest when debugging the system. This is explained
+ in <c>Format</c> and <c>Args</c> which may appear
+ as input to <c>io:format/2</c> or sent to the error_logger. By
+ default this event is printed with <c>io:format/2</c>.
+ </p>
+ </item>
+ <tag><c>{mnesia_error, Format, Args}</c></tag>
+ <item>
+ <p>Mnesia has encountered an error. The
+ reason for the error is explained i <c>Format</c> and <c>Args</c>
+ which may be given as input to <c>io:format/2</c> or sent to the
+ error_logger. By default this event is reported to the error_logger.
+ </p>
+ </item>
+ <tag><c>{mnesia_user, Event}</c></tag>
+ <item>
+ <p>An application has invoked the
+ function <c>mnesia:report_event(Event)</c>. <c>Event</c> may be any Erlang
+ data structure. When tracing a system of Mnesia applications
+ it is useful to be able to interleave Mnesia's own events with
+ application related events that give information about the
+ application context. Whenever the application starts with
+ a new and demanding Mnesia activity or enters a
+ new and interesting phase in its execution it may be a good idea
+ to use <c>mnesia:report_event/1</c>. </p>
+ </item>
+ </taglist>
+ </section>
+
+ <section>
+ <title>Table Events</title>
+ <p>Another category of events are table events, which are
+ events related to table updates. There are two types of table
+ events simple and detailed.
+ </p>
+ <p>The simple table events are tuples looking like this:
+ <c>{Oper, Record, ActivityId}</c>. Where <c>Oper</c> is the
+ operation performed. <c>Record</c> is the record involved in the
+ operation and <c>ActivityId</c> is the identity of the
+ transaction performing the operation. Note that the name of the
+ record is the table name even when the <c>record_name</c> has
+ another setting. The various table related events that may
+ occur are:
+ </p>
+ <taglist>
+ <tag><c>{write, NewRecord, ActivityId}</c></tag>
+ <item>
+ <p>a new record has been written.
+ NewRecord contains the new value of the record.
+ </p>
+ </item>
+ <tag><c>{delete_object, OldRecord, ActivityId}</c></tag>
+ <item>
+ <p>a record has possibly been deleted
+ with <c>mnesia:delete_object/1</c>. <c>OldRecord</c>
+ contains the value of the old record as stated as argument
+ by the application. Note that, other records with the same
+ key may be remaining in the table if it is a bag.
+ </p>
+ </item>
+ <tag><c>{delete, {Tab, Key}, ActivityId}</c></tag>
+ <item>
+ <p>one or more records possibly has
+ been deleted. All records with the key Key in the table
+ <c>Tab</c> have been deleted. </p>
+ </item>
+ </taglist>
+ <p>The detailed table events are tuples looking like
+ this: <c>{Oper, Table, Data, [OldRecs], ActivityId}</c>.
+ Where <c>Oper</c> is the operation
+ performed. <c>Table</c> is the table involved in the operation,
+ <c>Data</c> is the record/oid written/deleted.
+ <c>OldRecs</c> is the contents before the operation.
+ and <c>ActivityId</c> is the identity of the transaction
+ performing the operation.
+ The various table related events that may occur are:
+ </p>
+ <taglist>
+ <tag><c>{write, Table, NewRecord, [OldRecords], ActivityId}</c></tag>
+ <item>
+ <p>a new record has been written.
+ NewRecord contains the new value of the record and OldRecords
+ contains the records before the operation is performed.
+ Note that the new content is dependent on the type of the table.</p>
+ </item>
+ <tag><c>{delete, Table, What, [OldRecords], ActivityId}</c></tag>
+ <item>
+ <p>records has possibly been deleted
+ <c>What</c> is either {Table, Key} or a record {RecordName, Key, ...}
+ that was deleted.
+ Note that the new content is dependent on the type of the table.</p>
+ </item>
+ </taglist>
+ </section>
+ </section>
+
+ <section>
+ <title>Debugging Mnesia Applications</title>
+ <p>Debugging a Mnesia application can be difficult due to a number of reasons, primarily related
+ to difficulties in understanding how the transaction
+ and table load mechanisms work. An other source of
+ confusion may be the semantics of nested transactions.
+ </p>
+ <p>We may set the debug level of Mnesia by calling:
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:set_debug_level(Level)</c></item>
+ </list>
+ <p>Where the parameter <c>Level</c> is:
+ </p>
+ <taglist>
+ <tag><c>none</c></tag>
+ <item>
+ <p>no trace outputs at all. This is the default.
+ </p>
+ </item>
+ <tag><c>verbose</c></tag>
+ <item>
+ <p>activates tracing of important debug events. These
+ debug events will generate <c>{mnesia_info, Format, Args}</c>
+ system events. Processes may subscribe to these events with
+ <c>mnesia:subscribe/1</c>. The events are always sent to Mnesia's
+ event handler.
+ </p>
+ </item>
+ <tag><c>debug</c></tag>
+ <item>
+ <p>activates all events at the verbose level plus
+ traces of all debug events. These debug events will generate
+ <c>{mnesia_info, Format, Args}</c> system events. Processes may
+ subscribe to these events with <c>mnesia:subscribe/1</c>. The
+ events are always sent to Mnesia's event handler. On this
+ debug level Mnesia's event handler starts subscribing
+ updates in the schema table.
+ </p>
+ </item>
+ <tag><c>trace</c></tag>
+ <item>
+ <p>activates all events at the debug level. On this
+ debug level Mnesia's event handler starts subscribing
+ updates on all Mnesia tables. This level is only intended
+ for debugging small toy systems, since many large
+ events may be generated.</p>
+ </item>
+ <tag><c>false</c></tag>
+ <item>
+ <p>is an alias for none.</p>
+ </item>
+ <tag><c>true</c></tag>
+ <item>
+ <p>is an alias for debug.</p>
+ </item>
+ </taglist>
+ <p>The debug level of Mnesia itself, is also an application
+ parameter, thereby making it possible to start an Erlang system
+ in order to turn on Mnesia debug in the initial
+ start-up phase by using the following code:
+ </p>
+ <pre>
+ % erl -mnesia debug verbose
+ </pre>
+ </section>
+
+ <section>
+ <title>Concurrent Processes in Mnesia</title>
+ <p>Programming concurrent Erlang systems is the subject of
+ a separate book. However, it is worthwhile to draw attention to
+ the following features, which permit concurrent processes to
+ exist in a Mnesia system.
+ </p>
+ <p>A group of functions or processes can be called within a
+ transaction. A transaction may include statements that read,
+ write or delete data from the DBMS. A large number of such
+ transactions can run concurrently, and the programmer does not
+ have to explicitly synchronize the processes which manipulate
+ the data. All programs accessing the database through the
+ transaction system may be written as if they had sole access to
+ the data. This is a very desirable property since all
+ synchronization is taken care of by the transaction handler. If
+ a program reads or writes data, the system ensures that no other
+ program tries to manipulate the same data at the same time.
+ </p>
+ <p>It is possible to move tables, delete tables or reconfigure
+ the layout of a table in various ways. An important aspect of
+ the actual implementation of these functions is that it is
+ possible for user programs to continue to use a table while it
+ is being reconfigured. For example, it is possible to
+ simultaneously move a table and perform write operations to the
+ table . This is important for many applications that
+ require continuously available services. Refer to Chapter 4:
+ <seealso marker="Mnesia_chap4#trans_prop">Transactions and other access contexts</seealso> for more information.
+ </p>
+ </section>
+
+ <section>
+ <title>Prototyping</title>
+ <p>If and when we decide that we would like to start and manipulate
+ Mnesia, it is often easier to write the definitions and
+ data into an ordinary text file.
+ Initially, no tables and no data exist, or which
+ tables are required. At the initial stages of prototyping it
+ is prudent write all data into one file, process
+ that file and have the data in the file inserted into the database.
+ It is possible to initialize Mnesia with data read from a text file.
+ We have the following two functions to work with text files.
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>mnesia:load_textfile(Filename)</c> Which loads a
+ series of local table definitions and data found in the file
+ into Mnesia. This function also starts Mnesia and possibly
+ creates a new schema. The function only operates on the
+ local node.
+ </p>
+ </item>
+ <item>
+ <p><c>mnesia:dump_to_textfile(Filename)</c> Dumps
+ all local tables of a mnesia system into a text file which can
+ then be edited (by means of a normal text editor) and then
+ later reloaded.</p>
+ </item>
+ </list>
+ <p>These functions are of course much slower than the ordinary
+ store and load functions of Mnesia. However, this is mainly intended for minor experiments
+ and initial prototyping. The major advantages of these functions is that they are very easy
+ to use.
+ </p>
+ <p>The format of the text file is:
+ </p>
+ <pre>
+ {tables, [{Typename, [Options]},
+ {Typename2 ......}]}.
+
+ {Typename, Attribute1, Atrribute2 ....}.
+ {Typename, Attribute1, Atrribute2 ....}.
+ </pre>
+ <p><c>Options</c> is a list of <c>{Key,Value}</c> tuples conforming
+ to the options we could give to <c>mnesia:create_table/2</c>.
+ </p>
+ <p>For example, if we want to start playing with a small
+ database for healthy foods, we enter then following data into
+ the file <c>FRUITS</c>.
+ </p>
+ <codeinclude file="FRUITS" tag="%0" type="erl"></codeinclude>
+ <p>The following session with the Erlang shell then shows how
+ to load the fruits database.
+ </p>
+ <pre><![CDATA[
+ % erl
+ Erlang (BEAM) emulator version 4.9
+
+ Eshell V4.9 (abort with ^G)
+ 1> mnesia:load_textfile("FRUITS").
+ New table fruit
+ New table vegetable
+ {atomic,ok}
+ 2> mnesia:info().
+ ---> Processes holding locks <---
+ ---> Processes waiting for locks <---
+ ---> Pending (remote) transactions <---
+ ---> Active (local) transactions <---
+ ---> Uncertain transactions <---
+ ---> Active tables <---
+ vegetable : with 2 records occuping 299 words of mem
+ fruit : with 2 records occuping 291 words of mem
+ schema : with 3 records occuping 401 words of mem
+ ===> System info in version "1.1", debug level = none <===
+ opt_disc. Directory "/var/tmp/Mnesia.nonode@nohost" is used.
+ use fallback at restart = false
+ running db nodes = [nonode@nohost]
+ stopped db nodes = []
+ remote = []
+ ram_copies = [fruit,vegetable]
+ disc_copies = [schema]
+ disc_only_copies = []
+ [{nonode@nohost,disc_copies}] = [schema]
+ [{nonode@nohost,ram_copies}] = [fruit,vegetable]
+ 3 transactions committed, 0 aborted, 0 restarted, 2 logged to disc
+ 0 held locks, 0 in queue; 0 local transactions, 0 remote
+ 0 transactions waits for other nodes: []
+ ok
+ 3>
+ ]]></pre>
+ <p>Where we can see that the DBMS was initiated from a
+ regular text file.
+ </p>
+ </section>
+
+ <section>
+ <title>Object Based Programming with Mnesia</title>
+ <p>The Company database introduced in Chapter 2 has three tables
+ which store records (employee, dept, project), and three tables
+ which store relationships (manager, at_dep, in_proj). This is a
+ normalized data model, which has some advantages over a
+ non-normalized data model.
+ </p>
+ <p>It is more efficient to do a
+ generalized search in a normalized database. Some operations are
+ also easier to perform on a normalized data model. For example,
+ we can easily remove one project, as the following example
+ illustrates:
+ </p>
+ <codeinclude file="company.erl" tag="%13" type="erl"></codeinclude>
+ <p>In reality, data models are seldom fully normalized. A
+ realistic alternative to a normalized database model would be
+ a data model which is not even in first normal form. Mnesia
+ is very suitable for applications such as telecommunications,
+ because it is easy to organize data in a very flexible manner. A
+ Mnesia database is always organized as a set of tables. Each
+ table is filled with rows/objects/records. What sets Mnesia
+ apart is that individual fields in a record can contain any type
+ of compound data structures. An individual field in a record can
+ contain lists, tuples, functions, and even record code.
+ </p>
+ <p>Many telecommunications applications have unique requirements
+ on lookup times for certain types of records. If our Company
+ database had been a part of a telecommunications system, then it
+ could be that the lookup time of an employee <em>together</em>
+ with a list of the projects the employee is working on, should
+ be minimized. If this was the case, we might choose a
+ drastically different data model which has no direct
+ relationships. We would only have the records themselves, and
+ different records could contain either direct references to
+ other records, or they could contain other records which are not
+ part of the Mnesia schema.
+ </p>
+ <p>We could create the following record definitions:
+ </p>
+ <codeinclude file="company_o.hrl" tag="%0" type="erl"></codeinclude>
+ <p>An record which describes an employee might look like this:
+ </p>
+ <pre>
+ Me = #employee{emp_no= 104732,
+ name = klacke,
+ salary = 7,
+ sex = male,
+ phone = 99586,
+ room_no = {221, 015},
+ dept = 'B/SFR',
+ projects = [erlang, mnesia, otp],
+ manager = 114872},
+ </pre>
+ <p>This model only has three different tables, and the employee
+ records contain references to other records. We have the following
+ references in the record.
+ </p>
+ <list type="bulleted">
+ <item><c>'B/SFR'</c> refers to a <c>dept</c> record.
+ </item>
+ <item><c>[erlang, mnesia, otp]</c>. This is a list of three
+ direct references to three different <c>projects</c> records.
+ </item>
+ <item><c>114872</c>. This refers to another employee record.
+ </item>
+ </list>
+ <p>We could also use the Mnesia record identifiers (<c>{Tab, Key}</c>)
+ as references. In this case, the <c>dept</c> attribute would be
+ set to the value <c>{dept, 'B/SFR'}</c> instead of
+ <c>'B/SFR'</c>.
+ </p>
+ <p>With this data model, some operations execute considerably
+ faster than they do with the normalized data model in our
+ Company database. On the other hand, some other operations
+ become much more complicated. In particular, it becomes more
+ difficult to ensure that records do not contain dangling
+ pointers to other non-existent, or deleted, records.
+ </p>
+ <p>The following code exemplifies a search with a non-normalized
+ data model. To find all employees at department
+ <c>Dep</c> with a salary higher than <c>Salary</c>, use the following code:
+ </p>
+ <codeinclude file="company_o.erl" tag="%9" type="erl"></codeinclude>
+ <p>This code is not only easier to write and to understand, but it
+ also executes much faster.
+ </p>
+ <p>It is easy to show examples of code which executes faster if
+ we use a non-normalized data model, instead of a normalized
+ model. The main reason for this is that fewer tables are
+ required. For this reason, we can more easily combine data from
+ different tables in join operations. In the above example, the
+ <c>get_emps/2</c> function was transformed from a join operation
+ into a simple query which consists of a selection and a projection
+ on one single table.
+ </p>
+ </section>
+</chapter>
+
diff --git a/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc
new file mode 100644
index 0000000000..7078499fbf
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_chap7.xmlsrc
@@ -0,0 +1,890 @@
+<?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>Mnesia System Information</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></rev>
+ <file>Mnesia_chap7.xml</file>
+ </header>
+
+ <section>
+ <title>Database Configuration Data</title>
+ <p>The following two functions can be used to retrieve system
+ information. They are described in detail in the reference manual.
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:table_info(Tab, Key) -></c><c>Info | exit({aborted, Reason})</c>.
+ Returns information about one table. Such as the
+ current size of the table, on which nodes it resides etc.
+ </item>
+ <item><c>mnesia:system_info(Key) -> </c><c>Info | exit({aborted, Reason})</c>.
+ Returns information about the Mnesia system. For example, transaction
+ statistics, db_nodes, configuration parameters etc.
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Core Dumps</title>
+ <p>If Mnesia malfunctions, system information is dumped to a file
+ named <c>MnesiaCore.Node.When</c>. The type of system
+ information contained in this file can also be generated with
+ the function <c>mnesia_lib:coredump()</c>. If a Mnesia system
+ behaves strangely, it is recommended that a Mnesia core dump
+ file be included in the bug report.</p>
+ </section>
+
+ <section>
+ <title>Dumping Tables</title>
+ <p>Tables of type <c>ram_copies</c> are by definition stored in
+ memory only. It is possible, however, to dump these tables to
+ disc, either at regular intervals, or before the system is
+ shutdown. The function <c>mnesia:dump_tables(TabList)</c> dumps
+ all replicas of a set of RAM tables to disc. The tables can be
+ accessed while being dumped to disc. To dump the tables to
+ disc all replicas must have the storage type <c>ram_copies</c>.
+ </p>
+ <p>The table content is placed in a .DCD file on the
+ disc. When the Mnesia system is started, the RAM table will
+ initially be loaded with data from its .DCD file.
+ </p>
+ </section>
+
+ <section>
+ <marker id="checkpoints"></marker>
+ <title>Checkpoints</title>
+ <p>A checkpoint is a transaction consistent state that spans over
+ one or more tables. When a checkpoint is activated, the system
+ will remember the current content of the set of tables. The
+ checkpoint retains a transaction consistent state of the tables,
+ allowing the tables to be read and updated while the checkpoint
+ is active. A checkpoint is typically used to
+ back up tables to external media, but they are also used
+ internally in Mnesia for other purposes. Each checkpoint is
+ independent and a table may be involved in several checkpoints
+ simultaneously.
+ </p>
+ <p>Each table retains its old contents in a checkpoint retainer
+ and for performance critical applications, it may be important
+ to realize the processing overhead associated with checkpoints.
+ In a worst case scenario, the checkpoint retainer will consume
+ even more memory than the table itself. Each update will also be
+ slightly slower on those nodes where checkpoint
+ retainers are attached to the tables.
+ </p>
+ <p>For each table it is possible to choose if there should be one
+ checkpoint retainer attached to all replicas of the table, or if
+ it is enough to have only one checkpoint retainer attached to a
+ single replica. With a single checkpoint retainer per table, the
+ checkpoint will consume less memory, but it will be vulnerable
+ to node crashes. With several redundant checkpoint retainers the
+ checkpoint will survive as long as there is at least one active
+ checkpoint retainer attached to each table.
+ </p>
+ <p>Checkpoints may be explicitly deactivated with the function
+ <c>mnesia:deactivate_checkpoint(Name)</c>, where <c>Name</c> is
+ the name of an active checkpoint. This function returns
+ <c>ok</c> if successful, or <c>{error, Reason}</c> in the case
+ of an error. All tables in a checkpoint must be attached to at
+ least one checkpoint retainer. The checkpoint is automatically
+ de-activated by Mnesia, when any table lacks a checkpoint
+ retainer. This may happen when a node goes down or when a
+ replica is deleted. Use the <c>min</c> and
+ <c>max</c> arguments described below, to control the degree of
+ checkpoint retainer redundancy.
+ </p>
+ <p>Checkpoints are activated with the function <marker id="mnesia:chkpt(Args)"></marker>
+<c>mnesia:activate_checkpoint(Args)</c>,
+ where <c>Args</c> is a list of the following tuples:
+ </p>
+ <list type="bulleted">
+ <item><c>{name,Name}</c>. <c>Name</c> specifies a temporary name
+ of the checkpoint. The name may be re-used when the checkpoint
+ has been de-activated. If no name is specified, a name is
+ generated automatically.
+ </item>
+ <item><c>{max,MaxTabs}</c>. <c>MaxTabs</c> is a list of tables
+ which will be included in the checkpoint. The default is
+ <c>[]</c> (an empty list). For these tables, the redundancy
+ will be maximized. The old contents of the table will be
+ retained in the checkpoint retainer when the main table is
+ updated by the applications. The checkpoint becomes more fault
+ tolerant if the tables have several replicas. When new
+ replicas are added by means of the schema manipulation
+ function <c>mnesia:add_table_copy/3</c>, it will also
+ attach a local checkpoint retainer.
+ </item>
+ <item><c>{min,MinTabs}</c>. <c>MinTabs</c> is a list of tables
+ that should be included in the checkpoint. The default is
+ <c>[]</c>. For these tables, the redundancy will be minimized,
+ and there will be a single checkpoint retainer per table,
+ preferably at the local node.
+ </item>
+ <item><c>{allow_remote,Bool}</c>. <c>false</c> means that all
+ checkpoint retainers must be local. If a table does not reside
+ locally, the checkpoint cannot be activated. <c>true</c>
+ allows checkpoint retainers to be allocated on any node. The
+ defaults is <c>true</c>.
+ </item>
+ <item><c>{ram_overrides_dump,Bool}</c>. This argument only
+ applies to tables of type <c>ram_copies</c>. <c>Bool</c>
+ specifies if the table state in RAM should override the table
+ state on disc. <c>true</c> means that the latest committed
+ records in RAM are included in the checkpoint retainer. These
+ are the records that the application accesses. <c>false</c>
+ means that the records on the disc .DAT file are
+ included in the checkpoint retainer. These are the records
+ that will be loaded on start-up. Default is <c>false</c>.</item>
+ </list>
+ <p>The <c>mnesia:activate_checkpoint(Args)</c> returns one of the
+ following values:
+ </p>
+ <list type="bulleted">
+ <item><c>{ok, Name, Nodes}</c></item>
+ <item><c>{error, Reason}</c>.</item>
+ </list>
+ <p><c>Name</c> is the name of the checkpoint, and <c>Nodes</c> are
+ the nodes where the checkpoint is known.
+ </p>
+ <p>A list of active checkpoints can be obtained with the following
+ functions:
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:system_info(checkpoints)</c>. This function
+ returns all active checkpoints on the current node.</item>
+ <item><c>mnesia:table_info(Tab,checkpoints)</c>. This function
+ returns active checkpoints on a specific table.</item>
+ </list>
+ </section>
+
+ <section>
+ <title>Files</title>
+ <p>This section describes the internal files which are created and maintained by the Mnesia system,
+ in particular, the workings of the Mnesia log is described.
+ </p>
+
+ <section>
+ <title>Start-Up Files</title>
+ </section>
+ <p>In Chapter 3 we detailed the following pre-requisites for
+ starting Mnesia (refer Chapter 3: <seealso marker="Mnesia_chap3#start_mnesia">Starting Mnesia</seealso>:
+ </p>
+ <list type="bulleted">
+ <item>We must start an Erlang session and specify a Mnesia
+ directory for our database.
+ </item>
+ <item>We must initiate a database schema, using the function
+ <c>mnesia:create_schema/1</c>.
+ </item>
+ </list>
+ <p>The following example shows how these tasks are performed:
+ </p>
+ <list type="ordered">
+ <item>
+ <pre>
+% <input>erl -sname klacke -mnesia dir '"/ldisc/scratch/klacke"'</input> </pre>
+ </item>
+ <item>
+ <pre>
+Erlang (BEAM) emulator version 4.9
+
+Eshell V4.9 (abort with ^G)
+(klacke@gin)1> <input>mnesia:create_schema([node()]).</input>
+ok
+(klacke@gin)2>
+<input>^Z</input>
+Suspended </pre>
+ <p>We can inspect the Mnesia directory to see what files have been created. Enter the following command:
+ </p>
+ <pre>
+% <input>ls -l /ldisc/scratch/klacke</input>
+-rw-rw-r-- 1 klacke staff 247 Aug 12 15:06 FALLBACK.BUP </pre>
+ <p>The response shows that the file FALLBACK.BUP has been created. This is called a backup file, and it contains an initial schema. If we had specified more than one node in the <c>mnesia:create_schema/1</c> function, identical backup files would have been created on all nodes.
+ </p>
+ </item>
+ <item>
+ <p>Continue by starting Mnesia:</p>
+ <pre>
+(klacke@gin)3><input>mnesia:start( ).</input>
+ok </pre>
+ <p>We can now see the following listing in the Mnesia directory:
+ </p>
+ <pre>
+-rw-rw-r-- 1 klacke staff 86 May 26 19:03 LATEST.LOG
+-rw-rw-r-- 1 klacke staff 34507 May 26 19:03 schema.DAT </pre>
+ <p>The schema in the backup file FALLBACK.BUP has been used to generate the file <c>schema.DAT.</c> Since we have no other disc resident tables than the schema, no other data files were created. The file FALLBACK.BUP was removed after the successful "restoration". We also see a number of files that are for internal use by Mnesia.
+ </p>
+ </item>
+ <item>
+ <p>Enter the following command to create a table:</p>
+ <pre>
+(klacke@gin)4> <input>mnesia:create_table(foo,[{disc_copies, [node()]}]).</input>
+{atomic,ok} </pre>
+ <p>We can now see the following listing in the Mnesia directory:
+ </p>
+ <pre>
+% <input>ls -l /ldisc/scratch/klacke</input>
+-rw-rw-r-- 1 klacke staff 86 May 26 19:07 LATEST.LOG
+-rw-rw-r-- 1 klacke staff 94 May 26 19:07 foo.DCD
+-rw-rw-r-- 1 klacke staff 6679 May 26 19:07 schema.DAT </pre>
+ <p>Where a file <c>foo.DCD</c> has been created. This file will eventually store
+ all data that is written into the <c>foo</c> table.</p>
+ </item>
+ </list>
+
+ <section>
+ <title>The Log File</title>
+ <p>When starting Mnesia, a .LOG file called <c>LATEST.LOG</c>
+ was created and placed in the database directory. This file is
+ used by Mnesia to log disc based transactions. This includes all
+ transactions that write at least one record in a table which is
+ of storage type <c>disc_copies</c>, or
+ <c>disc_only_copies</c>. It also includes all operations which
+ manipulate the schema itself, such as creating new tables. The
+ format of the log can vary with different implementations of
+ Mnesia. The Mnesia log is currently implemented with the
+ standard library module <c>disc_log</c>.
+ </p>
+ <p>The log file will grow continuously and must be dumped at
+ regular intervals. "Dumping the log file" means that Mnesia will
+ perform all the operations listed in the log and place the
+ records in the corresponding .DAT, .DCD and .DCL data files. For
+ example, if the operation "write record <c>{foo, 4, elvis, 6}</c>"
+ is listed in the log, Mnesia inserts the operation into the
+ file <c>foo.DCL</c>, later when Mnesia thinks the .DCL has become to large
+ the data is moved to the .DCD file.
+ The dumping operation can be time consuming
+ if the log is very large. However, it is important to realize
+ that the Mnesia system continues to operate during log dumps.
+ </p>
+ <p>By default Mnesia either dumps the log whenever 100 records have
+ been written in the log or when 3 minutes have passed.
+ This is controlled by the two application parameters
+ <c>-mnesia dump_log_write_threshold WriteOperations</c> and
+ <c>-mnesia dump_log_time_threshold MilliSecs</c>.
+ </p>
+ <p>Before the log is dumped, the file <c>LATEST.LOG</c> is
+ renamed to <c>PREVIOUS.LOG</c>, and a new <c>LATEST.LOG</c> file
+ is created. Once the log has been successfully dumped, the file
+ <c>PREVIOUS.LOG</c> is deleted.
+ </p>
+ <p>The log is also dumped at start-up and whenever a schema
+ operation is performed.
+ </p>
+ </section>
+
+ <section>
+ <title>The Data Files</title>
+ <p>The directory listing also contains one .DAT file. This contain
+ the schema itself, contained in the <c>schema.DAT</c>
+ file. The DAT files are indexed files, and it is efficient to
+ insert and search for records in these files with a specific
+ key. The .DAT files are used for the schema and for <c>disc_only_copies</c>
+ tables. The Mnesia data files are currently implemented with the
+ standard library module <c>dets</c>, and all operations which
+ can be performed on <c>dets</c> files can also be performed on
+ the Mnesia data files. For example, <c>dets</c> contains a
+ function <c>dets:traverse/2</c> which can be used to view the
+ contents of a Mnesia DAT file. However, this can only be done
+ when Mnesia is not running. So, to view a our schema file, we
+ can: </p>
+ <pre>
+{ok, N} = dets:open_file(schema, [{file, "./schema.DAT"},{repair,false},
+{keypos, 2}]),
+F = fun(X) -> io:format("~p~n", [X]), continue end,
+dets:traverse(N, F),
+dets:close(N). </pre>
+ <note>
+ <p>Refer to the Reference Manual, <c>std_lib</c> for information about <c>dets</c>.</p>
+ </note>
+ <warning>
+ <p>The DAT files must always be opened with the <c>{repair, false}</c>
+ option. This ensures that these files are not
+ automatically repaired. Without this option, the database may
+ become inconsistent, because Mnesia may
+ believe that the files were properly closed. Refer to the reference
+ manual for information about the configuration parameter
+ <c>auto_repair</c>.</p>
+ </warning>
+ <warning>
+ <p>It is recommended that Data files are not tampered with while Mnesia is
+ running. While not prohibited, the behavior of Mnesia is unpredictable. </p>
+ </warning>
+ <p>The <c>disc_copies</c> tables are stored on disk with .DCL and .DCD files,
+ which are standard disk_log files.
+ </p>
+ </section>
+ </section>
+
+ <section>
+ <title>Loading of Tables at Start-up</title>
+ <p>At start-up Mnesia loads tables in order to make them accessible
+ for its applications. Sometimes Mnesia decides to load all tables
+ that reside locally, and sometimes the tables may not be
+ accessible until Mnesia brings a copy of the table
+ from another node.
+ </p>
+ <p>To understand the behavior of Mnesia at start-up it is
+ essential to understand how Mnesia reacts when it loses contact
+ with Mnesia on another node. At this stage, Mnesia cannot distinguish
+ between a communication failure and a "normal" node down. <br></br>
+
+ When this happens, Mnesia will assume that the other node is no longer running.
+ Whereas, in reality, the communication between the nodes has merely failed.
+ </p>
+ <p>To overcome this situation, simply try to restart the ongoing transactions that are
+ accessing tables on the failing node, and write a <c>mnesia_down</c> entry to a log file.
+ </p>
+ <p>At start-up, it must be noted that all tables residing on nodes
+ without a <c>mnesia_down</c> entry, may have fresher replicas.
+ Their replicas may have been updated after the termination
+ of Mnesia on the current node. In order to catch up with the latest
+ updates, transfer a copy of the table from one of these other
+ "fresh" nodes. If you are unlucky, other nodes may be down
+ and you must wait for the table to be
+ loaded on one of these nodes before receiving a fresh copy of
+ the table.
+ </p>
+ <p>Before an application makes its first access to a table,
+ <c>mnesia:wait_for_tables(TabList, Timeout)</c> ought to be executed
+ to ensure that the table is accessible from the local node. If
+ the function times out the application may choose to force a
+ load of the local replica with
+ <c>mnesia:force_load_table(Tab)</c> and deliberately lose all
+ updates that may have been performed on the other nodes while
+ the local node was down. If
+ Mnesia already has loaded the table on another node or intends
+ to do so, we will copy the table from that node in order to
+ avoid unnecessary inconsistency.
+ </p>
+ <warning>
+ <p>Keep in mind that it is only
+ one table that is loaded by <c>mnesia:force_load_table(Tab)</c>
+ and since committed transactions may have caused updates in
+ several tables, the tables may now become inconsistent due to
+ the forced load.</p>
+ </warning>
+ <p>The allowed <c>AccessMode</c> of a table may be defined to
+ either be <c>read_only</c> or <c>read_write</c>. And it may be
+ toggled with the function <c>mnesia:change_table_access_mode(Tab, AccessMode)</c> in runtime. <c>read_only</c> tables and
+ <c>local_content</c> tables will always be loaded locally, since
+ there are no need for copying the table from other nodes. Other
+ tables will primary be loaded remotely from active replicas on
+ other nodes if the table already has been loaded there, or if
+ the running Mnesia already has decided to load the table there.
+ </p>
+ <p>At start up, Mnesia will assume that its local replica is the
+ most recent version and load the table from disc if either
+ situation is detected:
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia_down</c> is returned from all other nodes that holds a disc
+ resident replica of the table; or,</item>
+ <item>if all replicas are <c>ram_copies</c></item>
+ </list>
+ <p>This is normally a wise decision, but it may turn out to
+ be disastrous if the nodes have been disconnected due to a
+ communication failure, since Mnesia's normal table load
+ mechanism does not cope with communication failures.
+ </p>
+ <p>When Mnesia is loading many tables the default load
+ order. However, it is possible to
+ affect the load order by explicitly changing the
+ <c>load_order</c> property for the tables, with the function
+ <c>mnesia:change_table_load_order(Tab, LoadOrder)</c>. The
+ <c>LoadOrder</c> is by default <c>0</c> for all tables, but it
+ can be set to any integer. The table with the highest
+ <c>load_order</c> will be loaded first. Changing load order is
+ especially useful for applications that need to ensure early
+ availability of fundamental tables. Large peripheral
+ tables should have a low load order value, perhaps set
+ below 0.
+ </p>
+ </section>
+
+ <section>
+ <title>Recovery from Communication Failure</title>
+ <p>There are several occasions when Mnesia may detect that the
+ network has been partitioned due to a communication failure.
+ </p>
+ <p>One is when Mnesia already is up and running and the Erlang
+ nodes gain contact again. Then Mnesia will try to contact Mnesia
+ on the other node to see if it also thinks that the network has
+ been partitioned for a while. If Mnesia on both nodes has logged
+ <c>mnesia_down</c> entries from each other, Mnesia generates a
+ system event, called <c>{inconsistent_database, running_partitioned_network, Node}</c> which is sent to Mnesia's
+ event handler and other possible subscribers. The default event
+ handler reports an error to the error logger.
+ </p>
+ <p>Another occasion when Mnesia may detect that the network has
+ been partitioned due to a communication failure, is at start-up.
+ If Mnesia detects that both the local node and another node received
+ <c>mnesia_down</c> from each other it generates a
+ <c>{inconsistent_database, starting_partitioned_network, Node}</c> system event and acts as described above.
+ </p>
+ <p>If the application detects that there has been a communication
+ failure which may have caused an inconsistent database, it may
+ use the function <c>mnesia:set_master_nodes(Tab, Nodes)</c> to
+ pinpoint from which nodes each table may be loaded.</p>
+ <p>At start-up Mnesia's normal table load algorithm will be
+ bypassed and the table will be loaded from one of the master
+ nodes defined for the table, regardless of potential
+ <c>mnesia_down</c> entries in the log. The <c>Nodes</c> may only
+ contain nodes where the table has a replica and if it is empty,
+ the master node recovery mechanism for the particular table will
+ be reset and the normal load mechanism will be used when next
+ restarting.
+ </p>
+ <p>The function <c>mnesia:set_master_nodes(Nodes)</c> sets master
+ nodes for all tables. For each table it will determine its
+ replica nodes and invoke <c>mnesia:set_master_nodes(Tab, TabNodes)</c> with those replica nodes that are included in the
+ <c>Nodes</c> list (i.e. <c>TabNodes</c> is the intersection of
+ <c>Nodes</c> and the replica nodes of the table). If the
+ intersection is empty the master node recovery mechanism for the
+ particular table will be reset and the normal load mechanism
+ will be used at next restart.
+ </p>
+ <p>The functions <c>mnesia:system_info(master_node_tables)</c> and
+ <c>mnesia:table_info(Tab, master_nodes)</c> may be used to
+ obtain information about the potential master nodes.
+ </p>
+ <p>The function <c>mnesia:force_load_table(Tab)</c> may be used to
+ force load the table regardless of which table load mechanism
+ is activated.
+ </p>
+ </section>
+
+ <section>
+ <title>Recovery of Transactions</title>
+ <p>A Mnesia table may reside on one or more nodes. When a table is
+ updated, Mnesia will ensure that the updates will be replicated
+ to all nodes where the table resides. If a replica happens to be
+ inaccessible for some reason (e.g. due to a temporary node down),
+ Mnesia will then perform the replication later.
+ </p>
+ <p>On the node where the application is started, there will be a
+ transaction coordinator process. If the transaction is
+ distributed, there will also be a transaction participant process on
+ all the other nodes where commit work needs to be performed.
+ </p>
+ <p>Internally Mnesia uses several commit protocols. The selected
+ protocol depends on which table that has been updated in
+ the transaction. If all the involved tables are symmetrically
+ replicated, (i.e. they all have the same <c>ram_nodes</c>,
+ <c>disc_nodes</c> and <c>disc_only_nodes</c> currently
+ accessible from the coordinator node), a lightweight transaction
+ commit protocol is used.
+ </p>
+ <p>The number of messages that the
+ transaction coordinator and its participants needs to exchange
+ is few, since Mnesia's table load mechanism takes care of the
+ transaction recovery if the commit protocol gets
+ interrupted. Since all involved tables are replicated
+ symmetrically the transaction will automatically be recovered by
+ loading the involved tables from the same node at start-up of a
+ failing node. We do not really care if the transaction was
+ aborted or committed as long as we can ensure the ACID
+ properties. The lightweight commit protocol is non-blocking,
+ i.e. the surviving participants and their coordinator will
+ finish the transaction, regardless of some node crashes in the
+ middle of the commit protocol or not.
+ </p>
+ <p>If a node goes down in the middle of a dirty operation the
+ table load mechanism will ensure that the update will be
+ performed on all replicas or none. Both asynchronous dirty
+ updates and synchronous dirty updates use the same recovery
+ principle as lightweight transactions.
+ </p>
+ <p>If a transaction involves updates of asymmetrically replicated
+ tables or updates of the schema table, a heavyweight commit
+ protocol will be used. The heavyweight commit protocol is able
+ to finish the transaction regardless of how the tables are
+ replicated. The typical usage of a heavyweight transaction is
+ when we want to move a replica from one node to another. Then we
+ must ensure that the replica either is entirely moved or left as
+ it was. We must never end up in a situation with replicas on both
+ nodes or no node at all. Even if a node crashes in the middle of
+ the commit protocol, the transaction must be guaranteed to be
+ atomic. The heavyweight commit protocol involves more messages
+ between the transaction coordinator and its participants than
+ a lightweight protocol and it will perform recovery work at
+ start-up in order to finish the abort or commit work.
+ </p>
+ <p>The heavyweight commit protocol is also non-blocking,
+ which allows the surviving participants and their coordinator to
+ finish the transaction regardless (even if a node crashes in the
+ middle of the commit protocol). When a node fails at start-up,
+ Mnesia will determine the outcome of the transaction and
+ recover it. Lightweight protocols, heavyweight protocols and dirty updates, are
+ dependent on other nodes to be up and running in order to make the
+ correct heavyweight transaction recovery decision.
+ </p>
+ <p>If Mnesia has not started on some of the nodes that are involved in the
+ transaction AND neither the local node or any of the already
+ running nodes know the outcome of the transaction, Mnesia will
+ by default wait for one. In the worst case scenario all other
+ involved nodes must start before Mnesia can make the correct decision
+ about the transaction and finish its start-up.
+ </p>
+ <p>This means that Mnesia (on one node)may hang if a double fault occurs, i.e. when two nodes crash simultaneously
+ and one attempts to start when the other refuses to
+ start e.g. due to a hardware error.
+ </p>
+ <p>It is possible to specify the maximum time that Mnesia
+ will wait for other nodes to respond with a transaction
+ recovery decision. The configuration parameter
+ <c>max_wait_for_decision</c> defaults to infinity (which may
+ cause the indefinite hanging as mentioned above) but if it is
+ set to a definite time period (eg.three minutes), Mnesia will then enforce a
+ transaction recovery decision if needed, in order to allow
+ Mnesia to continue with its start-up procedure. </p>
+ <p>The downside of an enforced transaction recovery decision, is that the decision may be
+ incorrect, due to insufficient information regarding the other nodes'
+ recovery decisions. This may result in an
+ inconsistent database where Mnesia has committed the transaction
+ on some nodes but aborted it on others. </p>
+ <p>In fortunate cases the inconsistency will only appear in tables belonging to a specific
+ application, but if a schema transaction has been inconsistently
+ recovered due to the enforced transaction recovery decision, the
+ effects of the inconsistency can be fatal.
+ However, if the higher priority is availability rather than
+ consistency, then it may be worth the risk. </p>
+ <p>If Mnesia
+ encounters a inconsistent transaction decision a
+ <c>{inconsistent_database, bad_decision, Node}</c> system event
+ will be generated in order to give the application a chance to
+ install a fallback or other appropriate measures to resolve the inconsistency. The default
+ behavior of the Mnesia event handler is the same as if the
+ database became inconsistent as a result of partitioned network (see
+ above).
+ </p>
+ </section>
+
+ <section>
+ <title>Backup, Fallback, and Disaster Recovery</title>
+ <p>The following functions are used to backup data, to install a
+ backup as fallback, and for disaster recovery.
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:backup_checkpoint(Name, Opaque, [Mod])</c>. This
+ function performs a backup of the tables included in the
+ checkpoint.
+ </item>
+ <item><c>mnesia:backup(Opaque, [Mod])</c>. This function
+ activates a new checkpoint which covers all Mnesia tables and
+ performs a backup. It is performed with maximum degree of
+ redundancy (also refer to the function <seealso marker="#checkpoints">mnesia:activate_checkpoint(Args)</seealso>,
+ <c>{max, MaxTabs} and {min, MinTabs}).</c></item>
+ <item><c>mnesia:traverse_backup(Source,[SourceMod,]</c><c>Target,[TargetMod,]Fun,Ac)</c>. This function can be used
+ to read an existing backup, create a new backup from an
+ existing one, or to copy a backup from one type media to
+ another.
+ </item>
+ <item><c>mnesia:uninstall_fallback()</c>. This function removes
+ previously installed fallback files.
+ </item>
+ <item><c>mnesia:restore(Opaque, Args)</c>. This function
+ restores a set of tables from a previous backup.
+ </item>
+ <item><c>mnesia:install_fallback(Opaque, [Mod])</c>. This
+ function can be configured to restart the Mnesia and reload data
+ tables, and possibly schema tables, from an existing
+ backup. This function is typically used for disaster recovery
+ purposes, when data or schema tables are corrupted.</item>
+ </list>
+ <p>These functions are explained in the following
+ sub-sections. Also refer to the the section <seealso marker="#checkpoints">Checkpoints</seealso> in this chapter, which
+ describes the two functions used to activate and de-activate
+ checkpoints.
+ </p>
+
+ <section>
+ <title>Backup</title>
+ <p>Backup operation are performed with the following functions:
+ </p>
+ <list type="bulleted">
+ <item><c>mnesia:backup_checkpoint(Name, Opaque, [Mod])</c></item>
+ <item><c>mnesia:backup(Opaque, [Mod])</c></item>
+ <item><c>mnesia:traverse_backup(Source, [SourceMod,],</c><c>Target,[TargetMod,]Fun,Acc)</c>.</item>
+ </list>
+ <p>By default, the actual access to the backup media is
+ performed via the <c>mnesia_backup</c> module for both read
+ and write. Currently <c>mnesia_backup</c> is implemented with
+ the standard library module <c>disc_log</c>, but it is possible to write
+ your own module with the same interface as
+ <c>mnesia_backup</c> and configure Mnesia so the alternate
+ module performs the actual accesses to the backup media. This
+ means that the user may put the backup on medias that Mnesia
+ does not know about, possibly on hosts where Erlang is not
+ running. Use the configuration parameter <c><![CDATA[-mnesia backup_module <module>]]></c> for this purpose. </p>
+ <p>The source
+ for a backup is an activated checkpoint. The backup function
+ most commonly used is <c>mnesia:backup_checkpoint(Name, Opaque,[Mod])</c>. This function returns either <c>ok</c>, or
+ <c>{error,Reason}</c>. It has the following arguments:
+ </p>
+ <list type="bulleted">
+ <item><c>Name</c> is the name of an activated
+ checkpoint. Refer to the section <seealso marker="#checkpoints">Checkpoints</seealso> in this chapter, the
+ function <c>mnesia:activate_checkpoint(ArgList)</c> for
+ details on how to include table names in checkpoints.
+ </item>
+ <item><c>Opaque</c>. Mnesia does not interpret this argument,
+ but it is forwarded to the backup module. The Mnesia default
+ backup module, <c>mnesia_backup</c> interprets this argument
+ as a local file name.
+ </item>
+ <item><c>Mod</c>. The name of an alternate backup module.
+ </item>
+ </list>
+ <p>The function <c>mnesia:backup(Opaque[, Mod])</c> activates a
+ new checkpoint which covers all Mnesia tables with maximum
+ degree of redundancy and performs a backup. Maximum
+ redundancy means that each table replica has a checkpoint
+ retainer. Tables with the <c>local_contents</c> property are
+ backed up as they
+ look on the current node.
+ </p>
+ <p>It is possible to iterate over a backup, either for the
+ purpose of transforming it into a new backup, or just reading
+ it. The function <c>mnesia:traverse_backup(Source, [SourceMod,]</c><c>Target, [TargeMod,] Fun, Acc)</c> which normally returns <c>{ok, LastAcc}</c>, is used for both of these purposes.
+ </p>
+ <p>Before the traversal starts, the source backup media is
+ opened with <c>SourceMod:open_read(Source)</c>, and the target
+ backup media is opened with
+ <c>TargetMod:open_write(Target)</c>. The arguments are:
+ </p>
+ <list type="bulleted">
+ <item><c>SourceMod</c> and <c>TargetMod</c> are module names.
+ </item>
+ <item><c>Source</c> and <c>Target</c> are opaque data used
+ exclusively by the modules <c>SourceMod</c> and
+ <c>TargetMod</c> for the purpose of initializing the backup
+ medias.
+ </item>
+ <item><c>Acc</c> is an initial accumulator value.
+ </item>
+ <item><c>Fun(BackupItems, Acc)</c> is applied to each item in
+ the backup. The Fun must return a tuple <c>{ValGoodBackupItems, NewAcc}</c>, where <c>ValidBackupItems</c> is a list of valid
+ backup items, and <c>NewAcc</c> is a new accumulator value.
+ The <c>ValidBackupItems</c> are written to the target backup
+ with the function <c>TargetMod:write/2</c>.
+ </item>
+ <item><c>LastAcc</c> is the last accumulator value. I.e.
+ the last <c>NewAcc</c> value that was returned by <c>Fun</c>.
+ </item>
+ </list>
+ <p>It is also possible to perform a read-only traversal of the
+ source backup without updating a target backup. If
+ <c>TargetMod==read_only</c>, then no target backup is accessed
+ at all.
+ </p>
+ <p>By setting <c>SourceMod</c> and <c>TargetMod</c> to different
+ modules it is possible to copy a backup from one kind of backup
+ media to another.
+ </p>
+ <p>Valid <c>BackupItems</c> are the following tuples:
+ </p>
+ <list type="bulleted">
+ <item><c>{schema, Tab}</c> specifies a table to be deleted.
+ </item>
+ <item><c>{schema, Tab, CreateList}</c> specifies a table to be
+ created. See <c>mnesia_create_table/2</c> for more
+ information about <c>CreateList</c>.
+ </item>
+ <item><c>{Tab, Key}</c> specifies the full identity of a record
+ to be deleted.
+ </item>
+ <item><c>{Record}</c> specifies a record to be inserted. It
+ can be a tuple with <c>Tab</c> as first field. Note that the
+ record name is set to the table name regardless of what
+ <c>record_name</c> is set to.
+ </item>
+ </list>
+ <p>The backup data is divided into two sections. The first
+ section contains information related to the schema. All schema
+ related items are tuples where the first field equals the atom
+ schema. The second section is the record section. It is not
+ possible to mix schema records with other records and all schema
+ records must be located first in the backup.
+ </p>
+ <p>The schema itself is a table and will possibly be included in
+ the backup. All nodes where the schema table resides are
+ regarded as a <c>db_node</c>.
+ </p>
+ <p>The following example illustrates how
+ <c>mnesia:traverse_backup</c> can be used to rename a db_node in
+ a backup file:
+ </p>
+ <codeinclude file="bup.erl" tag="%0" type="erl"></codeinclude>
+ </section>
+
+ <section>
+ <title>Restore</title>
+ <p>Tables can be restored on-line from a backup without
+ restarting Mnesia. A restore is performed with the function
+ <c>mnesia:restore(Opaque,Args)</c>, where <c>Args</c> can
+ contain the following tuples:
+ </p>
+ <list type="bulleted">
+ <item><c>{module,Mod}</c>. The backup module <c>Mod</c> is
+ used to access the backup media. If omitted, the default
+ backup module will be used.</item>
+ <item><c>{skip_tables, TableList}</c> Where <c>TableList</c>
+ is a list of tables which should not be read from the backup.</item>
+ <item><c>{clear_tables, TableList}</c> Where <c>TableList</c>
+ is a list of tables which should be cleared, before the
+ records from the backup are inserted, i.e. all records in
+ the tables are deleted before the tables are restored.
+ Schema information about the tables is not cleared or read
+ from backup.</item>
+ <item><c>{keep_tables, TableList}</c> Where <c>TableList</c>
+ is a list of tables which should be not be cleared, before
+ the records from the backup are inserted, i.e. the records
+ in the backup will be added to the records in the table.
+ Schema information about the tables is not cleared or read
+ from backup.</item>
+ <item><c>{recreate_tables, TableList}</c> Where <c>TableList</c>
+ is a list of tables which should be re-created, before the
+ records from the backup are inserted. The tables are first
+ deleted and then created with the schema information from the
+ backup. All the nodes in the backup needs to be up and running.</item>
+ <item><c>{default_op, Operation}</c> Where <c>Operation</c> is
+ one of the following operations <c>skip_tables</c>,
+ <c>clear_tables</c>, <c>keep_tables</c> or
+ <c>recreate_tables</c>. The default operation specifies
+ which operation should be used on tables from the backup
+ which are not specified in any of the lists above.
+ If omitted, the operation <c>clear_tables</c> will be used. </item>
+ </list>
+ <p>The argument <c>Opaque</c> is forwarded to the backup module.
+ It returns <c>{atomic, TabList}</c> if successful, or the
+ tuple <c>{aborted, Reason}</c> in the case of an error.
+ <c>TabList</c> is a list of the restored tables. Tables which
+ are restored are write locked for the duration of the restore
+ operation. However, regardless of any lock conflict caused by
+ this, applications can continue to do their work during the
+ restore operation.
+ </p>
+ <p>The restoration is performed as a single transaction. If the
+ database is very large, it may not be possible to restore it
+ online. In such a case the old database must be restored by
+ installing a fallback, and then restart.
+ </p>
+ </section>
+
+ <section>
+ <title>Fallbacks</title>
+ <p>The function <c>mnesia:install_fallback(Opaque, [Mod])</c> is
+ used to install a backup as fallback. It uses the backup module
+ <c>Mod</c>, or the default backup module, to access the backup
+ media. This function returns <c>ok</c> if successful, or
+ <c>{error, Reason}</c> in the case of an error.
+ </p>
+ <p>Installing a fallback is a distributed operation that is
+ <em>only</em> performed on all <c>db_nodes</c>. The fallback
+ is used to restore the database the next time the system is
+ started. If a Mnesia node with a fallback installed detects that
+ Mnesia on another node has died for some reason, it will
+ unconditionally terminate itself.
+ </p>
+ <p>A fallback is typically used when a system upgrade is
+ performed. A system typically involves the installation of new
+ software versions, and Mnesia tables are often transformed into
+ new layouts. If the system crashes during an upgrade, it is
+ highly probable re-installation of the old
+ applications will be required and restoration of the database
+ to its previous state. This can be done if a backup is performed and
+ installed as a fallback before the system upgrade begins.
+ </p>
+ <p>If the system upgrade fails, Mnesia must be restarted on all
+ <c>db_nodes</c> in order to restore the old database. The
+ fallback will be automatically de-installed after a successful
+ start-up. The function <c>mnesia:uninstall_fallback()</c> may
+ also be used to de-install the fallback after a
+ successful system upgrade. Again, this is a distributed
+ operation that is either performed on all <c>db_nodes</c>, or
+ none. Both the installation and de-installation of fallbacks
+ require Erlang to be up and running on all <c>db_nodes</c>, but
+ it does not matter if Mnesia is running or not.
+ </p>
+ </section>
+
+ <section>
+ <title>Disaster Recovery</title>
+ <p>The system may become inconsistent as a result of a power
+ failure. The UNIX <c>fsck</c> feature can possibly repair the
+ file system, but there is no guarantee that the file contents
+ will be consistent.
+ </p>
+ <p>If Mnesia detects that a file has not been properly closed,
+ possibly as a result of a power failure, it will attempt to
+ repair the bad file in a similar manner. Data may be lost, but
+ Mnesia can be restarted even if the data is inconsistent. The
+ configuration parameter <c><![CDATA[-mnesia auto_repair <bool>]]></c> can be
+ used to control the behavior of Mnesia at start-up. If
+ <c><![CDATA[<bool>]]></c> has the value <c>true</c>, Mnesia will attempt to
+ repair the file; if <c><![CDATA[<bool>]]></c> has the value <c>false</c>,
+ Mnesia will not restart if it detects a suspect file. This
+ configuration parameter affects the repair behavior of log
+ files, DAT files, and the default backup media.
+ </p>
+ <p>The configuration parameter <c><![CDATA[-mnesia dump_log_update_in_place <bool>]]></c> controls the safety level of
+ the <c>mnesia:dump_log()</c> function. By default, Mnesia will
+ dump the transaction log directly into the DAT files. If a power
+ failure happens during the dump, this may cause the randomly
+ accessed DAT files to become corrupt. If the parameter is set to
+ <c>false</c>, Mnesia will copy the DAT files and target the dump
+ to the new temporary files. If the dump is successful, the
+ temporary files will be renamed to their normal DAT
+ suffixes. The possibility for unrecoverable inconsistencies in
+ the data files will be much smaller with this strategy. On the
+ other hand, the actual dumping of the transaction log will be
+ considerably slower. The system designer must decide whether
+ speed or safety is the higher priority.
+ </p>
+ <p>Replicas of type <c>disc_only_copies</c> will only be
+ affected by this parameter during the initial dump of the log
+ file at start-up. When designing applications which have
+ <em>very</em> high requirements, it may be appropriate not to
+ use <c>disc_only_copies</c> tables at all. The reason for this
+ is the random access nature of normal operating system files. If
+ a node goes down for reason for a reason such as a power
+ failure, these files may be corrupted because they are not
+ properly closed. The DAT files for <c>disc_only_copies</c> are
+ updated on a per transaction basis.
+ </p>
+ <p>If a disaster occurs and the Mnesia database has been
+ corrupted, it can be reconstructed from a backup. This should be
+ regarded as a last resort, since the backup contains old data. The
+ data is hopefully consistent, but data will definitely be lost
+ when an old backup is used to restore the database.
+ </p>
+ </section>
+ </section>
+</chapter>
+
diff --git a/lib/mnesia/doc/src/Mnesia_chap8.xml b/lib/mnesia/doc/src/Mnesia_chap8.xml
new file mode 100644
index 0000000000..3d2e23cf57
--- /dev/null
+++ b/lib/mnesia/doc/src/Mnesia_chap8.xml
@@ -0,0 +1,64 @@
+<?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>Combining Mnesia with SNMP</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></rev>
+ <file>Mnesia_chap8.xml</file>
+ </header>
+
+ <section>
+ <title>Combining Mnesia and SNMP </title>
+ <p>Many telecommunications applications must be controlled and
+ reconfigured remotely. It is sometimes an advantage to perform
+ this remote control with an open protocol such as the Simple
+ Network Management Protocol (SNMP). The alternatives to this would
+ be:
+ </p>
+ <list type="bulleted">
+ <item>Not being able to control the application remotely at all.
+ </item>
+ <item>Using a proprietary control protocol.
+ </item>
+ <item>Using a bridge which maps control messages in a
+ proprietary protocol to a standardized management protocol and
+ vice versa.
+ </item>
+ </list>
+ <p>All of these approaches have different advantages and
+ disadvantages. Mnesia applications can easily be opened to the
+ SNMP protocol. It is possible to establish a direct one-to-one
+ mapping between Mnesia tables and SNMP tables. This
+ means that a Mnesia table can be configured to be <em>both</em>
+ a Mnesia table and an SNMP table. A number of functions to
+ control this behavior are described in the Mnesia reference
+ manual.
+ </p>
+ </section>
+</chapter>
+
diff --git a/lib/mnesia/doc/src/book.gif b/lib/mnesia/doc/src/book.gif
new file mode 100644
index 0000000000..94b3868792
--- /dev/null
+++ b/lib/mnesia/doc/src/book.gif
Binary files differ
diff --git a/lib/mnesia/doc/src/book.xml b/lib/mnesia/doc/src/book.xml
new file mode 100644
index 0000000000..5389e615c7
--- /dev/null
+++ b/lib/mnesia/doc/src/book.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE book SYSTEM "book.dtd">
+
+<book xmlns:xi="http://www.w3.org/2001/XInclude">
+ <header titlestyle="normal">
+ <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>Mnesia</title>
+ <prepared>Claes Wikstr&ouml;m, Hans Nilsson and H&aring;kan Mattsson</prepared>
+ <docno></docno>
+ <date>1997-05-27</date>
+ <rev>1.2</rev>
+ <file>book.sgml</file>
+ </header>
+ <insidecover>
+ </insidecover>
+ <pagetext>Mnesia DBMS</pagetext>
+ <preamble>
+ <contents level="2"></contents>
+ </preamble>
+ <parts lift="no">
+ <xi:include href="part.xml"/>
+ </parts>
+ <applications>
+ <xi:include href="ref_man.xml"/>
+ </applications>
+ <releasenotes>
+ <xi:include href="notes.xml"/>
+ </releasenotes>
+ <index></index>
+</book>
+
diff --git a/lib/mnesia/doc/src/bup.erl b/lib/mnesia/doc/src/bup.erl
new file mode 100644
index 0000000000..b9e541ad6a
--- /dev/null
+++ b/lib/mnesia/doc/src/bup.erl
@@ -0,0 +1,239 @@
+%% ``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 via the world wide web 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.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(bup).
+-export([
+ change_node_name/5,
+ view/2,
+ test/0,
+ test/1
+ ]).
+
+-export([
+ count/1,
+ display/1
+ ]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Management of backups, a few demos
+
+%0
+change_node_name(Mod, From, To, Source, Target) ->
+ Switch =
+ fun(Node) when Node == From -> To;
+ (Node) when Node == To -> throw({error, already_exists});
+ (Node) -> Node
+ end,
+ Convert =
+ fun({schema, db_nodes, Nodes}, Acc) ->
+ {[{schema, db_nodes, lists:map(Switch,Nodes)}], Acc};
+ ({schema, version, Version}, Acc) ->
+ {[{schema, version, Version}], Acc};
+ ({schema, cookie, Cookie}, Acc) ->
+ {[{schema, cookie, Cookie}], Acc};
+ ({schema, Tab, CreateList}, Acc) ->
+ Keys = [ram_copies, disc_copies, disc_only_copies],
+ OptSwitch =
+ fun({Key, Val}) ->
+ case lists:member(Key, Keys) of
+ true -> {Key, lists:map(Switch, Val)};
+ false-> {Key, Val}
+ end
+ end,
+ {[{schema, Tab, lists:map(OptSwitch, CreateList)}], Acc};
+ (Other, Acc) ->
+ {[Other], Acc}
+ end,
+ mnesia:traverse_backup(Source, Mod, Target, Mod, Convert, switched).
+
+view(Source, Mod) ->
+ View = fun(Item, Acc) ->
+ io:format("~p.~n",[Item]),
+ {[Item], Acc + 1}
+ end,
+ mnesia:traverse_backup(Source, Mod, dummy, read_only, View, 0).
+%0
+
+-record(bup_rec, {key, val}).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Test change of node name
+%%
+%% Assume db_nodes to be current node and on all other nodes but one
+%% Create new schema, start Mnesia on all db_nodes
+%% Create a table of disc_copies type which is replicated to all db_nodes
+%% Perform a backup and change current node to unused node in backup
+%% Start Mnesia on all nodes according to the new set of db_nodes
+test() ->
+ test(nodes()).
+
+test(Nodes)->
+ AllNodes = (Nodes -- [node()]) ++ [node()],
+ case length(AllNodes) of
+ Length when Length > 1 ->
+ OldBup = "old.BUP",
+ NewBup = "new.BUP",
+ Res = (catch test2(AllNodes, OldBup, NewBup)),
+ case Res of
+ {'EXIT', Reason} ->
+ file:delete(OldBup),
+ file:delete(NewBup),
+ {error, Reason};
+ ok ->
+ ok = count(NewBup),
+ file:delete(OldBup),
+ file:delete(NewBup),
+ ok
+ end;
+ _ ->
+ {error,{"Must run on at least one other node",AllNodes}}
+ end.
+
+test2(AllNodes, OldBup, NewBup) ->
+ ThisNode = node(),
+ OtherNode = hd(AllNodes -- [ThisNode]),
+ OldNodes = AllNodes -- [OtherNode],
+ NewNodes = AllNodes -- [ThisNode],
+ Mod = mnesia_backup, % Assume local files
+ file:delete(OldBup),
+ file:delete(NewBup),
+
+ %% Create old backup
+ rpc:multicall(AllNodes, mnesia, lkill, []),
+ ok = mnesia:delete_schema(AllNodes),
+ ok = mnesia:create_schema(OldNodes),
+ rpc:multicall(OldNodes, mnesia, start, []),
+ rpc:multicall(OldNodes, mnesia, wait_for_tables, [[schema], infinity]),
+
+ CreateList = [{disc_copies, OldNodes},
+ {attributes, record_info(fields, bup_rec)}],
+ {atomic, ok} = mnesia:create_table(bup_rec, CreateList),
+ rpc:multicall(OldNodes, mnesia, wait_for_tables, [[bup_rec], infinity]),
+ OldRecs = [#bup_rec{key = I, val = I * I} || I <- lists:seq(1, 10)],
+ lists:foreach(fun(R) -> ok = mnesia:dirty_write(R) end,OldRecs),
+ ok = mnesia:backup(OldBup, Mod),
+ ok = mnesia:dirty_write(#bup_rec{key = 4711, val = 4711}),
+ rpc:multicall(OldNodes, mnesia, stop, []),
+ {ok,_} = view(OldBup, Mod),
+
+ %% Change node name
+ {ok,_} = change_node_name(Mod, ThisNode, OtherNode, OldBup, NewBup),
+ ok = rpc:call(OtherNode, mnesia, install_fallback, [NewBup, Mod]),
+ {_NewStartRes,[]} = rpc:multicall(NewNodes, mnesia, start, []),
+ rpc:call(OtherNode, mnesia, wait_for_tables, [[bup_rec], infinity]),
+ Wild = rpc:call(OtherNode, mnesia, table_info, [bup_rec, wild_pattern]),
+ NewRecs = rpc:call(OtherNode, mnesia, dirty_match_object, [Wild]),
+ rpc:multicall(NewNodes, mnesia, stop, []),
+ {ok,_} = view(NewBup, Mod),
+
+ %% Sanity test
+ case {lists:sort(OldRecs), lists:sort(NewRecs)} of
+ {Same, Same} -> ok
+ end.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+-record(state, {counter_tab, size_tab, acc_size = 0, n_records = 0}).
+
+
+%% Iterates over a backup file and shows some statistics
+%% The identity of ets table containing the counters is not removed
+count(BupFile) ->
+ CounterTab = ets:new(?MODULE, [set, public]),
+ SizeTab = ets:new(?MODULE, [set, public]),
+ Mod = mnesia:system_info(backup_module),
+ State = #state{counter_tab = CounterTab, size_tab = SizeTab},
+ case mnesia:traverse_backup(BupFile, Mod, dummy, read_only, fun incr/2, State) of
+ {ok, State2} ->
+ Res = display(State2),
+ ets:delete(CounterTab),
+ ets:delete(SizeTab),
+ Res;
+ {error, Reason} ->
+ ets:delete(CounterTab),
+ ets:delete(SizeTab),
+ {error, Reason}
+ end.
+
+incr(Rec, State) ->
+ Tab = element(1, Rec),
+ Key = element(2, Rec),
+ Oid = {Tab, Key},
+ incr_counter(State#state.counter_tab, Oid),
+ Size = size(term_to_binary(Rec)),
+ max_size(State#state.size_tab, Tab, Key, Size),
+ AccSize = State#state.acc_size,
+ N = State#state.n_records,
+ State2 = State#state{acc_size = AccSize + Size, n_records = N + 1},
+ {[Rec], State2}.
+
+incr_counter(T, Counter) ->
+ case catch ets:update_counter(T, Counter, 1) of
+ {'EXIT', _} ->
+ ets:insert(T, {Counter, 1});
+ _ ->
+ ignore
+ end.
+
+max_size(T, Tab, Key, Size) ->
+ case catch ets:lookup_element(T, Tab, 2) of
+ {'EXIT', _} ->
+ ets:insert(T, {Tab, Size, Key});
+ OldSize when OldSize < Size ->
+ ets:insert(T, {Tab, Size, Key});
+ _ ->
+ ignore
+ end.
+
+%% Displays the statistics found in the ets table
+display(State) ->
+ CounterTab = State#state.counter_tab,
+ Tabs = [T || {{_, T}, _} <- match_tab(CounterTab, schema)],
+ io:format("~w tables with totally: ~w records, ~w keys, ~w bytes~n",
+ [length(Tabs),
+ State#state.n_records,
+ ets:info(CounterTab, size),
+ State#state.acc_size]),
+ display(State, lists:sort(Tabs)).
+
+display(State, [Tab | Tabs]) ->
+ Counters = match_tab(State#state.counter_tab, Tab),
+ io:format("~-10w records in table ~w~n", [length(Counters), Tab]),
+ Fun = fun({_Oid, Val}) when Val < 5 ->
+ ignore;
+ ({Oid, Val}) ->
+ io:format("~-10w *** records with key ~w~n", [Val, Oid])
+ end,
+ lists:foreach(Fun, Counters),
+ display_size(State#state.size_tab, Tab),
+ display(State, Tabs);
+display(_CounterTab, []) ->
+ ok.
+
+match_tab(T, Tab) ->
+ ets:match_object(T, {{Tab, '_'}, '_'}).
+
+display_size(T, Tab) ->
+ case catch ets:lookup(T, Tab) of
+ [] ->
+ ignore;
+ [{_, Size, Key}] when Size > 1000 ->
+ io:format("~-10w --- bytes occupied by largest record ~w~n",
+ [Size, {Tab, Key}]);
+ [{_, _, _}] ->
+ ignore
+ end.
diff --git a/lib/mnesia/doc/src/company.erl b/lib/mnesia/doc/src/company.erl
new file mode 100644
index 0000000000..28c32ed513
--- /dev/null
+++ b/lib/mnesia/doc/src/company.erl
@@ -0,0 +1,373 @@
+%% ``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 via the world wide web 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.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(company).
+
+-compile(export_all).
+
+%0
+
+-include_lib("stdlib/include/qlc.hrl").
+-include("company.hrl").
+
+init() ->
+ mnesia:create_table(employee,
+ [{attributes, record_info(fields, employee)}]),
+ mnesia:create_table(dept,
+ [{attributes, record_info(fields, dept)}]),
+ mnesia:create_table(project,
+ [{attributes, record_info(fields, project)}]),
+ mnesia:create_table(manager, [{type, bag},
+ {attributes, record_info(fields, manager)}]),
+ mnesia:create_table(at_dep,
+ [{attributes, record_info(fields, at_dep)}]),
+ mnesia:create_table(in_proj, [{type, bag},
+ {attributes, record_info(fields, in_proj)}]).
+
+%0
+
+%1
+
+insert_emp(Emp, DeptId, ProjNames) ->
+ Ename = Emp#employee.name,
+ Fun = fun() ->
+ mnesia:write(Emp),
+ AtDep = #at_dep{emp = Ename, dept_id = DeptId},
+ mnesia:write(AtDep),
+ mk_projs(Ename, ProjNames)
+ end,
+ mnesia:transaction(Fun).
+
+
+mk_projs(Ename, [ProjName|Tail]) ->
+ mnesia:write(#in_proj{emp = Ename, proj_name = ProjName}),
+ mk_projs(Ename, Tail);
+mk_projs(_, []) -> ok.
+
+
+%1
+
+%2
+females() ->
+ F = fun() ->
+ Q = qlc:q([E#employee.name || E <- mnesia:table(employee),
+ E#employee.sex == female]),
+ qlc:e(Q)
+ end,
+ mnesia:transaction(F).
+%2
+%20
+all_females() ->
+ F = fun() ->
+ Female = #employee{sex = female, name = '$1', _ = '_'},
+ mnesia:select(employee, [{Female, [], ['$1']}])
+ end,
+ mnesia:transaction(F).
+%20
+
+g() -> l.
+
+%3
+female_bosses() ->
+ Q = qlc:q( [{E#employee.name, Boss#employee.name} ||
+ E <- mnesia:table(employee),
+ Boss <- mnesia:table(employee),
+ Atdep <- mnesia:table(at_dep),
+ Mgr <- mnesia:table(manager),
+ E#employee.sex == female,
+ Atdep#at_dep.emp == E#employee.emp_no,
+ Mgr#manager.emp == Boss#employee.emp_no,
+ Atdep#at_dep.dept_id == Mgr#manager.dept]
+ ),
+ mnesia:transaction(fun() -> qlc:e(Q) end).
+%3
+
+%4
+raise_females(Amount) ->
+ F = fun() ->
+ Q = qlc:q([E || E <- mnesia:table(employee),
+ E#employee.sex == female]),
+ Fs = qlc:e(Q),
+ over_write(Fs, Amount)
+ end,
+ mnesia:transaction(F).
+
+over_write([E|Tail], Amount) ->
+ Salary = E#employee.salary + Amount,
+ New = E#employee{salary = Salary},
+ mnesia:write(New),
+ 1 + over_write(Tail, Amount);
+over_write([], _) ->
+ 0.
+%4
+
+%5
+raise(Eno, Raise) ->
+ F = fun() ->
+ [E] = mnesia:read(employee, Eno, write),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ mnesia:write(New)
+ end,
+ mnesia:transaction(F).
+%5
+
+
+%6
+bad_raise(Eno, Raise) ->
+ F = fun() ->
+ [E] = mnesia:read({employee, Eno}),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ io:format("Trying to write ... ~n", []),
+ mnesia:write(New)
+ end,
+ mnesia:transaction(F).
+%6
+
+%9
+get_emps(Salary, Dep) ->
+ Q = qlc:q(
+ [E || E <- mnesia:table(employee),
+ At <- mnesia:table(at_dep),
+ E#employee.salary > Salary,
+ E#employee.emp_no == At#at_dep.emp,
+ At#at_dep.dept_id == Dep]
+ ),
+ F = fun() -> qlc:e(Q) end,
+ mnesia:transaction(F).
+%9
+%10
+get_emps2(Salary, Dep) ->
+ Epat = mnesia:table_info(employee, wild_pattern),
+ Apat = mnesia:table_info(at_dep, wild_pattern),
+ F = fun() ->
+ All = mnesia:match_object(Epat),
+ High = filter(All, Salary),
+ Alldeps = mnesia:match_object(Apat),
+ filter_deps(High, Alldeps, Dep)
+ end,
+ mnesia:transaction(F).
+
+
+filter([E|Tail], Salary) ->
+ if
+ E#employee.salary > Salary ->
+ [E | filter(Tail, Salary)];
+ true ->
+ filter(Tail, Salary)
+ end;
+filter([], _) ->
+ [].
+
+filter_deps([E|Tail], Deps, Dep) ->
+ case search_deps(E#employee.name, Deps, Dep) of
+ true ->
+ [E | filter_deps(Tail, Deps, Dep)];
+ false ->
+ filter_deps(Tail, Deps, Dep)
+ end;
+filter_deps([], _,_) ->
+ [].
+
+
+search_deps(Name, [D|Tail], Dep) ->
+ if
+ D#at_dep.emp == Name,
+ D#at_dep.dept_id == Dep -> true;
+ true -> search_deps(Name, Tail, Dep)
+ end;
+search_deps(_Name, _Tail, _Dep) ->
+ false.
+
+%10
+
+
+
+%11
+bench1() ->
+ Me = #employee{emp_no= 104732,
+ name = klacke,
+ salary = 7,
+ sex = male,
+ phone = 99586,
+ room_no = {221, 015}},
+
+ F = fun() -> insert_emp(Me, 'B/DUR', [erlang, mnesia, otp]) end,
+ T1 = timer:tc(company, dotimes, [1000, F]),
+ mnesia:add_table_copy(employee, b@skeppet, ram_copies),
+ mnesia:add_table_copy(at_dep, b@skeppet, ram_copies),
+ mnesia:add_table_copy(in_proj, b@skeppet, ram_copies),
+ T2 = timer:tc(company, dotimes, [1000, F]),
+ {T1, T2}.
+
+dotimes(0, _) ->
+ ok;
+dotimes(I, F) ->
+ F(), dotimes(I-1, F).
+
+%11
+
+
+
+
+
+%12
+
+dist_init() ->
+ mnesia:create_table(employee,
+ [{ram_copies, [a@gin, b@skeppet]},
+ {attributes, record_info(fields,
+ employee)}]),
+ mnesia:create_table(dept,
+ [{ram_copies, [a@gin, b@skeppet]},
+ {attributes, record_info(fields, dept)}]),
+ mnesia:create_table(project,
+ [{ram_copies, [a@gin, b@skeppet]},
+ {attributes, record_info(fields, project)}]),
+ mnesia:create_table(manager, [{type, bag},
+ {ram_copies, [a@gin, b@skeppet]},
+ {attributes, record_info(fields,
+ manager)}]),
+ mnesia:create_table(at_dep,
+ [{ram_copies, [a@gin, b@skeppet]},
+ {attributes, record_info(fields, at_dep)}]),
+ mnesia:create_table(in_proj,
+ [{type, bag},
+ {ram_copies, [a@gin, b@skeppet]},
+ {attributes, record_info(fields, in_proj)}]).
+%12
+
+%13
+remove_proj(ProjName) ->
+ F = fun() ->
+ Ip = qlc:e(qlc:q([X || X <- mnesia:table(in_proj),
+ X#in_proj.proj_name == ProjName]
+ )),
+ mnesia:delete({project, ProjName}),
+ del_in_projs(Ip)
+ end,
+ mnesia:transaction(F).
+
+del_in_projs([Ip|Tail]) ->
+ mnesia:delete_object(Ip),
+ del_in_projs(Tail);
+del_in_projs([]) ->
+ done.
+%13
+
+%14
+sync() ->
+ case mnesia:wait_for_tables(tabs(), 10000) of
+ {timeout, RemainingTabs} ->
+ panic(RemainingTabs);
+ ok ->
+ synced
+ end.
+
+tabs() -> [employee, dept, project, at_dep, in_proj, manager].
+
+%14
+
+
+find_male_on_second_floor() ->
+ Select = fun() ->
+%21
+ MatchHead = #employee{name='$1', sex=male, room_no={'$2', '_'}, _='_'},
+ Guard = [{'>=', '$2', 220},{'<', '$2', 230}],
+ Result = '$1',
+ mnesia:select(employee,[{MatchHead, Guard, [Result]}])
+%21
+ end,
+ mnesia:transaction(Select).
+
+panic(X) -> exit({panic, X}).
+
+
+fill_tables() ->
+ Emps =
+ [
+ {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}}
+ ],
+
+ Dept = [
+ {dept, 'B/SF', "Open Telecom Platform"},
+ {dept, 'B/SFP', "OTP - Product Development"},
+ {dept, 'B/SFR', "Computer Science Laboratory"}
+ ],
+
+ Projects = [
+ {project, erlang, 1},
+ {project, otp, 2},
+ {project, beam, 3},
+ {project, mnesia, 5},
+ {project, wolf, 6},
+ {project, documentation, 7},
+ {project, www, 8}
+ ],
+
+ Manager = [
+ {manager, 104465, 'B/SF'},
+ {manager, 104465, 'B/SFP'},
+ {manager, 114872, 'B/SFR'}
+ ],
+
+ At_dep = [
+ {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'}
+ ],
+
+ In_proj = [
+ {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}
+ ],
+
+ [mnesia:dirty_write(W) || W <- Emps],
+ [mnesia:dirty_write(W) || W <- Dept],
+ [mnesia:dirty_write(W) || W <- Projects],
+ %% Relations
+ [mnesia:dirty_write(W) || W <- Manager],
+ [mnesia:dirty_write(W) || W <- At_dep],
+ [mnesia:dirty_write(W) || W <- In_proj],
+
+ ok.
diff --git a/lib/mnesia/doc/src/company.fig b/lib/mnesia/doc/src/company.fig
new file mode 100644
index 0000000000..9d5fcab041
--- /dev/null
+++ b/lib/mnesia/doc/src/company.fig
@@ -0,0 +1,88 @@
+#FIG 3.1
+Portrait
+Center
+Inches
+1200 2
+6 8550 2700 10950 3150
+2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5
+ 8550 2700 10950 2700 10950 3150 8550 3150 8550 2700
+4 0 -1 0 0 0 12 0.0000 4 180 495 8850 3000 Project\001
+-6
+6 4950 2700 7350 3150
+2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5
+ 4950 2700 7350 2700 7350 3150 4950 3150 4950 2700
+4 0 -1 0 0 0 12 0.0000 4 180 705 5325 3000 Employee\001
+-6
+6 1275 2775 3675 3225
+2 2 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5
+ 1275 2775 3675 2775 3675 3225 1275 3225 1275 2775
+4 0 -1 0 0 0 12 0.0000 4 180 345 1650 3075 Dept\001
+-6
+6 600 4500 2325 5700
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5
+ 600 5100 1425 5700 2325 5100 1425 4500 600 5100
+4 0 -1 0 0 0 12 0.0000 4 180 630 1125 5175 Manager\001
+-6
+6 9000 4500 10725 5700
+6 9600 5025 10125 5250
+6 9600 5025 10125 5250
+4 0 -1 0 0 0 12 0.0000 4 180 525 9600 5175 In_proj\001
+-6
+-6
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5
+ 9000 5100 9825 5700 10725 5100 9825 4500 9000 5100
+-6
+6 4125 2100 7950 2700
+1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 5287 2325 262 225 5025 2100 5550 2550
+1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 5812 2325 262 225 5550 2100 6075 2550
+1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 6337 2325 262 225 6075 2100 6600 2550
+1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 6862 2325 262 225 6600 2100 7125 2550
+1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 4612 2325 413 225 5025 2100 4200 2550
+1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 7537 2325 413 225 7950 2100 7125 2550
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 4800 2550 5925 2700
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 5475 2550 5850 2700
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 5850 2550 5850 2700
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 6300 2550 5850 2700
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 6750 2550 5850 2700
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 7275 2550 5925 2700
+4 0 -1 0 0 0 12 0.0000 4 180 3540 4350 2400 emp_no name salary sex phone room_no\001
+-6
+6 3300 4500 5100 5775
+6 3900 5025 4425 5250
+4 0 -1 0 0 0 12 0.0000 4 180 525 3900 5175 At_dep\001
+-6
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 5
+ 3323 5135 4148 5735 5048 5135 4148 4535 3323 5135
+-6
+1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 1875 2287 600 187 1275 2100 2475 2475
+1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 3075 2287 600 187 2475 2100 3675 2475
+1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 8850 2325 450 225 8400 2100 9300 2550
+1 2 0 1 -1 7 0 0 -1 0.000 1 0.0000 9750 2325 450 225 9300 2100 10200 2550
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 1575 3225 600 5100
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 2325 5100 5250 3150
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 5850 3150 5025 5175
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 3300 5100 2550 3225
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 7050 3150 9000 5100
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 10725 5100 9825 3150
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 1875 2475 2400 2775
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 3075 2475 2400 2775
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 9000 2550 9750 2700
+2 1 0 1 -1 7 0 0 -1 0.000 0 0 -1 0 0 2
+ 9750 2550 9675 2700
+4 0 -1 0 0 0 12 0.0000 4 135 1500 1575 2325 id name\001
+4 0 -1 0 0 0 12 0.0000 4 135 1635 8775 2400 Name number \001
diff --git a/lib/mnesia/doc/src/company.gif b/lib/mnesia/doc/src/company.gif
new file mode 100644
index 0000000000..3cd0185e69
--- /dev/null
+++ b/lib/mnesia/doc/src/company.gif
Binary files differ
diff --git a/lib/mnesia/doc/src/company.hrl b/lib/mnesia/doc/src/company.hrl
new file mode 100644
index 0000000000..85e0e6ff12
--- /dev/null
+++ b/lib/mnesia/doc/src/company.hrl
@@ -0,0 +1,50 @@
+%% ``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 via the world wide web 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.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+
+
+%0
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no}).
+
+-record(dept, {id,
+ name}).
+
+-record(project, {name,
+ number}).
+
+
+-record(manager, {emp,
+ dept}).
+
+-record(at_dep, {emp,
+ dept_id}).
+
+-record(in_proj, {emp,
+ proj_name}).
+
+%0
+
+
+
+
+
+
diff --git a/lib/mnesia/doc/src/company.ps b/lib/mnesia/doc/src/company.ps
new file mode 100644
index 0000000000..64a45d07f3
--- /dev/null
+++ b/lib/mnesia/doc/src/company.ps
@@ -0,0 +1,213 @@
+%!PS-Adobe-2.0
+%%Title: company.fig
+%%Creator: fig2dev Version 3.1 Patchlevel 2
+%%CreationDate: Thu Oct 31 18:09:46 1996
+%%For: klacke@gin (Claes Wikstrom,EUA/SU)
+%Magnification: 0.70
+%%Orientation: Portrait
+%%BoundingBox: 79 343 516 498
+%%Pages: 1
+%%BeginSetup
+%%IncludeFeature: *PageSize A4
+%%EndSetup
+%%EndComments
+/$F2psDict 200 dict def
+$F2psDict begin
+$F2psDict /mtrx matrix put
+/col-1 {0 setgray} bind def
+/col0 {0.000 0.000 0.000 srgb} bind def
+/col1 {0.000 0.000 1.000 srgb} bind def
+/col2 {0.000 1.000 0.000 srgb} bind def
+/col3 {0.000 1.000 1.000 srgb} bind def
+/col4 {1.000 0.000 0.000 srgb} bind def
+/col5 {1.000 0.000 1.000 srgb} bind def
+/col6 {1.000 1.000 0.000 srgb} bind def
+/col7 {1.000 1.000 1.000 srgb} bind def
+/col8 {0.000 0.000 0.560 srgb} bind def
+/col9 {0.000 0.000 0.690 srgb} bind def
+/col10 {0.000 0.000 0.820 srgb} bind def
+/col11 {0.530 0.810 1.000 srgb} bind def
+/col12 {0.000 0.560 0.000 srgb} bind def
+/col13 {0.000 0.690 0.000 srgb} bind def
+/col14 {0.000 0.820 0.000 srgb} bind def
+/col15 {0.000 0.560 0.560 srgb} bind def
+/col16 {0.000 0.690 0.690 srgb} bind def
+/col17 {0.000 0.820 0.820 srgb} bind def
+/col18 {0.560 0.000 0.000 srgb} bind def
+/col19 {0.690 0.000 0.000 srgb} bind def
+/col20 {0.820 0.000 0.000 srgb} bind def
+/col21 {0.560 0.000 0.560 srgb} bind def
+/col22 {0.690 0.000 0.690 srgb} bind def
+/col23 {0.820 0.000 0.820 srgb} bind def
+/col24 {0.500 0.190 0.000 srgb} bind def
+/col25 {0.630 0.250 0.000 srgb} bind def
+/col26 {0.750 0.380 0.000 srgb} bind def
+/col27 {1.000 0.500 0.500 srgb} bind def
+/col28 {1.000 0.630 0.630 srgb} bind def
+/col29 {1.000 0.750 0.750 srgb} bind def
+/col30 {1.000 0.880 0.880 srgb} bind def
+/col31 {1.000 0.840 0.000 srgb} bind def
+
+end
+save
+55.0 585.5 translate
+1 -1 scale
+
+/cp {closepath} bind def
+/ef {eofill} bind def
+/gr {grestore} bind def
+/gs {gsave} bind def
+/sa {save} bind def
+/rs {restore} bind def
+/l {lineto} bind def
+/m {moveto} bind def
+/rm {rmoveto} bind def
+/n {newpath} bind def
+/s {stroke} bind def
+/sh {show} bind def
+/slc {setlinecap} bind def
+/slj {setlinejoin} bind def
+/slw {setlinewidth} bind def
+/srgb {setrgbcolor} bind def
+/rot {rotate} bind def
+/sc {scale} bind def
+/sd {setdash} bind def
+/ff {findfont} bind def
+/sf {setfont} bind def
+/scf {scalefont} bind def
+/sw {stringwidth} bind def
+/tr {translate} bind def
+/tnt {dup dup currentrgbcolor
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add
+ 4 -2 roll dup 1 exch sub 3 -1 roll mul add srgb}
+ bind def
+/shd {dup dup currentrgbcolor 4 -2 roll mul 4 -2 roll mul
+ 4 -2 roll mul srgb} bind def
+ /DrawEllipse {
+ /endangle exch def
+ /startangle exch def
+ /yrad exch def
+ /xrad exch def
+ /y exch def
+ /x exch def
+ /savematrix mtrx currentmatrix def
+ x y tr xrad yrad sc 0 0 1 startangle endangle arc
+ closepath
+ savematrix setmatrix
+ } def
+
+/$F2psBegin {$F2psDict begin /$F2psEnteredState save def} def
+/$F2psEnd {$F2psEnteredState restore end} def
+%%EndProlog
+
+$F2psBegin
+10 setmiterlimit
+n 0 842 m 0 0 l 595 0 l 595 842 l cp clip
+ 0.04200 0.04200 sc
+%%Page: 1 1
+7.500 slw
+% Polyline
+n 8550 2700 m 10950 2700 l 10950 3150 l 8550 3150 l cp gs col-1 s gr
+/Times-Roman ff 180.00 scf sf
+8850 3000 m
+gs 1 -1 sc (Project) col-1 sh gr
+% Polyline
+n 4950 2700 m 7350 2700 l 7350 3150 l 4950 3150 l cp gs col-1 s gr
+/Times-Roman ff 180.00 scf sf
+5325 3000 m
+gs 1 -1 sc (Employee) col-1 sh gr
+% Polyline
+n 1275 2775 m 3675 2775 l 3675 3225 l 1275 3225 l cp gs col-1 s gr
+/Times-Roman ff 180.00 scf sf
+1650 3075 m
+gs 1 -1 sc (Dept) col-1 sh gr
+% Polyline
+n 600 5100 m 1425 5700 l 2325 5100 l 1425 4500 l 600 5100 l gs col-1 s gr
+/Times-Roman ff 180.00 scf sf
+1125 5175 m
+gs 1 -1 sc (Manager) col-1 sh gr
+/Times-Roman ff 180.00 scf sf
+9600 5175 m
+gs 1 -1 sc (In_proj) col-1 sh gr
+% Polyline
+n 9000 5100 m 9825 5700 l 10725 5100 l 9825 4500 l 9000 5100 l gs col-1 s gr
+% Ellipse
+n 5287 2325 262 225 0 360 DrawEllipse gs col-1 s gr
+
+% Ellipse
+n 5812 2325 262 225 0 360 DrawEllipse gs col-1 s gr
+
+% Ellipse
+n 6337 2325 262 225 0 360 DrawEllipse gs col-1 s gr
+
+% Ellipse
+n 6862 2325 262 225 0 360 DrawEllipse gs col-1 s gr
+
+% Ellipse
+n 4612 2325 413 225 0 360 DrawEllipse gs col-1 s gr
+
+% Ellipse
+n 7537 2325 413 225 0 360 DrawEllipse gs col-1 s gr
+
+% Polyline
+n 4800 2550 m 5925 2700 l gs col-1 s gr
+% Polyline
+n 5475 2550 m 5850 2700 l gs col-1 s gr
+% Polyline
+n 5850 2550 m 5850 2700 l gs col-1 s gr
+% Polyline
+n 6300 2550 m 5850 2700 l gs col-1 s gr
+% Polyline
+n 6750 2550 m 5850 2700 l gs col-1 s gr
+% Polyline
+n 7275 2550 m 5925 2700 l gs col-1 s gr
+/Times-Roman ff 180.00 scf sf
+4350 2400 m
+gs 1 -1 sc (emp_no name salary sex phone room_no) col-1 sh gr
+/Times-Roman ff 180.00 scf sf
+3900 5175 m
+gs 1 -1 sc (At_dep) col-1 sh gr
+% Polyline
+n 3323 5135 m 4148 5735 l 5048 5135 l 4148 4535 l 3323 5135 l gs col-1 s gr
+% Ellipse
+n 1875 2287 600 187 0 360 DrawEllipse gs col-1 s gr
+
+% Ellipse
+n 3075 2287 600 187 0 360 DrawEllipse gs col-1 s gr
+
+% Ellipse
+n 8850 2325 450 225 0 360 DrawEllipse gs col-1 s gr
+
+% Ellipse
+n 9750 2325 450 225 0 360 DrawEllipse gs col-1 s gr
+
+% Polyline
+n 1575 3225 m 600 5100 l gs col-1 s gr
+% Polyline
+n 2325 5100 m 5250 3150 l gs col-1 s gr
+% Polyline
+n 5850 3150 m 5025 5175 l gs col-1 s gr
+% Polyline
+n 3300 5100 m 2550 3225 l gs col-1 s gr
+% Polyline
+n 7050 3150 m 9000 5100 l gs col-1 s gr
+% Polyline
+n 10725 5100 m 9825 3150 l gs col-1 s gr
+% Polyline
+n 1875 2475 m 2400 2775 l gs col-1 s gr
+% Polyline
+n 3075 2475 m 2400 2775 l gs col-1 s gr
+% Polyline
+n 9000 2550 m 9750 2700 l gs col-1 s gr
+% Polyline
+n 9750 2550 m 9675 2700 l gs col-1 s gr
+/Times-Roman ff 180.00 scf sf
+1575 2325 m
+gs 1 -1 sc ( id name) col-1 sh gr
+/Times-Roman ff 180.00 scf sf
+8775 2400 m
+gs 1 -1 sc (Name number ) col-1 sh gr
+showpage
+$F2psEnd
+rs
diff --git a/lib/mnesia/doc/src/company_o.erl b/lib/mnesia/doc/src/company_o.erl
new file mode 100644
index 0000000000..3c7ad0d5e5
--- /dev/null
+++ b/lib/mnesia/doc/src/company_o.erl
@@ -0,0 +1,144 @@
+%% ``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 via the world wide web 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.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+-module(company_o).
+-compile(export_all).
+
+-import(mnesia, [transaction/1]).
+
+%0
+-include_lib("stdlib/include/qlc.hrl").
+-include("company_o.hrl").
+
+
+sinit() ->
+ mnesia:create_schema([node()]).
+
+init() ->
+ mnesia:create_table(employee,
+ [{attributes, record_info(fields, employee)}]),
+ mnesia:create_table(dept,
+ [{attributes, record_info(fields, dept)}]),
+ mnesia:create_table(project,
+ [{attributes, record_info(fields, project)}]).
+
+%0
+
+
+
+%1
+
+insert_emp(Emp, DeptId, ProjNames) ->
+ Fun = fun() ->
+ mnesia:write(Emp#employee{dept = DeptId,
+ projects = ProjNames})
+ end,
+ mnesia:transaction(Fun).
+
+
+%1
+
+%2
+females() ->
+ F = fun() ->
+ Q = qlc:q([E#employee.name || E <- mnesia:table(employee),
+ E#employee.sex == female]),
+ qlc:e(Q)
+ end,
+ mnesia:transaction(F).
+%2
+
+%3
+female_bosses() ->
+ F = fun() -> qlc:e(qlc:q(
+ [{E#employee.name, Boss#employee.name} ||
+ E <- mnesia:table(employee),
+ Boss <- mnesia:table(employee),
+ Boss#employee.emp_no == E#employee.manager,
+ E#employee.sex == female]
+ ))
+ end,
+ mnesia:transaction(F).
+
+
+%4
+raise_females(Amount) ->
+ F = fun() ->
+ Q = qlc:q([E || E <- mnesia:table(employee),
+ E#employee.sex == female]),
+ Fs = qlc:e(Q),
+ over_write(Fs, Amount)
+ end,
+ mnesia:transaction(F).
+
+over_write([E|Tail], Amount) ->
+ Salary = E#employee.salary + Amount,
+ New = E#employee{salary = Salary},
+ mnesia:write(New),
+ 1 + over_write(Tail, Amount);
+over_write([], _) ->
+ 0.
+%4
+
+%5
+raise(Eno, Raise) ->
+ F = fun() ->
+ [E] = mnesia:read({employee, Eno}),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ mnesia:write(New)
+ end,
+ mnesia:transaction(F).
+%5
+
+
+%6
+bad_raise(Eno, Raise) ->
+ F = fun() ->
+ [E] = mnesia:read({employee, Eno}),
+ Salary = E#employee.salary + Raise,
+ New = E#employee{salary = Salary},
+ io:format("Trying to write ... ~n", []),
+ mnesia:write(New)
+ end,
+ mnesia:transaction(F).
+%6
+
+%9
+get_emps(Salary, Dep) ->
+ Q = qlc:q(
+ [E || E <- mnesia:table(employee),
+ E#employee.salary > Salary,
+ E#employee.dept == Dep]
+ ),
+ F = fun() -> qlc:e(Q) end,
+ transaction(F).
+%9
+
+%10
+get_emps2(Salary, Dep) ->
+ Epat0 = mnesia:table_info(employee, wild_pattern),
+ Epat = Epat0#employee{dept = Dep},
+ F = fun() ->
+ All = mnesia:match_object(Epat),
+ [E || E <-All, E#employee.salary > Salary ]
+ end,
+ mnesia:transaction(F).
+
+
+%10
+
diff --git a/lib/mnesia/doc/src/company_o.hrl b/lib/mnesia/doc/src/company_o.hrl
new file mode 100644
index 0000000000..d8b584c296
--- /dev/null
+++ b/lib/mnesia/doc/src/company_o.hrl
@@ -0,0 +1,38 @@
+%% ``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 via the world wide web 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.
+%%
+%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
+%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
+%% AB. All Rights Reserved.''
+%%
+%% $Id$
+%%
+
+%0
+-record(employee, {emp_no,
+ name,
+ salary,
+ sex,
+ phone,
+ room_no,
+ dept,
+ projects,
+ manager}).
+
+
+-record(dept, {id,
+ name}).
+
+-record(project, {name,
+ number,
+ location}).
+
+%0
diff --git a/lib/mnesia/doc/src/fascicules.xml b/lib/mnesia/doc/src/fascicules.xml
new file mode 100644
index 0000000000..0678195e07
--- /dev/null
+++ b/lib/mnesia/doc/src/fascicules.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE fascicules SYSTEM "fascicules.dtd">
+
+<fascicules>
+ <fascicule file="part" href="part_frame.html" entry="no">
+ User's Guide
+ </fascicule>
+ <fascicule file="ref_man" href="ref_man_frame.html" entry="yes">
+ Reference Manual
+ </fascicule>
+ <fascicule file="part_notes" href="part_notes_frame.html" entry="no">
+ Release Notes
+ </fascicule>
+ <fascicule file="" href="../../../../doc/print.html" entry="no">
+ Off-Print
+ </fascicule>
+</fascicules>
+
diff --git a/lib/mnesia/doc/src/make.dep b/lib/mnesia/doc/src/make.dep
new file mode 100644
index 0000000000..6e79484cb3
--- /dev/null
+++ b/lib/mnesia/doc/src/make.dep
@@ -0,0 +1,46 @@
+# ----------------------------------------------------
+# >>>> Do not edit this file <<<<
+# This file was automaticly generated by
+# /home/otp/bin/docdepend
+# ----------------------------------------------------
+
+
+# ----------------------------------------------------
+# TeX files that the DVI file depend on
+# ----------------------------------------------------
+
+book.dvi: Mnesia_App_A.tex Mnesia_App_B.tex Mnesia_App_C.tex \
+ Mnesia_App_D.tex Mnesia_chap1.tex Mnesia_chap2.tex \
+ Mnesia_chap3.tex Mnesia_chap4.tex Mnesia_chap5.tex \
+ Mnesia_chap7.tex Mnesia_chap8.tex book.tex \
+ mnesia.tex mnesia_frag_hash.tex mnesia_registry.tex \
+ part.tex ref_man.tex
+
+# ----------------------------------------------------
+# Source inlined when transforming from source to LaTeX
+# ----------------------------------------------------
+
+Mnesia_App_B.tex: ../../src/mnesia_backup.erl
+
+Mnesia_App_C.tex: ../../src/mnesia_frag.erl
+
+Mnesia_App_D.tex: ../../src/mnesia_frag_hash.erl
+
+Mnesia_chap2.tex: company.erl company.hrl
+
+Mnesia_chap3.tex: company.erl
+
+Mnesia_chap4.tex: company.erl
+
+Mnesia_chap5.tex: FRUITS company.erl company_o.erl company_o.hrl
+
+Mnesia_chap7.tex: bup.erl
+
+book.tex: ref_man.xml
+
+# ----------------------------------------------------
+# Pictures that the DVI file depend on
+# ----------------------------------------------------
+
+book.dvi: company.ps
+
diff --git a/lib/mnesia/doc/src/mnesia.gif b/lib/mnesia/doc/src/mnesia.gif
new file mode 100644
index 0000000000..fbbabee5aa
--- /dev/null
+++ b/lib/mnesia/doc/src/mnesia.gif
Binary files differ
diff --git a/lib/mnesia/doc/src/mnesia.xml b/lib/mnesia/doc/src/mnesia.xml
new file mode 100644
index 0000000000..3484cd104a
--- /dev/null
+++ b/lib/mnesia/doc/src/mnesia.xml
@@ -0,0 +1,3100 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1996</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>mnesia</title>
+ <prepared>Claes Wikstr&ouml;m and H&aring;kan Mattsson</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date></date>
+ <rev></rev>
+ <file></file>
+ </header>
+ <module>mnesia</module>
+ <modulesummary>A Distributed Telecommunications DBMS </modulesummary>
+ <description>
+ <p><c>Mnesia</c> is a distributed DataBase Management System (DBMS),
+ appropriate for telecommunications applications and other Erlang
+ applications which require continuous operation and exhibit soft
+ real-time properties.
+ </p>
+ <p>Listed below are some of the most important and attractive capabilities, Mnesia provides:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p>A relational/object hybrid data model which is
+ suitable for telecommunications applications.
+ </p>
+ </item>
+ <item>
+ <p>A specifically designed DBMS query language, QLC (as an add-on library).
+ </p>
+ </item>
+ <item>
+ <p>Persistence. Tables may be coherently kept on disc as
+ well as in main memory.
+ </p>
+ </item>
+ <item>
+ <p>Replication. Tables may be replicated at several nodes.
+ </p>
+ </item>
+ <item>
+ <p>Atomic transactions. A series of table manipulation
+ operations can be grouped into a single atomic
+ transaction.
+ </p>
+ </item>
+ <item>
+ <p>Location transparency. Programs can be written without
+ knowledge of the actual location of data.
+ </p>
+ </item>
+ <item>
+ <p>Extremely fast real time data searches.
+ </p>
+ </item>
+ <item>
+ <p>Schema manipulation routines. It is possible to
+ reconfigure the DBMS at runtime without stopping the
+ system.
+ </p>
+ </item>
+ </list>
+ <p>This Reference Manual describes the Mnesia API. This includes
+ functions used to define and manipulate Mnesia tables.
+ </p>
+ <p>All functions documented in these pages can be used in any
+ combination with queries using the list comprehension notation. The
+ query notation is described in the QLC's man page.
+ </p>
+ <p>Data in Mnesia is organized as a set of tables. Each table
+ has a name which must be an atom. Each table is made up of
+ Erlang records. The user is responsible for the record
+ definitions. Each table also has a set of properties. Below
+ are some of the properties that are associated with each
+ table:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>type</c>. Each table can either have 'set',
+ 'ordered_set' or 'bag' semantics. Note: currently 'ordered_set'
+ is not supported for 'disc_only_copies'. If a table is of type
+ 'set' it means that each key leads to either one or zero
+ records. <br></br>
+If a new item is inserted with the same key as
+ an existing record, the old record is overwritten. On the
+ other hand, if a table is of type 'bag', each key can map to
+ several records. However, all records in type bag tables are
+ unique, only the keys may be duplicated.
+ </p>
+ </item>
+ <item>
+ <p><c>record_name</c>. All records stored in a table must
+ have the same name. You may say that the records must be
+ instances of the same record type.
+ </p>
+ </item>
+ <item>
+ <p><c>ram_copies</c> A table can be replicated on a number
+ of Erlang nodes. The <c>ram_copies</c> property specifies a
+ list of Erlang nodes where RAM copies are kept. These
+ copies can be dumped to disc at regular intervals. However,
+ updates to these copies are not written to disc on a
+ transaction basis.
+ </p>
+ </item>
+ <item>
+ <p><c>disc_copies</c> The <c>disc_copies</c> property
+ specifies a list of Erlang nodes where the table is kept in
+ RAM as well as on disc. All updates of the table are
+ performed on the actual table and are also logged to disc.
+ If a table is of type <c>disc_copies</c> at a certain node,
+ it means that the entire table is resident in RAM memory as
+ well as on disc. Each transaction performed on the table is
+ appended to a LOG file as well as written into the RAM
+ table.
+ </p>
+ </item>
+ <item>
+ <p><c>disc_only_copies</c> Some, or all, table replicas
+ can be kept on disc only. These replicas are considerably
+ slower than the RAM based replicas.
+ </p>
+ </item>
+ <item>
+ <p><c>index</c> This is a list of attribute names, or
+ integers, which specify the tuple positions on which
+ Mnesia shall build and maintain an extra index table.
+ </p>
+ </item>
+ <item>
+ <p><c>local_content</c> When an application requires
+ tables whose contents is local to each node,
+ <c>local_content</c> tables may be used. The name of the
+ table is known to all Mnesia nodes, but its contents is
+ unique on each node. This means that access to such a table
+ must be done locally. Set the <c>local_content</c> field to
+ <c>true</c> if you want to enable the <c>local_content</c>
+ behavior. The default is <c>false</c>.
+ </p>
+ </item>
+ <item>
+ <p><c>snmp</c> Each (set based) Mnesia table can be
+ automatically turned into an SNMP ordered table as well.
+ This property specifies the types of the SNMP keys.
+ </p>
+ </item>
+ <item>
+ <p><c>attributes</c>. The names of the attributes for the
+ records that are inserted in the table.
+ </p>
+ </item>
+ </list>
+ <p>See <c>mnesia:create_table/2</c> about the complete set of
+ table properties and their details.
+ </p>
+ <p>This document uses a table of persons to illustrate various
+ examples. The following record definition is assumed:
+ </p>
+ <code type="none">
+-record(person, {name,
+ age = 0,
+ address = unknown,
+ salary = 0,
+ children = []}),
+ </code>
+ <p>The first attribute of the record is the primary key, or key
+ for short.
+ </p>
+ <p>The function descriptions are sorted in alphabetic order. <em>Hint:</em>
+ start to read about <c>mnesia:create_table/2</c>,
+ <c>mnesia:lock/2</c> and <c>mnesia:activity/4</c> before you continue on
+ and learn about the rest.
+ </p>
+ <p>Writing or deleting in transaction context creates a local copy
+ of each modified record during the transaction. During iteration,
+ i.e. <c>mnesia:fold[lr]/4</c> <c>mnesia:next/2</c> <c>mnesia:prev/2</c>
+ <c>mnesia:snmp_get_next_index/2</c>, mnesia will compensate for
+ every written or deleted record, which may reduce the
+ performance. If possible avoid writing or deleting records in
+ the same transaction before iterating over the table.
+ </p>
+ </description>
+ <funcs>
+ <func>
+ <name>abort(Reason) -> transaction abort </name>
+ <fsummary>Abort the current transaction.</fsummary>
+ <desc>
+ <p>Makes the transaction silently
+ return the tuple <c>{aborted, Reason}</c>.
+ The abortion of a Mnesia transaction means that
+ an exception will be thrown to an enclosing <c>catch</c>.
+ Thus, the expression <c>catch mnesia:abort(x)</c> does
+ not abort the transaction. </p>
+ </desc>
+ </func>
+ <func>
+ <name>activate_checkpoint(Args) -> {ok,Name,Nodes} | {error,Reason}</name>
+ <fsummary>Activate a checkpoint.</fsummary>
+ <desc>
+ <p>A checkpoint is a consistent view of the system.
+ A checkpoint can be activated on a set of tables.
+ This checkpoint can then be traversed and will
+ present a view of the system as it existed at the time when
+ the checkpoint was activated, even if the tables are being or have been
+ manipulated.
+ </p>
+ <p><c>Args</c> is a list of the following tuples:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>{name,Name}</c>. <c>Name</c> of checkpoint. Each
+ checkpoint must have a name which is unique to the
+ associated nodes. The name can be reused only once the
+ checkpoint has been deactivated. By default, a name
+ which is probably unique is generated.
+ </p>
+ </item>
+ <item>
+ <p><c>{max,MaxTabs}</c><c>MaxTabs</c> is a list of
+ tables that should be included in the checkpoint. The
+ default is []. For these tables, the redundancy will be
+ maximized and checkpoint information will be retained together
+ with all replicas. The checkpoint becomes more fault
+ tolerant if the tables have several replicas. When a new
+ replica is added by means of the schema manipulation
+ function <c>mnesia:add_table_copy/3</c>, a retainer will
+ also be attached automatically.
+ </p>
+ </item>
+ <item>
+ <p><c>{min,MinTabs}</c>. <c>MinTabs</c> is a list of
+ tables that should be included in the checkpoint. The
+ default is []. For these tables, the redundancy will be
+ minimized and the checkpoint information will only be retained
+ with one replica, preferably on the local node.
+ </p>
+ </item>
+ <item>
+ <p><c>{allow_remote,Bool}</c>. <c>false</c> means that
+ all retainers must be local. The checkpoint cannot be
+ activated if a table does not reside locally.
+ <c>true</c> allows retainers to be allocated on any
+ node. Default is set to <c>true</c>.
+ </p>
+ </item>
+ <item>
+ <p><c>{ram_overrides_dump,Bool} </c> Only applicable
+ for <c>ram_copies</c>. <c>Bool</c> allows you to choose
+ to backup the table state as it is in RAM, or as it is on
+ disc. <c>true</c> means that the latest committed
+ records in RAM should be included in the checkpoint.
+ These are the records that the application accesses.
+ <c>false</c> means that the records dumped to DAT files
+ should be included in the checkpoint. These are the
+ records that will be loaded at startup. Default is
+ <c>false</c>.
+ </p>
+ </item>
+ </list>
+ <p>Returns <c>{ok,Name,Nodes}</c> or <c>{error,Reason}</c>.
+ <c>Name</c> is the (possibly generated) name of the
+ checkpoint. <c>Nodes</c> are the nodes that
+ are involved in the checkpoint. Only nodes that keep a
+ checkpoint retainer know about the checkpoint.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>activity(AccessContext, Fun [, Args]) -> ResultOfFun | exit(Reason)</name>
+ <fsummary>Execute <c>Fun</c>in <c>AccessContext</c>.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:activity(AccessContext, Fun, Args, AccessMod)</c> where <c>AccessMod</c> is the default
+ access callback module obtained by
+ <c>mnesia:system_info(access_module)</c>. <c>Args</c>
+ defaults to the empty list <c>[]</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>activity(AccessContext, Fun, Args, AccessMod) -> ResultOfFun | exit(Reason)</name>
+ <fsummary>Execute <c>Fun</c>in <c>AccessContext</c>.</fsummary>
+ <desc>
+ <p>This function executes the functional object <c>Fun</c>
+ with the arguments <c>Args</c>.
+ </p>
+ <p>The code which executes inside the activity can
+ consist of a series of table manipulation functions, which is
+ performed in a <c>AccessContext</c>. Currently, the following
+ access contexts are supported:
+ </p>
+ <taglist>
+ <tag><c>transaction</c></tag>
+ <item>
+ <p>Short for <c>{transaction, infinity}</c></p>
+ </item>
+ <tag><c>{transaction, Retries}</c></tag>
+ <item>
+ <p>Invokes <c>mnesia:transaction(Fun, Args, Retries)</c>. Note that the result from the <c>Fun</c> is
+ returned if the transaction was successful (atomic),
+ otherwise the function exits with an abort reason.
+ </p>
+ </item>
+ <tag><c>sync_transaction</c></tag>
+ <item>
+ <p>Short for <c>{sync_transaction, infinity}</c></p>
+ </item>
+ <tag><c>{sync_transaction, Retries}</c></tag>
+ <item>
+ <p>Invokes <c>mnesia:sync_transaction(Fun, Args, Retries)</c>. Note that the result from the <c>Fun</c> is
+ returned if the transaction was successful (atomic),
+ otherwise the function exits with an abort reason.
+ </p>
+ </item>
+ <tag><c>async_dirty</c></tag>
+ <item>
+ <p>Invokes <c>mnesia:async_dirty(Fun, Args)</c>.
+ </p>
+ </item>
+ <tag><c>sync_dirty</c></tag>
+ <item>
+ <p>Invokes <c>mnesia:sync_dirty(Fun, Args)</c>.
+ </p>
+ </item>
+ <tag><c>ets</c></tag>
+ <item>
+ <p>Invokes <c>mnesia:ets(Fun, Args)</c>.
+ </p>
+ </item>
+ </taglist>
+ <p>This function (<c>mnesia:activity/4</c>) differs in an
+ important aspect from the <c>mnesia:transaction</c>,
+ <c>mnesia:sync_transaction</c>,
+ <c>mnesia:async_dirty</c>, <c>mnesia:sync_dirty</c> and
+ <c>mnesia:ets</c> functions. The <c>AccessMod</c> argument
+ is the name of a callback module which implements the
+ <c>mnesia_access</c> behavior.
+ </p>
+ <p>Mnesia will forward calls to the following functions:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p>mnesia:write/3 (write/1, s_write/1)</p>
+ </item>
+ <item>
+ <p>mnesia:delete/3 (delete/1, s_delete/1)</p>
+ </item>
+ <item>
+ <p>mnesia:delete_object/3 (delete_object/1, s_delete_object/1)</p>
+ </item>
+ <item>
+ <p>mnesia:read/3 (read/1, wread/1)</p>
+ </item>
+ <item>
+ <p>mnesia:match_object/3 (match_object/1)</p>
+ </item>
+ <item>
+ <p>mnesia:all_keys/1</p>
+ </item>
+ <item>
+ <p>mnesia:first/1</p>
+ </item>
+ <item>
+ <p>mnesia:last/1</p>
+ </item>
+ <item>
+ <p>mnesia:prev/2</p>
+ </item>
+ <item>
+ <p>mnesia:next/2</p>
+ </item>
+ <item>
+ <p>mnesia:index_match_object/4 (index_match_object/2)</p>
+ </item>
+ <item>
+ <p>mnesia:index_read/3</p>
+ </item>
+ <item>
+ <p>mnesia:lock/2 (read_lock_table/1, write_lock_table/1)</p>
+ </item>
+ <item>
+ <p>mnesia:table_info/2</p>
+ </item>
+ </list>
+ <p>to the corresponding:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p>AccessMod:lock(ActivityId, Opaque, LockItem, LockKind)</p>
+ </item>
+ <item>
+ <p>AccessMod:write(ActivityId, Opaque, Tab, Rec, LockKind)</p>
+ </item>
+ <item>
+ <p>AccessMod:delete(ActivityId, Opaque, Tab, Key, LockKind)</p>
+ </item>
+ <item>
+ <p>AccessMod:delete_object(ActivityId, Opaque, Tab, RecXS, LockKind)</p>
+ </item>
+ <item>
+ <p>AccessMod:read(ActivityId, Opaque, Tab, Key, LockKind)</p>
+ </item>
+ <item>
+ <p>AccessMod:match_object(ActivityId, Opaque, Tab, Pattern, LockKind)</p>
+ </item>
+ <item>
+ <p>AccessMod:all_keys(ActivityId, Opaque, Tab, LockKind)</p>
+ </item>
+ <item>
+ <p>AccessMod:first(ActivityId, Opaque, Tab)</p>
+ </item>
+ <item>
+ <p>AccessMod:last(ActivityId, Opaque, Tab)</p>
+ </item>
+ <item>
+ <p>AccessMod:prev(ActivityId, Opaque, Tab, Key)</p>
+ </item>
+ <item>
+ <p>AccessMod:next(ActivityId, Opaque, Tab, Key)</p>
+ </item>
+ <item>
+ <p>AccessMod:index_match_object(ActivityId, Opaque, Tab, Pattern, Attr, LockKind)</p>
+ </item>
+ <item>
+ <p>AccessMod:index_read(ActivityId, Opaque, Tab, SecondaryKey, Attr, LockKind)</p>
+ </item>
+ <item>
+ <p>AccessMod:table_info(ActivityId, Opaque, Tab, InfoItem)</p>
+ </item>
+ </list>
+ <p>where <c>ActivityId</c> is a record which represents the
+ identity of the enclosing Mnesia activity. The first field
+ (obtained with <c>element(1, ActivityId)</c> contains an
+ atom which may be interpreted as the type of the activity:
+ <c>'ets'</c>, <c>'async_dirty'</c>, <c>'sync_dirty'</c> or
+ <c>'tid'</c>. <c>'tid'</c> means that the activity is a
+ transaction. The structure of the rest of the identity
+ record is internal to Mnesia.
+ </p>
+ <p><c>Opaque</c> is an opaque data structure which is internal
+ to Mnesia.</p>
+ </desc>
+ </func>
+ <func>
+ <name>add_table_copy(Tab, Node, Type) -> {aborted, R} | {atomic, ok}</name>
+ <fsummary>Copy a table to a remote node.</fsummary>
+ <desc>
+ <p>This function makes another copy of a table at the
+ node <c>Node</c>. The <c>Type</c> argument must be
+ either of the atoms <c>ram_copies</c>, <c>disc_copies</c>,
+ or
+ <c>disc_only_copies</c>. For example, the following call
+ ensures that a disc replica of the <c>person</c> table also
+ exists at node <c>Node</c>.</p>
+ <code type="none">
+mnesia:add_table_copy(person, Node, disc_copies)
+ </code>
+ <p>This function can also be used to add a replica of the
+ table named <c>schema</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>add_table_index(Tab, AttrName) -> {aborted, R} | {atomic, ok}</name>
+ <fsummary>Create an index for a table. </fsummary>
+ <desc>
+ <p>Table indices can and should be used whenever the user
+ wants to frequently use some other field than the key field
+ to look up records. If this other field has an index
+ associated with it, these lookups can occur in constant time
+ and space. For example, if our application wishes to use
+ the age field of persons to efficiently find all person with
+ a specific age, it might be a good idea to have an index on
+ the age field. This can be accomplished with the following
+ call:</p>
+ <code type="none">
+mnesia:add_table_index(person, age)
+ </code>
+ <p>Indices do not come free, they occupy space which is
+ proportional to the size of the table. They also cause insertions
+ into the table to execute slightly slower. </p>
+ </desc>
+ </func>
+ <func>
+ <name>all_keys(Tab) -> KeyList | transaction abort</name>
+ <fsummary>Return all keys in a table.</fsummary>
+ <desc>
+ <p>This function returns a list of all keys in the table
+ named <c>Tab</c>. The semantics of this function is context
+ sensitive. See <c>mnesia:activity/4</c> for more information. In
+ transaction context it acquires a read lock on the entire
+ table.</p>
+ </desc>
+ </func>
+ <func>
+ <name>async_dirty(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name>
+ <fsummary>Call the Fun in a context which is not protected by a transaction.</fsummary>
+ <desc>
+ <p>Call the <c>Fun</c> in a context which is not protected
+ by a transaction. The Mnesia function calls performed in the
+ <c>Fun</c> are mapped to the corresponding dirty
+ functions. This still involves logging, replication and
+ subscriptions, but there is no locking, local transaction
+ storage, or commit protocols involved. Checkpoint retainers
+ and indices are updated, but they will be updated dirty. As
+ for normal mnesia:dirty_* operations, the operations are
+ performed semi-asynchronously. See
+ <c>mnesia:activity/4</c> and the Mnesia User's Guide for
+ more details.
+ </p>
+ <p>It is possible to manipulate the Mnesia tables without
+ using transactions. This has some serious disadvantages, but
+ is considerably faster since the transaction manager is not
+ involved and no locks are set. A dirty operation does,
+ however, guarantee a certain level of consistency and it is
+ not possible for the dirty operations to return garbled
+ records. All dirty operations provide location transparency
+ to the programmer and a program does not have to be aware of
+ the whereabouts of a certain table in order to function.
+ </p>
+ <p><em>Note:</em>It is more than 10 times more efficient to read records dirty
+ than within a transaction.
+ </p>
+ <p>Depending on the application, it may be a good idea to use
+ the dirty functions for certain operations. Almost all
+ Mnesia functions which can be called within transactions
+ have a dirty equivalent which is much more
+ efficient. However, it must be noted that it is possible for
+ the database to be left in an inconsistent state if dirty
+ operations are used to update it. Dirty operations should
+ only be used for performance reasons when it is absolutely
+ necessary. </p>
+ <p><em>Note:</em> Calling (nesting) a <c>mnesia:[a]sync_dirty</c>
+ inside a transaction context will inherit the transaction semantics.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>backup(Opaque [, BackupMod]) -> ok | {error,Reason}</name>
+ <fsummary>Back up all tables in the database.</fsummary>
+ <desc>
+ <p>Activates a new checkpoint covering all Mnesia tables,
+ including the schema, with maximum degree of redundancy and
+ performs a backup using <c>backup_checkpoint/2/3</c>. The
+ default value of the backup callback module <c>BackupMod</c>
+ is obtained by <c>mnesia:system_info(backup_module)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>backup_checkpoint(Name, Opaque [, BackupMod]) -> ok | {error,Reason}</name>
+ <fsummary>Back up all tables in a checkpoint.</fsummary>
+ <desc>
+ <p>The tables are backed up to external media using the backup
+ module <c>BackupMod</c>. Tables with the local contents
+ property is being backed up as they exist on the current
+ node. <c>BackupMod</c> is the default backup callback
+ module obtained by
+ <c>mnesia:system_info(backup_module)</c>. See the User's
+ Guide about the exact callback interface (the
+ <c>mnesia_backup behavior</c>).</p>
+ </desc>
+ </func>
+ <func>
+ <name>change_config(Config, Value) -> {error, Reason} | {ok, ReturnValue}</name>
+ <fsummary>Change a configuration parameter.</fsummary>
+ <desc>
+ <p>The <c>Config</c> should be an atom of the following
+ configuration parameters: </p>
+ <taglist>
+ <tag><c>extra_db_nodes</c></tag>
+ <item>
+ <p><c>Value</c> is a list of nodes which Mnesia should try to connect to.
+ The <c>ReturnValue</c> will be those nodes in
+ <c>Value</c> that Mnesia are connected to.
+ <br></br>
+Note: This function shall only be used to connect to newly started ram nodes
+ (N.D.R.S.N.) with an empty schema. If for example it is used after the network
+ have been partitioned it may lead to inconsistent tables.
+ <br></br>
+Note: Mnesia may be connected to other nodes than those
+ returned in <c>ReturnValue</c>.</p>
+ </item>
+ <tag><c>dc_dump_limit</c></tag>
+ <item>
+ <p><c>Value</c> is a number. See description in
+ <c>Configuration Parameters</c> below.
+ The <c>ReturnValue</c> is the new value. Note this configuration parameter
+ is not persistent, it will be lost when mnesia stopped.</p>
+ </item>
+ </taglist>
+ </desc>
+ </func>
+ <func>
+ <name>change_table_access_mode(Tab, AccessMode) -> {aborted, R} | {atomic, ok}</name>
+ <fsummary>Change the access mode for the table.</fsummary>
+ <desc>
+ <p>The <c>AcccessMode</c> is by default the atom
+ <c>read_write</c> but it may also be set to the atom
+ <c>read_only</c>. If the <c>AccessMode</c> is set to
+ <c>read_only</c>, it means that it is not possible to perform
+ updates to the table. At startup Mnesia always loads
+ <c>read_only</c> tables locally regardless of when and if
+ Mnesia was terminated on other nodes.</p>
+ </desc>
+ </func>
+ <func>
+ <name>change_table_copy_type(Tab, Node, To) -> {aborted, R} | {atomic, ok}</name>
+ <fsummary>Change the storage type of a table.</fsummary>
+ <desc>
+ <p>For example:</p>
+ <code type="none">
+mnesia:change_table_copy_type(person, node(), disc_copies)
+ </code>
+ <p>Transforms our <c>person</c> table from a RAM table into
+ a disc based table at <c>Node</c>.
+ </p>
+ <p>This function can also be used to change the storage type of
+ the table named <c>schema</c>. The schema table can only
+ have <c>ram_copies</c> or <c>disc_copies</c> as the storage type. If the
+ storage type of the schema is <c>ram_copies</c>, no other table
+ can be disc resident on that node.</p>
+ </desc>
+ </func>
+ <func>
+ <name>change_table_load_order(Tab, LoadOrder) -> {aborted, R} | {atomic, ok}</name>
+ <fsummary>Change the load order priority for the table.</fsummary>
+ <desc>
+ <p>The <c>LoadOrder</c> priority is by default <c>0</c> (zero)
+ but may be set to any integer. The tables with the highest
+ <c>LoadOrder</c> priority will be loaded first at startup.</p>
+ </desc>
+ </func>
+ <func>
+ <name>clear_table(Tab) -> {aborted, R} | {atomic, ok}</name>
+ <fsummary>Deletes all entries in a table.</fsummary>
+ <desc>
+ <p>Deletes all entries in the table <c>Tab</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>create_schema(DiscNodes) -> ok | {error,Reason}</name>
+ <fsummary>Create a brand new schema on the specified nodes.</fsummary>
+ <desc>
+ <p>Creates a new database on disc. Various files are
+ created in the local Mnesia directory of each node. Note
+ that the directory must be unique for each node. Two nodes
+ may never share the same directory. If possible, use a local
+ disc device in order to improve performance.</p>
+ <p><c>mnesia:create_schema/1</c> fails if any of the
+ Erlang nodes given as <c>DiscNodes</c> are not alive, if
+ Mnesia is running on anyone of the nodes, or if anyone of
+ the nodes already has a schema. Use
+ <c>mnesia:delete_schema/1</c> to get rid of old faulty
+ schemas.
+ </p>
+ <p><em>Note:</em> Only nodes with disc should be
+ included in <c>DiscNodes</c>. Disc-less nodes, that is nodes
+ where all tables including the schema only resides in RAM,
+ may not be included.</p>
+ </desc>
+ </func>
+ <func>
+ <name>create_table(Name, TabDef) -> {atomic, ok} | {aborted, Reason}</name>
+ <fsummary>Create a Mnesia table called <c>Name</c>with properties as described by the argument <c>TabDef</c>.</fsummary>
+ <desc>
+ <p>This function creates a Mnesia table called <c>Name</c>
+ according to the
+ argument <c>TabDef</c>. This list must be a list of
+ <c>{Item, Value}</c> tuples, where the following values are
+ allowed:</p>
+ <list type="bulleted">
+ <item>
+ <p><c>{access_mode, Atom}</c>. The access mode is by
+ default the atom <c>read_write</c> but it may also be
+ set to the atom <c>read_only</c>. If the
+ <c>AccessMode</c> is set to <c>read_only</c>, it means
+ that it is not possible to perform updates to the table.
+ </p>
+ <p>At startup Mnesia always loads <c>read_only</c> tables
+ locally regardless of when and if Mnesia was terminated
+ on other nodes. This argument returns the access mode of
+ the table. The access mode may either be read_only or
+ read_write.
+ </p>
+ </item>
+ <item>
+ <p><c>{attributes, AtomList}</c> a list of the
+ attribute names for the records that are supposed to
+ populate the table. The default value is <c>[key, val]</c>. The table must have at least one extra
+ attribute in addition to the key.
+ </p>
+ <p>When accessing single attributes in a record, it is not
+ necessary, or even recommended, to hard code any
+ attribute names as atoms. Use the construct
+ <c>record_info(fields, RecordName)</c> instead. It can be
+ used for records of type <c>RecordName</c></p>
+ </item>
+ <item>
+ <p><c>{disc_copies, Nodelist}</c>, where
+ <c>Nodelist</c> is a list of the nodes where this table
+ is supposed to have disc copies. If a table replica is
+ of type <c>disc_copies</c>, all write operations on this
+ particular replica of the table are written to disc as
+ well as to the RAM copy of the table.
+ </p>
+ <p>It is possible
+ to have a replicated table of type <c>disc_copies</c>
+ on one node, and another type on another node. The
+ default value is <c>[]</c></p>
+ </item>
+ <item>
+ <p><c>{disc_only_copies, Nodelist}</c>, where
+ <c>Nodelist</c> is a list of the nodes where this table
+ is supposed to have <c>disc_only_copies</c>. A disc only
+ table replica is kept on disc only and unlike the other
+ replica types, the contents of the replica will not
+ reside in RAM. These replicas are considerably slower
+ than replicas held in RAM.
+ </p>
+ </item>
+ <item>
+ <p><c>{index, Intlist}</c>, where
+ <c>Intlist</c> is a list of attribute names (atoms) or
+ record fields for which Mnesia shall build and maintain
+ an extra index table. The <c>qlc</c> query compiler may
+ or may not utilize any additional indices while
+ processing queries on a table.
+ </p>
+ </item>
+ <item>
+ <p><c>{load_order, Integer}</c>. The load order
+ priority is by default <c>0</c> (zero) but may be set to
+ any integer. The tables with the highest load order
+ priority will be loaded first at startup.
+ </p>
+ </item>
+ <item>
+ <p><c>{ram_copies, Nodelist}</c>, where
+ <c>Nodelist</c> is a list of the nodes where this table
+ is supposed to have RAM copies. A table replica of type
+ <c>ram_copies</c> is obviously not written to disc on a
+ per transaction basis. It is possible to dump
+ <c>ram_copies</c> replicas to disc with the function
+ <c>mnesia:dump_tables(Tabs)</c>. The default value for
+ this attribute is <c>[node()]</c>.
+ </p>
+ </item>
+ <item>
+ <p><c>{record_name, Name}</c>, where <c>Name</c> must
+ be an atom. All records, stored in the table, must have
+ this name as the first element. It defaults to the same
+ name as the name of the table.
+ </p>
+ </item>
+ <item>
+ <p><c>{snmp, SnmpStruct}</c>. See
+ <c>mnesia:snmp_open_table/2</c> for a description of
+ <c>SnmpStruct</c>. If this attribute is present in the
+ <c>ArgList</c> to <c>mnesia:create_table/2</c>, the
+ table is immediately accessible by means of the Simple
+ Network Management Protocol (SNMP). This means that
+ applications which use SNMP to manipulate and control
+ the system can be designed easily, since Mnesia provides
+ a direct mapping between the logical tables that make up
+ an SNMP control application and the physical data which
+ makes up a Mnesia table.
+ </p>
+ </item>
+ <item>
+ <p><c>{type, Type}</c>, where <c>Type</c> must be
+ either of the atoms <c>set</c>, <c>ordered_set</c> or
+ <c>bag</c>. The default value is <c>set</c>. In a
+ <c>set</c> all records have unique keys and in a
+ <c>bag</c> several records may have the same key, but
+ the record content is unique. If a non-unique record is
+ stored the old, conflicting record(s) will simply be
+ overwritten. Note: currently 'ordered_set'
+ is not supported for 'disc_only_copies'.
+ </p>
+ </item>
+ <item>
+ <p><c>{local_content, Bool}</c>, where <c>Bool</c> must be
+ either <c>true</c> or <c>false</c>. The default value is <c>false</c>.\011 </p>
+ </item>
+ </list>
+ <p>For example, the following call creates the <c>person</c> table
+ previously defined and replicates it on 2 nodes:
+ </p>
+ <code type="none">
+mnesia:create_table(person,
+ [{ram_copies, [N1, N2]},
+ {attributes, record_info(fields,person)}]).
+ </code>
+ <p>If it was required that Mnesia build and maintain an extra index
+ table on the <c>address</c> attribute of all the <c>person</c>
+ records that are inserted in the table, the following code would be issued:
+ </p>
+ <code type="none">
+mnesia:create_table(person,
+ [{ram_copies, [N1, N2]},
+ {index, [address]},
+ {attributes, record_info(fields,person)}]).
+ </code>
+ <p>The specification of <c>index</c> and <c>attributes</c> may be
+ hard coded as <c>{index, [2]}</c> and
+ <c>{attributes, [name, age, address, salary, children]}</c>
+ respectively.
+ </p>
+ <p><c>mnesia:create_table/2</c> writes records into the
+ <c>schema</c> table. This function, as well as all other
+ schema manipulation functions, are implemented with the
+ normal transaction management system. This guarantees that
+ schema updates are performed on all nodes in an atomic
+ manner.</p>
+ </desc>
+ </func>
+ <func>
+ <name>deactivate_checkpoint(Name) -> ok | {error, Reason}</name>
+ <fsummary>Deactivate a checkpoint.</fsummary>
+ <desc>
+ <p>The checkpoint is automatically deactivated when some of
+ the tables involved have no retainer attached to them. This may
+ happen when nodes go down or when a replica is deleted.
+ Checkpoints will also be deactivated with this function.
+ <c>Name</c> is the name of an active checkpoint.</p>
+ </desc>
+ </func>
+ <func>
+ <name>del_table_copy(Tab, Node) -> {aborted, R} | {atomic, ok}</name>
+ <fsummary>Delete the replica of table <c>Tab</c>at node <c>Node</c>.</fsummary>
+ <desc>
+ <p>Deletes the replica of table <c>Tab</c> at node <c>Node</c>.
+ When the last replica is deleted with this
+ function, the table disappears entirely.
+ </p>
+ <p>This function may also be used to delete a replica of
+ the table named <c>schema</c>. Then the mnesia node will be removed.
+ Note: Mnesia must be stopped on the node first.</p>
+ </desc>
+ </func>
+ <func>
+ <name>del_table_index(Tab, AttrName) -> {aborted, R} | {atomic, ok}</name>
+ <fsummary>Delete an index in a table. </fsummary>
+ <desc>
+ <p>This function deletes the index on attribute with name
+ <c>AttrName</c> in a table.</p>
+ </desc>
+ </func>
+ <func>
+ <name>delete({Tab, Key}) -> transaction abort | ok </name>
+ <fsummary>Delete all records in table <c>Tab</c>with the key <c>Key</c>.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:delete(Tab, Key, write)</c></p>
+ </desc>
+ </func>
+ <func>
+ <name>delete(Tab, Key, LockKind) -> transaction abort | ok </name>
+ <fsummary>Delete all records in table <c>Tab</c>with the key <c>Key</c>.</fsummary>
+ <desc>
+ <p>Deletes all records in table <c>Tab</c> with the key
+ <c>Key</c>.
+ </p>
+ <p>The semantics of this function is context sensitive. See
+ <c>mnesia:activity/4</c> for more information. In transaction
+ context it acquires a lock of type <c>LockKind</c> in the
+ record. Currently the lock types <c>write</c> and
+ <c>sticky_write</c> are supported.</p>
+ </desc>
+ </func>
+ <func>
+ <name>delete_object(Record) -> transaction abort | ok </name>
+ <fsummary>Delete a record</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:delete_object(Tab, Record, write)</c> where
+ <c>Tab</c> is <c>element(1, Record)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>delete_object(Tab, Record, LockKind) -> transaction abort | ok </name>
+ <fsummary>Delete a record</fsummary>
+ <desc>
+ <p>If a table is of type <c>bag</c>, we may sometimes
+ want to delete only some of the records with a certain
+ key. This can be done with the <c>delete_object/3</c>
+ function. A complete record must be supplied to this
+ function.
+ </p>
+ <p>The semantics of this function is context sensitive. See
+ <c>mnesia:activity/4</c> for more information. In transaction
+ context it acquires a lock of type <c>LockKind</c> on the
+ record. Currently the lock types <c>write</c> and
+ <c>sticky_write</c> are supported.</p>
+ </desc>
+ </func>
+ <func>
+ <name>delete_schema(DiscNodes) -> ok | {error,Reason}</name>
+ <fsummary>Delete the schema on the given nodes</fsummary>
+ <desc>
+ <p>Deletes a database created with
+ <c>mnesia:create_schema/1</c>.
+ <c>mnesia:delete_schema/1</c> fails if any of the Erlang
+ nodes given as <c>DiscNodes</c> is not alive, or if Mnesia
+ is running on any of the nodes.
+ </p>
+ <p>After the database has been deleted, it may still be
+ possible to start Mnesia as a disc-less node. This depends on
+ how the configuration parameter <c>schema_location</c> is set.
+ </p>
+ <warning>
+ <p>This function must be used with extreme
+ caution since it makes existing persistent data
+ obsolete. Think twice before using it. </p>
+ </warning>
+ </desc>
+ </func>
+ <func>
+ <name>delete_table(Tab) -> {aborted, Reason} | {atomic, ok} </name>
+ <fsummary>Delete permanently all replicas of table <c>Tab</c>.</fsummary>
+ <desc>
+ <p>Permanently deletes all replicas of table <c>Tab</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_all_keys(Tab) -> KeyList | exit({aborted, Reason}).</name>
+ <fsummary>Dirty search for all record keys in table.</fsummary>
+ <desc>
+ <p>This is the dirty equivalent of the
+ <c>mnesia:all_keys/1</c> function.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_delete({Tab, Key}) -> ok | exit({aborted, Reason}) </name>
+ <fsummary>Dirty delete of a record.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:dirty_delete(Tab, Key)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_delete(Tab, Key) -> ok | exit({aborted, Reason}) </name>
+ <fsummary>Dirty delete of a record. </fsummary>
+ <desc>
+ <p>This is the dirty equivalent of the
+ <c>mnesia:delete/3</c> function.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_delete_object(Record) </name>
+ <fsummary>Dirty delete of a record.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:dirty_delete_object(Tab, Record)</c>
+ where <c>Tab</c> is <c>element(1, Record)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_delete_object(Tab, Record) </name>
+ <fsummary>Dirty delete of a record. </fsummary>
+ <desc>
+ <p>This is the dirty equivalent of the
+ <c>mnesia:delete_object/3</c> function.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_first(Tab) -> Key | exit({aborted, Reason}) </name>
+ <fsummary>Return the key for the first record in a table.</fsummary>
+ <desc>
+ <p>Records in <c>set</c> or <c>bag</c> tables are not ordered.
+ However, there
+ is an ordering of the records which is not known
+ to the user. Accordingly, it is possible to traverse a table by means
+ of this function in conjunction with the <c>mnesia:dirty_next/2</c>
+ function.
+ </p>
+ <p>If there are no records at all in the table, this function
+ returns the atom <c>'$end_of_table'</c>. For this reason, it
+ is highly undesirable, but not disallowed, to use this atom
+ as the key for any user records.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_index_match_object(Pattern, Pos)</name>
+ <fsummary>Dirty pattern match using index.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:dirty_index_match_object(Tab, Pattern, Pos)</c> where <c>Tab</c> is <c>element(1, Pattern)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_index_match_object(Tab, Pattern, Pos)</name>
+ <fsummary>Dirty pattern match using index.</fsummary>
+ <desc>
+ <p>This is the dirty equivalent of the
+ <c>mnesia:index_match_object/4</c> function.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_index_read(Tab, SecondaryKey, Pos)</name>
+ <fsummary>Dirty read using index.</fsummary>
+ <desc>
+ <p>This is the dirty equivalent of the
+ <c>mnesia:index_read/3</c> function.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_last(Tab) -> Key | exit({aborted, Reason}) </name>
+ <fsummary>Return the key for the last record in a table.</fsummary>
+ <desc>
+ <p>This function works exactly
+ <c>mnesia:dirty_first/1</c> but returns the last object in
+ Erlang term order for the <c>ordered_set</c> table type. For
+ all other table types, <c>mnesia:dirty_first/1</c> and
+ <c>mnesia:dirty_last/1</c> are synonyms.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_match_object(Pattern) -> RecordList | exit({aborted, Reason}).</name>
+ <fsummary>Dirty pattern match pattern.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:dirty_match_object(Tab, Pattern)</c>
+ where <c>Tab</c> is <c>element(1, Pattern)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_match_object(Tab, Pattern) -> RecordList | exit({aborted, Reason}).</name>
+ <fsummary>Dirty pattern match pattern.</fsummary>
+ <desc>
+ <p>This is the dirty equivalent of the
+ <c>mnesia:match_object/3</c> function.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_next(Tab, Key) -> Key | exit({aborted, Reason}) </name>
+ <fsummary>Return the next key in a table. </fsummary>
+ <desc>
+ <p>This function makes it possible to traverse a table
+ and perform operations on all records in the table. When
+ the end of the table is reached, the special key
+ <c>'$end_of_table'</c> is returned. Otherwise, the function
+ returns a key which can be used to read the actual record.The
+ behavior is undefined if another Erlang process performs write
+ operations on the table while it is being traversed with the
+ <c>mnesia:dirty_next/2</c> function.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_prev(Tab, Key) -> Key | exit({aborted, Reason}) </name>
+ <fsummary>Return the previous key in a table. </fsummary>
+ <desc>
+ <p>This function works exactly
+ <c>mnesia:dirty_next/2</c> but returns the previous object in
+ Erlang term order for the ordered_set table type. For
+ all other table types, <c>mnesia:dirty_next/2</c> and
+ <c>mnesia:dirty_prev/2</c> are synonyms.\011 </p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_read({Tab, Key}) -> ValueList | exit({aborted, Reason}</name>
+ <fsummary>Dirty read of records.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:dirty_read(Tab, Key)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_read(Tab, Key) -> ValueList | exit({aborted, Reason}</name>
+ <fsummary>Dirty read of records.</fsummary>
+ <desc>
+ <p>This is the dirty equivalent of the
+ <c>mnesia:read/3</c> function.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_select(Tab, MatchSpec) -> ValueList | exit({aborted, Reason}</name>
+ <fsummary>Dirty match the objects in <c>Tab</c>against <c>MatchSpec</c>.</fsummary>
+ <desc>
+ <p>This is the dirty equivalent of the
+ <c>mnesia:select/2</c> function.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_slot(Tab, Slot) -> RecordList | exit({aborted, Reason})</name>
+ <fsummary>Return the list of records that are associated with Slot in a table.</fsummary>
+ <desc>
+ <p>This function can be used to traverse a table in a
+ manner similar to the <c>mnesia:dirty_next/2</c> function.
+ A table has a number of slots which range from 0 (zero) to some
+ unknown upper bound. The function
+ <c>mnesia:dirty_slot/2</c> returns the special atom
+ <c>'$end_of_table'</c> when the end of the table is reached.
+ The behavior of this function is undefined if a write
+ operation is performed on the table while it is being
+ traversed.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_update_counter({Tab, Key}, Incr) -> NewVal | exit({aborted, Reason})</name>
+ <fsummary>Dirty update of a counter record.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:dirty_update_counter(Tab, Key, Incr)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_update_counter(Tab, Key, Incr) -> NewVal | exit({aborted, Reason})</name>
+ <fsummary>Dirty update of a counter record.</fsummary>
+ <desc>
+ <p>There are no special counter records in Mnesia. However,
+ records of the form <c>{Tab, Key, Integer}</c> can be used
+ as (possibly disc resident) counters, when <c>Tab</c> is a
+ <c>set</c>. This function updates a counter with a
+ positive or negative number. However, counters can never become less
+ than zero. There are two significant differences between
+ this function and the action of first reading the record,
+ performing the arithmetics, and then writing the record:</p>
+ <list type="bulleted">
+ <item>It is much more efficient</item>
+ <item><c>mnesia:dirty_update_counter/3</c> is
+ performed as an atomic operation despite the fact that it is not
+ protected by a transaction.</item>
+ </list>
+ <p>If two processes perform <c>mnesia:dirty_update_counter/3</c>
+ simultaneously, both updates will take effect without the
+ risk of loosing one of the updates. The new value
+ <c>NewVal</c> of the counter is returned.</p>
+ <p>If <c>Key</c> don't exits, a new record is created with the value
+ <c>Incr</c> if it is larger than 0, otherwise it is set to 0.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_write(Record) -> ok | exit({aborted, Reason})</name>
+ <fsummary>Dirty write of a record.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:dirty_write(Tab, Record)</c>
+ where <c>Tab</c> is <c>element(1, Record)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dirty_write(Tab, Record) -> ok | exit({aborted, Reason})</name>
+ <fsummary>Dirty write of a record.</fsummary>
+ <desc>
+ <p>This is the dirty equivalent of <c>mnesia:write/3</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dump_log() -> dumped</name>
+ <fsummary>Perform a user initiated dump of the local log file.</fsummary>
+ <desc>
+ <p>Performs a user initiated dump of the local log file.
+ This is usually not necessary since Mnesia, by default,
+ manages this automatically.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dump_tables(TabList) -> {atomic, ok} | {aborted, Reason}</name>
+ <fsummary>Dump all RAM tables to disc.</fsummary>
+ <desc>
+ <p>This function dumps a set of <c>ram_copies</c> tables
+ to disc. The next time the system is started, these tables
+ are initiated with the data found in the files that are the
+ result of this dump. None of the tables may have disc
+ resident replicas.</p>
+ </desc>
+ </func>
+ <func>
+ <name>dump_to_textfile(Filename) </name>
+ <fsummary>Dump local tables into a text file.</fsummary>
+ <desc>
+ <p>Dumps all local tables of a mnesia system into a text file
+ which can then be edited (by means of a normal text editor)
+ and then later be reloaded with
+ <c>mnesia:load_textfile/1</c>. Only use this function for
+ educational purposes. Use other functions to deal with real
+ backups.</p>
+ </desc>
+ </func>
+ <func>
+ <name>error_description(Error) -> String </name>
+ <fsummary>Return a string describing a particular Mnesia error.</fsummary>
+ <desc>
+ <p>All Mnesia transactions, including all the schema
+ update functions, either return the value <c>{atomic, Val}</c> or the tuple <c>{aborted, Reason}</c>. The
+ <c>Reason</c> can be either of the following atoms. The
+ <c>error_description/1</c> function returns a descriptive
+ string which describes the error.
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>nested_transaction</c>. Nested transactions are
+ not allowed in this context.
+ </p>
+ </item>
+ <item>
+ <p><c>badarg</c>. Bad or invalid argument, possibly
+ bad type.
+ </p>
+ </item>
+ <item>
+ <p><c>no_transaction</c>. Operation not allowed
+ outside transactions.
+ </p>
+ </item>
+ <item>
+ <p><c>combine_error</c>. Table options were illegally
+ combined.
+ </p>
+ </item>
+ <item>
+ <p><c>bad_index</c>. Index already exists or was out
+ of bounds.
+ </p>
+ </item>
+ <item>
+ <p><c>already_exists</c>. Schema option is already set.
+ </p>
+ </item>
+ <item>
+ <p><c>index_exists</c>. Some operations cannot be performed on
+ tabs with index.
+ </p>
+ </item>
+ <item>
+ <p><c>no_exists</c>. Tried to perform operation on
+ non-existing, or not alive, item.
+ </p>
+ </item>
+ <item>
+ <p><c>system_limit</c>. Some system_limit was exhausted.
+ </p>
+ </item>
+ <item>
+ <p><c>mnesia_down</c>. A transaction involving
+ records at some remote node which died while
+ transaction was executing. Record(s) are no longer
+ available elsewhere in the network.
+ </p>
+ </item>
+ <item>
+ <p><c>not_a_db_node</c>. A node which does not exist
+ in the schema was mentioned.
+ </p>
+ </item>
+ <item>
+ <p><c>bad_type</c>. Bad type on some arguments.
+ </p>
+ </item>
+ <item>
+ <p><c>node_not_running</c>. Node not running.
+ </p>
+ </item>
+ <item>
+ <p><c>truncated_binary_file</c>. Truncated binary in file.
+ </p>
+ </item>
+ <item>
+ <p><c>active</c>. Some delete operations require that
+ all active records are removed.
+ </p>
+ </item>
+ <item>
+ <p><c>illegal</c>. Operation not supported on record.
+ </p>
+ </item>
+ </list>
+ <p>The <c>Error</c> may be <c>Reason</c>,
+ <c>{error, Reason}</c>, or <c>{aborted, Reason}</c>. The
+ <c>Reason</c> may be an atom or a tuple with <c>Reason</c>
+ as an atom in the first field.</p>
+ </desc>
+ </func>
+ <func>
+ <name>ets(Fun, [, Args]) -> ResultOfFun | exit(Reason)</name>
+ <fsummary>Call the Fun in a raw context which is not protected by a transaction.</fsummary>
+ <desc>
+ <p>Call the <c>Fun</c> in a raw context which is not protected by
+ a transaction. The Mnesia function call is performed in the
+ <c>Fun</c> are performed directly on the local <c>ets</c> tables on
+ the assumption that the local storage type is
+ <c>ram_copies</c> and the tables are not replicated to other
+ nodes. Subscriptions are not triggered and checkpoints are
+ not updated, but it is extremely fast. This function can
+ also be applied to <c>disc_copies</c> tables if all
+ operations are read only. See <c>mnesia:activity/4</c>
+ and the Mnesia User's Guide for more details.</p>
+ <p><em>Note:</em> Calling (nesting) a <c>mnesia:ets</c>
+ inside a transaction context will inherit the transaction semantics.</p>
+ </desc>
+ </func>
+ <func>
+ <name>first(Tab) -> Key | transaction abort </name>
+ <fsummary>Return the key for the first record in a table.</fsummary>
+ <desc>
+ <p>Records in <c>set</c> or <c>bag</c> tables are not ordered.
+ However, there
+ is an ordering of the records which is not known
+ to the user. Accordingly, it is possible to traverse a table by means
+ of this function in conjunction with the <c>mnesia:next/2</c>
+ function.
+ </p>
+ <p>If there are no records at all in the table, this function
+ returns the atom <c>'$end_of_table'</c>. For this reason, it
+ is highly undesirable, but not disallowed, to use this atom
+ as the key for any user records.</p>
+ </desc>
+ </func>
+ <func>
+ <name>foldl(Function, Acc, Table) -> NewAcc | transaction abort </name>
+ <fsummary>Call Function for each record in Table </fsummary>
+ <desc>
+ <p>Iterates over the table <c>Table</c> and calls
+ <c>Function(Record, NewAcc)</c> for each <c>Record</c> in the table.
+ The term returned from <c>Function</c> will be used as the second
+ argument in the next call to the <c>Function</c>.
+ </p>
+ <p><c>foldl</c> returns the same term as the last call to
+ <c>Function</c> returned.</p>
+ </desc>
+ </func>
+ <func>
+ <name>foldr(Function, Acc, Table) -> NewAcc | transaction abort </name>
+ <fsummary>Call Function for each record in Table </fsummary>
+ <desc>
+ <p>This function works exactly as
+ <c>foldl/3</c> but iterates the table in the opposite order
+ for the <c>ordered_set</c> table type. For
+ all other table types, <c>foldr/3</c> and
+ <c>foldl/3</c> are synonyms.</p>
+ </desc>
+ </func>
+ <func>
+ <name>force_load_table(Tab) -> yes | ErrorDescription </name>
+ <fsummary>Force a table to be loaded into the system </fsummary>
+ <desc>
+ <p>The Mnesia algorithm for table load might lead to a
+ situation where a table cannot be loaded. This situation
+ occurs when a node is started and Mnesia concludes, or
+ suspects, that another copy of the table was active after
+ this local copy became inactive due to a system crash.
+ </p>
+ <p>If this situation is not acceptable, this function can be
+ used to override the strategy of the Mnesia table load
+ algorithm. This could lead to a situation where some
+ transaction effects are lost with a inconsistent database as
+ result, but for some applications high availability is more
+ important than consistent data.</p>
+ </desc>
+ </func>
+ <func>
+ <name>index_match_object(Pattern, Pos) -> transaction abort | ObjList</name>
+ <fsummary>Match records and utilizes index information.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:index_match_object(Tab, Pattern, Pos, read)</c> where <c>Tab</c> is <c>element(1, Pattern)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>index_match_object(Tab, Pattern, Pos, LockKind) -> transaction abort | ObjList</name>
+ <fsummary>Match records and utilizes index information.</fsummary>
+ <desc>
+ <p>In a manner similar to the <c>mnesia:index_read/3</c>
+ function, we can also utilize any index information when we
+ try to match records. This function takes a pattern which
+ obeys the same rules as the <c>mnesia:match_object/3</c>
+ function with the exception that this function requires the
+ following conditions:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p>The table <c>Tab</c> must have an index on
+ position <c>Pos</c>.
+ </p>
+ </item>
+ <item>
+ <p>The element in position <c>Pos</c> in
+ <c>Pattern</c> must be bound. <c>Pos</c> may either be
+ an integer (#record.Field), or an attribute name.</p>
+ </item>
+ </list>
+ <p>The two index search functions described here are
+ automatically invoked when searching tables with <c>qlc</c>
+ list comprehensions and also when using the low level
+ <c>mnesia:[dirty_]match_object</c> functions.
+ </p>
+ <p></p>
+ <p>The semantics of this function is context sensitive. See
+ <c>mnesia:activity/4</c> for more information. In transaction
+ context it acquires a lock of type <c>LockKind</c> on the
+ entire table or on a single record. Currently, the lock type
+ <c>read</c> is supported.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>index_read(Tab, SecondaryKey, Pos) -> transaction abort | RecordList </name>
+ <fsummary>Read records via index table. </fsummary>
+ <desc>
+ <p>Assume there is an index on position <c>Pos</c> for a
+ certain record type. This function can be used to read the
+ records without knowing the actual key for the record. For
+ example, with an index in position 1 of the <c>person</c>
+ table, the call <c>mnesia:index_read(person, 36, #person.age)</c> returns a list of all persons with age
+ equal to 36. <c>Pos</c> may also be an attribute name
+ (atom), but if the notation <c>mnesia:index_read(person, 36, age)</c> is used, the field position will be searched for in
+ runtime, for each call.
+ </p>
+ <p>The semantics of this function is context sensitive. See
+ <c>mnesia:activity/4</c> for more information. In transaction
+ context it acquires a read lock on the entire table.</p>
+ </desc>
+ </func>
+ <func>
+ <name>info() -> ok </name>
+ <fsummary>Print some information about the system on the tty.</fsummary>
+ <desc>
+ <p>Prints some information about the system on the tty.
+ This function may be used even if Mnesia is not started.
+ However, more information will be displayed if Mnesia is
+ started.</p>
+ </desc>
+ </func>
+ <func>
+ <name>install_fallback(Opaque) -> ok | {error,Reason}</name>
+ <fsummary>Install a backup as fallback.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:install_fallback(Opaque, Args)</c> where
+ <c>Args</c> is <c>[{scope, global}]</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>install_fallback(Opaque), BackupMod) -> ok | {error,Reason}</name>
+ <fsummary>Install a backup as fallback.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:install_fallback(Opaque, Args)</c> where
+ <c>Args</c> is <c>[{scope, global}, {module, BackupMod}]</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>install_fallback(Opaque, Args) -> ok | {error,Reason}</name>
+ <fsummary>Install a backup as fallback.</fsummary>
+ <desc>
+ <p>This function is used to install a backup as fallback. The
+ fallback will be used to restore the database at the next
+ start-up. Installation of fallbacks requires Erlang to be up
+ and running on all the involved nodes, but it does not
+ matter if Mnesia is running or not. The installation of the
+ fallback will fail if the local node is not one of the disc
+ resident nodes in the backup.
+ </p>
+ <p><c>Args</c> is a list of the following tuples:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>{module, BackupMod}</c>.
+ All accesses of the backup media is performed via a
+ callback module named <c>BackupMod</c>. The
+ <c>Opaque</c> argument is forwarded to the callback
+ module which may interpret it as it wish. The default
+ callback module is called <c>mnesia_backup</c> and it
+ interprets the <c>Opaque</c> argument as a local
+ filename. The default for this module is also
+ configurable via the <c>-mnesia mnesia_backup</c>
+ configuration parameter. </p>
+ </item>
+ <item>
+ <p><c>{scope, Scope}</c>
+ The <c>Scope</c> of a fallback may either be
+ <c>global</c> for the entire database or <c>local</c>
+ for one node. By default, the installation of a fallback
+ is a global operation which either is performed all
+ nodes with disc resident schema or none. Which nodes
+ that are disc resident or not, is determined from the
+ schema info in the backup.</p>
+ <p>If the <c>Scope</c> of the operation is <c>local</c>
+ the fallback will only be installed on the local node.</p>
+ </item>
+ <item>
+ <p><c>{mnesia_dir, AlternateDir}</c>
+ This argument is only valid if the scope of the
+ installation is <c>local</c>. Normally the installation
+ of a fallback is targeted towards the Mnesia directory
+ as configured with the <c>-mnesia dir</c> configuration
+ parameter. But by explicitly supplying an
+ <c>AlternateDir</c> the fallback will be installed there
+ regardless of the Mnesia directory configuration
+ parameter setting. After installation of a fallback on
+ an alternate Mnesia directory that directory is fully
+ prepared for usage as an active Mnesia directory.
+ </p>
+ <p>This is a somewhat dangerous feature which must be
+ used with care. By unintentional mixing of directories
+ you may easily end up with a inconsistent database, if
+ the same backup is installed on more than one directory.</p>
+ </item>
+ </list>
+ </desc>
+ </func>
+ <func>
+ <name>is_transaction() -> boolean </name>
+ <fsummary>Check if code is running in a transaction.</fsummary>
+ <desc>
+ <p>When this function is executed inside a transaction context
+ it returns <c>true</c>, otherwise <c>false</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>last(Tab) -> Key | transaction abort </name>
+ <fsummary>Return the key for the last record in a table.</fsummary>
+ <desc>
+ <p>This function works exactly
+ <c>mnesia:first/1</c> but returns the last object in
+ Erlang term order for the <c>ordered_set</c> table type. For
+ all other table types, <c>mnesia:first/1</c> and
+ <c>mnesia:last/1</c> are synonyms.</p>
+ </desc>
+ </func>
+ <func>
+ <name>load_textfile(Filename)</name>
+ <fsummary>Load tables from a text file.</fsummary>
+ <desc>
+ <p>Loads a series of definitions and data found in the
+ text file (generated with <c>mnesia:dump_to_textfile/1</c>)
+ into Mnesia. This function also starts Mnesia and possibly
+ creates a new schema. This function is intended for
+ educational purposes only and using other functions to deal
+ with real backups, is recommended.</p>
+ </desc>
+ </func>
+ <func>
+ <name>lock(LockItem, LockKind) -> Nodes | ok | transaction abort</name>
+ <fsummary>Explicit grab lock.</fsummary>
+ <desc>
+ <p>Write locks are normally acquired on all nodes where a
+ replica of the table resides (and is active). Read locks are
+ acquired on one node (the local node if a local
+ replica exists). Most of the context sensitive access functions
+ acquire an implicit lock if they are invoked in a
+ transaction context. The granularity of a lock may either
+ be a single record or an entire table.
+ </p>
+ <p>The normal usage is to call the function without checking
+ the return value since it exits if it fails and the
+ transaction is restarted by the transaction manager. It
+ returns all the locked nodes if a write lock is acquired, and
+ <c>ok</c> if it was a read lock.
+ </p>
+ <p>This function <c>mnesia:lock/2</c> is intended to support
+ explicit locking on tables but also intended for situations
+ when locks need to be acquired regardless of how tables are
+ replicated. Currently, two <c>LockKind</c>'s are supported:
+ </p>
+ <taglist>
+ <tag><c>write</c></tag>
+ <item>
+ <p>Write locks are exclusive, which means that if one
+ transaction manages to acquire a write lock on an item,
+ no other transaction may acquire any kind of lock on the
+ same item.
+ </p>
+ </item>
+ <tag><c>read</c></tag>
+ <item>
+ <p>Read locks may be shared, which means that if one
+ transaction manages to acquire a read lock on an item,
+ other transactions may also acquire a read lock on the
+ same item. However, if someone has a read lock no one can
+ acquire a write lock at the same item. If some one has a
+ write lock no one can acquire a read lock nor
+ a write lock at the same item.</p>
+ </item>
+ </taglist>
+ <p>Conflicting lock requests are automatically queued if there
+ is no risk of a deadlock. Otherwise the transaction must be
+ aborted and executed again. Mnesia does this automatically
+ as long as the upper limit of maximum <c>retries</c> is not
+ reached. See <c>mnesia:transaction/3</c> for the details.
+ </p>
+ <p>For the sake of completeness sticky write locks will also
+ be described here even if a sticky write lock is not
+ supported by this particular function:
+ </p>
+ <taglist>
+ <tag><c>sticky_write</c></tag>
+ <item>
+ <p>Sticky write locks are a mechanism which can be used
+ to optimize write lock acquisition. If your application
+ uses replicated tables mainly for fault tolerance (as
+ opposed to read access optimization purpose), sticky
+ locks may be the best option available.
+ </p>
+ <p>When a sticky write lock is acquired, all nodes will be
+ informed which node is locked. Subsequently,
+ sticky lock requests from the same node will be
+ performed as a local operation without any
+ communication with other nodes. The sticky lock
+ lingers on the node even after the transaction has
+ ended. See the Mnesia User's Guide for more information.</p>
+ </item>
+ </taglist>
+ <p>Currently, two kinds of <c>LockItem</c>'s are supported by
+ this function:
+ </p>
+ <taglist>
+ <tag><c>{table, Tab}</c></tag>
+ <item>
+ <p>This acquires a lock of type <c>LockKind</c> on the
+ entire table <c>Tab</c>.
+ </p>
+ </item>
+ <tag><c>{global, GlobalKey, Nodes}</c></tag>
+ <item>
+ <p>This acquires a lock of type <c>LockKind</c> on the
+ global resource <c>GlobalKey</c>. The lock is acquired
+ on all active nodes in the <c>Nodes</c> list. </p>
+ </item>
+ </taglist>
+ <p>Locks are released when the outermost transaction ends.
+ </p>
+ <p>The semantics of this function is context sensitive. See
+ <c>mnesia:activity/4</c> for more information. In transaction
+ context it acquires locks otherwise it just ignores the
+ request.</p>
+ </desc>
+ </func>
+ <func>
+ <name>match_object(Pattern) ->transaction abort | RecList </name>
+ <fsummary>Match <c>Pattern</c>for records. </fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:match_object(Tab, Pattern, read)</c> where
+ <c>Tab</c> is <c>element(1, Pattern)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>match_object(Tab, Pattern, LockKind) ->transaction abort | RecList </name>
+ <fsummary>Match <c>Pattern</c>for records. </fsummary>
+ <desc>
+ <p>This function takes a pattern with 'don't care' variables
+ denoted as a '_' parameter. This function returns a list of
+ records which matched the pattern. Since the second element
+ of a record in a table is considered to be the key for the
+ record, the performance of this function depends on whether
+ this key is bound or not.
+ </p>
+ <p>For example, the call <c>mnesia:match_object(person, {person, '_', 36, '_', '_'}, read)</c> returns a list of all person records with an
+ age field of thirty-six (36).
+ </p>
+ <p>The function <c>mnesia:match_object/3</c>
+ automatically uses indices if these exist. However, no
+ heuristics are performed in order to select the best
+ index.
+ </p>
+ <p>The semantics of this function is context sensitive. See
+ <c>mnesia:activity/4</c> for more information. In transaction
+ context it acquires a lock of type <c>LockKind</c> on the
+ entire table or a single record. Currently, the lock type
+ <c>read</c> is supported.</p>
+ </desc>
+ </func>
+ <func>
+ <name>move_table_copy(Tab, From, To) -> {aborted, Reason} | {atomic, ok}</name>
+ <fsummary>Move the copy of table <c>Tab</c>from node<c>From</c>to node <c>To</c>.</fsummary>
+ <desc>
+ <p>Moves the copy of table <c>Tab</c> from node
+ <c>From</c> to node <c>To</c>.
+ </p>
+ <p>The storage type is preserved. For example, a RAM table
+ moved from one node remains a RAM on the new node. It is
+ still possible for other transactions to read and write in
+ the table while it is being moved.
+ </p>
+ <p>This function cannot be used on <c>local_content</c> tables.</p>
+ </desc>
+ </func>
+ <func>
+ <name>next(Tab, Key) -> Key | transaction abort </name>
+ <fsummary>Return the next key in a table. </fsummary>
+ <desc>
+ <p>This function makes it possible to traverse a table
+ and perform operations on all records in the table. When
+ the end of the table is reached, the special key
+ <c>'$end_of_table'</c> is returned. Otherwise, the function
+ returns a key which can be used to read the actual record.</p>
+ </desc>
+ </func>
+ <func>
+ <name>prev(Tab, Key) -> Key | transaction abort </name>
+ <fsummary>Return the previous key in a table. </fsummary>
+ <desc>
+ <p>This function works exactly
+ <c>mnesia:next/2</c> but returns the previous object in
+ Erlang term order for the ordered_set table type. For
+ all other table types, <c>mnesia:next/2</c> and
+ <c>mnesia:prev/2</c> are synonyms.\011 </p>
+ </desc>
+ </func>
+ <func>
+ <name>read({Tab, Key}) -> transaction abort | RecordList </name>
+ <fsummary>Read records(s) with a given key. </fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:read(Tab, Key, read)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>read(Tab, Key) -> transaction abort | RecordList </name>
+ <fsummary>Read records(s) with a given key. </fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:read(Tab, Key, read)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>read(Tab, Key, LockKind) -> transaction abort | RecordList </name>
+ <fsummary>Read records(s) with a given key. </fsummary>
+ <desc>
+ <p>This function reads all records from table <c>Tab</c> with
+ key <c>Key</c>. This function has the same semantics
+ regardless of the location of <c>Tab</c>. If the table is
+ of type <c>bag</c>, the <c>mnesia:read(Tab, Key)</c> can
+ return an arbitrarily long list. If the table is of type
+ <c>set</c>, the list is either of length 1, or <c>[]</c>.
+ </p>
+ <p>The semantics of this function is context sensitive. See
+ <c>mnesia:activity/4</c> for more information. In transaction
+ context it acquires a lock of type
+ <c>LockKind</c>. Currently, the lock types <c>read</c>,
+ <c>write</c> and <c>sticky_write</c> are supported.
+ </p>
+ <p>If the user wants to update the record it is more efficient to
+ use <c>write/sticky_write</c> as the LockKind.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>read_lock_table(Tab) -> ok | transaction abort</name>
+ <fsummary>Set a read lock on an entire table.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:lock({table, Tab}, read)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>report_event(Event) -> ok</name>
+ <fsummary>Report a user event to Mnesia's event handler.</fsummary>
+ <desc>
+ <p>When tracing a system of Mnesia applications it is useful
+ to be able to interleave Mnesia's own events with
+ application related events that give information about the
+ application context.
+ </p>
+ <p>Whenever the application begins a
+ new and demanding Mnesia task, or if it is entering a new
+ interesting phase in its execution, it may be a good idea to
+ use <c>mnesia:report_event/1</c>. The <c>Event</c> may be
+ any term and generates a <c>{mnesia_user, Event}</c> event
+ for any processes that subscribe to Mnesia system
+ events.</p>
+ </desc>
+ </func>
+ <func>
+ <name>restore(Opaque, Args) -> {atomic, RestoredTabs} |{aborted, Reason}</name>
+ <fsummary>Online restore of backup.</fsummary>
+ <desc>
+ <p>With this function, tables may be restored online from a
+ backup without restarting Mnesia. <c>Opaque</c> is forwarded
+ to the backup module. <c>Args</c> is a list of the following
+ tuples:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>{module,BackupMod}</c> The backup module
+ <c>BackupMod</c> will be used to access the backup
+ media. If omitted, the default backup module will be
+ used.
+ </p>
+ </item>
+ <item><c>{skip_tables, TabList}</c> Where <c>TabList</c>
+ is a list of tables which should not be read from the
+ backup.
+ </item>
+ <item><c>{clear_tables, TabList}</c> Where
+ <c>TabList</c> is a list of tables which should be
+ cleared, before the records from the backup are inserted,
+ ie. all records in the tables are deleted before the
+ tables are restored. Schema information about the tables
+ is not cleared or read from backup.
+ </item>
+ <item><c>{keep_tables, TabList}</c> Where <c>TabList</c>
+ is a list of tables which should be not be cleared, before
+ the records from the backup are inserted, i.e. the records
+ in the backup will be added to the records in the table.
+ Schema information about the tables is not cleared or read
+ from backup.
+ </item>
+ <item><c>{recreate_tables, TabList}</c> Where
+ <c>TabList</c> is a list of tables which should be
+ re-created, before the records from the backup are
+ inserted. The tables are first deleted and then created with
+ the schema information from the backup. All the nodes in the
+ backup needs to be up and running.
+ </item>
+ <item><c>{default_op, Operation}</c> Where <c>Operation</c> is
+ one of the following operations <c>skip_tables</c>,
+ <c>clear_tables</c>, <c>keep_tables</c> or
+ <c>recreate_tables</c>. The default operation specifies
+ which operation should be used on tables from the backup
+ which are not specified in any of the lists above. If
+ omitted, the operation <c>clear_tables</c> will be used.
+ </item>
+ </list>
+ <p>The affected tables are write locked during the
+ restoration, but regardless of the lock conflicts caused by
+ this, the applications can continue to do their work while
+ the restoration is being performed. The restoration is
+ performed as one single transaction.
+ </p>
+ <p>If the database is
+ huge, it may not be possible to restore it online. In such
+ cases, the old database must be restored by installing a
+ fallback and then restart.</p>
+ </desc>
+ </func>
+ <func>
+ <name>s_delete({Tab, Key}) -> ok | transaction abort </name>
+ <fsummary>Set sticky lock and delete records.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:delete(Tab, Key, sticky_write)</c></p>
+ </desc>
+ </func>
+ <func>
+ <name>s_delete_object(Record) -> ok | transaction abort </name>
+ <fsummary>Set sticky lock and delete record.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:delete_object(Tab, Record, sticky_write)</c> where <c>Tab</c> is <c>element(1, Record)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>s_write(Record) -> ok | transaction abort </name>
+ <fsummary>Write <c>Record</c>and sets stick lock.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:write(Tab, Record, sticky_write)</c>
+ where <c>Tab</c> is <c>element(1, Record)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>schema() -> ok </name>
+ <fsummary>Print information about all table definitions on the tty. </fsummary>
+ <desc>
+ <p>Prints information about all table definitions on the tty.</p>
+ </desc>
+ </func>
+ <func>
+ <name>schema(Tab) -> ok </name>
+ <fsummary>Print information about one table definition on the tty.</fsummary>
+ <desc>
+ <p>Prints information about one table definition on the tty.</p>
+ </desc>
+ </func>
+ <func>
+ <name>select(Tab, MatchSpec [, Lock]) -> transaction abort | [Object] </name>
+ <fsummary>Match the objects in <c>Tab</c>against <c>MatchSpec</c>.</fsummary>
+ <desc>
+ <p>Matches the objects in the table <c>Tab</c> using a
+ match_spec as described in the ERTS Users Guide. Optionally a lock
+ <c>read</c> or <c>write</c> can be given as the third
+ argument, default is <c>read</c>. The return value depends
+ on the <c>MatchSpec</c>.</p>
+ <p><em>Note:</em> for best performance <c>select</c> should
+ be used before any modifying operations are done on that table
+ in the same transaction, i.e. don't use <c>write</c> or <c>delete</c>
+ before a <c>select</c>.</p>
+ <p>In its simplest forms the match_spec's look like this:</p>
+ <list type="bulleted">
+ <item>MatchSpec = [MatchFunction]</item>
+ <item>MatchFunction = {MatchHead, [Guard], [Result]}</item>
+ <item>MatchHead = tuple() | record()</item>
+ <item>Guard = {"Guardtest name", ...}</item>
+ <item>Result = "Term construct"</item>
+ </list>
+ <p>See the ERTS Users Guide and <c>ets</c> documentation for a
+ complete description of the select.</p>
+ <p>For example to find the names of all male persons with an age over 30 in table
+ Tab do:</p>
+ <code type="none">
+\011 MatchHead = #person{name='$1', sex=male, age='$2', _='_'},
+\011 Guard = {'>', '$2', 30},
+\011 Result = '$1',
+\011 mnesia:select(Tab,[{MatchHead, [Guard], [Result]}]),
+ </code>
+ </desc>
+ </func>
+ <func>
+ <name>select(Tab, MatchSpec, NObjects, Lock) -> transaction abort | {[Object],Cont} | '$end_of_table'</name>
+ <fsummary>Match the objects in <c>Tab</c>against <c>MatchSpec</c>.</fsummary>
+ <desc>
+ <p>Matches the objects in the table <c>Tab</c> using a
+ match_spec as described in ERTS users guide, and returns
+ a chunk of terms and a continuation, the wanted number
+ of returned terms is specified by the <c>NObjects</c> argument.
+ The lock argument can be <c>read</c> or <c>write</c>.
+ The continuation should be used as argument to <c>mnesia:select/1</c>,
+ if more or all answers are needed.</p>
+ <p><em>Note:</em> for best performance <c>select</c> should
+ be used before any modifying operations are done on that
+ table in the same transaction, i.e. don't use
+ <c>mnesia:write</c> or <c>mnesia:delete</c> before a
+ <c>mnesia:select</c>. For efficiency the <c>NObjects</c> is
+ a recommendation only and the result may contain anything
+ from an empty list to all available results. </p>
+ </desc>
+ </func>
+ <func>
+ <name>select(Cont) -> transaction abort | {[Object],Cont} | '$end_of_table'</name>
+ <fsummary>Continues selecting objects. </fsummary>
+ <desc>
+ <p>Selects more objects with the match specification initiated
+ by <c>mnesia:select/4</c>.
+ </p>
+ <p><em>Note:</em> Any modifying operations, i.e. <c>mnesia:write</c>
+ or <c>mnesia:delete</c>, that are done between the <c>mnesia:select/4</c>
+ and <c>mnesia:select/1</c> calls will not be visible in the result.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_debug_level(Level) -> OldLevel</name>
+ <fsummary>Change the internal debug level of Mnesia</fsummary>
+ <desc>
+ <p>Changes the internal debug level of Mnesia. See the
+ chapter about configuration parameters for details.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_master_nodes(MasterNodes) -> ok | {error, Reason} </name>
+ <fsummary>Set the master nodes for all tables</fsummary>
+ <desc>
+ <p>For each table Mnesia will determine its replica nodes
+ (<c>TabNodes</c>) and invoke <c>mnesia:set_master_nodes(Tab, TabMasterNodes)</c> where <c>TabMasterNodes</c> is the
+ intersection of <c>MasterNodes</c> and <c>TabNodes</c>. See
+ <c>mnesia:set_master_nodes/2</c> about the semantics.</p>
+ </desc>
+ </func>
+ <func>
+ <name>set_master_nodes(Tab, MasterNodes) -> ok | {error, Reason} </name>
+ <fsummary>Set the master nodes for a table</fsummary>
+ <desc>
+ <p>If the application detects that there has been a
+ communication failure (in a potentially partitioned network) which
+ may have caused an inconsistent database, it may use the
+ function <c>mnesia:set_master_nodes(Tab, MasterNodes)</c> to
+ define from which nodes each table will be loaded.
+ At startup Mnesia's normal table load algorithm will be
+ bypassed and the table will be loaded from one of the master
+ nodes defined for the table, regardless of when and if Mnesia
+ was terminated on other nodes. The <c>MasterNodes</c> may only
+ contain nodes where the table has a replica and if the
+ <c>MasterNodes</c> list is empty, the master node recovery
+ mechanism for the particular table will be reset and the
+ normal load mechanism will be used at next restart.
+ </p>
+ <p>The master node setting is always local and it may be
+ changed regardless of whether Mnesia is started or not.
+ </p>
+ <p>The database may also become inconsistent if the
+ <c>max_wait_for_decision</c> configuration parameter is used
+ or if <c>mnesia:force_load_table/1</c> is used.</p>
+ </desc>
+ </func>
+ <func>
+ <name>snmp_close_table(Tab) -> {aborted, R} | {atomic, ok}</name>
+ <fsummary>Remove the possibility for SNMP to manipulate the table.</fsummary>
+ <desc>
+ <p>Removes the possibility for SNMP to manipulate the
+ table.</p>
+ </desc>
+ </func>
+ <func>
+ <name>snmp_get_mnesia_key(Tab, RowIndex) -> {ok, Key} | undefined</name>
+ <fsummary>Get the corresponding Mnesia key from an SNMP index.</fsummary>
+ <type>
+ <v>Tab ::= atom()</v>
+ <v>RowIndex ::= [integer()]</v>
+ <v>Key ::= key() | {key(), key(), ...}</v>
+ <v>key() ::= integer() | string() | [integer()]</v>
+ </type>
+ <desc>
+ <p>Transforms an SNMP index to the corresponding Mnesia key.
+ If the SNMP table has multiple keys, the key is a tuple of
+ the key columns.</p>
+ </desc>
+ </func>
+ <func>
+ <name>snmp_get_next_index(Tab, RowIndex) -> {ok, NextIndex} | endOfTable</name>
+ <fsummary>Get the index of the next lexicographical row.</fsummary>
+ <type>
+ <v>Tab ::= atom()</v>
+ <v>RowIndex ::= [integer()]</v>
+ <v>NextIndex ::= [integer()]</v>
+ </type>
+ <desc>
+ <p>The <c>RowIndex</c> may specify a non-existing row.
+ Specifically, it might be the empty list. Returns the index
+ of the next lexicographical row. If <c>RowIndex</c> is the
+ empty list, this function will return the index of the first row
+ in the table.</p>
+ </desc>
+ </func>
+ <func>
+ <name>snmp_get_row(Tab, RowIndex) -> {ok, Row} | undefined</name>
+ <fsummary>Retrieve a row indexed by an SNMP index.</fsummary>
+ <type>
+ <v>Tab ::= atom()</v>
+ <v>RowIndex ::= [integer()]</v>
+ <v>Row ::= record(Tab)</v>
+ </type>
+ <desc>
+ <p>Makes it possible to read a row by its SNMP index. This
+ index is specified as an SNMP OBJECT IDENTIFIER, a list of
+ integers.</p>
+ </desc>
+ </func>
+ <func>
+ <name>snmp_open_table(Tab, SnmpStruct) -> {aborted, R} | {atomic, ok}</name>
+ <fsummary>Organize a Mnesia table as an SNMP table.</fsummary>
+ <type>
+ <v>Tab ::= atom()</v>
+ <v>SnmpStruct ::= [{key, type()}]</v>
+ <v>type() ::= type_spec() | {type_spec(), type_spec(), ...}</v>
+ <v>type_spec() ::= fix_string | string | integer</v>
+ </type>
+ <desc>
+ <p>It is possible to establish a direct one to one mapping
+ between Mnesia tables and SNMP tables. Many
+ telecommunication applications are controlled and monitored
+ by the SNMP protocol. This connection between Mnesia and
+ SNMP makes it simple and convenient to achieve this.
+ </p>
+ <p>The <c>SnmpStruct</c> argument is a list of SNMP
+ information. Currently, the only information needed is
+ information about the key types in the table. It is not
+ possible to handle multiple keys in Mnesia, but many SNMP
+ tables have multiple keys. Therefore, the following
+ convention is used: if a table has multiple keys, these must
+ always be stored as a tuple of the keys. Information about
+ the key types is specified as a tuple of atoms describing
+ the types. The only significant type is
+ <c>fix_string</c>. This means that a string has fixed
+ size. For example:
+ </p>
+ <code type="none">
+mnesia:snmp_open_table(person, [{key, string}])
+ </code>
+ <p>causes the <c>person</c> table to be ordered as an SNMP
+ table.
+ </p>
+ <p>Consider the following schema for a table of company
+ employees. Each employee is identified by department number
+ and name. The other table column stores the telephone number:
+ </p>
+ <code type="none">
+mnesia:create_table(employee,
+ [{snmp, [{key, {integer, string}}]},
+ {attributes, record_info(fields, employees)}]),
+ </code>
+ <p>The corresponding SNMP table would have three columns;
+ <c>department</c>, <c>name</c> and <c>telno</c>.
+ </p>
+ <p>It is possible to have table columns that are not visible
+ through the SNMP protocol. These columns must be the last
+ columns of the table. In the previous example, the SNMP
+ table could have columns <c>department</c> and <c>name</c>
+ only. The application could then use the <c>telno</c> column
+ internally, but it would not be visible to the SNMP
+ managers.
+ </p>
+ <p>In a table monitored by SNMP, all elements must be
+ integers, strings, or lists of integers.
+ </p>
+ <p>When a table is SNMP ordered, modifications are more
+ expensive than usual, O(logN). And more memory is used.
+ </p>
+ <p><em>Note:</em>Only the lexicographical SNMP ordering is
+ implemented in Mnesia, not the actual SNMP monitoring.</p>
+ </desc>
+ </func>
+ <func>
+ <name>start() -> ok | {error, Reason} </name>
+ <fsummary>Start a local Mnesia system.</fsummary>
+ <desc>
+ <p>The start-up procedure for a set of Mnesia nodes is a
+ fairly complicated operation. A Mnesia system consists of a set
+ of nodes, with Mnesia started locally on all
+ participating nodes. Normally, each node has a directory where
+ all the Mnesia files are written. This directory will be
+ referred to as the Mnesia directory. Mnesia may also be
+ started on disc-less nodes. See <c>mnesia:create_schema/1</c>
+ and the Mnesia User's Guide for more information about disc-less
+ nodes.
+ </p>
+ <p>The set of nodes which makes up a Mnesia system is kept in
+ a schema and it is possible to add and remove Mnesia nodes
+ from the schema. The initial schema is normally created on
+ disc with the function <c>mnesia:create_schema/1</c>. On
+ disc-less nodes, a tiny default schema is generated each time
+ Mnesia is started. During the start-up procedure, Mnesia
+ will exchange schema information between the nodes in order
+ to verify that the table definitions are compatible.
+ </p>
+ <p>Each schema has a unique cookie which may be regarded as a
+ unique schema identifier. The cookie must be the same on all
+ nodes where Mnesia is supposed to run. See the Mnesia
+ User's Guide for more information about these details.
+ </p>
+ <p>The schema file, as well as all other files which Mnesia
+ needs, are kept in the Mnesia directory. The command line
+ option <c>-mnesia dir Dir</c> can be used to specify the
+ location of this directory to the Mnesia system. If no such
+ command line option is found, the name of the directory
+ defaults to <c>Mnesia.Node</c>.
+ </p>
+ <p><c>application:start(mnesia)</c> may also be used.</p>
+ </desc>
+ </func>
+ <func>
+ <name>stop() -> stopped </name>
+ <fsummary>Stop Mnesia locally.</fsummary>
+ <desc>
+ <p>Stops Mnesia locally on the current node.
+ </p>
+ <p><c>application:stop(mnesia)</c> may also be used.</p>
+ </desc>
+ </func>
+ <func>
+ <name>subscribe(EventCategory)</name>
+ <fsummary>Subscribe to events of type <c>EventCategory</c>.</fsummary>
+ <desc>
+ <p>Ensures that a copy of all events of type
+ <c>EventCategory</c> are sent to the caller. The event
+ types available are described in the Mnesia User's Guide.</p>
+ </desc>
+ </func>
+ <func>
+ <name>sync_dirty(Fun, [, Args]) -> ResultOfFun | exit(Reason) </name>
+ <fsummary>Call the Fun in a context which is not protected by a transaction.</fsummary>
+ <desc>
+ <p>Call the <c>Fun</c> in a context which is not protected
+ by a transaction. The Mnesia function calls performed in the
+ <c>Fun</c> are mapped to the corresponding dirty functions.
+ It is performed in almost the same context as
+ <c>mnesia:async_dirty/1,2</c>. The difference is that the
+ operations are performed synchronously. The caller waits for
+ the updates to be performed on all active replicas before
+ the <c>Fun</c> returns. See <c>mnesia:activity/4</c> and the
+ Mnesia User's Guide for more details.</p>
+ </desc>
+ </func>
+ <func>
+ <name>sync_transaction(Fun, [[, Args], Retries]) -> {aborted, Reason} | {atomic, ResultOfFun} </name>
+ <fsummary>Synchronously execute a transaction.</fsummary>
+ <desc>
+ <p>This function waits until data have been committed and
+ logged to disk (if disk is used) on every involved node before
+ it returns, otherwise it behaves as
+ <c>mnesia:transaction/[1,2,3]</c>.</p>
+ <p>This functionality can be used to avoid that one process may overload
+ a database on another node.</p>
+ </desc>
+ </func>
+ <func>
+ <name>system_info(InfoKey) -> Info | exit({aborted, Reason})</name>
+ <fsummary>Return information about the Mnesia system</fsummary>
+ <desc>
+ <p>Returns information about the Mnesia system, such as
+ transaction statistics, db_nodes, and configuration parameters.
+ Valid keys are:</p>
+ <list type="bulleted">
+ <item>
+ <p><c>all</c>. This argument returns a list of all
+ local system information. Each element is a
+ <c>{InfoKey, InfoVal}</c> tuples.<em>Note:</em> New <c>InfoKey</c>'s may
+ be added and old undocumented <c>InfoKey</c>'s may be removed without
+ notice.</p>
+ </item>
+ <item>
+ <p><c>access_module</c>. This argument returns the name of
+ the module which is configured to be the activity access
+ callback module.
+ </p>
+ </item>
+ <item>
+ <p><c>auto_repair</c>. This argument returns
+ <c>true</c> or <c>false</c> to indicate if Mnesia is
+ configured to invoke the auto repair facility on corrupted
+ disc files.
+ </p>
+ </item>
+ <item>
+ <p><c>backup_module</c>. This argument returns the name of
+ the module which is configured to be the backup
+ callback module.
+ </p>
+ </item>
+ <item>
+ <p><c>checkpoints</c>. This argument
+ returns a list of the names of the
+ checkpoints currently active on this node.
+ </p>
+ </item>
+ <item>
+ <p><c>event_module</c>. This argument returns the name of
+ the module which is the event handler callback module.
+ </p>
+ </item>
+ <item>
+ <p><c>db_nodes</c>. This argument returns
+ the nodes which make up the persistent database. Disc
+ less nodes will only be included in the list of nodes if
+ they explicitly has been added to the schema, e.g. with
+ <c>mnesia:add_table_copy/3</c>. The function can be
+ invoked even if Mnesia is not yet running.
+ </p>
+ </item>
+ <item>
+ <p><c>debug</c>. This argument returns the current
+ debug level of Mnesia.
+ </p>
+ </item>
+ <item>
+ <p><c>directory</c>. This argument returns the name of
+ the Mnesia directory. It can be invoked even if Mnesia is
+ not yet running.
+ </p>
+ </item>
+ <item>
+ <p><c>dump_log_load_regulation</c>. This argument
+ returns a boolean which tells whether Mnesia is
+ configured to load regulate the dumper process or not.
+ This feature is temporary and will disappear in future
+ releases.
+ </p>
+ </item>
+ <item>
+ <p><c>dump_log_time_threshold</c>. This argument
+ returns the time threshold for transaction log dumps in
+ milliseconds.
+ </p>
+ </item>
+ <item>
+ <p><c>dump_log_update_in_place</c>. This argument
+ returns a boolean which tells whether Mnesia is
+ configured to perform the updates in the dets files
+ directly or if the updates should be performed in a copy
+ of the dets files.
+ </p>
+ </item>
+ <item>
+ <p><c>dump_log_write_threshold</c>. This argument
+ returns the write threshold for transaction log dumps as
+ the number of writes to the transaction log.
+ </p>
+ </item>
+ <item>
+ <p><c>extra_db_nodes</c>. This argument returns a list
+ of extra db_nodes to be contacted at start-up.
+ </p>
+ </item>
+ <item>
+ <p><c>fallback_activated</c>. This argument returns
+ true if a fallback is activated, otherwise false.
+ </p>
+ </item>
+ <item>
+ <p><c>held_locks</c>. This argument returns a list of
+ all locks held by the local Mnesia lock manager.
+ </p>
+ </item>
+ <item>
+ <p><c>is_running</c>. This argument returns <c>yes</c>
+ or <c>no</c> to indicate if Mnesia is running. It may
+ also return <c>starting</c> or <c>stopping</c>. Can be
+ invoked even if Mnesia is not yet running.
+ </p>
+ </item>
+ <item>
+ <p><c>local_tables</c>. This argument returns a list
+ of all tables which are configured to reside locally.
+ </p>
+ </item>
+ <item>
+ <p><c>lock_queue</c>. This argument returns a list of
+ all transactions that are queued for execution by the
+ local lock manager.
+ </p>
+ </item>
+ <item>
+ <p><c>log_version</c>. This argument returns the
+ version number of the Mnesia transaction log format.
+ </p>
+ </item>
+ <item>
+ <p><c>master_node_tables</c>. This argument returns a
+ list of all tables with at least one master node.
+ </p>
+ </item>
+ <item>
+ <p><c>protocol_version</c>. This argument
+ returns the version number
+ of the Mnesia inter-process communication protocol.
+ </p>
+ </item>
+ <item>
+ <p><c>running_db_nodes</c>. This argument returns a
+ list of nodes where Mnesia currently is running. This
+ function can be invoked even if Mnesia is not yet
+ running, but it will then have slightly different
+ semantics. If Mnesia is down on the local node, the
+ function will return those other <c>db_nodes</c> and
+ <c>extra_db_nodes</c> that for the moment are up and
+ running. If Mnesia is started, the function will return
+ those nodes that Mnesia on the local node is fully
+ connected to. Only those nodes that Mnesia has exchanged
+ schema information with are included as
+ <c>running_db_nodes</c>. After the merge of schemas, the
+ local Mnesia system is fully operable and applications
+ may perform access of remote replicas. Before the schema
+ merge Mnesia will only operate locally. Sometimes there
+ may be more nodes included in the
+ <c>running_db_nodes</c> list than all <c>db_nodes</c>
+ and <c>extra_db_nodes</c> together.
+ </p>
+ </item>
+ <item>
+ <p><c>schema_location</c>. This argument returns the
+ initial schema location.
+ </p>
+ </item>
+ <item>
+ <p><c>subscribers</c>. This argument returns a list of
+ local processes currently subscribing to system events.
+ </p>
+ </item>
+ <item>
+ <p><c>tables</c>. This argument returns a list of all
+ locally known tables.
+ </p>
+ </item>
+ <item>
+ <p><c>transactions</c>. This argument returns a list
+ of all currently active local transactions.
+ </p>
+ </item>
+ <item>
+ <p><c>transaction_failures</c>. This argument returns
+ a number which indicates how many transactions have
+ failed since Mnesia was started.
+ </p>
+ </item>
+ <item>
+ <p><c>transaction_commits</c>. This argument returns a
+ number which indicates how many transactions have
+ terminated successfully since Mnesia was started.
+ </p>
+ </item>
+ <item>
+ <p><c>transaction_restarts</c>. This argument returns
+ a number which indicates how many transactions have been
+ restarted since Mnesia was started.
+ </p>
+ </item>
+ <item>
+ <p><c>transaction_log_writes</c>. This argument
+ returns a number which indicates the number of write
+ operation that have been performed to the transaction
+ log since start-up.
+ </p>
+ </item>
+ <item>
+ <p><c>use_dir</c>. This argument returns a boolean
+ which indicates whether the Mnesia directory is used or
+ not. Can be invoked even if Mnesia is not yet running.
+ </p>
+ </item>
+ <item>
+ <p><c>version</c>. This argument returns the current
+ version number of Mnesia.
+ </p>
+ </item>
+ </list>
+ </desc>
+ </func>
+ <func>
+ <name>table(Tab [,[Option]]) -> QueryHandle </name>
+ <fsummary>Return a QLC query handle.</fsummary>
+ <desc>
+ <p> <marker id="qlc_table"></marker>
+Returns a QLC (Query List Comprehension) query handle, see
+ <seealso marker="stdlib:qlc">qlc(3)</seealso>.The module <c>qlc</c> implements a query language, it
+ can use mnesia tables as sources of data. Calling
+ <c>mnesia:table/1,2</c> is the means to make the <c>mnesia</c>
+ table <c>Tab</c> usable to QLC.</p>
+ <p>The list of Options may contain mnesia options or QLC
+ options, the following options are recognized by Mnesia:
+ <c>{traverse, SelectMethod},{lock, Lock},{n_objects,Number}</c>, any other option is forwarded
+ to QLC. The <c>lock</c> option may be <c>read</c> or
+ <c>write</c>, default is <c>read</c>. The option
+ <c>n_objects</c> specifies (roughly) the number of objects
+ returned from mnesia to QLC. Queries to remote tables may
+ need a larger chunks to reduce network overhead, default
+ <c>100</c> objects at a time are returned. The option
+ <c>traverse</c> determines the method to traverse the whole
+ table (if needed), the default method is <c>select</c>:</p>
+ <list type="bulleted">
+ <item>
+ <p><c>select</c>. The table is traversed by calling
+ <c>mnesia:select/4</c> and <c>mnesia:select/1</c>. The
+ match specification (the second argument of <c>select/3</c>)
+ is assembled by QLC: simple filters are
+ translated into equivalent match specifications while
+ more complicated filters have to be applied to all
+ objects returned by <c>select/3</c> given a match
+ specification that matches all objects.</p>
+ </item>
+ <item>
+ <p><c>{select, MatchSpec}</c>. As for <c>select</c>
+ the table is traversed by calling <c>mnesia:select/3</c> and
+ <c>mnesia:select/1</c>. The difference is that the match
+ specification is explicitly given. This is how to state
+ match specifications that cannot easily be expressed
+ within the syntax provided by QLC.</p>
+ </item>
+ </list>
+ </desc>
+ </func>
+ <func>
+ <name>table_info(Tab, InfoKey) -> Info | exit({aborted, Reason})</name>
+ <fsummary>Return local information about table.</fsummary>
+ <desc>
+ <p>The <c>table_info/2</c> function takes two arguments.
+ The first is the name of a Mnesia table, the second is one of
+ the following keys:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>all</c>. This argument returns a list of all
+ local table information. Each element is a <c>{InfoKey, ItemVal}</c> tuples. <em>Note:</em> New <c>InfoItem</c>'s may be
+ added and old undocumented <c>InfoItem</c>'s may be removed without
+ notice.</p>
+ </item>
+ <item>
+ <p><c>access_mode</c>. This argument returns the
+ access mode of the table. The access mode may either be
+ read_only or read_write.
+ </p>
+ </item>
+ <item>
+ <p><c>arity</c>. This argument returns the arity of
+ records in the table as specified in the schema.
+ </p>
+ </item>
+ <item>
+ <p><c>attributes</c>. This argument returns the table
+ attribute names which are specified in the schema.
+ </p>
+ </item>
+ <item>
+ <p><c>checkpoints</c>. This argument returns the names
+ of the currently active checkpoints which involves this
+ table on this node.
+ </p>
+ </item>
+ <item>
+ <p><c>cookie</c>. This argument returns a table cookie
+ which is a unique system generated identifier for the
+ table. The cookie is used internally to ensure that two
+ different table definitions using the same table name
+ cannot accidentally be intermixed. The cookie is
+ generated when the table is initially created.
+ </p>
+ </item>
+ <item>
+ <p><c>disc_copies</c>. This argument returns the nodes
+ where a disc_copy of the table resides according to the
+ schema.
+ </p>
+ </item>
+ <item>
+ <p><c>disc_only_copies </c>. This argument returns the
+ nodes where a disc_only_copy of the table resides
+ according to the schema.
+ </p>
+ </item>
+ <item>
+ <p><c>index</c>. This argument returns the list of
+ index position integers for the table.
+ </p>
+ </item>
+ <item>
+ <p><c>load_node</c>. This argument returns the name of
+ the node that Mnesia loaded the table from. The
+ structure of the returned value is unspecified but may
+ be useful for debugging purposes.
+ </p>
+ </item>
+ <item>
+ <p><c>load_order</c>. This argument returns the load
+ order priority of the table. It is an integer and
+ defaults to <c>0</c> (zero).
+ </p>
+ </item>
+ <item>
+ <p><c>load_reason</c>. This argument returns the
+ reason of why Mnesia decided to load the table. The
+ structure of the returned value is unspecified but may
+ be useful for debugging purposes.
+ </p>
+ </item>
+ <item>
+ <p><c>local_content</c>. This argument returns
+ <c>true</c> or <c>false</c> to indicate whether the
+ table is configured to have locally unique content on
+ each node.
+ </p>
+ </item>
+ <item>
+ <p><c>master_nodes</c>. This argument returns the
+ master nodes of a table.
+ </p>
+ </item>
+ <item>
+ <p><c>memory</c>. This argument returns the number of
+ words allocated to the table on this node.
+ </p>
+ </item>
+ <item>
+ <p><c>ram_copies</c>. This argument returns the nodes
+ where a ram_copy of the table resides according to the
+ schema.
+ </p>
+ </item>
+ <item>
+ <p><c>record_name</c>. This argument returns the
+ record name, common for all records in the table
+ </p>
+ </item>
+ <item>
+ <p><c>size</c>. This argument returns the number of
+ records inserted in the table.
+ </p>
+ </item>
+ <item>
+ <p><c>snmp</c>. This argument returns the SNMP struct.
+ <c>[]</c>meaning that the table currently has no SNMP
+ properties.
+ </p>
+ </item>
+ <item>
+ <p><c>storage_type</c>.This argument returns the local
+ storage type of the table. It can be <c>disc_copies</c>,
+ <c>ram_copies</c>, <c>disc_only_copies</c>, or the atom
+ <c>unknown</c>. <c>unknown</c> is returned for all
+ tables which only reside remotely.
+ </p>
+ </item>
+ <item>
+ <p><c>subscribers</c>. This argument returns a list
+ of local processes currently subscribing to local table
+ events which involve this table on this node.
+ </p>
+ </item>
+ <item>
+ <p><c>type</c>. This argument returns the table type,
+ which is either <c>bag</c>, <c>set</c> or <c>ordered_set</c>..
+ </p>
+ </item>
+ <item>
+ <p><c>user_properties</c>. This argument returns the
+ user associated table properties of the table. It is a
+ list of the stored property records.
+ </p>
+ </item>
+ <item>
+ <p><c>version</c>. This argument returns the current
+ version of the table definition. The table version is
+ incremented when the table definition is changed. The
+ table definition may be incremented directly when the
+ table definition has been changed in a schema
+ transaction, or when a committed table definition is
+ merged with table definitions from other nodes during
+ start-up.
+ </p>
+ </item>
+ <item>
+ <p><c>where_to_read</c>.This argument returns the node
+ where the table can be read. If the value <c>nowhere</c>
+ is returned, the table is not loaded, or it resides at a
+ remote node which is not running.
+ </p>
+ </item>
+ <item>
+ <p><c>where_to_write</c>. This argument returns a list
+ of the nodes that currently hold an active replica of
+ the table.
+ </p>
+ </item>
+ <item>
+ <p><c>wild_pattern</c>. This argument returns a
+ structure which can be given to the various match
+ functions for a certain table. A record tuple is where all
+ record fields have the value <c>'_'</c>.
+ </p>
+ </item>
+ </list>
+ </desc>
+ </func>
+ <func>
+ <name>transaction(Fun [[, Args], Retries]) -> {aborted, Reason} | {atomic, ResultOfFun}</name>
+ <fsummary>Execute a transaction.</fsummary>
+ <desc>
+ <p>This function executes the functional object <c>Fun</c>
+ with arguments <c>Args</c> as a transaction.
+ </p>
+ <p>The code which executes inside the transaction
+ can consist of a series of table manipulation functions.
+ If something goes wrong inside the transaction as a result of a
+ user error or a certain table not being available, the
+ entire transaction is aborted and the function
+ <c>transaction/1</c> returns the tuple
+ <c>{aborted, Reason}</c>.
+ </p>
+ <p>If all is well, <c>{atomic, ResultOfFun}</c> is returned where
+ <c>ResultOfFun</c> is the value of the last expression in
+ <c>Fun</c>.
+ </p>
+ <p>A function which adds a family to the database can be
+ written as follows if we have a structure <c>{family, Father, Mother, ChildrenList}</c>:
+ </p>
+ <code type="none">
+add_family({family, F, M, Children}) ->
+ ChildOids = lists:map(fun oid/1, Children),
+ Trans = fun() ->
+ mnesia:write(F#person{children = ChildOids},
+ mnesia:write(M#person{children = ChildOids},
+ Write = fun(Child) -> mnesia:write(Child) end,
+ lists:foreach(Write, Children)
+ end,
+ mnesia:transaction(Trans).
+
+oid(Rec) -> {element(1, Rec), element(2, Rec)}.
+ </code>
+ <p>This code adds a set of people to the database. Running this code
+ within one transaction will ensure that either the whole
+ family is added to the database, or the whole transaction
+ aborts. For example, if the last child is badly formatted,
+ or the executing process terminates due to an
+ <c>'EXIT'</c> signal while executing the family code, the
+ transaction aborts. Accordingly, the situation where half a
+ family is added can never occur.
+ </p>
+ <p>It is also useful to update the database within a transaction
+ if several processes concurrently update the same records.
+ For example, the function <c>raise(Name, Amount)</c>, which
+ adds <c>Amount</c> to the salary field of a person, should
+ be implemented as follows:
+ </p>
+ <code type="none">
+raise(Name, Amount) ->
+ mnesia:transaction(fun() ->
+ case mnesia:wread({person, Name}) of
+ [P] ->
+ Salary = Amount + P#person.salary,
+ P2 = P#person{salary = Salary},
+ mnesia:write(P2);
+ _ ->
+ mnesia:abort("No such person")
+ end
+ end).
+ </code>
+ <p>When this function executes within a transaction,
+ several processes running on different nodes can concurrently
+ execute the <c>raise/2</c> function without interfering
+ with each other.
+ </p>
+ <p>Since Mnesia detects deadlocks, a transaction can be
+ restarted any number of times. This function will attempt a restart as specified in
+ <c>Retries</c>. <c>Retries</c> must
+ be an integer greater than 0 or the atom <c>infinity</c>. Default is
+ <c>infinity</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>transform_table(Tab, Fun, NewAttributeList, NewRecordName) -> {aborted, R} | {atomic, ok} </name>
+ <fsummary>Change format on all records in table. <c>Tab</c></fsummary>
+ <desc>
+ <p>This function applies the argument <c>Fun</c> to all
+ records in the table. <c>Fun</c> is a function which takes a
+ record of the old type and returns a transformed record of the
+ new type. The <c>Fun</c> argument can also be the atom
+ <c>ignore</c>, it indicates that only the meta data about the table will
+ be updated. Usage of <c>ignore</c> is not recommended but included
+ as a possibility for the user do to his own transform.
+ <c>NewAttributeList</c> and <c>NewRecordName</c>
+ specifies the attributes and the new record type of converted
+ table. Table name will always remain unchanged, if the
+ record_name is changed only the mnesia functions which
+ uses table identifiers will work, e.g. <c>mnesia:write/3</c>
+ will work but <c>mnesia:write/1</c> will not.</p>
+ </desc>
+ </func>
+ <func>
+ <name>transform_table(Tab, Fun, NewAttributeList) -> {aborted, R} | {atomic, ok} </name>
+ <fsummary>Change format on all records in table. <c>Tab</c></fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:transform_table(Tab, Fun, NewAttributeList, RecName)</c>
+ where <c>RecName</c> is <c>mnesia:table_info(Tab, record_name)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>traverse_backup(Source, [SourceMod,] Target, [TargetMod,] Fun, Acc) -> {ok, LastAcc} | {error, Reason}</name>
+ <fsummary>Traversal of a backup.</fsummary>
+ <desc>
+ <p>With this function it is possible to iterate over a backup,
+ either for the purpose of transforming it into a new backup,
+ or just reading it. The arguments are explained briefly
+ below. See the Mnesia User's Guide for additional
+ details.
+ </p>
+ <list type="bulleted">
+ <item><c>SourceMod</c> and <c>TargetMod</c> are the names of
+ the modules which actually access the backup
+ media.
+ </item>
+ <item><c>Source</c> and <c>Target</c> are opaque data used
+ exclusively by the modules <c>SourceMod</c> and
+ <c>TargetMod</c> for the purpose of initializing the
+ backup media.
+ </item>
+ <item><c>Acc</c> is an initial accumulator value.
+ </item>
+ <item><c>Fun(BackupItems, Acc)</c> is applied to each item in
+ the backup. The Fun must return a tuple
+ <c>{BackupItems,NewAcc}</c>, where <c>BackupItems</c> is
+ a list of valid backup items, and <c>NewAcc</c> is a new
+ accumulator value. The returned backup items are written
+ in the target backup.
+ </item>
+ <item><c>LastAcc</c> is the last accumulator value. This is
+ the last <c>NewAcc</c> value that was returned by <c>Fun</c>.
+ </item>
+ </list>
+ </desc>
+ </func>
+ <func>
+ <name>uninstall_fallback() -> ok | {error,Reason}</name>
+ <fsummary>Uninstall a fallback.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:uninstall_fallback([{scope, global}])</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>uninstall_fallback(Args) -> ok | {error,Reason}</name>
+ <fsummary>Uninstall a fallback.</fsummary>
+ <desc>
+ <p>This function is used to de-install a fallback before it
+ has been used to restore the database. This is normally a
+ distributed operation that is either performed on all
+ nodes with disc resident schema or none. Uninstallation of
+ fallbacks requires Erlang to be up and running on all
+ involved nodes, but it does not matter if Mnesia is running
+ or not. Which nodes that are considered as disc-resident
+ nodes is determined from the schema info in the local
+ fallback.
+ </p>
+ <p><c>Args</c> is a list of the following tuples:
+ </p>
+ <list type="bulleted">
+ <item>
+ <p><c>{module, BackupMod}</c>.
+ See <c>mnesia:install_fallback/2</c> about the
+ semantics.</p>
+ </item>
+ <item>
+ <p><c>{scope, Scope}</c>
+ See <c>mnesia:install_fallback/2</c> about the
+ semantics.</p>
+ </item>
+ <item>
+ <p><c>{mnesia_dir, AlternateDir}</c>
+ See <c>mnesia:install_fallback/2</c> about the
+ semantics.</p>
+ </item>
+ </list>
+ </desc>
+ </func>
+ <func>
+ <name>unsubscribe(EventCategory)</name>
+ <fsummary>Subscribe to events of type <c>EventCategory</c>.</fsummary>
+ <desc>
+ <p>Stops sending events of type
+ <c>EventCategory</c> to the caller.</p>
+ </desc>
+ </func>
+ <func>
+ <name>wait_for_tables(TabList,Timeout) -> ok | {timeout, BadTabList} | {error, Reason} </name>
+ <fsummary>Wait for tables to be accessible.</fsummary>
+ <desc>
+ <p>Some applications need to wait for certain tables to
+ be accessible in order to do useful work.
+ <c>mnesia:wait_for_tables/2</c> hangs until all tables in the
+ <c>TabList</c> are accessible, or until <c>timeout</c> is
+ reached.</p>
+ </desc>
+ </func>
+ <func>
+ <name>wread({Tab, Key}) -> transaction abort | RecordList </name>
+ <fsummary>Read records with given key.</fsummary>
+ <desc>
+ <p>Invoke <c>mnesia:read(Tab, Key, write)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>write(Record) -> transaction abort | ok </name>
+ <fsummary>Writes a record into the database.</fsummary>
+ <desc>
+ <p>Invoke <c>mnesia:write(Tab, Record, write)</c> where
+ <c>Tab</c> is <c>element(1, Record)</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>write(Tab, Record, LockKind) -> transaction abort | ok </name>
+ <fsummary>Write an record into the database.</fsummary>
+ <desc>
+ <p>Writes the record <c>Record</c> to the table <c>Tab</c>.
+ </p>
+ <p>The function returns <c>ok</c>, or aborts if an error
+ occurs. For example, the transaction aborts if no
+ <c>person</c> table exists.
+ </p>
+ <p>The semantics of this function is context sensitive. See
+ <c>mnesia:activity/4</c> for more information. In transaction
+ context it acquires a lock of type <c>LockKind</c>. The
+ following lock types are supported: <c>write</c> and
+ <c>sticky_write</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>write_lock_table(Tab) -> ok | transaction abort</name>
+ <fsummary>Set write lock on an entire table.</fsummary>
+ <desc>
+ <p>Invokes <c>mnesia:lock({table, Tab}, write)</c>.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>Configuration Parameters</title>
+ <p>Mnesia reads the following application configuration
+ parameters:</p>
+ <list type="bulleted">
+ <item>
+ <p><c>-mnesia access_module Module</c>. The
+ name of the Mnesia activity access callback module. The default is
+ <c>mnesia</c>.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia auto_repair true | false</c>. This flag controls
+ whether Mnesia will try to automatically repair
+ files that have not been properly closed. The default is
+ <c>true</c>.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia backup_module Module</c>. The
+ name of the Mnesia backup callback module. The default is
+ <c>mnesia_backup</c>.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia debug Level</c>
+ Controls the debug level of Mnesia.
+ Possible values are:</p>
+ <taglist>
+ <tag><c>none</c></tag>
+ <item>
+ <p>No trace outputs at all. This is the default setting.
+ </p>
+ </item>
+ <tag><c>verbose</c></tag>
+ <item>
+ <p>Activates tracing of important debug events. These
+ debug events generate <c>{mnesia_info, Format, Args}</c>
+ system events. Processes may subscribe to these events with
+ <c>mnesia:subscribe/1</c>. The events are always sent to Mnesia's
+ event handler.
+ </p>
+ </item>
+ <tag><c>debug</c></tag>
+ <item>
+ <p>Activates all events at the verbose level plus full
+ trace of all debug events. These debug events generate
+ <c>{mnesia_info, Format, Args}</c> system events. Processes may
+ subscribe to these events with <c>mnesia:subscribe/1</c>. The
+ events are always sent to the Mnesia event handler. On this
+ debug level, the Mnesia event handler starts subscribing to
+ updates in the schema table.
+ </p>
+ </item>
+ <tag><c>trace</c></tag>
+ <item>
+ <p>Activates all events at the level debug. On this
+ debug level, the Mnesia event handler starts subscribing to
+ updates on all Mnesia tables. This level is only intended
+ for debugging small toy systems since many large
+ events may be generated.
+ </p>
+ </item>
+ <tag><c>false</c></tag>
+ <item>
+ <p>An alias for none.
+ </p>
+ </item>
+ <tag><c>true</c></tag>
+ <item>
+ <p>An alias for debug.
+ </p>
+ </item>
+ </taglist>
+ </item>
+ <item>
+ <p><c>-mnesia core_dir Directory</c>. The name of the
+ directory where Mnesia core files is stored or
+ false. Setting it implies that also ram only nodes, will
+ generate a core file if a crash occurs. </p>
+ </item>
+ <item>
+ <p><c>-mnesia dc_dump_limit Number</c>.
+ Controls how often <c>disc_copies</c> tables are dumped from memory.
+ Tables are dumped when
+ <c>filesize(Log) > (filesize(Tab)/Dc_dump_limit)</c>.
+ Lower values reduces cpu overhead but increases disk space and
+ startup times. The default is 4.</p>
+ </item>
+ <item>
+ <p><c>-mnesia dir Directory</c>. The name of the directory
+ where all Mnesia data is stored. The name of the directory must
+ be unique for the current node. Two nodes may, under no
+ circumstances, share the same Mnesia directory. The results are
+ totally unpredictable.</p>
+ </item>
+ <item>
+ <p><c>-mnesia dump_log_load_regulation true | false</c>.
+ Controls if the log dumps should be performed as fast as
+ possible or if the dumper should do its own load
+ regulation. This feature is temporary and will disappear in a
+ future release. The default is <c>false</c>.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia dump_log_update_in_place true | false</c>.
+ Controls if log dumps are performed on a copy of
+ the original data file, or if the log dump is
+ performed on the original data file. The default is <c>true</c></p>
+ </item>
+ <item>
+ <p><c>-mnesia dump_log_write_threshold Max</c>, where
+ <c>Max</c> is an integer which specifies the maximum number of writes
+ allowed to the transaction log before a new dump of the log
+ is performed. It defaults to 100 log writes.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia dump_log_time_threshold Max</c>,
+ where <c>Max</c> is an integer which
+ specifies the dump log interval in milliseconds. It defaults
+ to 3 minutes. If a dump has not been performed within
+ <c>dump_log_time_threshold</c> milliseconds, then a new dump is
+ performed regardless of how many writes have been
+ performed.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia event_module Module</c>. The
+ name of the Mnesia event handler callback module. The default is
+ <c>mnesia_event</c>.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia extra_db_nodes Nodes</c> specifies a list of
+ nodes, in addition to the ones found in the schema, with which
+ Mnesia should also establish contact. The default value
+ is the empty list <c>[]</c>.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia fallback_error_function {UserModule, UserFunc}</c>
+ specifies a user supplied callback function
+ which will be called if a fallback is installed and mnesia
+ goes down on another node. Mnesia will call the function
+ with one argument the name of the dying node, e.g.
+ <c>UserModule:UserFunc(DyingNode)</c>.
+ Mnesia should be restarted or else
+ the database could be inconsistent.
+ The default behaviour is to terminate mnesia.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia max_wait_for_decision Timeout</c>. Specifies
+ how long Mnesia will wait for other nodes to share their
+ knowledge regarding the outcome of an unclear transaction. By
+ default the <c>Timeout</c> is set to the atom
+ <c>infinity</c>, which implies that if Mnesia upon startup
+ encounters a "heavyweight transaction" whose outcome is
+ unclear, the local Mnesia will wait until Mnesia is started
+ on some (in worst cases all) of the other nodes that were
+ involved in the interrupted transaction. This is a very rare
+ situation, but when/if it happens, Mnesia does not guess if
+ the transaction on the other nodes was committed or aborted.
+ Mnesia will wait until it knows the outcome and then act
+ accordingly.
+ </p>
+ <p>If <c>Timeout</c> is set to an integer value in
+ milliseconds, Mnesia will force "heavyweight transactions"
+ to be finished, even if the outcome of the transaction for
+ the moment is unclear. After <c>Timeout</c> milliseconds,
+ Mnesia will commit/abort the transaction and continue with
+ the startup. This may lead to a situation where the
+ transaction is committed on some nodes and aborted on other
+ nodes. If the transaction was a schema transaction, the
+ inconsistency may be fatal.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia no_table_loaders NUMBER</c> specifies the number of
+ parallel table loaders during start. More loaders can be good if the
+ network latency is high or if many tables contains few records.
+ The default value is <c>2</c>.
+ </p>
+ </item>
+ <item>
+ <p><c>-mnesia schema_location Loc</c> controls where
+ Mnesia will look for its schema. The parameter
+ <c>Loc</c> may be one of the following atoms: </p>
+ <taglist>
+ <tag><c>disc</c></tag>
+ <item>
+ <p>Mandatory disc. The schema is assumed to be located
+ in the Mnesia directory. If the schema cannot be found,
+ Mnesia refuses to start. This is the old behavior.
+ </p>
+ </item>
+ <tag><c>ram</c></tag>
+ <item>
+ <p>Mandatory RAM. The schema resides in RAM
+ only. At start-up, a tiny new schema is generated. This
+ default schema just contains the definition of the schema
+ table and only resides on the local node. Since no other
+ nodes are found in the default schema, the configuration
+ parameter <c>extra_db_nodes</c> must be used in
+ order to let the
+ node share its table definitions with other nodes. (The
+ <c>extra_db_nodes</c> parameter may also be used on disc based nodes.)
+ </p>
+ </item>
+ <tag><c>opt_disc</c></tag>
+ <item>
+ <p>Optional disc. The schema may reside either on disc
+ or in RAM. If the schema is found on disc, Mnesia starts as a
+ disc based node and the storage type of the schema table is
+ <c>disc_copies</c>. If no schema is found on disc, Mnesia starts
+ as a disc-less node and the storage type of the schema table is
+ <c>ram_copies</c>. The default value for the application parameter
+ is <c>opt_disc</c>.
+ </p>
+ </item>
+ </taglist>
+ </item>
+ </list>
+ <p>First the SASL application parameters are checked, then
+ the command line flags are checked, and finally, the default
+ value is chosen.
+ </p>
+ </section>
+
+ <section>
+ <title>See Also</title>
+ <p>mnesia_registry(3), mnesia_session(3), qlc(3),
+ dets(3), ets(3), disk_log(3), application(3)
+ </p>
+ </section>
+
+</erlref>
+
diff --git a/lib/mnesia/doc/src/mnesia_frag_hash.xml b/lib/mnesia/doc/src/mnesia_frag_hash.xml
new file mode 100644
index 0000000000..ca03327994
--- /dev/null
+++ b/lib/mnesia/doc/src/mnesia_frag_hash.xml
@@ -0,0 +1,166 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>2002</year>
+ <year>2007</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.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>mnesia_frag_hash</title>
+ <prepared>H&aring;kan Mattsson</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>2002-09-10</date>
+ <rev>A</rev>
+ <file>mnesia_frag_hash.sgml</file>
+ </header>
+ <module>mnesia_frag_hash</module>
+ <modulesummary>Defines mnesia_frag_hash callback behaviour</modulesummary>
+ <description>
+ <p>The module <c>mnesia_frag_hash</c> defines a callback
+ behaviour for user defined hash functions of fragmented tables.</p>
+ <p>Which module that is selected to implement the <c>mnesia_frag_hash</c>
+ behaviour for a particular fragmented table is specified together
+ with the other <c>frag_properties</c>. The <c>hash_module</c> defines
+ the module name. The <c>hash_state</c> defines the initial hash state.</p>
+ <p>It implements dynamic hashing which is a kind of hashing
+ that grows nicely when new fragments are added. It is well
+ suited for scalable hash tables</p>
+ </description>
+ <funcs>
+ <func>
+ <name>init_state(Tab, State) -> NewState | abort(Reason)</name>
+ <fsummary>Initiate the hash state for a new table</fsummary>
+ <type>
+ <v>Tab = atom()</v>
+ <v>State = term()</v>
+ <v>NewState = term()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>This function is invoked when a fragmented table is
+ created with <c>mnesia:create_table/2</c> or when a
+ normal (un-fragmented) table is converted to be a
+ fragmented table with <c>mnesia:change_table_frag/2</c>.</p>
+ <p>Note that the <c>add_frag/2</c> function will be invoked
+ one time each for the rest of the fragments (all but number 1)
+ as a part of the table creation procedure.</p>
+ <p><c>State</c> is the initial value of the <c>hash_state</c><c>frag_property</c>. The <c>NewState</c> will be stored as
+ <c>hash_state</c> among the other <c>frag_properties</c>.
+ </p>
+ </desc>
+ </func>
+ <func>
+ <name>add_frag(State) -> {NewState, IterFrags, AdditionalLockFrags} | abort(Reason)</name>
+ <fsummary>This function is invoked when a new fragment is added to a fragmented table</fsummary>
+ <type>
+ <v>State = term()</v>
+ <v>NewState = term()</v>
+ <v>IterFrags = [integer()]</v>
+ <v>AdditionalLockFrags = [integer()]</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>In order to scale well, it is a good idea ensure that the
+ records are evenly distributed over all fragments including
+ the new one.</p>
+ <p>The <c>NewState</c> will be stored as <c>hash_state</c> among the
+ other <c>frag_properties</c>.
+ </p>
+ <p>As a part of the <c>add_frag</c> procedure, Mnesia will iterate
+ over all fragments corresponding to the <c>IterFrags</c> numbers
+ and invoke <c>key_to_frag_number(NewState,RecordKey)</c> for
+ each record. If the new fragment differs from the old
+ fragment, the record will be moved to the new fragment.</p>
+ <p>As the <c>add_frag</c> procedure is a part of a schema
+ transaction Mnesia will acquire a write locks on the
+ affected tables. That is both the fragments corresponding
+ to <c>IterFrags</c> and those corresponding to
+ <c>AdditionalLockFrags</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>del_frag(State) -> {NewState, IterFrags, AdditionalLockFrags} | abort(Reason)</name>
+ <fsummary>This function is invoked when a fragment is deleted from a fragmented table</fsummary>
+ <type>
+ <v>State = term()</v>
+ <v>NewState = term()</v>
+ <v>IterFrags = [integer()]</v>
+ <v>AdditionalLockFrags = [integer()]</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>The <c>NewState</c> will be stored as <c>hash_state</c> among the
+ other <c>frag_properties</c>.
+ </p>
+ <p>As a part of the <c>del_frag</c> procedure, Mnesia will iterate
+ over all fragments corresponding to the <c>IterFrags</c> numbers
+ and invoke <c>key_to_frag_number(NewState,RecordKey)</c> for
+ each record. If the new fragment differs from the old
+ fragment, the record will be moved to the new fragment.</p>
+ <p>Note that all records in the last fragment must be moved to
+ another fragment as the entire fragment will be deleted.</p>
+ <p>As the <c>del_frag</c> procedure is a part of a schema
+ transaction Mnesia will acquire a write locks on the
+ affected tables. That is both the fragments corresponding
+ to <c>IterFrags</c> and those corresponding to
+ <c>AdditionalLockFrags</c>.</p>
+ </desc>
+ </func>
+ <func>
+ <name>key_to_frag_number(State, Key) -> FragNum | abort(Reason)</name>
+ <fsummary>Resolves the key of a record into a fragment number</fsummary>
+ <type>
+ <v>FragNum = integer()()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>This function is invoked whenever Mnesia needs to determine
+ which fragment a certain record belongs to. It is typically
+ invoked at read, write and delete.</p>
+ </desc>
+ </func>
+ <func>
+ <name>match_spec_to_frag_numbers(State, MatchSpec) -> FragNums | abort(Reason)</name>
+ <fsummary>Resolves a MatchSpec into a list of fragment numbers</fsummary>
+ <type>
+ <v>MatcSpec = ets_select_match_spec()</v>
+ <v>FragNums = [FragNum]</v>
+ <v>FragNum = integer()</v>
+ <v>Reason = term()</v>
+ </type>
+ <desc>
+ <p>This function is invoked whenever Mnesia needs to determine
+ which fragments that needs to be searched for a MatchSpec.
+ It is typically invoked at select and match_object.</p>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p>mnesia(3)
+ </p>
+ </section>
+
+</erlref>
+
diff --git a/lib/mnesia/doc/src/mnesia_registry.xml b/lib/mnesia/doc/src/mnesia_registry.xml
new file mode 100644
index 0000000000..966134d508
--- /dev/null
+++ b/lib/mnesia/doc/src/mnesia_registry.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE erlref SYSTEM "erlref.dtd">
+
+<erlref>
+ <header>
+ <copyright>
+ <year>1998</year>
+ <year>2007</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.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>mnesia_registry</title>
+ <prepared>Dan Gudmundsson and H&aring;kan Mattsson</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>98-04-24</date>
+ <rev>A</rev>
+ <file>mnesia_registry.sgml</file>
+ </header>
+ <module>mnesia_registry</module>
+ <modulesummary>Dump support for registries in erl_interface. </modulesummary>
+ <description>
+ <p>The module <c>mnesia_registry</c> is usually part of
+ <c>erl_interface</c>, but for the time being, it is a part of the
+ Mnesia application.
+ </p>
+ <p><c>mnesia_registry</c> is mainly an module intended for
+ internal usage within OTP, but it has two functions that
+ are exported for public use.
+ </p>
+ <p>On C-nodes <c>erl_interface</c> has support for registry
+ tables. These reside in RAM on the C-node but they may also be
+ dumped into Mnesia tables. By default, the dumping of registry
+ tables via <c>erl_interface</c> causes a corresponding Mnesia
+ table to be created with <c>mnesia_registry:create_table/1</c>
+ if necessary.
+ </p>
+ <p>The tables that are created with these functions can be
+ administered as all other Mnesia tables. They may be included in
+ backups or replicas may be added etc. The tables are in fact
+ normal Mnesia tables owned by the user of the corresponding
+ <c>erl_interface</c> registries.
+ </p>
+ </description>
+ <funcs>
+ <func>
+ <name>create_table(Tab) -> ok | exit(Reason)</name>
+ <fsummary>Creates a registry table in Mnesia.</fsummary>
+ <desc>
+ <p>This is a wrapper function for
+ <c>mnesia:create_table/2</c> which creates a table (if there is no existing table)
+ with an appropriate set of <c>attributes</c>. The table will
+ only reside on the local node and its storage type will be
+ the same as the <c>schema</c> table on the local
+ node, ie. <c>{ram_copies,[node()]}</c> or
+ <c>{disc_copies,[node()]}</c>.
+ </p>
+ <p>It is this function that is used by <c>erl_interface</c> to
+ create the Mnesia table if it did not already exist.</p>
+ </desc>
+ </func>
+ <func>
+ <name>create_table(Tab, TabDef) -> ok | exit(Reason)</name>
+ <fsummary>Creates a customized registry table in Mnesia. </fsummary>
+ <desc>
+ <p>This is a wrapper function for
+ <c>mnesia:create_table/2</c> which creates a table (if there is no existing table)
+ with an appropriate set of <c>attributes</c>. The attributes
+ and <c>TabDef</c> are forwarded to
+ <c>mnesia:create_table/2</c>. For example, if the table should
+ reside as <c>disc_only_copies</c> on all nodes a call would
+ look like:</p>
+ <code type="none">
+ TabDef = [{{disc_only_copies, node()|nodes()]}],
+ mnesia_registry:create_table(my_reg, TabDef)
+ </code>
+ </desc>
+ </func>
+ </funcs>
+
+ <section>
+ <title>See Also</title>
+ <p>mnesia(3), erl_interface(3)
+ </p>
+ </section>
+
+</erlref>
+
diff --git a/lib/mnesia/doc/src/note.gif b/lib/mnesia/doc/src/note.gif
new file mode 100644
index 0000000000..6fffe30419
--- /dev/null
+++ b/lib/mnesia/doc/src/note.gif
Binary files differ
diff --git a/lib/mnesia/doc/src/notes.gif b/lib/mnesia/doc/src/notes.gif
new file mode 100644
index 0000000000..e000cca26a
--- /dev/null
+++ b/lib/mnesia/doc/src/notes.gif
Binary files differ
diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml
new file mode 100644
index 0000000000..69f2185cd8
--- /dev/null
+++ b/lib/mnesia/doc/src/notes.xml
@@ -0,0 +1,383 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>1996</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>Mnesia Release Notes</title>
+ <prepared>Dan Gudmundsson and H&aring;kan Mattsson</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>05-01-26</date>
+ <rev>AE</rev>
+ <file>notes.xml</file>
+ </header>
+ <p>This document describes the changes made to the Mnesia system
+ from version to version. The intention of this document is to
+ list all incompatibilities as well as all enhancements and
+ bugfixes for every release of Mnesia. Each release of Mnesia
+ thus constitutes one section in this document. The title of each
+ section is the version number of Mnesia.</p>
+
+ <section><title>Mnesia 4.4.12</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ The documentation is now built with open source tools
+ (xsltproc and fop) that exists on most platforms. One
+ visible change is that the frames are removed.</p>
+ <p>
+ Own Id: OTP-8250</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+ <section><title>Mnesia 4.4.11</title>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Fixed duplicate results with mnesia:index_read() on
+ ordered_set tables. Reported by Sam Bobroff. </p>
+ <p>
+ Fixed locking in mnesia:index_read() which now grabs a read
+ table lock to ensure correctness, this may slow down the
+ operation or block other processes trying to reach the
+ same table. </p>
+ <p>
+ Calling mnesia:dump_log() could crash mnesia,
+ Reported by Igor Ribeiro Sucupira.</p>
+ <p> Own Id: OTP-8074</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+ <section><title>Mnesia 4.4.10</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Mnesia crashed if a qlc query was running inside a
+ transaction when mnesia stopped at another node. Thanks
+ Teemu Antti-Poika.</p>
+ <p>
+ Own Id: OTP-7968</p>
+ </item>
+ <item>
+ <p>
+ Mnesia could crash when loading local_content tables.</p>
+ <p>
+ Own Id: OTP-8002 Aux Id: seq11277 </p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Minor (smp) optimizations.</p>
+ <p>
+ Own Id: OTP-7928</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+
+ <section><title>Mnesia 4.4.9</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ mnesia:clear_table/1 crashed instead of returning
+ <c>{aborted,..}</c> if it was called inside a
+ transaction.</p>
+ <p>
+ Own Id: OTP-7911</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+ <section><title>Mnesia 4.4.8</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ With bad timing several api functions could return or
+ exit with a bad error message when mnesia was shutting
+ down.</p>
+ <p>
+ Own Id: OTP-7753 Aux Id: seq11179 </p>
+ </item>
+ <item>
+ <p>
+ <c>mnesia:clear_table/1</c> cleared all nodes table
+ content even if the table was <c>local_content</c> only
+ type.</p>
+ <p>
+ Own Id: OTP-7835</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+ <section><title>Mnesia 4.4.7</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Disallowed match patterns ('_', and '$n') as argument to
+ <c>mnesia:delete_object/1</c> and friends.</p>
+ <p>
+ Own Id: OTP-7524</p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Introduced a few new functions in Mnesia: <c>mnesia:read/2</c>,
+ <c>mnesia:first/3</c>, <c>mnesia:last/3</c>, <c>mnesia:prev/4</c>,
+ <c>mnesia:next/4</c>, <c>mnesia_frag:first/1</c>, <c>mnesia_frag:last/1</c>,
+ <c>mnesia_frag:prev/2</c>, <c>mnesia_frag:next/2</c>.</p>
+ <p>
+ Own Id: OTP-7625</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+ <section><title>Mnesia 4.4.6</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ <c>mnesia:restore/2</c> aborted if a <c>EXIT</c> message
+ appeared in the client message queue.</p>
+ <p>
+ Own Id: OTP-7585 Aux Id: seq11046 </p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section><title>Mnesia 4.4.5</title>
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ mnesia:clear_table/1 does not require that all
+ replicas of the table are available anymore.</p>
+ <p>
+ Own Id: OTP-7466 Aux Id: seq11015</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section><title>Mnesia 4.4.4</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Mnesia did not garbage collect transaction decisions on
+ disk based nodes if no transactions where made on the
+ local node.</p>
+ <p>
+ Own Id: OTP-7419</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+ <section><title>Mnesia 4.4.3</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Table referred to by foreign key did not have node_pool
+ properly cleaned up when a node was removed from the
+ schema. Thanks Paul Mineiro.</p>
+ <p>
+ Own Id: OTP-7340</p>
+ </item>
+ <item>
+ <p>
+ Mnesia crashed and generated a core dump if a
+ schema_transaction was running when mnesia stopped.</p>
+ <p>
+ Own Id: OTP-7378 Aux Id: seq10964 </p>
+ </item>
+ </list>
+ </section>
+
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ It is now possible to delete a db node even when other
+ disk resident nodes are down. Thanks Paul Mineiro.</p>
+ <p>
+ Own Id: OTP-7383</p>
+ </item>
+ </list>
+ </section>
+
+</section>
+
+ <section><title>Mnesia 4.4.2</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Sticky locks could lead to hanging transactions.</p>
+ <p>
+ Own Id: OTP-7205 Aux Id: seq10793 </p>
+ </item>
+ <item>
+ <p>
+ <c>mnesia:snmp_get_next_index/2</c> didn't work with
+ partial index keys. Argument checking is now done
+ according to documentation, in functions
+ <c>mnesia:snmp_get_row/2</c>,
+ <c>mnesia:snmp_get_mnesia_key/2</c> and
+ <c>mnesia:snmp_get_next_index/2</c>. These functions now
+ require that <c>RowIndex</c> is a list.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-7208</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section><title>Mnesia 4.4.1</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Snmp index tables was not initialized correctly in
+ <c>mnesia-4.4</c>.</p>
+ <p>
+ Own Id: OTP-7170 Aux Id: seq10870 </p>
+ </item>
+ </list>
+ </section>
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ Rearranging fragmented tables is an O(N^2) operation.</p>
+ <p>
+ Own Id: OTP-6300</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section><title>Mnesia 4.4</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Mnesia ignored the module argument to
+ <c>mnesia:restore/2</c>. Thanks Paul Minerio.</p>
+ <p>
+ Own Id: OTP-6981</p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Improvements and New Features</title>
+ <list>
+ <item>
+ <p>
+ Mnesia's snmp operations <c>snmp_get_row/2</c>,
+ <c>snmp_get_next_index/2</c> and
+ <c>snmp_get_mnesia_key/2</c> have been made context
+ aware, i.e. inside a transaction they will compensate for
+ table updates made in earlier in the same transaction.
+ This might cause a performance drop if a lot of updates
+ have been made before the invocation of these functions.</p>
+ <p>
+ *** POTENTIAL INCOMPATIBILITY ***</p>
+ <p>
+ Own Id: OTP-6856 Aux Id: seq10671 </p>
+ </item>
+ <item>
+ <p>
+ Introduced erlang:phash/2 as new default for fragmented
+ tables. Already existing tables will continue to use
+ whatever hash function they where using.</p>
+ <p>
+ Own Id: OTP-6923</p>
+ </item>
+ <item>
+ <p>
+ Introduced <c>mnesia:is_transaction/0</c>.</p>
+ <p>
+ Own Id: OTP-6995 Aux Id: seq10812 </p>
+ </item>
+ </list>
+ </section>
+
+ <section><title>Known Bugs and Problems</title>
+ <list>
+ <item>
+ <p>
+ Rearranging fragmented tables is an O(N^2) operation.</p>
+ <p>
+ Own Id: OTP-6300</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <!-- section>
+ <title>Previous Notes</title>
+ <p>For information about older versions see <url href="part_notes_history_frame.html">release notes history</url>.</p>
+ </section -->
+</chapter>
+
diff --git a/lib/mnesia/doc/src/notes_history.xml b/lib/mnesia/doc/src/notes_history.xml
new file mode 100644
index 0000000000..0984e33376
--- /dev/null
+++ b/lib/mnesia/doc/src/notes_history.xml
@@ -0,0 +1,322 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE chapter SYSTEM "chapter.dtd">
+
+<chapter>
+ <header>
+ <copyright>
+ <year>2004</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>Mnesia Release Notes</title>
+ <prepared>Dan Gudmundsson and H&aring;kan Mattsson</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>04-08-22</date>
+ <rev>AE</rev>
+ <file>notes_history.sgml</file>
+ </header>
+ <p>This document describes the changes made to the Mnesia system
+ from version to version. The intention of this document is to
+ list all incompatibilities as well as all enhancements and
+ bugfixes for every release of Mnesia. Each release of Mnesia
+ thus constitutes one section in this document. The title of each
+ section is the version number of Mnesia.</p>
+
+ <section><title>Mnesia 4.3.7</title>
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ Removed a memory leak on ram-only nodes, introduced in
+ <c>mnesia-4.3.6</c>.</p>
+ <p>
+ Own Id: OTP-6936 Aux Id: seq10786 </p>
+ </item>
+ </list>
+ </section>
+ <section>
+ <title>Known Bugs and Problems</title>
+ <list type="bulleted">
+ <item>
+ <p>Rearranging fragmented tables is an O(N^2)
+ operation.</p>
+ <p>Own Id: OTP-6300</p>
+ </item>
+ </list>
+ </section>
+
+ </section>
+
+
+ <section>
+ <title>Mnesia 4.3.6</title>
+
+ <section><title>Fixed Bugs and Malfunctions</title>
+ <list>
+ <item>
+ <p>
+ A bug causing lots of records to be lost at startup from
+ an installed fallback has been fixed. The bug did however
+ not show up when a backup file generated with
+ <c>mnesia:backup/1</c> or
+ <c>mnesia:backup_checkpoint/2</c> was installed as
+ fallback. In order to trigger the bug, the items in the
+ backup file had to be rearranged in such an order that
+ records from different tables were interleaved with each
+ other.</p>
+ <p>
+ Own Id: OTP-6903 Aux Id: seq10763 </p>
+ </item>
+ <item>
+ <p>
+ Mnesia sometimes failed to commit schema operations on
+ all nodes, this have been seen on smp machines but could
+ happen on single processor as well with some bad timing.</p>
+ <p>
+ Own Id: OTP-6904</p>
+ </item>
+ <item>
+ <p>
+ <c>mnesia:select/1</c> failed to return all matches on
+ remote nodes if something was written to the table
+ earlier in the same transaction.</p>
+ <p>
+ Own Id: OTP-6908</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <list type="bulleted">
+ <item>
+ <p>Rearranging fragmented tables is an O(N^2)
+ operation.</p>
+ <p>Own Id: OTP-6300</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Mnesia 4.3.5</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>The internal index tables on bag tables where not always
+ cleaned correctly. Thanks Christopher Faulet and Salazard
+ Remy.</p>
+ <p>Own Id: OTP-6587</p>
+ </item>
+ <item>
+ <p>Changing the copy type with
+ <c>mnesia:change_table_copy/3</c> on a node which was down
+ was not handled correctly, that caused an eternal table
+ lock on the alive nodes. Thanks Hal Snyder.</p>
+ <p>Own
+ Id: OTP-6709</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <list type="bulleted">
+ <item>
+ <p>Rearranging fragmented tables is an O(N^2)
+ operation.</p>
+ <p>Own Id: OTP-6300</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Mnesia 4.3.4</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Adding fragments to ram_copies tables was allowed on
+ nodes that where down.</p>
+ <p>Own Id: OTP-6367</p>
+ </item>
+ <item>
+ <p>Mnesia leaked transaction decisions (memory and disk
+ space).</p>
+ <p>Own Id: OTP-6464</p>
+ </item>
+ <item>
+ <p><c>dirty_update_counter/3</c> did not work properly on
+ disc tables when the counter was not initiated (Thanks to
+ Sebastien Saint-Sevin).</p>
+ <p>Own Id: OTP-6545</p>
+ </item>
+ <item>
+ <p>Chunked <c>mnesia:select</c> on fragmented tables could
+ crash (Thanks to Primanathan Reddy).</p>
+ <p>Own Id:
+ OTP-6548</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>Introduced a new configure parameter dc_dump_limit.</p>
+ <p>Removed dead code (dialyzer warnings) and debugging
+ features that called interpreter commands.</p>
+ <p>Minor
+ performance increase when a lot of simultaneous
+ transactions where active. </p>
+ <p>Thank you Scott Lystig
+ Fritchie for debugging and bug reports.</p>
+ <p>Own Id:
+ OTP-6478</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <list type="bulleted">
+ <item>
+ <p>Rearranging fragmented tables is an O(N^2)
+ operation.</p>
+ <p>Own Id: OTP-6300</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Mnesia 4.3.3</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Mnesia could crash during startup when loading tables
+ from remote node. </p>
+ <p>Own Id: OTP-6298 Aux Id: seq10402 </p>
+ </item>
+ <item>
+ <p>Mnesia could fail to update all copies during
+ del_table_copy. </p>
+ <p>Own Id: OTP-6299</p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Known Bugs and Problems</title>
+ <list type="bulleted">
+ <item>
+ <p>Rearranging fragmented tables is an O(N^2) operation.</p>
+ <p>Own Id: OTP-6300</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Mnesia 4.3.2</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Mnesia sometimes failed to remove [d]ets table fixation,
+ when using <c>mnesia:first/1</c>,<c>mnesia:next/2</c> or
+ <c>qlc</c> this could cause that deleted records are not
+ actually deleted in the [d]ets table and that
+ <c>mnesia:[dirty_]first/1</c> reported the wrong key. </p>
+ <p>Own Id: OTP-6193 Aux Id: seq10376</p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Mnesia 4.3.1</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Mnesia could crash (bad match in mnesia_controller)
+ during start. </p>
+ <p>Own Id: OTP-6116 Aux Id: seq10305 </p>
+ </item>
+ </list>
+ </section>
+ </section>
+
+ <section>
+ <title>Mnesia 4.3</title>
+
+ <section>
+ <title>Fixed Bugs and Malfunctions</title>
+ <list type="bulleted">
+ <item>
+ <p>Deleting tables during the start of mnesia on another
+ node caused problems. </p>
+ <p>Own Id: OTP-5928 Aux Id:
+ seq10111 </p>
+ </item>
+ <item>
+ <p>Killing processes that runs nested transactions could
+ crash mnesia. </p>
+ <p>Own Id: OTP-6027 Aux Id: seq10244 </p>
+ </item>
+ <item>
+ <p>Creating or deleting tables with a checkpoint activated
+ could crash mnesia </p>
+ <p>Own Id: OTP-6064</p>
+ </item>
+ <item>
+ <p>Table loading could be mixed with schema operations
+ which could cause troubles. </p>
+ <p>Own Id: OTP-6065 Aux Id:
+ seq10291 </p>
+ </item>
+ </list>
+ </section>
+
+ <section>
+ <title>Improvements and New Features</title>
+ <list type="bulleted">
+ <item>
+ <p>Added parallel table loaders to increase startup
+ performance when the system have many small tables. The
+ configuration variable <c>no_table_loaders</c> configures
+ the number of loaders, default is two. </p>
+ <p>Own Id:
+ OTP-6087</p>
+ </item>
+ </list>
+ </section>
+ </section>
+</chapter>
+
diff --git a/lib/mnesia/doc/src/part.xml b/lib/mnesia/doc/src/part.xml
new file mode 100644
index 0000000000..b9654a4207
--- /dev/null
+++ b/lib/mnesia/doc/src/part.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <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>Mnesia User's Guide</title>
+ <prepared>Claes Wikstr&ouml;m, Hans Nilsson and H&aring;kan Mattsson</prepared>
+ <docno></docno>
+ <date></date>
+ <rev></rev>
+ <file>part.sgml</file>
+ </header>
+ <description>
+ <p><em>Mnesia</em> is a distributed DataBase Management
+ System(DBMS), appropriate for telecommunications applications and other
+ Erlang applications which require continuous operation and exhibit soft
+ real-time properties.</p>
+ </description>
+ <xi:include href="Mnesia_chap1.xml"/>
+ <xi:include href="Mnesia_chap2.xml"/>
+ <xi:include href="Mnesia_chap3.xml"/>
+ <xi:include href="Mnesia_chap4.xml"/>
+ <xi:include href="Mnesia_chap5.xml"/>
+ <xi:include href="Mnesia_chap7.xml"/>
+ <xi:include href="Mnesia_chap8.xml"/>
+ <xi:include href="Mnesia_App_A.xml"/>
+ <xi:include href="Mnesia_App_B.xml"/>
+ <xi:include href="Mnesia_App_C.xml"/>
+ <xi:include href="Mnesia_App_D.xml"/>
+</part>
+
diff --git a/lib/mnesia/doc/src/part_notes.xml b/lib/mnesia/doc/src/part_notes.xml
new file mode 100644
index 0000000000..caa155585d
--- /dev/null
+++ b/lib/mnesia/doc/src/part_notes.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part xmlns:xi="http://www.w3.org/2001/XInclude">
+ <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>MNESIA Release Notes</title>
+ <prepared>Claes Wikstr&ouml;m, Hans Nilsson and H&aring;kan Mattsson</prepared>
+ <docno></docno>
+ <date>1997-05-27</date>
+ <rev>1.2</rev>
+ <file>part_notes.xml</file>
+ </header>
+ <description>
+ <p><em>Mnesia</em> is a Distributed DataBase Management
+ System (DBMS), appropriate for telecommunications applications and other
+ Erlang applications which require continuous operation and exhibit soft
+ real-time properties. </p>
+ <p>For information about older versions see
+ <url href="part_notes_history_frame.html">release notes history</url>.</p>
+ </description>
+ <xi:include href="notes.xml"/>
+</part>
+
diff --git a/lib/mnesia/doc/src/part_notes_history.xml b/lib/mnesia/doc/src/part_notes_history.xml
new file mode 100644
index 0000000000..177738623c
--- /dev/null
+++ b/lib/mnesia/doc/src/part_notes_history.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE part SYSTEM "part.dtd">
+
+<part>
+ <header>
+ <copyright>
+ <year>2004</year>
+ <year>2007</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.
+
+ The Initial Developer of the Original Code is Ericsson AB.
+ </legalnotice>
+
+ <title>MNESIA Release Notes</title>
+ <prepared>Claes Wikstr&ouml;m, Hans Nilsson and H&aring;kan Mattsson</prepared>
+ <docno></docno>
+ <date>1997-05-27</date>
+ <rev>1.2</rev>
+ <file>part_notes_history.sgml</file>
+ </header>
+ <description>
+ <p><em>Mnesia</em> is a Distributed DataBase Management
+ System (DBMS), appropriate for telecommunications applications and other
+ Erlang applications which require continuous operation and exhibit soft
+ real-time properties. </p>
+ </description>
+ <include file="notes_history"></include>
+</part>
+
diff --git a/lib/mnesia/doc/src/ref_man.gif b/lib/mnesia/doc/src/ref_man.gif
new file mode 100644
index 0000000000..b13c4efd53
--- /dev/null
+++ b/lib/mnesia/doc/src/ref_man.gif
Binary files differ
diff --git a/lib/mnesia/doc/src/ref_man.xml b/lib/mnesia/doc/src/ref_man.xml
new file mode 100644
index 0000000000..417423641d
--- /dev/null
+++ b/lib/mnesia/doc/src/ref_man.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="latin1" ?>
+<!DOCTYPE application SYSTEM "application.dtd">
+
+<application xmlns:xi="http://www.w3.org/2001/XInclude">
+ <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>Mnesia Reference Manual</title>
+ <prepared>H&aring;kan Mattsson, Hans Nilsson, Claes Wikstr&ouml;m</prepared>
+ <responsible></responsible>
+ <docno></docno>
+ <approved></approved>
+ <checked></checked>
+ <date>1998-04-24</date>
+ <rev>A</rev>
+ <file>refman.sgml</file>
+ </header>
+ <description>
+ <p><em>Mnesia</em> is a distributed DataBase Management
+ System (DBMS), appropriate for telecommunications applications and other
+ Erlang applications which require continuous operation and exhibit soft
+ real-time properties. </p>
+ </description>
+ <xi:include href="mnesia.xml"/>
+ <xi:include href="mnesia_frag_hash.xml"/>
+ <xi:include href="mnesia_registry.xml"/>
+</application>
+
diff --git a/lib/mnesia/doc/src/summary.html.src b/lib/mnesia/doc/src/summary.html.src
new file mode 100644
index 0000000000..2941a2f46a
--- /dev/null
+++ b/lib/mnesia/doc/src/summary.html.src
@@ -0,0 +1 @@
+A heavy duty real-time distributed database \ No newline at end of file
diff --git a/lib/mnesia/doc/src/user_guide.gif b/lib/mnesia/doc/src/user_guide.gif
new file mode 100644
index 0000000000..e6275a803d
--- /dev/null
+++ b/lib/mnesia/doc/src/user_guide.gif
Binary files differ
diff --git a/lib/mnesia/doc/src/warning.gif b/lib/mnesia/doc/src/warning.gif
new file mode 100644
index 0000000000..96af52360e
--- /dev/null
+++ b/lib/mnesia/doc/src/warning.gif
Binary files differ