mirror of
https://github.com/berkeleydb/libdb.git
synced 2024-11-17 09:36:24 +00:00
684 lines
25 KiB
HTML
684 lines
25 KiB
HTML
|
<?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>Program Listing</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 Replicated Berkeley DB Applications" />
|
|||
|
<link rel="up" href="txnapp.html" title="Chapter 2. Transactional Application" />
|
|||
|
<link rel="prev" href="txnapp.html" title="Chapter 2. Transactional Application" />
|
|||
|
<link rel="next" href="repapp.html" title="Chapter 3. The DB Replication Manager" />
|
|||
|
</head>
|
|||
|
<body>
|
|||
|
<div xmlns="" class="navheader">
|
|||
|
<div class="libver">
|
|||
|
<p>Library Version 11.2.5.2</p>
|
|||
|
</div>
|
|||
|
<table width="100%" summary="Navigation header">
|
|||
|
<tr>
|
|||
|
<th colspan="3" align="center">Program Listing</th>
|
|||
|
</tr>
|
|||
|
<tr>
|
|||
|
<td width="20%" align="left"><a accesskey="p" href="txnapp.html">Prev</a> </td>
|
|||
|
<th width="60%" align="center">Chapter 2. Transactional Application</th>
|
|||
|
<td width="20%" align="right"> <a accesskey="n" href="repapp.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="simpleprogramlisting"></a>Program Listing</h2>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="toc">
|
|||
|
<dl>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="simpleprogramlisting.html#repconfiginfo_cxx">
|
|||
|
<span>Class: RepConfigInfo</span>
|
|||
|
|
|||
|
</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="simpleprogramlisting.html#repmgr_cxx">Class: excxx_repquote_gsg_simple</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="simpleprogramlisting.html#usage_cxx">Function: usage()</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="simpleprogramlisting.html#main_cxx">Function: main()</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="simpleprogramlisting.html#repmgr_init_cxx">Method: SimpleTxn::init()</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="simpleprogramlisting.html#doloop_cxx">Method: SimpleTxn::doloop()</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
<dt>
|
|||
|
<span class="sect2">
|
|||
|
<a href="simpleprogramlisting.html#printstocks_c">
|
|||
|
|
|||
|
<span>Method: SimpleTxn::print_stocks()</span>
|
|||
|
|
|||
|
</a>
|
|||
|
</span>
|
|||
|
</dt>
|
|||
|
</dl>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
Our example program is a fairly simple transactional
|
|||
|
application. At this early stage of its development, the
|
|||
|
application contains no hint that it must be network-aware
|
|||
|
so the only command line argument that it takes is one that
|
|||
|
allows us to specify the environment home directory.
|
|||
|
(Eventually, we will specify things like host names and
|
|||
|
ports from the command line).
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Note that the application performs all writes under the
|
|||
|
protection of a transaction; however, multiple database
|
|||
|
operations are not performed per transaction. Consequently,
|
|||
|
we simplify things a bit by using autocommit for our
|
|||
|
database writes.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Also, this application is single-threaded. It is possible
|
|||
|
to write a multi-threaded or multi-process application that
|
|||
|
performs replication. That said, the concepts described in
|
|||
|
this book are applicable to both single threaded and
|
|||
|
multi-threaded applications so nothing
|
|||
|
is gained by multi-threading this application other than
|
|||
|
distracting complexity. This manual
|
|||
|
does, however, identify where care must be taken when
|
|||
|
performing replication with a non-single threaded
|
|||
|
application.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Finally, remember that transaction processing is not described in
|
|||
|
this manual. Rather, see the
|
|||
|
<em class="citetitle">Berkeley DB Getting Started with Transaction Processing</em> guide for details on
|
|||
|
that topic.
|
|||
|
</p>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="repconfiginfo_cxx"></a>
|
|||
|
<span>Class: RepConfigInfo</span>
|
|||
|
|
|||
|
</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
Before we begin, we present a
|
|||
|
class that we will use to maintain useful
|
|||
|
information for us. Under normal circumstances,
|
|||
|
this class would not be necessary for a simple
|
|||
|
transactional example such as this. However, this code will
|
|||
|
grow into a replicated example that needs to
|
|||
|
track a lot more information for the
|
|||
|
application, and so we lay the groundwork for
|
|||
|
it here.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
The class that we create is called
|
|||
|
<code class="classname">RepConfigInfo</code>
|
|||
|
|
|||
|
and its only purpose at this time is to track
|
|||
|
the location of our environment home directory.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">#include <db_cxx.h>
|
|||
|
|
|||
|
class RepConfigInfo {
|
|||
|
public:
|
|||
|
RepConfigInfo();
|
|||
|
virtual ~RepConfigInfo();
|
|||
|
|
|||
|
public:
|
|||
|
char* home;
|
|||
|
};
|
|||
|
|
|||
|
|
|||
|
RepConfigInfo::RepConfigInfo()
|
|||
|
{
|
|||
|
home = "TESTDIR";
|
|||
|
}
|
|||
|
|
|||
|
RepConfigInfo::~RepConfigInfo()
|
|||
|
{
|
|||
|
} </pre>
|
|||
|
</div>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="repmgr_cxx"></a>Class: excxx_repquote_gsg_simple</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
Our transactional example will
|
|||
|
instantiate a class,
|
|||
|
<code class="classname">SimpleTxn</code>, that performs
|
|||
|
all our work for us. Before we implement our
|
|||
|
<code class="function">main()</code> function, we show
|
|||
|
the <code class="classname">SimpleTxn</code> class
|
|||
|
declaration.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
First, we provide some declarations and
|
|||
|
definitions that are needed later in
|
|||
|
our example:
|
|||
|
</p>
|
|||
|
<pre class="programlisting">#include <iostream>
|
|||
|
#include <db_cxx.h>
|
|||
|
#include "RepConfig.h"
|
|||
|
|
|||
|
|
|||
|
using std::cout;
|
|||
|
using std::cin;
|
|||
|
using std::cerr;
|
|||
|
using std::endl;
|
|||
|
using std::flush;
|
|||
|
|
|||
|
#define CACHESIZE (10 * 1024 * 1024)
|
|||
|
#define DATABASE "quote.db"
|
|||
|
|
|||
|
const char *progname = "excxx_reqquote_gsg_simple";
|
|||
|
|
|||
|
#ifdef _WIN32
|
|||
|
#define WIN32_LEAN_AND_MEAN
|
|||
|
#include <windows.h>
|
|||
|
#include <direct.h>
|
|||
|
|
|||
|
extern "C" {
|
|||
|
extern int getopt(int, char * const *, const char *);
|
|||
|
extern char *optarg;
|
|||
|
}
|
|||
|
#else
|
|||
|
#include <errno.h>
|
|||
|
#endif </pre>
|
|||
|
<p>
|
|||
|
And then we define our <code class="classname">SimpleTxn</code> class:
|
|||
|
</p>
|
|||
|
<pre class="programlisting">class SimpleTxn
|
|||
|
{
|
|||
|
public:
|
|||
|
// Constructor.
|
|||
|
SimpleTxn();
|
|||
|
// Initialization method. Creates and opens our environment handle.
|
|||
|
int init(RepConfigInfo* config);
|
|||
|
// The doloop is where all the work is performed.
|
|||
|
int doloop();
|
|||
|
// terminate() provides our shutdown code.
|
|||
|
int terminate();
|
|||
|
|
|||
|
private:
|
|||
|
// disable copy constructor.
|
|||
|
SimpleTxn(const SimpleTxn &);
|
|||
|
void operator = (const SimpleTxn &);
|
|||
|
|
|||
|
// internal data members.
|
|||
|
RepConfigInfo *app_config;
|
|||
|
DbEnv dbenv;
|
|||
|
|
|||
|
// private methods.
|
|||
|
// print_stocks() is used to display the contents of our database.
|
|||
|
static int print_stocks(Db *dbp);
|
|||
|
}; </pre>
|
|||
|
<p>
|
|||
|
Note that we show the implementation of the various
|
|||
|
<code class="classname">SimpleTxn</code> methods later in this section.
|
|||
|
</p>
|
|||
|
</div>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="usage_cxx"></a>Function: usage()</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
Our <code class="function">usage()</code> is at this
|
|||
|
stage of development trivial because we only
|
|||
|
have one command line argument to manage.
|
|||
|
Still, we show it here for the sake of
|
|||
|
completeness.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">static void usage()
|
|||
|
{
|
|||
|
cerr << "usage: " << progname << endl
|
|||
|
<< "-h home" << endl;
|
|||
|
|
|||
|
exit(EXIT_FAILURE);
|
|||
|
} </pre>
|
|||
|
</div>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="main_cxx"></a>Function: main()</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
Now we provide our <code class="function">main()</code>
|
|||
|
function. This is a trivial function whose only
|
|||
|
job is to collect command line information,
|
|||
|
then instantiate a <code class="classname">SimpleTxn</code>
|
|||
|
object, run it, then terminate it.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
We begin by declaring some useful variables. Of
|
|||
|
these, note that we instantiate our
|
|||
|
<code class="classname">RepConfigInfo</code>
|
|||
|
object here. Recall that this is used to store
|
|||
|
information useful to our code. This class becomes more
|
|||
|
interesting later in this book.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">int main(int argc, char **argv)
|
|||
|
{
|
|||
|
RepConfigInfo config;
|
|||
|
char ch;
|
|||
|
int ret; </pre>
|
|||
|
<p>
|
|||
|
Then we collect our command line information. Again, this is at
|
|||
|
this point fairly trivial:
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> // Extract the command line parameters
|
|||
|
while ((ch = getopt(argc, argv, "h:")) != EOF) {
|
|||
|
switch (ch) {
|
|||
|
case 'h':
|
|||
|
config.home = optarg;
|
|||
|
break;
|
|||
|
case '?':
|
|||
|
default:
|
|||
|
usage();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Error check command line.
|
|||
|
if (config.home == NULL)
|
|||
|
usage(); </pre>
|
|||
|
<p>
|
|||
|
Now we instantiate and initialize our <code class="classname">SimpleTxn</code>
|
|||
|
class, which is what is responsible for doing all our real work.
|
|||
|
The <code class="methodname">SimpleTxn::init()</code> method creates and
|
|||
|
opens our environment handle.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> SimpleTxn runner;
|
|||
|
try {
|
|||
|
if((ret = runner.init(&config)) != 0)
|
|||
|
goto err; </pre>
|
|||
|
<p>
|
|||
|
Then we call the <code class="methodname">SimpleTxn::doloop()</code>
|
|||
|
method, which is where the actual transactional work is
|
|||
|
performed for this application.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> if((ret = runner.doloop()) != 0)
|
|||
|
goto err; </pre>
|
|||
|
<p>
|
|||
|
Finally, catch exceptions and terminate the program:
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> } catch (DbException dbe) {
|
|||
|
cerr << "Caught an exception during initialization or"
|
|||
|
<< " processing: " << dbe.what() << endl;
|
|||
|
}
|
|||
|
err:
|
|||
|
runner.terminate();
|
|||
|
return 0;
|
|||
|
} </pre>
|
|||
|
</div>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="repmgr_init_cxx"></a>Method: SimpleTxn::init()</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
The <code class="methodname">SimpleTxn::init()</code>
|
|||
|
method is used to create and open our environment handle.
|
|||
|
For readers familiar with writing transactional
|
|||
|
DB applications, there should be no surprises
|
|||
|
here. However, we will be adding to this in later
|
|||
|
chapters as we roll replication into this example.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
First, we show the class constructor
|
|||
|
implementation, which is only used to initialize a
|
|||
|
few variables:
|
|||
|
</p>
|
|||
|
<pre class="programlisting">SimpleTxn::SimpleTxn() : app_config(0), dbenv(0)
|
|||
|
{
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
We now provide the <code class="methodname">init()</code> method
|
|||
|
implementation. The only thing of interest here is that we specify
|
|||
|
<code class="literal">DB_TXN_NOSYNC</code> to our environment. This causes
|
|||
|
our transactional commits to become non-durable, which is something
|
|||
|
that we are doing only because of the nature of our example.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">int SimpleTxn::init(RepConfigInfo *config)
|
|||
|
{
|
|||
|
int ret = 0;
|
|||
|
|
|||
|
app_config = config;
|
|||
|
|
|||
|
dbenv.set_errfile(stderr);
|
|||
|
dbenv.set_errpfx(progname);
|
|||
|
|
|||
|
/*
|
|||
|
* We can now open our environment.
|
|||
|
*/
|
|||
|
dbenv.set_cachesize(0, CACHESIZE, 0);
|
|||
|
dbenv.set_flags(DB_TXN_NOSYNC, 1);
|
|||
|
|
|||
|
try {
|
|||
|
dbenv.open(app_config->home,
|
|||
|
DB_CREATE |
|
|||
|
DB_RECOVER |
|
|||
|
DB_INIT_LOCK |
|
|||
|
DB_INIT_LOG |
|
|||
|
DB_INIT_MPOOL |
|
|||
|
DB_INIT_TXN,
|
|||
|
0);
|
|||
|
} catch(DbException dbe) {
|
|||
|
cerr << "Caught an exception during DB environment open." << endl
|
|||
|
<< "Ensure that the home directory is created prior to"
|
|||
|
<< " starting the application." << endl;
|
|||
|
ret = ENOENT;
|
|||
|
goto err;
|
|||
|
}
|
|||
|
|
|||
|
err:
|
|||
|
return ret;
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Finally, we present the <code class="methodname">SimpleTxn::terminate()</code>
|
|||
|
method here. All this does is close the environment handle. Again,
|
|||
|
there should be no surprises here, but we provide the
|
|||
|
implementation for the sake of completeness anyway.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">int SimpleTxn::terminate()
|
|||
|
{
|
|||
|
try {
|
|||
|
dbenv.close(0);
|
|||
|
} catch (DbException dbe) {
|
|||
|
cerr << "error closing environment: " << dbe.what() << endl;
|
|||
|
}
|
|||
|
return 0;
|
|||
|
} </pre>
|
|||
|
</div>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="doloop_cxx"></a>Method: SimpleTxn::doloop()</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
Having written our <code class="function">main()</code>
|
|||
|
function and support utility methods, we now implement
|
|||
|
our application's
|
|||
|
primary data processing method. This
|
|||
|
method provides a command prompt at which the
|
|||
|
user can enter a stock ticker value and a price for
|
|||
|
that value. This information is then entered to the
|
|||
|
database.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
To display the database, simply enter
|
|||
|
<code class="literal">return</code> at the prompt.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
To begin, we declare a database pointer,
|
|||
|
several <code class="classname">Dbt</code> variables, and
|
|||
|
the usual assortment of variables used for buffers
|
|||
|
and return codes. We also initialize all of this.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">#define BUFSIZE 1024
|
|||
|
int SimpleTxn::doloop()
|
|||
|
{
|
|||
|
Db *dbp;
|
|||
|
Dbt key, data;
|
|||
|
char buf[BUFSIZE], *rbuf;
|
|||
|
int ret;
|
|||
|
|
|||
|
dbp = NULL;
|
|||
|
memset(&key, 0, sizeof(key));
|
|||
|
memset(&data, 0, sizeof(data));
|
|||
|
ret = 0; </pre>
|
|||
|
<p>
|
|||
|
Next, we begin the loop and we immediately open our
|
|||
|
database if it has not already been opened. Notice that
|
|||
|
we specify autocommit when we open the database. In
|
|||
|
this case, autocommit is important because we will only
|
|||
|
ever write to our database using it. There is no need
|
|||
|
for explicit transaction handles and commit/abort code
|
|||
|
in this application, because we are not combining
|
|||
|
multiple database operations together under a single
|
|||
|
transaction.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Autocommit is described in greater detail in the
|
|||
|
<em class="citetitle">Berkeley DB Getting Started with Transaction Processing</em> guide.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> for (;;) {
|
|||
|
if (dbp == NULL) {
|
|||
|
dbp = new Db(&dbenv, 0);
|
|||
|
|
|||
|
try {
|
|||
|
dbp->open(NULL, DATABASE, NULL, DB_BTREE,
|
|||
|
DB_CREATE | DB_AUTO_COMMIT, 0);
|
|||
|
} catch(DbException dbe) {
|
|||
|
dbenv.err(ret, "DB->open");
|
|||
|
throw dbe;
|
|||
|
}
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Now we implement our command prompt. This is a simple and not
|
|||
|
very robust implementation of a command prompt.
|
|||
|
If the user enters the keywords <code class="literal">exit</code>
|
|||
|
or <code class="literal">quit</code>, the loop is exited and the
|
|||
|
application ends. If the user enters nothing and instead simply
|
|||
|
presses <code class="literal">return</code>, the entire contents of the
|
|||
|
database is displayed. We use our
|
|||
|
<code class="function">print_stocks()</code> method to display the
|
|||
|
database. (That implementation is shown next in this chapter.)
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
Notice that very little error checking is performed on the data
|
|||
|
entered at this prompt. If the user fails to enter at least one
|
|||
|
space in the value string, a simple help message is printed and
|
|||
|
the prompt is returned to the user. That is the only error
|
|||
|
checking performed here. In a real-world application,
|
|||
|
at a minimum the application would probably check to ensure
|
|||
|
that the price was in fact an integer or float value.
|
|||
|
However, in order to keep this example code as simple as
|
|||
|
possible, we refrain from implementing a thorough user interface.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> cout << "QUOTESERVER" ;
|
|||
|
cout << "> " << flush;
|
|||
|
|
|||
|
if (fgets(buf, sizeof(buf), stdin) == NULL)
|
|||
|
break;
|
|||
|
if (strtok(&buf[0], " \t\n") == NULL) {
|
|||
|
switch ((ret = print_stocks(dbp))) {
|
|||
|
case 0:
|
|||
|
continue;
|
|||
|
default:
|
|||
|
dbp->err(ret, "Error traversing data");
|
|||
|
goto err;
|
|||
|
}
|
|||
|
}
|
|||
|
rbuf = strtok(NULL, " \t\n");
|
|||
|
if (rbuf == NULL || rbuf[0] == '\0') {
|
|||
|
if (strncmp(buf, "exit", 4) == 0 ||
|
|||
|
strncmp(buf, "quit", 4) == 0)
|
|||
|
break;
|
|||
|
dbenv.errx("Format: TICKER VALUE");
|
|||
|
continue;
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Now we assign data to the <code class="classname">Dbt</code>s that
|
|||
|
we will use to write the new information to the database.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> key.set_data(buf);
|
|||
|
key.set_size((u_int32_t)strlen(buf));
|
|||
|
|
|||
|
data.set_data(rbuf);
|
|||
|
data.set_size((u_int32_t)strlen(rbuf)); </pre>
|
|||
|
<p>
|
|||
|
Having done that, we can write the new information to the
|
|||
|
database. Remember that this application uses autocommit,
|
|||
|
so no explicit transaction management is required. Also,
|
|||
|
the database is not configured for duplicate records, so
|
|||
|
the data portion of a record is overwritten if the provided
|
|||
|
key already exists in the database. However, in this case
|
|||
|
DB returns <code class="literal">DB_KEYEXIST</code> — which
|
|||
|
we ignore.
|
|||
|
</p>
|
|||
|
<pre class="programlisting"> if ((ret = dbp->put(NULL, &key, &data, 0)) != 0)
|
|||
|
{
|
|||
|
dbp->err(ret, "DB->put");
|
|||
|
if (ret != DB_KEYEXIST)
|
|||
|
goto err;
|
|||
|
}
|
|||
|
} </pre>
|
|||
|
<p>
|
|||
|
Finally, we close our database before returning from the
|
|||
|
method.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">err: if (dbp != NULL) {
|
|||
|
(void)dbp->close(DB_NOSYNC);
|
|||
|
cout << "database closed" << endl;
|
|||
|
}
|
|||
|
|
|||
|
return (ret);
|
|||
|
} </pre>
|
|||
|
</div>
|
|||
|
<div class="sect2" lang="en" xml:lang="en">
|
|||
|
<div class="titlepage">
|
|||
|
<div>
|
|||
|
<div>
|
|||
|
<h3 class="title"><a id="printstocks_c"></a>
|
|||
|
|
|||
|
<span>Method: SimpleTxn::print_stocks()</span>
|
|||
|
|
|||
|
</h3>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<p>
|
|||
|
The <code class="function">print_stocks()</code>
|
|||
|
|
|||
|
|
|||
|
<span>method</span>
|
|||
|
simply takes a database handle, opens a cursor, and uses
|
|||
|
it to display all the information it finds in a database.
|
|||
|
This is trivial cursor operation that should hold
|
|||
|
no surprises for you. We simply provide it here for
|
|||
|
the sake of completeness.
|
|||
|
</p>
|
|||
|
<p>
|
|||
|
If you are unfamiliar with basic cursor operations,
|
|||
|
please see the <em class="citetitle">Getting Started with Berkeley DB</em>
|
|||
|
guide.
|
|||
|
</p>
|
|||
|
<pre class="programlisting">int SimpleTxn::print_stocks(Db *dbp)
|
|||
|
{
|
|||
|
Dbc *dbc;
|
|||
|
Dbt key, data;
|
|||
|
#define MAXKEYSIZE 10
|
|||
|
#define MAXDATASIZE 20
|
|||
|
char keybuf[MAXKEYSIZE + 1], databuf[MAXDATASIZE + 1];
|
|||
|
int ret, t_ret;
|
|||
|
u_int32_t keysize, datasize;
|
|||
|
|
|||
|
if ((ret = dbp->cursor(NULL, &dbc, 0)) != 0) {
|
|||
|
dbp->err(ret, "can't open cursor");
|
|||
|
return (ret);
|
|||
|
}
|
|||
|
|
|||
|
memset(&key, 0, sizeof(key));
|
|||
|
memset(&data, 0, sizeof(data));
|
|||
|
|
|||
|
cout << "\tSymbol\tPrice" << endl
|
|||
|
<< "\t======\t=====" << endl;
|
|||
|
|
|||
|
for (ret = dbc->get(&key, &data, DB_FIRST);
|
|||
|
ret == 0;
|
|||
|
ret = dbc->get(&key, &data, DB_NEXT)) {
|
|||
|
keysize = key.get_size() > MAXKEYSIZE ? MAXKEYSIZE :
|
|||
|
key.get_size();
|
|||
|
memcpy(keybuf, key.get_data(), keysize);
|
|||
|
keybuf[keysize] = '\0';
|
|||
|
|
|||
|
datasize = data.get_size() >=
|
|||
|
MAXDATASIZE ? MAXDATASIZE : data.get_size();
|
|||
|
memcpy(databuf, data.get_data(), datasize);
|
|||
|
databuf[datasize] = '\0';
|
|||
|
|
|||
|
cout << "\t" << keybuf << "\t" << databuf << endl;
|
|||
|
}
|
|||
|
cout << endl << flush;
|
|||
|
|
|||
|
if ((t_ret = dbc->close()) != 0 && ret == 0) {
|
|||
|
cout << "closed cursor" << endl;
|
|||
|
ret = t_ret;
|
|||
|
}
|
|||
|
|
|||
|
switch (ret) {
|
|||
|
case 0:
|
|||
|
case DB_NOTFOUND:
|
|||
|
return (0);
|
|||
|
default:
|
|||
|
return (ret);
|
|||
|
}
|
|||
|
} </pre>
|
|||
|
</div>
|
|||
|
</div>
|
|||
|
<div class="navfooter">
|
|||
|
<hr />
|
|||
|
<table width="100%" summary="Navigation footer">
|
|||
|
<tr>
|
|||
|
<td width="40%" align="left"><a accesskey="p" href="txnapp.html">Prev</a> </td>
|
|||
|
<td width="20%" align="center">
|
|||
|
<a accesskey="u" href="txnapp.html">Up</a>
|
|||
|
</td>
|
|||
|
<td width="40%" align="right"> <a accesskey="n" href="repapp.html">Next</a></td>
|
|||
|
</tr>
|
|||
|
<tr>
|
|||
|
<td width="40%" align="left" valign="top">Chapter 2. Transactional Application </td>
|
|||
|
<td width="20%" align="center">
|
|||
|
<a accesskey="h" href="index.html">Home</a>
|
|||
|
</td>
|
|||
|
<td width="40%" align="right" valign="top"> Chapter 3. The DB Replication Manager</td>
|
|||
|
</tr>
|
|||
|
</table>
|
|||
|
</div>
|
|||
|
</body>
|
|||
|
</html>
|