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