summaryrefslogtreecommitdiff
path: root/docs/gsg/CXX/coreindexusage.html
blob: 25f6d38148cbc1e35259ea3d091e2d385bb64ddc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
<?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>Secondary Database Example</title>
    <link rel="stylesheet" href="gettingStarted.css" type="text/css" />
    <meta name="generator" content="DocBook XSL Stylesheets V1.73.2" />
    <link rel="start" href="index.html" title="Getting Started with Berkeley DB" />
    <link rel="up" href="indexes.html" title="Chapter 5. Secondary Databases" />
    <link rel="prev" href="joins.html" title="Database Joins" />
    <link rel="next" href="dbconfig.html" title="Chapter 6. Database Configuration" />
  </head>
  <body>
    <div xmlns="" class="navheader">
      <div class="libver">
        <p>Library Version 12.1.6.1</p>
      </div>
      <table width="100%" summary="Navigation header">
        <tr>
          <th colspan="3" align="center">Secondary Database Example</th>
        </tr>
        <tr>
          <td width="20%" align="left"><a accesskey="p" href="joins.html">Prev</a> </td>
          <th width="60%" align="center">Chapter 5. Secondary Databases</th>
          <td width="20%" align="right"> <a accesskey="n" href="dbconfig.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="coreindexusage"></a>Secondary Database Example</h2>
          </div>
        </div>
      </div>
      <div class="toc">
        <dl>
          <dt>
            <span class="sect2">
              <a href="coreindexusage.html#edlWIndexes">Secondary Databases with example_database_load</a>
            </span>
          </dt>
          <dt>
            <span class="sect2">
              <a href="coreindexusage.html#edrWIndexes">Secondary Databases with example_database_read</a>
            </span>
          </dt>
        </dl>
      </div>
      <p>
        In previous chapters in this book, we built applications that load
        and display several DB databases. In this example, we will extend those
        examples to use secondary databases. Specifically:
    </p>
      <div class="itemizedlist">
        <ul type="disc">
          <li>
            <p>
            In 
                 
                <a class="xref" href="DbCXXUsage.html" title="Database Usage Example">Database Usage Example</a> 
            we built an application that can open and load data into several databases.
            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
            that application to also open a secondary database for the purpose
            of indexing inventory item names.
        </p>
          </li>
          <li>
            <p>
            In <a class="xref" href="CoreCursorUsage.html" title="Cursor Example">Cursor Example</a> we
            built an application to display our inventory database (and related
            vendor information). In 
            <a class="xref" href="coreindexusage.html#edrWIndexes" title="Secondary Databases with example_database_read">Secondary Databases with example_database_read</a>
            we will extend that application to
            show inventory records based on the index we cause to be loaded using
            <code class="function">example_database_load</code>.
        </p>
          </li>
        </ul>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="edlWIndexes"></a>Secondary Databases with example_database_load</h3>
            </div>
          </div>
        </div>
        <p>
            In order to update <code class="function">example_database_load</code> 
            to maintain an index of inventory item names, all we really need 
            to do is:
        </p>
        <div class="orderedlist">
          <ol type="1">
            <li>
              <p>
                    Create a new database to be used as a secondary database.
                </p>
            </li>
            <li>
              <p>
                    Associate our new database to the inventory primary
                    database.
                </p>
            </li>
          </ol>
        </div>
        <p>
            We also need a function that can create our secondary keys for us.
        </p>
        <p>
            Because DB maintains secondary databases for us; once this work
            is done we need not make any other changes to <code class="function">example_database_load</code>. 

            
        </p>
        <p>
            Remember that you can find the complete implementation of these functions
            in:
        </p>
        <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples/cxx/getting_started</pre>
        <p>
            where <code class="literal"><span class="emphasis"><em>DB_INSTALL</em></span></code> is the location where you
            placed your DB distribution.
        </p>
        <p>
            To begin, we go to <code class="filename">gettingStartedCommon.hpp</code> and
            we write our secondary key extractor function. This is a fairly
            trivial function to write because we have already done most of the
            work when we wrote the <code class="classname">InventoryData</code> class.
            Recall that when we wrote that class, we provided a constructor that
            accepts a pointer to a buffer and unpacks the contents of the buffer
            for us (see <a class="xref" href="DbCXXUsage.html#InventoryData" title="Example 3.2 InventoryData Class">InventoryData Class</a>
            for the implementation). We now make use of that constructor.
        </p>
        <a id="cxx_index10"></a>
        <pre class="programlisting">// File: gettingStartedCommon.hpp
// Forward declarations
class Db;
class Dbt;

