mirror of
https://github.com/berkeleydb/libdb.git
synced 2024-11-16 17:16:25 +00:00
263 lines
9.8 KiB
HTML
263 lines
9.8 KiB
HTML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||
<title>Foreign key indexes</title>
|
||
<link rel="stylesheet" href="gettingStarted.css" type="text/css" />
|
||
<meta name="generator" content="DocBook XSL Stylesheets V1.73.2" />
|
||
<link rel="start" href="index.html" title="Berkeley DB Programmer's Reference Guide" />
|
||
<link rel="up" href="am.html" title="Chapter 3. Access Method Operations" />
|
||
<link rel="prev" href="am_second.html" title="Secondary indexes" />
|
||
<link rel="next" href="am_cursor.html" title="Cursor operations" />
|
||
</head>
|
||
<body>
|
||
<div xmlns="" class="navheader">
|
||
<div class="libver">
|
||
<p>Library Version 11.2.5.3</p>
|
||
</div>
|
||
<table width="100%" summary="Navigation header">
|
||
<tr>
|
||
<th colspan="3" align="center">Foreign key indexes</th>
|
||
</tr>
|
||
<tr>
|
||
<td width="20%" align="left"><a accesskey="p" href="am_second.html">Prev</a> </td>
|
||
<th width="60%" align="center">Chapter 3.
|
||
Access Method Operations
|
||
</th>
|
||
<td width="20%" align="right"> <a accesskey="n" href="am_cursor.html">Next</a></td>
|
||
</tr>
|
||
</table>
|
||
<hr />
|
||
</div>
|
||
<div class="sect1" lang="en" xml:lang="en">
|
||
<div class="titlepage">
|
||
<div>
|
||
<div>
|
||
<h2 class="title" style="clear: both"><a id="am_foreign"></a>Foreign key indexes</h2>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Foreign keys are used to ensure a level of consistency between
|
||
two different databases in terms of the keys that the databases use.
|
||
In a foreign key relationship, one database is the
|
||
<span class="emphasis"><em>constrained</em></span> database. This database is actually a
|
||
secondary database which is associated with a primary database. The
|
||
other database in this relationship is the
|
||
<span class="emphasis"><em>foreign key</em></span> database. Once this relationship has
|
||
been established between a constrained database and a foreign key
|
||
database, then:
|
||
</p>
|
||
<div class="orderedlist">
|
||
<ol type="1">
|
||
<li>
|
||
<p>
|
||
Key/data items cannot be added to the
|
||
constrained database unless that same key
|
||
already exists in the foreign key
|
||
database.
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
A key/data pair cannot be deleted from the foreign
|
||
key database unless some action is also taken to
|
||
keep the constrained database consistent with
|
||
the foreign key database.
|
||
</p>
|
||
</li>
|
||
</ol>
|
||
</div>
|
||
<p>
|
||
Because the constrained database is a secondary database, by ensuring
|
||
it's consistency with a foreign key database you are actually ensuring
|
||
that a primary database (the one to which the secondary database is
|
||
associated) is consistent with the foreign key database.
|
||
</p>
|
||
<p>
|
||
Deletions of keys in the foreign key database affect the constrained database
|
||
in one of three ways, as specified by the application:
|
||
</p>
|
||
<div class="itemizedlist">
|
||
<ul type="disc">
|
||
<li>
|
||
<p>
|
||
<code class="literal">Abort</code>
|
||
</p>
|
||
<p>
|
||
The deletion of a record from the foreign database will not proceed if that key
|
||
exists in the constrained primary database. Transactions must be used to prevent
|
||
the aborted delete from corrupting either of the databases.
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
<code class="literal">Cascade</code>
|
||
</p>
|
||
<p>
|
||
The deletion of a record from the foreign database will also cause any records
|
||
in the constrained primary database that use that key to also be
|
||
automatically deleted.
|
||
</p>
|
||
</li>
|
||
<li>
|
||
<p>
|
||
<code class="literal">Nullify</code>
|
||
</p>
|
||
<p>
|
||
The deletion of a record from the foreign database will cause a user specified
|
||
callback function to be called, in order to alter or nullify any records
|
||
using that key in the constrained primary database.
|
||
</p>
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
<p>
|
||
Note that it is possible to delete a key from the constrained database,
|
||
but not from the foreign key database. For this reason, if you want the
|
||
keys used in both databases to be 100% accurate, then you will have to
|
||
write code to ensure that when a key is removed from the constrained
|
||
database, it is also removed from the foreign key database.
|
||
</p>
|
||
<p>
|
||
As an example of how foreign key indexes might be used, consider a database
|
||
of customer information and a database of order information. A typical
|
||
customer database would use a customer ID as the key and those keys
|
||
would also appear in the order database. To ensure an order is not
|
||
booked for a non-existent customer, the customer database can be
|
||
associated with the order database as a foreign index.
|
||
</p>
|
||
<p>
|
||
In order to do this, you create a secondary index of the order
|
||
database, which uses customer IDs as the key for its key/data pairs.
|
||
This secondary index is, then, the constrained database. But because
|
||
the secondary index is constrained, so too is the order database
|
||
because the contents of the secondary index are programmatically tied
|
||
to the contents of the order database.
|
||
</p>
|
||
<p>
|
||
The customer database, then, is the foreign key database. It is
|
||
associated to the order database's secondary index using the
|
||
<a href="../api_reference/C/dbassociate_foreign.html" class="olink">DB->associate_foreign()</a> method. In this way,
|
||
an order cannot be added to the order database unless the customer ID
|
||
already exists in the customer database.
|
||
</p>
|
||
<p>
|
||
Note that this relationship can also be configured to delete any
|
||
outstanding orders for a customer when that customer is deleted from
|
||
the customer database.
|
||
</p>
|
||
<p>In SQL, this would be done by executing something like the following:</p>
|
||
<pre class="programlisting">CREATE TABLE customers(cust_id CHAR(4) NOT NULL,
|
||
lastname CHAR(15), firstname CHAR(15), PRIMARY KEY(cust_id));
|
||
CREATE TABLE orders(order_id CHAR(4) NOT NULL, order_num int NOT NULL,
|
||
cust_id CHAR(4), PRIMARY KEY (order_id),
|
||
FOREIGN KEY (cust_id) REFERENCES customers(cust_id)
|
||
ON DELETE CASCADE);
|
||
</pre>
|
||
<p>In Berkeley DB, this would work as follows:</p>
|
||
<a id="prog_am18"></a>
|
||
<pre class="programlisting">struct customer {
|
||
char cust_id[4];
|
||
char last_name[15];
|
||
char first_name[15];
|
||
};
|
||
struct order {
|
||
char order_id[4];
|
||
int order_number;
|
||
char cust_id[4];
|
||
};
|
||
|
||
....
|
||
|
||
void
|
||
foreign()
|
||
{
|
||
DB *dbp, *sdbp, *fdbp;
|
||
int ret;
|
||
|
||
/* Open/create order database */
|
||
if ((ret = db_create(&dbp, dbenv, 0)) != 0)
|
||
handle_error(ret);
|
||
if ((ret = dbp->open(dbp, NULL,
|
||
"orders.db", NULL, DB_BTREE, DB_CREATE, 0600)) != 0)
|
||
handle_error(ret);
|
||
|
||
/*
|
||
* Open/create secondary index on customer id. Note that it
|
||
* supports duplicates because a customer may have multiple
|
||
* orders.
|
||
*/
|
||
if ((ret = db_create(&sdbp, dbenv, 0)) != 0)
|
||
handle_error(ret);
|
||
if ((ret = sdbp->set_flags(sdbp, DB_DUP | DB_DUPSORT)) != 0)
|
||
handle_error(ret);
|
||
if ((ret = sdbp->open(sdbp, NULL, "orders_cust_ids.db",
|
||
NULL, DB_BTREE, DB_CREATE, 0600)) != 0)
|
||
handle_error(ret);
|
||
|
||
/* Associate the secondary with the primary. */
|
||
if ((ret = dbp->associate(dbp, NULL, sdbp, getcustid, 0)) != 0)
|
||
handle_error(ret);
|
||
|
||
/* Open/create customer database */
|
||
if ((ret = db_create(&fdbp, dbenv, 0)) != 0)
|
||
handle_error(ret);
|
||
if ((ret = fdbp->open(fdbp, NULL,
|
||
"customers.db", NULL, DB_BTREE, DB_CREATE, 0600)) != 0)
|
||
handle_error(ret);
|
||
|
||
/* Associate the foreign with the secondary. */
|
||
if ((ret = fdbp->associate_foreign(
|
||
fdbp, sdbp, NULL, DB_FOREIGN_CASCADE)) != 0)
|
||
handle_error(ret);
|
||
|
||
}
|
||
|
||
/*
|
||
* getcustid -- extracts a secondary key (the customer id) from a primary
|
||
* key/data pair
|
||
*/
|
||
int
|
||
getcustid(secondary, pkey, pdata, skey)
|
||
DB *secondary;
|
||
const DBT *pkey, *pdata;
|
||
DBT *skey;
|
||
{
|
||
/*
|
||
* Since the secondary key is a simple structure member of the
|
||
* record, we don't have to do anything fancy to return it. If
|
||
* we have composite keys that need to be constructed from the
|
||
* record, rather than simply pointing into it, then the user's
|
||
* function might need to allocate space and copy data. In
|
||
* this case, the DB_DBT_APPMALLOC flag should be set in the
|
||
* secondary key DBT.
|
||
*/
|
||
memset(skey, 0, sizeof(DBT));
|
||
skey->data = ((struct order *)pdata->data)->cust_id;
|
||
skey->size = 4;
|
||
return (0);
|
||
}</pre>
|
||
</div>
|
||
<div class="navfooter">
|
||
<hr />
|
||
<table width="100%" summary="Navigation footer">
|
||
<tr>
|
||
<td width="40%" align="left"><a accesskey="p" href="am_second.html">Prev</a> </td>
|
||
<td width="20%" align="center">
|
||
<a accesskey="u" href="am.html">Up</a>
|
||
</td>
|
||
<td width="40%" align="right"> <a accesskey="n" href="am_cursor.html">Next</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td width="40%" align="left" valign="top">Secondary indexes </td>
|
||
<td width="20%" align="center">
|
||
<a accesskey="h" href="index.html">Home</a>
|
||
</td>
|
||
<td width="40%" align="right" valign="top"> Cursor operations</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</body>
|
||
</html>
|