1<?xml version="1.0" encoding="UTF-8" standalone="no"?>
2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
3<html xmlns="http://www.w3.org/1999/xhtml">
4  <head>
5    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
6    <title>Secondary Database Example</title>
7    <link rel="stylesheet" href="gettingStarted.css" type="text/css" />
8    <meta name="generator" content="DocBook XSL Stylesheets V1.62.4" />
9    <link rel="home" href="index.html" title="Getting Started with Berkeley DB" />
10    <link rel="up" href="indexes.html" title="Chapter 5. Secondary Databases" />
11    <link rel="previous" href="joins.html" title="Database Joins" />
12    <link rel="next" href="dbconfig.html" title="Chapter 6. Database Configuration" />
13  </head>
14  <body>
15    <div class="navheader">
16      <table width="100%" summary="Navigation header">
17        <tr>
18          <th colspan="3" align="center">Secondary Database Example</th>
19        </tr>
20        <tr>
21          <td width="20%" align="left"><a accesskey="p" href="joins.html">Prev</a> </td>
22          <th width="60%" align="center">Chapter 5. Secondary Databases</th>
23          <td width="20%" align="right"> <a accesskey="n" href="dbconfig.html">Next</a></td>
24        </tr>
25      </table>
26      <hr />
27    </div>
28    <div class="sect1" lang="en" xml:lang="en">
29      <div class="titlepage">
30        <div>
31          <div>
32            <h2 class="title" style="clear: both"><a id="coreindexusage"></a>Secondary Database Example</h2>
33          </div>
34        </div>
35        <div></div>
36      </div>
37      <p>
38        In previous chapters in this book, we built applications that load
39        and display several DB databases. In this example, we will extend those
40        examples to use secondary databases. Specifically:
41    </p>
42      <div class="itemizedlist">
43        <ul type="disc">
44          <li>
45            <p>
46            In 
47                <a href="DbUsage.html">Database Usage Example</a> 
48                 
49            we built an application that can open and load data into several databases.
50            In <a href="coreindexusage.html#edlWIndexes">Secondary Databases with example_database_load</a> we will extend
51            that application to also open a secondary database for the purpose
52            of indexing inventory item names.
53        </p>
54          </li>
55          <li>
56            <p>
57            In <a href="CoreCursorUsage.html">Cursor Example</a> we
58            built an application to display our inventory database (and related
59            vendor information). In 
60            <a href="coreindexusage.html#edrWIndexes">Secondary Databases with example_database_read</a>
61            we will extend that application to
62            show inventory records based on the index we cause to be loaded using
63            <tt class="function">example_database_load</tt>.
64        </p>
65          </li>
66        </ul>
67      </div>
68      <div class="sect2" lang="en" xml:lang="en">
69        <div class="titlepage">
70          <div>
71            <div>
72              <h3 class="title"><a id="edlWIndexes"></a>Secondary Databases with example_database_load</h3>
73            </div>
74          </div>
75          <div></div>
76        </div>
77        <p>
78            <tt class="function">example_database_load</tt> uses several utility
79            functions to open and close its databases. In order to cause 
80            <tt class="function">example_database_load</tt> to maintain an index of
81            inventory item names, all we really need to do is update the utility
82            functions to:
83        </p>
84        <div class="orderedlist">
85          <ol type="1">
86            <li>
87              <p>
88                    Create a new database to be used as a secondary database.
89                </p>
90            </li>
91            <li>
92              <p>
93                    Associate our new database to the inventory primary
94                    database.
95                </p>
96            </li>
97            <li>
98              <p>
99                    Close the secondary database when we close the rest of our
100                    databases.
101                </p>
102            </li>
103          </ol>
104        </div>
105        <p>
106            We also need a function that can create our secondary keys for us.
107        </p>
108        <p>
109            Because DB maintains secondary databases for us; once this work
110            is done we need not make any other changes to <tt class="function">example_database_load</tt>. 
111
112            <span>Therefore, we can limit
113            all our work to the code found in <tt class="filename">gettingstarted_common.h</tt>
114            and <tt class="filename">gettingstarted_common.c</tt>.
115            </span>
116        </p>
117        <p>
118            Remember that you can find the complete implementation of these functions
119            in:
120        </p>
121        <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_c/getting_started</pre>
122        <p>
123            where <tt class="literal"><span class="emphasis"><em>DB_INSTALL</em></span></tt> is the location where you
124            placed your DB distribution.
125        </p>
126        <p>
127            To begin, we need to update the <tt class="literal">stock_dbs</tt>
128            structure to accommodate the additional database. We defined this
129            structure in <tt class="filename">gettingstarted_common.h</tt>. We can
130            limit our update to this file to just that structure definition:
131        </p>
132        <p>
133            Remember that new code is in <b class="userinput"><tt>bold</tt></b>.
134        </p>
135        <a id="c_index10"></a>
136        <pre class="programlisting">/* file: gettingstarted_common.h */
137#include &lt;db.h&gt;
138
139typedef struct stock_dbs {
140    DB *inventory_dbp; /* Database containing inventory information */
141    DB *vendor_dbp;    /* Database containing vendor information */
142    <b class="userinput"><tt>DB *itemname_sdbp; /* Index based on the item name index */</tt></b>
143                                                                                                                                
144    char *db_home_dir;       /* Directory containing the database files */
145    <b class="userinput"><tt>char *itemname_db_name;  /* Itemname secondary database */</tt></b>
146    char *inventory_db_name; /* Name of the inventory database */
147    char *vendor_db_name;    /* Name of the vendor database */
148} STOCK_DBS;
149
150/* Function prototypes */
151int databases_setup(STOCK_DBS *, const char *, FILE *);
152int databases_close(STOCK_DBS *);
153void initialize_stockdbs(STOCK_DBS *);
154int open_database(DB **, const char *, const char *, FILE *, <b class="userinput"><tt>int</tt></b>);
155void set_db_filenames(STOCK_DBS *my_stock);
156
157</pre>
158        <p>
159            Because we updated our stock_dbs structure, we need to update our
160            stock_dbs utility functions 
161            (<a href="CoreDbUsage.html#stock-db-functions">The stock_db Utility Functions</a>)
162            accordingly. The updates are trivial and so we won't show them here
163            in the interest of space. You can find their complete implementation
164            in the <tt class="filename">gettingstarted_common.c</tt> file
165            accompanying this example in your DB distribution.
166        </p>
167        <p>
168            More importantly, however, we need to go to 
169            <tt class="filename">gettingstarted_common.c</tt>
170            and create our secondary key extractor function. When we
171            store our inventory items, we place the item name in the buffer
172            immediately after a <tt class="literal">float</tt> and an
173            <tt class="literal">int</tt>, so retrieving the string from the buffer is
174            fairly easy to do:
175        </p>
176        <a id="c_index11"></a>
177        <pre class="programlisting">/* file: gettingstarted_common.c */
178#include "gettingstarted_common.h"
179                                                                                                                                  
180<b class="userinput"><tt>/*
181 * Used to extract an inventory item's name from an
182 * inventory database record. This function is used to create
183 * keys for secondary database records.
184 */
185int
186get_item_name(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey)
187{
188    int offset;
189                                                                                                                                  
190    /*
191     * First, obtain the buffer location where we placed the
192     * item's name. In this example, the item's name is located
193     * in the primary data. It is the first string in the
194     * buffer after the price (a float) and the quantity (an int).
195     *
196     * See load_inventory_database() in example_database_load.c
197     * for how we marshalled the inventory information into the
198     * data DBT.
199     */
200    offset = sizeof(float) + sizeof(int);
201                                                                                                                                  
202    /* Check to make sure there's data */
203    if (pdata-&gt;size &lt; offset)
204        return (-1); /* Returning non-zero means that the
205                      * secondary record is not created/updated.
206                      */
207                                                                                                                                  
208    /* Now set the secondary key's data to be the item name */
209    memset(skey, 0, sizeof(DBT));
210    skey-&gt;data = pdata-&gt;data + offset;
211    skey-&gt;size = strlen(skey-&gt;data) + 1;
212                                                                                                                                  
213    return (0);
214}</tt></b> </pre>
215        <p>
216        Having completed that function, we need to update 
217        <tt class="function">set_db_filenames()</tt> and 
218        <tt class="function">initialize_stockdbs()</tt> to handle the
219        new secondary databases that our application will now use.
220        These functions were originally introduced in 
221        <a href="CoreDbUsage.html#stock-db-functions">The stock_db Utility Functions</a>.
222    </p>
223        <a id="c_index11.1"></a>
224        <pre class="programlisting">
225/* Initializes the STOCK_DBS struct.*/
226void
227initialize_stockdbs(STOCK_DBS *my_stock)
228{
229    my_stock-&gt;db_home_dir = DEFAULT_HOMEDIR;
230    my_stock-&gt;inventory_dbp = NULL;
231    my_stock-&gt;vendor_dbp = NULL;
232    <b class="userinput"><tt>my_stock-&gt;itemname_sdbp = NULL;</tt></b>
233
234    my_stock-&gt;inventory_db_name = NULL;
235    my_stock-&gt;vendor_db_name = NULL;
236    <b class="userinput"><tt>my_stock-&gt;itemname_db_name = NULL;</tt></b>
237}
238
239/* Identify all the files that will hold our databases. */
240void
241set_db_filenames(STOCK_DBS *my_stock)
242{
243    size_t size;
244
245    /* Create the Inventory DB file name */
246    size = strlen(my_stock-&gt;db_home_dir) + strlen(INVENTORYDB) + 1;
247    my_stock-&gt;inventory_db_name = malloc(size);
248    snprintf(my_stock-&gt;inventory_db_name, size, "%s%s",
249      my_stock-&gt;db_home_dir, INVENTORYDB);
250    
251    /* Create the Vendor DB file name */
252    size = strlen(my_stock-&gt;db_home_dir) + strlen(VENDORDB) + 1;
253    my_stock-&gt;vendor_db_name = malloc(size);
254    snprintf(my_stock-&gt;vendor_db_name, size, "%s%s",
255      my_stock-&gt;db_home_dir, VENDORDB);
256
257    <b class="userinput"><tt>/* Create the itemname DB file name */
258    size = strlen(my_stock-&gt;db_home_dir) + strlen(ITEMNAMEDB) + 1;
259    my_stock-&gt;itemname_db_name = malloc(size);
260    snprintf(my_stock-&gt;itemname_db_name, size, "%s%s",
261      my_stock-&gt;db_home_dir, ITEMNAMEDB);</tt></b>
262} </pre>
263        <p>
264        We also need to update the
265        <tt class="function">open_database()</tt> (as described in
266        <a href="CoreDbUsage.html#open-db">open_database() Function</a>)
267        to take special actions if we are
268        opening a secondary database. Unlike our primary databases, we want to
269        support sorted duplicates for our secondary database. This is because we
270        are indexing based on an item's name, and item names are
271        shared by multiple inventory records. As a result every key the secondary
272        database (an item name) will be used by multiple records (pointers to
273        records in our primary database). We allow this by configuring our
274        secondary database to support duplicate records. Further, because
275        BTrees perform best when their records are sorted, we go ahead and
276        configure our secondary database for sorted duplicates.
277    </p>
278        <p>
279        To do this, we add a parameter to the function that indicates whether we are
280        opening a secondary database, and we add in the few lines of code
281        necessary to set the sorted duplicates flags.
282    </p>
283        <a id="c_index12"></a>
284        <pre class="programlisting">/* Opens a database */
285int
286open_database(DB **dbpp,       /* The DB handle that we are opening */
287    const char *file_name,     /* The file in which the db lives */
288    const char *program_name,  /* Name of the program calling this
289                                * function */
290    FILE *error_file_pointer,
291    <b class="userinput"><tt>int is_secondary</tt></b>)
292{
293    DB *dbp;    /* For convenience */
294    u_int32_t open_flags;
295    int ret;
296
297    /* Initialize the DB handle */
298    ret = db_create(&amp;dbp, NULL, 0);
299    if (ret != 0) {
300        fprintf(error_file_pointer, "%s: %s\n", program_name,
301                db_strerror(ret));
302        return (ret);
303    }
304
305    /* Point to the memory malloc'd by db_create() */
306    *dbpp = dbp;
307
308    /* Set up error handling for this database */
309    dbp-&gt;set_errfile(dbp, error_file_pointer);
310    dbp-&gt;set_errpfx(dbp, program_name);
311
312    <b class="userinput"><tt>/*
313     * If this is a secondary database, then we want to allow
314     * sorted duplicates.
315     */
316    if (is_secondary) {
317        ret = dbp-&gt;set_flags(dbp, DB_DUPSORT);
318        if (ret != 0) {
319            dbp-&gt;err(dbp, ret, "Attempt to set DUPSORT flag failed.",
320              file_name);
321            return (ret);
322        }
323    }</tt></b>
324
325    /* Set the open flags */
326    open_flags = DB_CREATE;
327
328    /* Now open the database */
329    ret = dbp-&gt;open(dbp,        /* Pointer to the database */
330                    NULL,       /* Txn pointer */
331                    file_name,  /* File name */
332                    NULL,       /* Logical db name (unneeded) */
333                    DB_BTREE,   /* Database type (using btree) */
334                    open_flags, /* Open flags */
335                    0);         /* File mode. Using defaults */
336    if (ret != 0) {
337        dbp-&gt;err(dbp, ret, "Database '%s' open failed.", file_name);
338        return (ret);
339    }
340
341    return (ret);
342}   </pre>
343        <p>
344        That done, we can now update <tt class="function">databases_setup()</tt> 
345        (see <a href="CoreDbUsage.html#databasesetup">The databases_setup() Function</a>) to create
346        and open our secondary database. To do this, we have to add a flag to
347        each call to <tt class="function">open_database()</tt> that indicates whether
348        the database is a secondary. We also have to associate our secondary
349        database with the inventory database (the primary). 
350    </p>
351        <p>
352        Note that we do not anywhere in this example show the definition of
353        <tt class="literal">PRIMARY_DB</tt> and <tt class="literal">SECONDARY_DB</tt>. See
354        <tt class="filename">gettingstarted_common.h</tt> in your DB examples
355        directory for those definitions (they are just <tt class="literal">0</tt> and
356        <tt class="literal">1</tt>, respectively).
357    </p>
358        <a id="c_index13"></a>
359        <pre class="programlisting">/* opens all databases */
360int
361databases_setup(STOCK_DBS *my_stock, const char *program_name,
362  FILE *error_file_pointer)
363{
364    int ret;
365
366    /* Open the vendor database */
367    ret = open_database(&amp;(my_stock-&gt;vendor_dbp),
368      my_stock-&gt;vendor_db_name,
369      program_name, error_file_pointer,
370      <b class="userinput"><tt>PRIMARY_DB</tt></b>);
371    if (ret != 0)
372        /*
373         * Error reporting is handled in open_database() so just return
374         * the return code here.
375         */
376        return (ret);
377
378    /* Open the inventory database */
379    ret = open_database(&amp;(my_stock-&gt;inventory_dbp),
380      my_stock-&gt;inventory_db_name,
381      program_name, error_file_pointer,
382      <b class="userinput"><tt>PRIMARY_DB</tt></b>);
383    if (ret != 0)
384        /*
385         * Error reporting is handled in open_database() so just return
386         * the return code here.
387         */
388        return (ret);
389
390    <b class="userinput"><tt>/*
391     * Open the itemname secondary database. This is used to
392     * index the product names found in the inventory
393     * database.
394     */
395    ret = open_database(&amp;(my_stock-&gt;itemname_sdbp),
396      my_stock-&gt;itemname_db_name,
397      program_name, error_file_pointer,
398      SECONDARY_DB);
399    if (ret != 0)
400        /*
401         * Error reporting is handled in open_database() so just return
402         * the return code here.
403         */
404        return (ret);
405                                                                                                                                
406    /*
407     * Associate the itemname db with its primary db
408     * (inventory db).
409     */
410     my_stock-&gt;inventory_dbp-&gt;associate(
411       my_stock-&gt;inventory_dbp,    /* Primary db */
412       NULL,                       /* txn id */
413       my_stock-&gt;itemname_sdbp,    /* Secondary db */
414       get_item_name,              /* Secondary key extractor */
415       0);                         /* Flags */
416     </tt></b>
417
418    printf("databases opened successfully\n");
419    return (0);
420}</pre>
421        <p>
422        Finally, we need to update <tt class="function">databases_close()</tt> 
423        (<a href="CoreDbUsage.html#database_close">The databases_close() Function</a>)
424        to close our
425        new secondary database. Note that we are careful to close the secondary
426        before the primary, even though the database close routine is single
427        threaded.
428    </p>
429        <a id="c_index14"></a>
430        <pre class="programlisting">/* Closes all the databases and secondary databases. */
431int
432databases_close(STOCK_DBS *my_stock)
433{
434    int ret;
435    /*
436     * Note that closing a database automatically flushes its cached data
437     * to disk, so no sync is required here.
438     */
439
440    <b class="userinput"><tt>if (my_stock-&gt;itemname_sdbp != NULL) {
441        ret = my_stock-&gt;itemname_sdbp-&gt;close(my_stock-&gt;itemname_sdbp, 0);
442        if (ret != 0)
443            fprintf(stderr, "Itemname database close failed: %s\n",
444              db_strerror(ret));
445    }</tt></b>
446
447    if (my_stock-&gt;inventory_dbp != NULL) {
448        ret = my_stock-&gt;inventory_dbp-&gt;close(my_stock-&gt;inventory_dbp, 0);
449        if (ret != 0)
450            fprintf(stderr, "Inventory database close failed: %s\n",
451              db_strerror(ret));
452    }
453
454    if (my_stock-&gt;vendor_dbp != NULL) {
455        ret = my_stock-&gt;vendor_dbp-&gt;close(my_stock-&gt;vendor_dbp, 0);
456        if (ret != 0)
457            fprintf(stderr, "Vendor database close failed: %s\n",
458              db_strerror(ret));
459    }
460
461    printf("databases closed.\n");
462    return (0);
463} </pre>
464        <p>
465        And the implementation changes slightly to take advantage of the new
466        boolean. Note that to save space, we just show the constructor where the
467        code actually changes:
468    </p>
469        <p>
470        That completes our update to <tt class="function">example_database_load</tt>.
471        Now when this program is called, it will automatically index inventory
472        items based on their names. We can then query for those items using the
473        new index. We show how to do that in the next section.
474    </p>
475      </div>
476      <div class="sect2" lang="en" xml:lang="en">
477        <div class="titlepage">
478          <div>
479            <div>
480              <h3 class="title"><a id="edrWIndexes"></a>Secondary Databases with example_database_read</h3>
481            </div>
482          </div>
483          <div></div>
484        </div>
485        <p>
486            In <a href="CoreCursorUsage.html">Cursor Example</a> we
487            wrote an application that displays every inventory item in the
488            Inventory database. In this section, we will update that example to
489            allow us to search for and display an inventory item given a
490            specific name. To do this, we will make use of the secondary
491            database that <tt class="function">example_database_load</tt> now
492            creates.
493        </p>
494        <p>
495            Because we manage all our database open and close activities in
496            <tt class="function">databases_setup()</tt> and
497            <tt class="function">databases_close()</tt>,
498            the update to <tt class="function">example_database_read</tt> is
499            relatively modest. We need only add a command line parameter on
500            which we can specify the item name, and we will need a new function
501            in which we will perform the query and display the results.
502        </p>
503        <p>
504            To begin, we add a single forward declaration to the application,
505            and update our usage function slightly:
506        </p>
507        <a id="c_index15"></a>
508        <pre class="programlisting">/* File: example_database_read.c */
509/* gettingstarted_common.h includes db.h for us */
510#include "gettingstarted_common.h"
511                                                                                                                                  
512/* Forward declarations */
513char * show_inventory_item(void *);
514int show_all_records(STOCK_DBS *);
515<b class="userinput"><tt>int show_records(STOCK_DBS *, char *);</tt></b>
516int show_vendor_record(char *, DB *); </pre>
517        <p>
518        Next, we update <tt class="function">main()</tt> to 
519        
520        accept the new command line switch.
521        We also need a new variable to contain the item's name.        
522    </p>
523        <a id="c_index16"></a>
524        <pre class="programlisting">/*
525<b class="userinput"><tt> * Searches for a inventory item based on that item's name. The search is
526 * performed using the item name secondary database. Displays all
527 * inventory items that use the specified name, as well as the vendor
528 * associated with that inventory item.
529 *
530 * If no item name is provided, then all inventory items are displayed.</tt></b>
531 */
532int
533main(int argc, char *argv[])
534{
535    STOCK_DBS my_stock;
536    int ret;
537    <b class="userinput"><tt>char *itemname</tt></b>;
538                                                                                                                                         
539    /* Initialize the STOCK_DBS struct */
540    initialize_stockdbs(&amp;my_stock);
541                                                                                                                                         
542    <b class="userinput"><tt>itemname = NULL;</tt></b>
543    /*
544     * Parse the command line arguments here and determine
545     * the location of the database files <b class="userinput"><tt>as well as the
546     * inventory item we want displayed, if any.</tt></b> This step is
547     * omitted for brevity.
548     */
549                                                                                                                                         
550    /*
551     * Identify the files that will hold our databases
552     * This function uses information obtained from the
553     * command line to identify the directory in which
554     * the database files reside.
555     */
556    set_db_filenames(&amp;my_stock);
557                                                                                                                                         
558    /* Open all databases */
559    ret = databases_setup(&amp;my_stock, "example_database_read", stderr);
560    if (ret != 0) {
561        fprintf(stderr, "Error opening databases\n");
562        databases_close(&amp;my_stock);
563        return (ret);
564    }</pre>
565        <p>
566        The final update to the <tt class="function">main()</tt> entails a little bit
567        of logic to determine whether we want to display all available inventory
568        items, or just the ones that match a name provided on the
569        <tt class="literal">-i</tt> command line parameter.
570    </p>
571        <a id="c_index17"></a>
572        <pre class="programlisting">    /* 
573     * Show either a single item or all items, depending
574     * on whether itemname is set to a value.
575     */
576    <b class="userinput"><tt>if (itemname == NULL)
577        ret = show_all_records(&amp;my_stock);
578    else
579        ret = show_records(&amp;my_stock, itemname);</tt></b>
580                                                                                                                                  
581    /* Close our databases */
582    databases_close(&amp;my_stock);
583    return (ret);
584} </pre>
585        <p>
586        The only other thing that we need to add to the application is the
587        implementation of the 
588            <tt class="function">show_records()</tt> 
589             
590        function.
591    </p>
592        <div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
593          <h3 class="title">Note</h3>
594          <p>
595            In the interest of space, we refrain from showing the other
596            functions used by this application. For their implementation, please
597            see <a href="CoreCursorUsage.html">Cursor Example</a>.
598            Alternatively, you can see the entire implementation of this
599            application
600            in:
601        </p>
602          <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_c/getting_started</pre>
603          <p>
604            where <tt class="literal"><span class="emphasis"><em>DB_INSTALL</em></span></tt> is the location where you
605            placed your DB distribution.
606        </p>
607        </div>
608        <a id="c_index18"></a>
609        <pre class="programlisting">/*
610 * Search for an inventory item given its name (using the inventory item
611 * secondary database) and display that record and any duplicates that may
612 * exist.
613 */
614int
615show_records(STOCK_DBS *my_stock, char *itemname)
616{
617    DBC *itemname_cursorp;
618    DBT key, data;
619    char *the_vendor;
620    int ret, exit_value;
621                                                                                                                                  
622    /* Initialize our DBTs. */
623    memset(&amp;key, 0, sizeof(DBT));
624    memset(&amp;data, 0, sizeof(DBT));
625                                                                                                                                  
626    /* Get a cursor to the itemname db */
627    my_stock-&gt;itemname_sdbp-&gt;cursor(my_stock-&gt;itemname_sdbp, 0,
628      &amp;itemname_cursorp, 0);
629                                                                                                                                  
630    /*
631     * Get the search key. This is the name on the inventory
632     * record that we want to examine.
633     */
634    key.data = itemname;
635    key.size = strlen(itemname) + 1;
636                                                                                                                                  
637    /*
638     * Position our cursor to the first record in the secondary
639     * database that has the appropriate key.
640     */
641    exit_value = 0;
642    ret = itemname_cursorp-&gt;get(itemname_cursorp, &amp;key, &amp;data, DB_SET);
643    if (!ret) {
644        do {
645            /*
646             * Show the inventory record and the vendor responsible
647             * for this inventory item.
648             */
649            the_vendor = show_inventory_item(data.data);
650            ret = show_vendor_record(the_vendor, my_stock-&gt;vendor_dbp);
651            if (ret) {
652                exit_value = ret;
653                break;
654            }
655            /*
656             * Our secondary allows duplicates, so we need to loop over
657             * the next duplicate records and show them all. This is done
658             * because an inventory item's name is not a unique value.
659             */
660        } while(itemname_cursorp-&gt;get(itemname_cursorp, &amp;key, &amp;data,
661            DB_NEXT_DUP) == 0);
662    } else {
663        printf("No records found for '%s'\n", itemname);
664    }
665                                                                                                                                  
666    /* Close the cursor */
667    itemname_cursorp-&gt;close(itemname_cursorp);
668                                                                                                                                  
669    return (exit_value);
670} </pre>
671        <p>
672        This completes our update to
673        <tt class="classname">example_inventory_read</tt>. Using this update, you
674        can now search for and show all inventory items that match a particular
675        name. For example:
676    </p>
677        <pre class="programlisting">    example_inventory_read -i "Zulu Nut"</pre>
678      </div>
679    </div>
680    <div class="navfooter">
681      <hr />
682      <table width="100%" summary="Navigation footer">
683        <tr>
684          <td width="40%" align="left"><a accesskey="p" href="joins.html">Prev</a> </td>
685          <td width="20%" align="center">
686            <a accesskey="u" href="indexes.html">Up</a>
687          </td>
688          <td width="40%" align="right"> <a accesskey="n" href="dbconfig.html">Next</a></td>
689        </tr>
690        <tr>
691          <td width="40%" align="left" valign="top">Database Joins </td>
692          <td width="20%" align="center">
693            <a accesskey="h" href="index.html">Home</a>
694          </td>
695          <td width="40%" align="right" valign="top"> Chapter 6. Database Configuration</td>
696        </tr>
697      </table>
698    </div>
699  </body>
700</html>
701