257 lines
10 KiB
HTML
257 lines
10 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>Implementing Key
|
||
|
||
Extractors
|
||
</title>
|
||
<link rel="stylesheet" href="gettingStarted.css" type="text/css" />
|
||
<meta name="generator" content="DocBook XSL Stylesheets V1.62.4" />
|
||
<link rel="home" href="index.html" title="Getting Started with Berkeley DB" />
|
||
<link rel="up" href="indexes.html" title="Chapter 5. Secondary Databases" />
|
||
<link rel="previous" href="indexes.html" title="Chapter 5. Secondary Databases" />
|
||
<link rel="next" href="readSecondary.html" title="Reading Secondary Databases" />
|
||
</head>
|
||
<body>
|
||
<div class="navheader">
|
||
<table width="100%" summary="Navigation header">
|
||
<tr>
|
||
<th colspan="3" align="center">Implementing Key
|
||
|
||
Extractors
|
||
</th>
|
||
</tr>
|
||
<tr>
|
||
<td width="20%" align="left"><a accesskey="p" href="indexes.html">Prev</a> </td>
|
||
<th width="60%" align="center">Chapter 5. Secondary Databases</th>
|
||
<td width="20%" align="right"> <a accesskey="n" href="readSecondary.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="keyCreator"></a>Implementing Key
|
||
|
||
<span>Extractors</span>
|
||
</h2>
|
||
</div>
|
||
</div>
|
||
<div></div>
|
||
</div>
|
||
<p>
|
||
You must provide every secondary database with a
|
||
|
||
<span>callback</span>
|
||
that creates keys from primary records. You identify this
|
||
|
||
<span>callback</span>
|
||
|
||
|
||
<span>
|
||
when you associate your secondary database to your primary.
|
||
</span>
|
||
</p>
|
||
<p>
|
||
You can create keys using whatever data you want. Typically you will
|
||
base your key on some information found in a record's data, but you
|
||
can also use information found in the primary record's key. How you build
|
||
your keys is entirely dependent upon the nature of the index that you
|
||
want to maintain.
|
||
</p>
|
||
<p>
|
||
You implement a key extractor by writing a function that extracts
|
||
the necessary information from a primary record's key or data.
|
||
This function must conform to a specific prototype, and it must be
|
||
provided as a callback to the <tt class="methodname">associate()</tt>
|
||
method.
|
||
</p>
|
||
<p>
|
||
For example, suppose your primary database records contain data that
|
||
uses the following structure:
|
||
</p>
|
||
<a id="c_index3"></a>
|
||
<pre class="programlisting">typedef struct vendor {
|
||
char name[MAXFIELD]; /* Vendor name */
|
||
char street[MAXFIELD]; /* Street name and number */
|
||
char city[MAXFIELD]; /* City */
|
||
char state[3]; /* Two-digit US state code */
|
||
char zipcode[6]; /* US zipcode */
|
||
char phone_number[13]; /* Vendor phone number */
|
||
char sales_rep[MAXFIELD]; /* Name of sales representative */
|
||
char sales_rep_phone[MAXFIELD]; /* Sales rep's phone number */
|
||
} VENDOR; </pre>
|
||
<p>
|
||
Further suppose that you want to be able to query your primary database
|
||
based on the name of a sales representative. Then you would write a
|
||
function that looks like this:
|
||
</p>
|
||
<a id="c_index4"></a>
|
||
<pre class="programlisting">#include <db.h>
|
||
|
||
...
|
||
|
||
int
|
||
get_sales_rep(DB *sdbp, /* secondary db handle */
|
||
const DBT *pkey, /* primary db record's key */
|
||
const DBT *pdata, /* primary db record's data */
|
||
DBT *skey) /* secondary db record's key */
|
||
{
|
||
VENDOR *vendor;
|
||
|
||
/* First, extract the structure contained in the primary's data */
|
||
vendor = pdata->data;
|
||
|
||
/* Now set the secondary key's data to be the representative's name */
|
||
memset(skey, 0, sizeof(DBT));
|
||
skey->data = vendor->sales_rep;
|
||
skey->size = strlen(vendor->sales_rep) + 1;
|
||
|
||
/* Return 0 to indicate that the record can be created/updated. */
|
||
return (0);
|
||
} </pre>
|
||
<p>
|
||
In order to use this function, you provide it on the
|
||
<tt class="methodname">associate()</tt> method after the primary and
|
||
secondary databases have been created and opened:
|
||
</p>
|
||
<a id="c_index5"></a>
|
||
<pre class="programlisting">dbp->associate(dbp, /* Primary database */
|
||
NULL, /* TXN id */
|
||
sdbp, /* Secondary database */
|
||
get_sales_rep, /* Callback used for key creation. */
|
||
0); /* Flags */</pre>
|
||
<div class="sect2" lang="en" xml:lang="en">
|
||
<div class="titlepage">
|
||
<div>
|
||
<div>
|
||
<h3 class="title"><a id="multikeys"></a>Working with Multiple Keys</h3>
|
||
</div>
|
||
</div>
|
||
<div></div>
|
||
</div>
|
||
<p>
|
||
Until now we have only discussed indexes as if there is
|
||
a one-to-one relationship between the secondary key and
|
||
the primary database record. In fact, it is possible to
|
||
generate multiple keys for any given record, provided
|
||
that you take appropriate steps in your key creator
|
||
to do so.
|
||
</p>
|
||
<p>
|
||
For example, suppose you had a database that contained
|
||
information about books. Suppose further that you
|
||
sometimes want to look up books by author. Because
|
||
sometimes books have multiple authors, you may want to
|
||
return multiple secondary keys for every book that you
|
||
index.
|
||
</p>
|
||
<p>
|
||
To do this, you write a key extractor that returns a
|
||
<span>DBT</span>
|
||
|
||
whose <tt class="literal">data</tt> member points to an array of
|
||
<span>DBTs.</span>
|
||
|
||
Each such member of this array contains a single secondary key.
|
||
In addition, the
|
||
<span>DBT</span>
|
||
|
||
returned by your key extractor must have a size field
|
||
equal to the number of elements contained in the
|
||
<span>DBT</span>
|
||
|
||
array. Also, the flag field for the
|
||
<span>DBT</span>
|
||
|
||
returned by the callback must include
|
||
<tt class="literal">DB_DBT_MULTIPLE</tt>. For example:
|
||
</p>
|
||
<div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
|
||
<h3 class="title">Note</h3>
|
||
<p>
|
||
It is important that the array of secondary
|
||
keys created by your callback not contain
|
||
repeats. That is, every element in the array
|
||
must be unique. If the array does not contain
|
||
a unique set, then the secondary can get out
|
||
of sync with the primary.
|
||
</p>
|
||
</div>
|
||
<pre class="programlisting">int
|
||
my_callback(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey)
|
||
{
|
||
DBT *tmpdbt;
|
||
char *tmpdata1, tmpdata2;
|
||
|
||
/*
|
||
* This example skips the step of extracting the data you
|
||
* want to use for building your secondary keys from the
|
||
* pkey or pdata DBT.
|
||
*
|
||
* Assume for the purpose of this example that the data
|
||
* is temporarily stored in two variables,
|
||
* tmpdata1 and tmpdata2.
|
||
*/
|
||
|
||
/*
|
||
* Create an array of DBTs that is large enough for the
|
||
* number of keys that you want to return. In this case,
|
||
* we go with an array of size two.
|
||
*/
|
||
|
||
tmpdbt = malloc(sizeof(DBT) * 2);
|
||
memset(tmpdbt, 0, sizeof(DBT) * 2);
|
||
|
||
/* Now assign secondary keys to each element of the array. */
|
||
tmpdbt[0].data = tmpdata1;
|
||
tmpdbt[0].size = (u_int32_t)strlen(tmpdbt[0].data) + 1;
|
||
tmpdbt[1].data = tmpdata2;
|
||
tmpdbt[1].size = (u_int32_t)strlen(tmpdbt[1].data) + 1;
|
||
|
||
/*
|
||
* Now we set flags for the returned DBT. DB_DBT_MULTIPLE is
|
||
* required in order for DB to know that the DBT references an
|
||
* array. In addition, we set DB_DBT_APPMALLOC because we
|
||
* dynamically allocated memory for the DBT's data field.
|
||
* DB_DBT_APPMALLOC causes DB to release that memory once it
|
||
* is done with the returned DBT.
|
||
*/
|
||
skey->flags = DB_DBT_MULTIPLE | DB_DBT_APPMALLOC;
|
||
|
||
/* Point the results data field to the arrays of DBTs */
|
||
skey->data = tmpdbt;
|
||
|
||
/* Indicate the returned array is of size 2 */
|
||
skey->size = 2;
|
||
|
||
return (0);
|
||
} </pre>
|
||
</div>
|
||
</div>
|
||
<div class="navfooter">
|
||
<hr />
|
||
<table width="100%" summary="Navigation footer">
|
||
<tr>
|
||
<td width="40%" align="left"><a accesskey="p" href="indexes.html">Prev</a> </td>
|
||
<td width="20%" align="center">
|
||
<a accesskey="u" href="indexes.html">Up</a>
|
||
</td>
|
||
<td width="40%" align="right"> <a accesskey="n" href="readSecondary.html">Next</a></td>
|
||
</tr>
|
||
<tr>
|
||
<td width="40%" align="left" valign="top">Chapter 5. Secondary Databases </td>
|
||
<td width="20%" align="center">
|
||
<a accesskey="h" href="index.html">Home</a>
|
||
</td>
|
||
<td width="40%" align="right" valign="top"> Reading Secondary Databases</td>
|
||
</tr>
|
||
</table>
|
||
</div>
|
||
</body>
|
||
</html>
|