// Used to extract an inventory item's name from an
// inventory database record. This function is used to create
// keys for secondary database records.
int
get_item_name(Db *dbp, const Dbt *pkey, const Dbt *pdata, Dbt *skey)
{
    // Obtain the buffer location where the we placed the item's name. In
    // this example, the item's name is located in the primary data. It is
    // the first string in the buffer after the price (a double) and
    // the quantity (a long).
    size_t offset = sizeof(double) + sizeof(long);
    char * itemname = (char *)pdata-&gt;get_data() + offset;

    // unused
    (void)pkey;

    // If the offset is beyond the end of the data, then there is a
    // problem with the buffer contained in pdata, or there's a
    // programming error in how the buffer is marshalled/unmarshalled.
    // This should never happen!
    if (offset &gt; pdata-&gt;get_size()) {
        dbp-&gt;errx("get_item_name: buffer sizes do not match!");
        // When we return non-zero, the index record is not
        // added/updated.
        return (-1);
    }
    // Now set the secondary key's data to be the item name 

    skey-&gt;set_data(itemname);
    skey-&gt;set_size((u_int32_t)strlen(itemname) + 1);

    return (0);
}; </pre>
        <p>
        Having written our key extractor callback, we now need to make
        a trivial update to our <code class="classname">MyDb</code> implementation.
        Because an item name is used by multiple inventory records, we need our
        secondary database to support sorted duplicates. We therefore must
        update <code class="classname">MyDb</code> to handle this detail.
    </p>
        <p>
        The <code class="classname">MyDb</code> class definition changes to add a
        boolean to the constructor (remember that new code is in
        <strong class="userinput"><code>bold</code></strong>):
    </p>
        <a id="cxx_index11"></a>
        <pre class="programlisting">// File: MyDb.hpp
#include &lt;db_cxx.h&gt;

class MyDb
{
public:
    // Constructor requires a path to the database,
    // and a database name.
    MyDb(std::string &amp;path, std::string &amp;dbName,
         <strong class="userinput"><code>bool isSecondary = false</code></strong>);

    // Our destructor just calls our private close method.
    ~MyDb() { close(); }

    inline Db &amp;getDb() {return db_;}

private:
    Db db_;
    std::string dbFileName_;
    u_int32_t cFlags_;

    // Make sure the default constructor is private
    // We don't want it used.
    MyDb() : db_(0, 0) {}

    // We put our database close activity here.
    // This is called from our destructor. In
    // a more complicated example, we might want
    // to make this method public, but a private
    // method is more appropriate for this example.
    void close();
}; </pre>
        <p>
        And the implementation changes slightly to take advantage of the new
        boolean. Note that to save space, we just show the constructor where the
        code actually changes:
    </p>
        <a id="cxx_index12"></a>
        <pre class="programlisting">// File: MyDb.cpp
#include "MyDb.hpp"

// Class constructor. Requires a path to the location
// where the database is located, and a database name
MyDb::MyDb(std::string &amp;path, std::string &amp;dbName,
           <strong class="userinput"><code>bool isSecondary</code></strong>)
    : db_(NULL, 0),               // Instantiate Db object
      dbFileName_(path + dbName), // Database file name
      cFlags_(DB_CREATE)          // If the database doesn't yet exist,
                                  // allow it to be created.
{
    try
    {
        // Redirect debugging information to std::cerr
        db_.set_error_stream(&amp;std::cerr);

        <strong class="userinput"><code>// If this is a secondary database, support
        // sorted duplicates
        if (isSecondary)
            db_.set_flags(DB_DUPSORT);</code></strong>

        // Open the database
        db_.open(NULL, dbFileName_.c_str(), NULL, DB_BTREE, cFlags_, 0);
    }
    // DbException is not a subclass of std::exception, so we
    // need to catch them both.
    catch(DbException &amp;e)
    {
        std::cerr &lt;&lt; "Error opening database: " &lt;&lt; dbFileName_ &lt;&lt; "\n";
        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
    }
    catch(std::exception &amp;e)
    {
        std::cerr &lt;&lt; "Error opening database: " &lt;&lt; dbFileName_ &lt;&lt; "\n";
        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
    }
} </pre>
        <p>
        That done, we can now update
        <code class="function">example_database_load</code> to open our new secondary
        database and associate it to the inventory database.
    </p>
        <p>
        To save space, we do not show the entire implementation for this program
        here. Instead, we show just the <code class="function">main()</code> function,
        which is where all our modifications occur. To
        see the rest of the implementation for this command, see 
          <a class="xref" href="DbCXXUsage.html#exampledbload-cxx" title="Example 3.3 example_database_load">example_database_load</a>.
    </p>
        <a id="cxx_index13"></a>
        <pre class="programlisting">// Loads the contents of vendors.txt and inventory.txt into
