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                 
48                <a href="DbCXXUsage.html">Database Usage Example</a> 
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            In order to update <tt class="function">example_database_load</tt> 
79            to maintain an index of inventory item names, all we really need 
80            to do is:
81        </p>
82        <div class="orderedlist">
83          <ol type="1">
84            <li>
85              <p>
86                    Create a new database to be used as a secondary database.
87                </p>
88            </li>
89            <li>
90              <p>
91                    Associate our new database to the inventory primary
92                    database.
93                </p>
94            </li>
95          </ol>
96        </div>
97        <p>
98            We also need a function that can create our secondary keys for us.
99        </p>
100        <p>
101            Because DB maintains secondary databases for us; once this work
102            is done we need not make any other changes to <tt class="function">example_database_load</tt>. 
103
104            
105        </p>
106        <p>
107            Remember that you can find the complete implementation of these functions
108            in:
109        </p>
110        <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_cxx/getting_started</pre>
111        <p>
112            where <tt class="literal"><span class="emphasis"><em>DB_INSTALL</em></span></tt> is the location where you
113            placed your DB distribution.
114        </p>
115        <p>
116            To begin, we go to <tt class="filename">gettingStartedCommon.hpp</tt> and
117            we write our secondary key extractor function. This is a fairly
118            trivial function to write because we have already done most of the
119            work when we wrote the <tt class="classname">InventoryData</tt> class.
120            Recall that when we wrote that class, we provided a constructor that
121            accepts a pointer to a buffer and unpacks the contents of the buffer
122            for us (see <a href="DbCXXUsage.html#InventoryData">InventoryData Class</a>
123            for the implementation). We now make use of that constructor.
124        </p>
125        <a id="cxx_index10"></a>
126        <pre class="programlisting">// File: gettingStartedCommon.hpp
127// Forward declarations
128class Db;
129class Dbt;
130
131// Used to extract an inventory item's name from an
132// inventory database record. This function is used to create
133// keys for secondary database records.
134int
135get_item_name(Db *dbp, const Dbt *pkey, const Dbt *pdata, Dbt *skey)
136{
137    // Obtain the buffer location where the we placed the item's name. In
138    // this example, the item's name is located in the primary data. It is
139    // the first string in the buffer after the price (a double) and
140    // the quantity (a long).
141    size_t offset = sizeof(double) + sizeof(long);
142    char * itemname = (char *)pdata-&gt;get_data() + offset;
143
144    // unused
145    (void)pkey;
146
147    // If the offset is beyond the end of the data, then there is a
148    // problem with the buffer contained in pdata, or there's a
149    // programming error in how the buffer is marshalled/unmarshalled.
150    // This should never happen!
151    if ((u_int32_t)id.getBufferSize() != pdata-&gt;get_size()) {
152        dbp-&gt;errx("get_item_name: buffer sizes do not match!");
153        // When we return non-zero, the index record is not
154        // added/updated.
155        return (-1);
156    }
157    // Now set the secondary key's data to be the item name 
158
159    skey-&gt;set_data(itemname);
160    skey-&gt;set_size(strlen(itemname) + 1);
161
162    return (0);
163}; </pre>
164        <p>
165        Having written our key extractor callback, we now need to make
166        a trivial update to our <tt class="classname">MyDb</tt> implementation.
167        Because an item name is used by multiple inventory records, we need our
168        secondary database to support sorted duplicates. We therefore must
169        update <tt class="classname">MyDb</tt> to handle this detail.
170    </p>
171        <p>
172        The <tt class="classname">MyDb</tt> class definition changes to add a
173        boolean to the constructor (remember that new code is in
174        <b class="userinput"><tt>bold</tt></b>):
175    </p>
176        <a id="cxx_index11"></a>
177        <pre class="programlisting">// File: MyDb.hpp
178#include &lt;db_cxx.h&gt;
179
180class MyDb
181{
182public:
183    // Constructor requires a path to the database,
184    // and a database name.
185    MyDb(std::string &amp;path, std::string &amp;dbName,
186         <b class="userinput"><tt>bool isSecondary = false</tt></b>);
187                                                                                                                                    
188    // Our destructor just calls our private close method.
189    ~MyDb() { close(); }
190                                                                                                                                    
191    inline Db &amp;getDb() {return db_;}
192                                                                                                                                    
193private:
194    Db db_;
195    std::string dbFileName_;
196    u_int32_t cFlags_;
197                                                                                                                                    
198    // Make sure the default constructor is private
199    // We don't want it used.
200    MyDb() : db_(0, 0) {}
201                                                                                                                                    
202    // We put our database close activity here.
203    // This is called from our destructor. In
204    // a more complicated example, we might want
205    // to make this method public, but a private
206    // method is more appropriate for this example.
207    void close();
208}; </pre>
209        <p>
210        And the implementation changes slightly to take advantage of the new
211        boolean. Note that to save space, we just show the constructor where the
212        code actually changes:
213    </p>
214        <a id="cxx_index12"></a>
215        <pre class="programlisting">// File: MyDb.cpp
216#include "MyDb.hpp"
217
218// Class constructor. Requires a path to the location
219// where the database is located, and a database name
220MyDb::MyDb(std::string &amp;path, std::string &amp;dbName,
221           <b class="userinput"><tt>bool isSecondary</tt></b>)
222    : db_(NULL, 0),               // Instantiate Db object
223      dbFileName_(path + dbName), // Database file name
224      cFlags_(DB_CREATE)          // If the database doesn't yet exist,
225                                  // allow it to be created.
226{
227    try
228    {
229        // Redirect debugging information to std::cerr
230        db_.set_error_stream(&amp;std::cerr);
231                                                                                                                                    
232        <b class="userinput"><tt>// If this is a secondary database, support
233        // sorted duplicates
234        if (isSecondary)
235            db_.set_flags(DB_DUPSORT);</tt></b>
236                                                                                                                                    
237        // Open the database
238        db_.open(NULL, dbFileName_.c_str(), NULL, DB_BTREE, cFlags_, 0);
239    }
240    // DbException is not a subclass of std::exception, so we
241    // need to catch them both.
242    catch(DbException &amp;e)
243    {
244        std::cerr &lt;&lt; "Error opening database: " &lt;&lt; dbFileName_ &lt;&lt; "\n";
245        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
246    }
247    catch(std::exception &amp;e)
248    {
249        std::cerr &lt;&lt; "Error opening database: " &lt;&lt; dbFileName_ &lt;&lt; "\n";
250        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
251    }
252} </pre>
253        <p>
254        That done, we can now update
255        <tt class="function">example_database_load</tt> to open our new secondary
256        database and associate it to the inventory database.
257    </p>
258        <p>
259        To save space, we do not show the entire implementation for this program
260        here. Instead, we show just the <tt class="function">main()</tt> function,
261        which is where all our modifications occur. To
262        see the rest of the implementation for this command, see 
263          <a href="DbCXXUsage.html#exampledbload-cxx">example_database_load</a>.
264    </p>
265        <a id="cxx_index13"></a>
266        <pre class="programlisting">// Loads the contents of vendors.txt and inventory.txt into
267// Berkeley DB databases.
268int
269main(int argc, char *argv[])
270{
271    // Initialize the path to the database files
272    std::string basename("./");
273    std::string databaseHome("./");
274                                                                                                                                         
275    // Database names
276    std::string vDbName("vendordb.db");
277    std::string iDbName("inventorydb.db");
278    <b class="userinput"><tt>std::string itemSDbName("itemname.sdb");</tt></b>
279                                                                                                                                         
280    // Parse the command line arguments here and determine
281    // the location of the flat text files containing the
282    // inventory data here. This step is omitted for clarity.
283                                                                                                                                         
284    //  Identify the full name for our input files, which should
285    //  also include some path information.
286    std::string inventoryFile = basename + "inventory.txt";
287    std::string vendorFile = basename + "vendors.txt";
288                                                                                                                                         
289    try
290    {
291        // Open all databases.
292        MyDb inventoryDB(databaseHome, iDbName);
293        MyDb vendorDB(databaseHome, vDbName);
294        <b class="userinput"><tt>MyDb itemnameSDB(databaseHome, itemSDbName, true);
295
296        // Associate the primary and the secondary
297        inventoryDB.getDb().associate(NULL,
298                                      &amp;(itemnameSDB.getDb()),
299                                      get_item_name,
300                                      0);</tt></b>
301
302        // Load the vendor database
303        loadVendorDB(vendorDB, vendorFile);
304                                                                                                                                         
305        // Load the inventory database
306        loadInventoryDB(inventoryDB, inventoryFile);
307    } catch(DbException &amp;e) {
308        std::cerr &lt;&lt; "Error loading databases. " &lt;&lt; std::endl;
309        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
310        return(e.get_errno());
311    } catch(std::exception &amp;e) {
312        std::cerr &lt;&lt; "Error loading databases. " &lt;&lt; std::endl;
313        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
314        return(-1);
315    }
316                                                                                                                                         
317    return(0);
318} // End main </pre>
319        <p>
320        Note that the order in which we instantiate our
321        <tt class="classname">MyDb</tt> class instances is important. In general you
322        want to close a secondary database before closing the primary with which
323        it is associated. This is particularly true for multi-threaded or
324        multi-processed applications where the database closes are not single
325        threaded. Even so, it is a good habit to adopt, even for simple
326        applications such as this one. Here, we ensure that the databases are
327        closed in the desired order by opening the secondary database last.
328        This works because our <tt class="classname">MyDb</tt> objects are on
329        the stack, and therefore the last one opened is the first one closed.
330    </p>
331        <p>
332        That completes our update to <tt class="function">example_database_load</tt>.
333        Now when this program is called, it will automatically index inventory
334        items based on their names. We can then query for those items using the
335        new index. We show how to do that in the next section.
336    </p>
337      </div>
338      <div class="sect2" lang="en" xml:lang="en">
339        <div class="titlepage">
340          <div>
341            <div>
342              <h3 class="title"><a id="edrWIndexes"></a>Secondary Databases with example_database_read</h3>
343            </div>
344          </div>
345          <div></div>
346        </div>
347        <p>
348            In <a href="CoreCursorUsage.html">Cursor Example</a> we
349            wrote an application that displays every inventory item in the
350            Inventory database. In this section, we will update that example to
351            allow us to search for and display an inventory item given a
352            specific name. To do this, we will make use of the secondary
353            database that <tt class="function">example_database_load</tt> now
354            creates.
355        </p>
356        <p>
357            The update to <tt class="function">example_database_read</tt> is
358            relatively modest. We need to open the new secondary database
359            in exactly the same way was we do for
360            <tt class="function">example_database_load</tt>. 
361            We also need to add a command line parameter on
362            which we can specify the item name, and we will need a new function
363            in which we will perform the query and display the results.
364        </p>
365        <p>
366            To begin, we add a single forward declaration to the application,
367            and update our usage function slightly:
368        </p>
369        <a id="cxx_index14"></a>
370        <pre class="programlisting">// File: example_database_read.cpp
371#include &lt;iostream&gt;
372#include &lt;fstream&gt;
373#include &lt;cstdlib&gt;
374                                                                                                                                         
375#include "MyDb.hpp"
376#include "gettingStartedCommon.hpp"
377                                                                                                                                         
378// Forward declarations
379int show_all_records(MyDb &amp;inventoryDB, MyDb &amp;vendorDB);
380<b class="userinput"><tt>int show_item(MyDb &amp;itemnameSDB, MyDb &amp;vendorDB, std::string &amp;itemName);</tt></b>
381int show_vendor(MyDb &amp;vendorDB, const char *vendor); </pre>
382        <p>
383        Next, we update <tt class="function">main()</tt> to 
384        <span>open the new secondary database and</span>
385        accept the new command line switch.
386        We also need a new variable to contain the item's name.        
387    </p>
388        <p>
389        The final update to the <tt class="function">main()</tt> entails a little bit
390        of logic to determine whether we want to display all available inventory
391        items, or just the ones that match a name provided on the
392        <tt class="literal">-i</tt> command line parameter.
393    </p>
394        <a id="cxx_index15"></a>
395        <pre class="programlisting">// Displays all inventory items and the associated vendor record.
396int
397main (int argc, char *argv[])
398{
399    // Initialize the path to the database files
400    std::string databaseHome("./");
401    <b class="userinput"><tt>std::string itemName;</tt></b>
402                                                                                                                                         
403    // Database names
404    std::string vDbName("vendordb.db");
405    std::string iDbName("inventorydb.db");
406    <b class="userinput"><tt>std::string itemSDbName("itemname.sdb");</tt></b>
407                                                                                                                                         
408    // Parse the command line arguments
409    // Omitted for brevity
410                                                                                                                                         
411    try
412    {
413        // Open all databases.
414        MyDb inventoryDB(databaseHome, iDbName);
415        MyDb vendorDB(databaseHome, vDbName);
416        <b class="userinput"><tt>MyDb itemnameSDB(databaseHome, itemSDbName, true);
417                                                                                                                                    
418        // Associate the secondary to the primary
419        inventoryDB.getDb().associate(NULL,
420                                      &amp;(itemnameSDB.getDb()),
421                                      get_item_name,
422                                      0);
423
424        if (itemName.empty())
425        {</tt></b>
426            show_all_records(inventoryDB, vendorDB);
427        <b class="userinput"><tt>} else {
428            show_item(itemnameSDB, vendorDB, itemName);
429        }</tt></b>
430    } catch(DbException &amp;e) {
431        std::cerr &lt;&lt; "Error reading databases. " &lt;&lt; std::endl;
432        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
433        return(e.get_errno());
434    } catch(std::exception &amp;e) {
435        std::cerr &lt;&lt; "Error reading databases. " &lt;&lt; std::endl;
436        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
437        return(-1);
438    }
439                                                                                                                                         
440    return(0);
441} // End main </pre>
442        <p>
443        The only other thing that we need to add to the application is the
444        implementation of the 
445             
446            <tt class="function">show_item()</tt> 
447        function.
448    </p>
449        <div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
450          <h3 class="title">Note</h3>
451          <p>
452            In the interest of space, we refrain from showing the other
453            functions used by this application. For their implementation, please
454            see <a href="CoreCursorUsage.html">Cursor Example</a>.
455            Alternatively, you can see the entire implementation of this
456            application
457            in:
458        </p>
459          <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples_cxx/getting_started</pre>
460          <p>
461            where <tt class="literal"><span class="emphasis"><em>DB_INSTALL</em></span></tt> is the location where you
462            placed your DB distribution.
463        </p>
464        </div>
465        <a id="cxx_index16"></a>
466        <pre class="programlisting">// Shows the records in the inventory database that
467// have a specific item name. For each inventory record
468// shown, the appropriate vendor record is also displayed.
469int
470show_item(MyDb &amp;itemnameSDB, MyDb &amp;vendorDB, std::string &amp;itemName)
471{
472    // Get a cursor to the itemname secondary db
473    Dbc *cursorp;
474                                                                                                                                    
475    try {
476        itemnameSDB.getDb().cursor(NULL, &amp;cursorp, 0);
477                                                                                                                                    
478        // Get the search key. This is the name on the inventory
479        // record that we want to examine.
480        std::cout &lt;&lt; "Looking for " &lt;&lt; itemName &lt;&lt; std::endl;
481        Dbt key((void *)itemName.c_str(), itemName.length() + 1);
482        Dbt data;
483                                                                                                                                    
484        // Position the cursor to the first record in the secondary
485        // database that has the appropriate key.
486        int ret = cursorp-&gt;get(&amp;key, &amp;data, DB_SET);
487        if (!ret) {
488            do {
489                InventoryData inventoryItem(data.get_data());
490                inventoryItem.show();
491                                                                                                                                    
492                show_vendor(vendorDB, inventoryItem.getVendor().c_str());
493                                                                                                                                    
494            } while(cursorp-&gt;get(&amp;key, &amp;data, DB_NEXT_DUP) == 0);
495        } else {
496            std::cerr &lt;&lt; "No records found for '" &lt;&lt; itemName
497                      &lt;&lt; "'" &lt;&lt; std::endl;
498        }
499    } catch(DbException &amp;e) {
500        itemnameSDB.getDb().err(e.get_errno(), "Error in show_item");
501        cursorp-&gt;close();
502        throw e;
503    } catch(std::exception &amp;e) {
504        itemnameSDB.getDb().errx("Error in show_item: %s", e.what());
505        cursorp-&gt;close();
506        throw e;
507    }
508                                                                                                                                    
509    cursorp-&gt;close();
510    return (0);
511}
512
513</pre>
514        <p>
515        This completes our update to
516        <tt class="classname">example_inventory_read</tt>. Using this update, you
517        can now search for and show all inventory items that match a particular
518        name. For example:
519    </p>
520        <pre class="programlisting">    example_inventory_read -i "Zulu Nut"</pre>
521      </div>
522    </div>
523    <div class="navfooter">
524      <hr />
525      <table width="100%" summary="Navigation footer">
526        <tr>
527          <td width="40%" align="left"><a accesskey="p" href="joins.html">Prev</a>��</td>
528          <td width="20%" align="center">
529            <a accesskey="u" href="indexes.html">Up</a>
530          </td>
531          <td width="40%" align="right">��<a accesskey="n" href="dbconfig.html">Next</a></td>
532        </tr>
533        <tr>
534          <td width="40%" align="left" valign="top">Database Joins��</td>
535          <td width="20%" align="center">
536            <a accesskey="h" href="index.html">Home</a>
537          </td>
538          <td width="40%" align="right" valign="top">��Chapter��6.��Database Configuration</td>
539        </tr>
540      </table>
541    </div>
542  </body>
543</html>
544