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>Program Listing</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 Replicated Berkeley DB Applications" /> 10 <link rel="up" href="txnapp.html" title="Chapter 2. Transactional Application" /> 11 <link rel="previous" href="txnapp.html" title="Chapter 2. Transactional Application" /> 12 <link rel="next" href="repapp.html" title="Chapter 3. The DB Replication Framework" /> 13 </head> 14 <body> 15 <div class="navheader"> 16 <table width="100%" summary="Navigation header"> 17 <tr> 18 <th colspan="3" align="center">Program Listing</th> 19 </tr> 20 <tr> 21 <td width="20%" align="left"><a accesskey="p" href="txnapp.html">Prev</a> </td> 22 <th width="60%" align="center">Chapter 2. Transactional Application</th> 23 <td width="20%" align="right"> <a accesskey="n" href="repapp.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="simpleprogramlisting"></a>Program Listing</h2> 33 </div> 34 </div> 35 <div></div> 36 </div> 37 <p> 38 Our example program is a fairly simple transactional 39 application. At this early stage of its development, the 40 application contains no hint that it must be network-aware 41 so the only command line argument that it takes is one that 42 allows us to specify the environment home directory. 43 (Eventually, we will specify things like host names and 44 ports from the command line). 45 </p> 46 <p> 47 Note that the application performs all writes under the 48 protection of a transaction; however, multiple database 49 operations are not performed per transaction. Consequently, 50 we simplify things a bit by using autocommit for our 51 database writes. 52 </p> 53 <p> 54 Also, this application is single-threaded. It is possible 55 to write a multi-threaded or multi-process application that 56 performs replication. That said, the concepts described in 57 this book are applicable to both single threaded and 58 multi-threaded applications so nothing 59 is gained by multi-threading this application other than 60 distracting complexity. This manual 61 does, however, identify where care must be taken when 62 performing replication with a non-single threaded 63 application. 64 </p> 65 <p> 66 Finally, remember that transaction processing is not described in 67 this manual. Rather, see the 68 <i class="citetitle">Berkeley DB Getting Started with Transaction Processing</i> guide for details on 69 that topic. 70 </p> 71 <div class="sect2" lang="en" xml:lang="en"> 72 <div class="titlepage"> 73 <div> 74 <div> 75 <h3 class="title"><a id="main_c"></a>Function: main()</h3> 76 </div> 77 </div> 78 <div></div> 79 </div> 80 <p> 81 Our program begins with the usual assortment of 82 include statements. 83 </p> 84 <pre class="programlisting">/* 85 * File: simple_txn.c 86 */ 87 88#include <stdlib.h> 89#include <string.h> 90#ifndef _WIN32 91#include <unistd.h> 92#endif 93 94#include <db.h> 95 96#ifdef _WIN32 97extern int getopt(int, char * const *, const char *); 98#endif </pre> 99 <p> 100 We then define a few values. One is the size of our cache, 101 which we keep deliberately small for this example, and the 102 other is the name of our database. We also provide a global 103 variable that is the name of our program; this is used for 104 error reporting later on. 105 </p> 106 <pre class="programlisting">#define CACHESIZE (10 * 1024 * 1024) 107#define DATABASE "quote.db" 108 109const char *progname = "simple_txn"; </pre> 110 <p> 111 Then we perform a couple of forward declarations. The first 112 of these, <tt class="function">create_env()</tt> and 113 <tt class="function">env_init()</tt> are used to open 114 and initialize our environment. 115 </p> 116 <p> 117 Next we declare 118 <tt class="function">doloop()</tt>, which is the function that we use to 119 add data to the database and then display its contents. This is 120 essentially a big <tt class="literal">do</tt> loop, hence the 121 function's name. 122 </p> 123 <p> 124 Finally, we have <tt class="function">print_stocks</tt>, which is 125 used to display a database record once it has been retrieved from the 126 database. 127 </p> 128 <pre class="programlisting"> 129int create_env(const char *, DB_ENV **); 130int env_init(DB_ENV *, const char *); 131int doloop (DB_ENV *); 132int print_stocks(DBC *); </pre> 133 <p> 134 Next we need our <tt class="function">usage()</tt> function, 135 which is fairly trivial at this point: 136 </p> 137 <pre class="programlisting">/* Usage function */ 138static void 139usage() 140{ 141 fprintf(stderr, "usage: %s ", progname); 142 fprintf(stderr, "[-h home]\n"); 143 exit(EXIT_FAILURE); 144} </pre> 145 <p> 146 That completed, we can jump into our application's 147 <tt class="function">main()</tt> function. If you are familiar with 148 DB transactional applications, you will not find any 149 surprises here. We begin by declaring and initializing the 150 usual set of variables: 151 </p> 152 <pre class="programlisting">int 153main(int argc, char *argv[]) 154{ 155 extern char *optarg; 156 DB_ENV *dbenv; 157 const char *home; 158 char ch; 159 int ret; 160 161 dbenv = NULL; 162 163 ret = 0; 164 home = NULL; </pre> 165 <p> 166 Now we create and configure our environment handle. 167 We do this with our <tt class="function">create_env()</tt> function, which we will 168 show a little later in this example. 169 170 </p> 171 <pre class="programlisting"> if ((ret = create_env(progname, &dbenv)) != 0) 172 goto err; </pre> 173 <p> 174 Then we parse the command line arguments: 175 </p> 176 <pre class="programlisting"> while ((ch = getopt(argc, argv, "h:")) != EOF) 177 switch (ch) { 178 case 'h': 179 home = optarg; 180 break; 181 case '?': 182 default: 183 usage(); 184 } 185 186 /* Error check command line. */ 187 if (home == NULL) 188 usage(); </pre> 189 <p> 190 Now we can open our environment. We do this with our 191 <tt class="function">env_init()</tt> function which we will describe 192 a little later in this chapter. 193 </p> 194 <pre class="programlisting"> if ((ret = env_init(dbenv, home)) != 0) 195 goto err; </pre> 196 <p> 197 Now that we have opened the environment, we can call our 198 <tt class="function">doloop()</tt> function. This function performs the basic 199 database interaction. Notice that we have not yet opened any databases. In 200 a traditional transactional application we would probably open the 201 databases before calling our our main data processing function. 202 However, the eventual replicated application will want to handle 203 database open and close in the main processing loop, so in a nod to what this 204 application will eventually become we do a slightly unusual thing 205 here. 206 </p> 207 <pre class="programlisting"> if ((ret = doloop(dbenv)) != 0) { 208 dbenv->err(dbenv, ret, "Application failed"); 209 goto err; 210 } </pre> 211 <p> 212 Finally, we provide our application shutdown code. Note, again, 213 that in a traditional transactional application all databases would 214 also be closed here. But, again, due to the way this application 215 will eventually behave, we cause the database close to occur in the 216 <tt class="function">doloop()</tt> function. 217 </p> 218 <pre class="programlisting">err: if (dbenv != NULL) 219 (void)dbenv->close(dbenv, 0); 220 221 return (ret); 222} </pre> 223 </div> 224 <div class="sect2" lang="en" xml:lang="en"> 225 <div class="titlepage"> 226 <div> 227 <div> 228 <h3 class="title"><a id="create_env_c"></a>Function: create_env()</h3> 229 </div> 230 </div> 231 <div></div> 232 </div> 233 <p> 234 Having written our <tt class="function">main()</tt> 235 function, we now implement the first of our utility 236 functions that we use to manage our environments. 237 This function exists only to make our code easier 238 to manage, and all it does is create an environment 239 handle for us. 240 </p> 241 <pre class="programlisting">int 242create_env(char *progname, DB_ENV **dbenvp) 243{ 244 DB_ENV *dbenv; 245 int ret; 246 247 if ((ret = db_env_create(&dbenv, 0)) != 0) { 248 fprintf(stderr, "can't create env handle: %s\n", 249 db_strerror(ret)); 250 return (ret); 251 } 252 253 dbenv->set_errfile(dbenv, stderr); 254 dbenv->set_errpfx(dbenv, progname); 255 256 *dbenvp = dbenv; 257 return (0); 258} </pre> 259 </div> 260 <div class="sect2" lang="en" xml:lang="en"> 261 <div class="titlepage"> 262 <div> 263 <div> 264 <h3 class="title"><a id="env_init_c"></a>Function: env_init()</h3> 265 </div> 266 </div> 267 <div></div> 268 </div> 269 <p> 270 Having written the function that initializes an 271 environment handle, we now implement the function 272 that opens the handle. Again, there should be no 273 surprises here for anyone familiar with DB 274 applications. The open flags that we use are those 275 normally used for a transactional application. 276 </p> 277 <pre class="programlisting"> int 278env_init(DB_ENV *dbenv, const char *home) 279{ 280 u_int32_t flags; 281 int ret; 282 283 (void)dbenv->set_cachesize(dbenv, 0, CACHESIZE, 0); 284 (void)dbenv->set_flags(dbenv, DB_TXN_NOSYNC, 1); 285 286 flags = DB_CREATE | 287 DB_INIT_LOCK | 288 DB_INIT_LOG | 289 DB_INIT_MPOOL | 290 DB_INIT_TXN | 291 DB_RECOVER; 292 if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0) 293 dbenv->err(dbenv, ret, "can't open environment"); 294 return (ret); 295} </pre> 296 </div> 297 <div class="sect2" lang="en" xml:lang="en"> 298 <div class="titlepage"> 299 <div> 300 <div> 301 <h3 class="title"><a id="doloop_c"></a>Function: doloop()</h3> 302 </div> 303 </div> 304 <div></div> 305 </div> 306 <p> 307 Having written our <tt class="function">main()</tt> 308 function and utility functions, we now implement 309 our application's 310 primary data processing function. This 311 function provides a command prompt at which the 312 user can enter a stock ticker value and a price for 313 that value. This information is then entered to the 314 database. 315 </p> 316 <p> 317 To display the database, simply enter 318 <tt class="literal">return</tt> at the prompt. 319 </p> 320 <p> 321 To begin, we declare a database pointer, 322 several <tt class="classname">DBT</tt> variables, and 323 the usual assortment of variables used for buffers 324 and return codes. We also initialize all of this. 325 </p> 326 <pre class="programlisting">#define BUFSIZE 1024 327int 328doloop(DB_ENV *dbenv) 329{ 330 DB *dbp; 331 DBT key, data; 332 char buf[BUFSIZE], *rbuf; 333 int ret; 334 u_int32_t db_flags; 335 336 dbp = NULL; 337 memset(&key, 0, sizeof(key)); 338 memset(&data, 0, sizeof(data)); 339 ret = 0; </pre> 340 <p> 341 Next, we begin the loop and we immediately open our 342 database if it has not already been opened. Notice that 343 we specify autocommit when we open the database. In 344 this case, autocommit is important because we will only 345 ever write to our database using it. There is no need 346 for explicit transaction handles and commit/abort code 347 in this application, because we are not combining 348 multiple database operations together under a single 349 transaction. 350 </p> 351 <p> 352 Autocommit is described in greater detail in the 353 <i class="citetitle">Berkeley DB Getting Started with Transaction Processing</i> guide. 354 </p> 355 <pre class="programlisting"> for (;;) { 356 357 if (dbp == NULL) { 358 if ((ret = db_create(&dbp, dbenv, 0)) != 0) 359 return (ret); 360 361 /* Set page size small so page allocation is cheap. */ 362 if ((ret = dbp->set_pagesize(dbp, 512)) != 0) 363 goto err; 364 365 db_flags = DB_AUTO_COMMIT | DB_CREATE; 366 367 if ((ret = dbp->open(dbp, NULL, DATABASE, 368 NULL, DB_BTREE, db_flags, 0)) != 0) { 369 dbenv->err(dbenv, ret, "DB->open"); 370 goto err; 371 } 372 } </pre> 373 <p> 374 Now we implement our command prompt. This is a simple and not 375 very robust implementation of a command prompt. 376 If the user enters the keywords <tt class="literal">exit</tt> 377 or <tt class="literal">quit</tt>, the loop is exited and the 378 application ends. If the user enters nothing and instead simply 379 presses <tt class="literal">return</tt>, the entire contents of the 380 database is displayed. We use our 381 <tt class="function">print_stocks()</tt> function to display the 382 database. (That implementation is shown next in this chapter.) 383 </p> 384 <p> 385 Notice that very little error checking is performed on the data 386 entered at this prompt. If the user fails to enter at least one 387 space in the value string, a simple help message is printed and 388 the prompt is returned to the user. That is the only error 389 checking performed here. In a real-world application, 390 at a minimum the application would probably check to ensure 391 that the price was in fact an integer or float value. 392 However, in order to keep this example code as simple as 393 possible, we refrain from implementing a thorough user interface. 394 </p> 395 <pre class="programlisting"> printf("QUOTESERVER > "); 396 fflush(stdout); 397 398 if (fgets(buf, sizeof(buf), stdin) == NULL) 399 break; 400 if (strtok(&buf[0], " \t\n") == NULL) { 401 switch ((ret = print_stocks(dbp))) { 402 case 0: 403 continue; 404 default: 405 dbp->err(dbp, ret, "Error traversing data"); 406 goto err; 407 } 408 } 409 rbuf = strtok(NULL, " \t\n"); 410 if (rbuf == NULL || rbuf[0] == '\0') { 411 if (strncmp(buf, "exit", 4) == 0 || 412 strncmp(buf, "quit", 4) == 0) 413 break; 414 dbenv->errx(dbenv, "Format: TICKER VALUE"); 415 continue; 416 } </pre> 417 <p> 418 Now we assign data to the <tt class="classname">DBT</tt>s that 419 we will use to write the new information to the database. 420 </p> 421 <pre class="programlisting"> key.data = buf; 422 key.size = (u_int32_t)strlen(buf); 423 424 data.data = rbuf; 425 data.size = (u_int32_t)strlen(rbuf); </pre> 426 <p> 427 Having done that, we can write the new information to the 428 database. Remember that this application uses autocommit, 429 so no explicit transaction management is required. Also, 430 the database is not configured for duplicate records, so 431 the data portion of a record is overwritten if the provided 432 key already exists in the database. However, in this case 433 DB returns <tt class="literal">DB_KEYEXIST</tt> — which 434 we ignore. 435 </p> 436 <pre class="programlisting"> if ((ret = dbp->put(dbp, NULL, &key, &data, 0)) != 0) 437 { 438 dbp->err(dbp, ret, "DB->put"); 439 if (ret != DB_KEYEXIST) 440 goto err; 441 } 442 } </pre> 443 <p> 444 Finally, we close our database before returning from the 445 function. 446 </p> 447 <pre class="programlisting">err: if (dbp != NULL) 448 (void)dbp->close(dbp, DB_NOSYNC); 449 450 return (ret); 451} </pre> 452 </div> 453 <div class="sect2" lang="en" xml:lang="en"> 454 <div class="titlepage"> 455 <div> 456 <div> 457 <h3 class="title"><a id="printstocks_c"></a> 458 <span>Function: print_stocks()</span> 459 460 461 </h3> 462 </div> 463 </div> 464 <div></div> 465 </div> 466 <p> 467 The <tt class="function">print_stocks()</tt> 468 469 <span>function</span> 470 471 simply takes a database handle, opens a cursor, and uses 472 it to display all the information it finds in a database. 473 This is trivial cursor operation that should hold 474 no surprises for you. We simply provide it here for 475 the sake of completeness. 476 </p> 477 <p> 478 If you are unfamiliar with basic cursor operations, 479 please see the <i class="citetitle">Getting Started with Berkeley DB</i> 480 guide. 481 </p> 482 <pre class="programlisting">/* 483 * A function that takes a cursor and displays the entire 484 * contents of the database to which the cursor belongs. 485 */ 486int 487print_stocks(DB *dbp) 488{ 489 DBC *dbc; 490 DBT key, data; 491#define MAXKEYSIZE 10 492#define MAXDATASIZE 20 493 char keybuf[MAXKEYSIZE + 1], databuf[MAXDATASIZE + 1]; 494 int ret, t_ret; 495 u_int32_t keysize, datasize; 496 497 if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) { 498 dbp->err(dbp, ret, "can't open cursor"); 499 return (ret); 500 } 501 502 memset(&key, 0, sizeof(key)); 503 memset(&data, 0, sizeof(data)); 504 505 printf("\tSymbol\tPrice\n"); 506 printf("\t======\t=====\n"); 507 508 for (ret = dbc->get(dbc, &key, &data, DB_FIRST); 509 ret == 0; 510 ret = dbc->get(dbc, &key, &data, DB_NEXT)) { 511 keysize = key.size > MAXKEYSIZE ? MAXKEYSIZE : key.size; 512 memcpy(keybuf, key.data, keysize); 513 keybuf[keysize] = '\0'; 514 515 datasize = data.size >= MAXDATASIZE ? MAXDATASIZE : data.size; 516 memcpy(databuf, data.data, datasize); 517 databuf[datasize] = '\0'; 518 519 printf("\t%s\t%s\n", keybuf, databuf); 520 } 521 printf("\n"); 522 fflush(stdout); 523 524 if ((t_ret = dbc->close(dbc)) != 0 && ret == 0) 525 ret = t_ret; 526 527 switch (ret) { 528 case 0: 529 case DB_NOTFOUND: 530 return (0); 531 default: 532 return (ret); 533 } 534} </pre> 535 </div> 536 </div> 537 <div class="navfooter"> 538 <hr /> 539 <table width="100%" summary="Navigation footer"> 540 <tr> 541 <td width="40%" align="left"><a accesskey="p" href="txnapp.html">Prev</a> </td> 542 <td width="20%" align="center"> 543 <a accesskey="u" href="txnapp.html">Up</a> 544 </td> 545 <td width="40%" align="right"> <a accesskey="n" href="repapp.html">Next</a></td> 546 </tr> 547 <tr> 548 <td width="40%" align="left" valign="top">Chapter 2. Transactional Application </td> 549 <td width="20%" align="center"> 550 <a accesskey="h" href="index.html">Home</a> 551 </td> 552 <td width="40%" align="right" valign="top"> Chapter 3. The DB Replication Framework</td> 553 </tr> 554 </table> 555 </div> 556 </body> 557</html> 558