// Berkeley DB databases.
int
main(int argc, char *argv[])
{
    // Initialize the path to the database files
    std::string basename("./");
    std::string databaseHome("./");

    // Database names
    std::string vDbName("vendordb.db");
    std::string iDbName("inventorydb.db");
    <strong class="userinput"><code>std::string itemSDbName("itemname.sdb");</code></strong>

    // Parse the command line arguments here and determine
    // the location of the flat text files containing the
    // inventory data here. This step is omitted for clarity.

    //  Identify the full name for our input files, which should
    //  also include some path information.
    std::string inventoryFile = basename + "inventory.txt";
    std::string vendorFile = basename + "vendors.txt";

    try
    {
        // Open all databases.
        MyDb inventoryDB(databaseHome, iDbName);
        MyDb vendorDB(databaseHome, vDbName);
        <strong class="userinput"><code>MyDb itemnameSDB(databaseHome, itemSDbName, true);

        // Associate the primary and the secondary
        inventoryDB.getDb().associate(NULL,
                                      &amp;(itemnameSDB.getDb()),
                                      get_item_name,
                                      0);</code></strong>

        // Load the inventory database
        loadInventoryDB(inventoryDB, inventoryFile);

        // Load the vendor database
        loadVendorDB(vendorDB, vendorFile);

    } catch(DbException &amp;e) {
        std::cerr &lt;&lt; "Error loading databases. " &lt;&lt; std::endl;
        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
        return(e.get_errno());
    } catch(std::exception &amp;e) {
        std::cerr &lt;&lt; "Error loading databases. " &lt;&lt; std::endl;
        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
        return(-1);
    }

    return(0);
} // End main </pre>
        <p>
        Note that the order in which we instantiate our
        <code class="classname">MyDb</code> class instances is important. In general you
        want to close a secondary database before closing the primary with which
        it is associated. This is particularly true for multi-threaded or
        multi-processed applications where the database closes are not single
        threaded. Even so, it is a good habit to adopt, even for simple
        applications such as this one. Here, we ensure that the databases are
        closed in the desired order by opening the secondary database last.
        This works because our <code class="classname">MyDb</code> objects are on
        the stack, and therefore the last one opened is the first one closed.
    </p>
        <p>
        That completes our update to <code class="function">example_database_load</code>.
        Now when this program is called, it will automatically index inventory
        items based on their names. We can then query for those items using the
        new index. We show how to do that in the next section.
    </p>
      </div>
      <div class="sect2" lang="en" xml:lang="en">
        <div class="titlepage">
          <div>
            <div>
              <h3 class="title"><a id="edrWIndexes"></a>Secondary Databases with example_database_read</h3>
            </div>
          </div>
        </div>
        <p>
            In <a class="xref" href="CoreCursorUsage.html" title="Cursor Example">Cursor Example</a> we
            wrote an application that displays every inventory item in the
            Inventory database. In this section, we will update that example to
            allow us to search for and display an inventory item given a
            specific name. To do this, we will make use of the secondary
            database that <code class="function">example_database_load</code> now
            creates.
        </p>
        <p>
            The update to <code class="function">example_database_read</code> is
            relatively modest. We need to open the new secondary database
            in exactly the same way was we do for
            <code class="function">example_database_load</code>. 
            We also need to add a command line parameter on
            which we can specify the item name, and we will need a new function
            in which we will perform the query and display the results.
        </p>
        <p>
            To begin, we add a single forward declaration to the application,
            and update our usage function slightly:
        </p>
        <a id="cxx_index14"></a>
        <pre class="programlisting">// File: excxx_example_database_read.cpp
#include &lt;iostream&gt;
#include &lt;fstream&gt;
#include &lt;cstdlib&gt;

#include "MyDb.hpp"
#include "gettingStartedCommon.hpp"

// Forward declarations
int show_all_records(MyDb &amp;inventoryDB, MyDb &amp;vendorDB);
<strong class="userinput"><code>int show_item(MyDb &amp;itemnameSDB, MyDb &amp;vendorDB, std::string &amp;itemName);</code></strong>
int show_vendor(MyDb &amp;vendorDB, const char *vendor); </pre>
        <p>
        Next, we update <code class="function">main()</code> to 
        <span>open the new secondary database and</span>
        accept the new command line switch.
        We also need a new variable to contain the item's name.        
    </p>
        <p>
        The final update to the <code class="function">main()</code> entails a little bit
        of logic to determine whether we want to display all available inventory
        items, or just the ones that match a name provided on the
        <code class="literal">-i</code> command line parameter.
    </p>
        <a id="cxx_index15"></a>
        <pre class="programlisting">// Displays all inventory items and the associated vendor record.
