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 <db.h> 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->size < 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->data = pdata->data + offset; 211 skey->size = strlen(skey->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->db_home_dir = DEFAULT_HOMEDIR; 230 my_stock->inventory_dbp = NULL; 231 my_stock->vendor_dbp = NULL; 232 <b class="userinput"><tt>my_stock->itemname_sdbp = NULL;</tt></b> 233 234 my_stock->inventory_db_name = NULL; 235 my_stock->vendor_db_name = NULL; 236 <b class="userinput"><tt>my_stock->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->db_home_dir) + strlen(INVENTORYDB) + 1; 247 my_stock->inventory_db_name = malloc(size); 248 snprintf(my_stock->inventory_db_name, size, "%s%s", 249 my_stock->db_home_dir, INVENTORYDB); 250 251 /* Create the Vendor DB file name */ 252 size = strlen(my_stock->db_home_dir) + strlen(VENDORDB) + 1; 253 my_stock->vendor_db_name = malloc(size); 254 snprintf(my_stock->vendor_db_name, size, "%s%s", 255 my_stock->db_home_dir, VENDORDB); 256 257 <b class="userinput"><tt>/* Create the itemname DB file name */ 258 size = strlen(my_stock->db_home_dir) + strlen(ITEMNAMEDB) + 1; 259 my_stock->itemname_db_name = malloc(size); 260 snprintf(my_stock->itemname_db_name, size, "%s%s", 261 my_stock->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(&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->set_errfile(dbp, error_file_pointer); 310 dbp->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->set_flags(dbp, DB_DUPSORT); 318 if (ret != 0) { 319 dbp->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->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->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(&(my_stock->vendor_dbp), 368 my_stock->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(&(my_stock->inventory_dbp), 380 my_stock->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(&(my_stock->itemname_sdbp), 396 my_stock->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->inventory_dbp->associate( 411 my_stock->inventory_dbp, /* Primary db */ 412 NULL, /* txn id */ 413 my_stock->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->itemname_sdbp != NULL) { 441 ret = my_stock->itemname_sdbp->close(my_stock->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->inventory_dbp != NULL) { 448 ret = my_stock->inventory_dbp->close(my_stock->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->vendor_dbp != NULL) { 455 ret = my_stock->vendor_dbp->close(my_stock->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(&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(&my_stock); 557 558 /* Open all databases */ 559 ret = databases_setup(&my_stock, "example_database_read", stderr); 560 if (ret != 0) { 561 fprintf(stderr, "Error opening databases\n"); 562 databases_close(&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(&my_stock); 578 else 579 ret = show_records(&my_stock, itemname);</tt></b> 580 581 /* Close our databases */ 582 databases_close(&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(&key, 0, sizeof(DBT)); 624 memset(&data, 0, sizeof(DBT)); 625 626 /* Get a cursor to the itemname db */ 627 my_stock->itemname_sdbp->cursor(my_stock->itemname_sdbp, 0, 628 &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->get(itemname_cursorp, &key, &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->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->get(itemname_cursorp, &key, &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->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