int
main (int argc, char *argv[])
{
    // Initialize the path to the database files
    std::string databaseHome("./");
    <strong class="userinput"><code>std::string itemName;</code></strong>

    // Database names
    std::string vDbName("vendordb.db");
    std::string iDbName("inventorydb.db");
    <strong class="userinput"><code>std::string itemSDbName("itemname.sdb");</code></strong>

    // Parse the command line arguments
    // Omitted for brevity

    try
    {
        // Open all databases.
        MyDb inventoryDB(databaseHome, iDbName);
        MyDb vendorDB(databaseHome, vDbName);
        <strong class="userinput"><code>MyDb itemnameSDB(databaseHome, itemSDbName, true);

        // Associate the secondary to the primary
        inventoryDB.getDb().associate(NULL,
                                      &amp;(itemnameSDB.getDb()),
                                      get_item_name,
                                      0);

        if (itemName.empty())
        {</code></strong>
            show_all_records(inventoryDB, vendorDB);
        <strong class="userinput"><code>} else {
            show_item(itemnameSDB, vendorDB, itemName);
        }</code></strong>
    } catch(DbException &amp;e) {
        std::cerr &lt;&lt; "Error reading databases. " &lt;&lt; std::endl;
        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
        return(e.get_errno());
    } catch(std::exception &amp;e) {
        std::cerr &lt;&lt; "Error reading databases. " &lt;&lt; std::endl;
        std::cerr &lt;&lt; e.what() &lt;&lt; std::endl;
        return(-1);
    }

    return(0);
} // End main </pre>
        <p>
        The only other thing that we need to add to the application is the
        implementation of the 
             
            <code class="function">show_item()</code> 
        function.
    </p>
        <div class="note" style="margin-left: 0.5in; margin-right: 0.5in;">
          <h3 class="title">Note</h3>
          <p>
            In the interest of space, we refrain from showing the other
            functions used by this application. For their implementation, please
            see <a class="xref" href="CoreCursorUsage.html" title="Cursor Example">Cursor Example</a>.
            Alternatively, you can see the entire implementation of this
            application
            in:
        </p>
          <pre class="programlisting"><span class="emphasis"><em>DB_INSTALL</em></span>/examples/cxx/getting_started</pre>
          <p>
            where <code class="literal"><span class="emphasis"><em>DB_INSTALL</em></span></code> is the location where you
            placed your DB distribution.
        </p>
        </div>
        <a id="cxx_index16"></a>
        <pre class="programlisting">// Shows the records in the inventory database that
// have a specific item name. For each inventory record
// shown, the appropriate vendor record is also displayed.
int
show_item(MyDb &amp;itemnameSDB, MyDb &amp;vendorDB, std::string &amp;itemName)
{
    // Get a cursor to the itemname secondary db
    Dbc *cursorp;

    try {
        itemnameSDB.getDb().cursor(NULL, &amp;cursorp, 0);

        // Get the search key. This is the name on the inventory
        // record that we want to examine.
        std::cout &lt;&lt; "Looking for " &lt;&lt; itemName &lt;&lt; std::endl;
        Dbt key((void *)itemName.c_str(),
            (u_int32_t)itemName.length() + 1);
        Dbt data;

        // Position the cursor to the first record in the secondary
        // database that has the appropriate key.
        int ret = cursorp-&gt;get(&amp;key, &amp;data, DB_SET);
        if (!ret) {
            do {
                InventoryData inventoryItem(data.get_data());
                inventoryItem.show();

                show_vendor(vendorDB, inventoryItem.getVendor().c_str());

            } while(cursorp-&gt;get(&amp;key, &amp;data, DB_NEXT_DUP) == 0);
        } else {
            std::cerr &lt;&lt; "No records found for '" &lt;&lt; itemName
                      &lt;&lt; "'" &lt;&lt; std::endl;
        }
    } catch(DbException &amp;e) {
        itemnameSDB.getDb().err(e.get_errno(), "Error in show_item");
        cursorp-&gt;close();
        throw e;
    } catch(std::exception &amp;e) {
        itemnameSDB.getDb().errx("Error in show_item: %s", e.what());
        cursorp-&gt;close();
        throw e;
    }

    cursorp-&gt;close();
    return (0);
}

</pre>
        <p>
        This completes our update to
        <code class="classname">example_inventory_read</code>. Using this update, you
        can now search for and show all inventory items that match a particular
        name. For example:
    </p>
        <pre class="programlisting">    example_inventory_read -i "Zulu Nut"</pre>
      </div>
    </div>
    <div class="navfooter">
      <hr />
      <table width="100%" summary="Navigation footer">
        <tr>
          <td width="40%" align="left"><a accesskey="p" href="joins.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="dbconfig.html">Next</a></td>
        </tr>
        <tr>
          <td width="40%" align="left" valign="top">Database Joins </td>
          <td width="20%" align="center">
            <a accesskey="h" href="index.html">Home</a>
          </td>
          <td width="40%" align="right" valign="top"> Chapter 6. Database Configuration</td>
        </tr>
      </table>
    </div>
  </body>
</html>