mirror of
https://github.com/berkeleydb/libdb.git
synced 2024-11-16 17:16:25 +00:00
599 lines
23 KiB
HTML
599 lines
23 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.3</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#main_c">Function: main()</a>
|
||
</span>
|
||
</dt>
|
||
<dt>
|
||
<span class="sect2">
|
||
<a href="simpleprogramlisting.html#create_env_c">Function: create_env()</a>
|
||
</span>
|
||
</dt>
|
||
<dt>
|
||
<span class="sect2">
|
||
<a href="simpleprogramlisting.html#env_init_c">Function: env_init()</a>
|
||
</span>
|
||
</dt>
|
||
<dt>
|
||
<span class="sect2">
|
||
<a href="simpleprogramlisting.html#doloop_c">Function: doloop()</a>
|
||
</span>
|
||
</dt>
|
||
<dt>
|
||
<span class="sect2">
|
||
<a href="simpleprogramlisting.html#printstocks_c">
|
||
<span>Function: 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="main_c"></a>Function: main()</h3>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Our program begins with the usual assortment of
|
||
include statements.
|
||
</p>
|
||
<a id="c_simpletxn1"></a>
|
||
<pre class="programlisting">/*
|
||
* File: ex_rep_gsg_simple.c
|
||
*/
|
||
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#ifndef _WIN32
|
||
#include <unistd.h>
|
||
#endif
|
||
|
||
#include <db.h>
|
||
|
||
#ifdef _WIN32
|
||
extern int getopt(int, char * const *, const char *);
|
||
#endif </pre>
|
||
<p>
|
||
We then define a few values. One is the size of our cache,
|
||
which we keep deliberately small for this example, and the
|
||
other is the name of our database. We also provide a global
|
||
variable that is the name of our program; this is used for
|
||
error reporting later on.
|
||
</p>
|
||
<a id="c_simpletxn2"></a>
|
||
<pre class="programlisting">#define CACHESIZE (10 * 1024 * 1024)
|
||
#define DATABASE "quote.db"
|
||
|
||
const char *progname = "ex_rep_gsg_simple"; </pre>
|
||
<p>
|
||
Then we perform a couple of forward declarations. The first
|
||
of these, <code class="function">create_env()</code> and
|
||
<code class="function">env_init()</code> are used to open
|
||
and initialize our environment.
|
||
</p>
|
||
<p>
|
||
Next we declare
|
||
<code class="function">doloop()</code>, which is the function that we use to
|
||
add data to the database and then display its contents. This is
|
||
essentially a big <code class="literal">do</code> loop, hence the
|
||
function's name.
|
||
</p>
|
||
<p>
|
||
Finally, we have <code class="function">print_stocks</code>, which is
|
||
used to display a database record once it has been retrieved from the
|
||
database.
|
||
</p>
|
||
<a id="c_simpletxn3"></a>
|
||
<pre class="programlisting">int create_env(const char *, DB_ENV **);
|
||
int env_init(DB_ENV *, const char *);
|
||
int doloop (DB_ENV *);
|
||
int print_stocks(DB *); </pre>
|
||
<p>
|
||
Next we need our <code class="function">usage()</code> function,
|
||
which is fairly trivial at this point:
|
||
</p>
|
||
<a id="c_simpletxn4"></a>
|
||
<pre class="programlisting">/* Usage function */
|
||
static void
|
||
usage()
|
||
{
|
||
fprintf(stderr, "usage: %s ", progname);
|
||
fprintf(stderr, "-h home\n");
|
||
exit(EXIT_FAILURE);
|
||
} </pre>
|
||
<p>
|
||
That completed, we can jump into our application's
|
||
<code class="function">main()</code> function. If you are familiar with
|
||
DB transactional applications, you will not find any
|
||
surprises here. We begin by declaring and initializing the
|
||
usual set of variables:
|
||
</p>
|
||
<a id="c_simpletxn5"></a>
|
||
<pre class="programlisting">int
|
||
main(int argc, char *argv[])
|
||
{
|
||
extern char *optarg;
|
||
DB_ENV *dbenv;
|
||
const char *home;
|
||
char ch;
|
||
int ret;
|
||
|
||
dbenv = NULL;
|
||
|
||
ret = 0;
|
||
home = NULL; </pre>
|
||
<p>
|
||
Now we create and configure our environment handle.
|
||
We do this with our <code class="function">create_env()</code> function, which we will
|
||
show a little later in this example.
|
||
|
||
</p>
|
||
<a id="c_simpletxn6"></a>
|
||
<pre class="programlisting"> if ((ret = create_env(progname, &dbenv)) != 0)
|
||
goto err; </pre>
|
||
<p>
|
||
Then we parse the command line arguments:
|
||
</p>
|
||
<a id="c_simpletxn7"></a>
|
||
<pre class="programlisting"> while ((ch = getopt(argc, argv, "h:")) != EOF)
|
||
switch (ch) {
|
||
case 'h':
|
||
home = optarg;
|
||
break;
|
||
case '?':
|
||
default:
|
||
usage();
|
||
}
|
||
|
||
/* Error check command line. */
|
||
if (home == NULL)
|
||
usage(); </pre>
|
||
<p>
|
||
Now we can open our environment. We do this with our
|
||
<code class="function">env_init()</code> function which we will describe
|
||
a little later in this chapter.
|
||
</p>
|
||
<a id="c_simpletxn8"></a>
|
||
<pre class="programlisting"> if ((ret = env_init(dbenv, home)) != 0)
|
||
goto err; </pre>
|
||
<p>
|
||
Now that we have opened the environment, we can call our
|
||
<code class="function">doloop()</code> function. This function performs the basic
|
||
database interaction. Notice that we have not yet opened any databases. In
|
||
a traditional transactional application we would probably open the
|
||
databases before calling our our main data processing function.
|
||
However, the eventual replicated application will want to handle
|
||
database open and close in the main processing loop, so in a nod to what this
|
||
application will eventually become we do a slightly unusual thing
|
||
here.
|
||
</p>
|
||
<a id="c_simpletxn9"></a>
|
||
<pre class="programlisting"> if ((ret = doloop(dbenv)) != 0) {
|
||
dbenv->err(dbenv, ret, "Application failed");
|
||
goto err;
|
||
} </pre>
|
||
<p>
|
||
Finally, we provide our application shutdown code. Note, again,
|
||
that in a traditional transactional application all databases would
|
||
also be closed here. But, again, due to the way this application
|
||
will eventually behave, we cause the database close to occur in the
|
||
<code class="function">doloop()</code> function.
|
||
</p>
|
||
<a id="c_simpletxn10"></a>
|
||
<pre class="programlisting">err: if (dbenv != NULL)
|
||
(void)dbenv->close(dbenv, 0);
|
||
|
||
return (ret);
|
||
} </pre>
|
||
</div>
|
||
<div class="sect2" lang="en" xml:lang="en">
|
||
<div class="titlepage">
|
||
<div>
|
||
<div>
|
||
<h3 class="title"><a id="create_env_c"></a>Function: create_env()</h3>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Having written our <code class="function">main()</code>
|
||
function, we now implement the first of our utility
|
||
functions that we use to manage our environments.
|
||
This function exists only to make our code easier
|
||
to manage, and all it does is create an environment
|
||
handle for us.
|
||
</p>
|
||
<a id="c_create_env1"></a>
|
||
<pre class="programlisting">int
|
||
create_env(const char *progname, DB_ENV **dbenvp)
|
||
{
|
||
DB_ENV *dbenv;
|
||
int ret;
|
||
|
||
if ((ret = db_env_create(&dbenv, 0)) != 0) {
|
||
fprintf(stderr, "can't create env handle: %s\n",
|
||
db_strerror(ret));
|
||
return (ret);
|
||
}
|
||
|
||
dbenv->set_errfile(dbenv, stderr);
|
||
dbenv->set_errpfx(dbenv, progname);
|
||
|
||
*dbenvp = dbenv;
|
||
return (0);
|
||
} </pre>
|
||
</div>
|
||
<div class="sect2" lang="en" xml:lang="en">
|
||
<div class="titlepage">
|
||
<div>
|
||
<div>
|
||
<h3 class="title"><a id="env_init_c"></a>Function: env_init()</h3>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Having written the function that initializes an
|
||
environment handle, we now implement the function
|
||
that opens the handle. Again, there should be no
|
||
surprises here for anyone familiar with DB
|
||
applications. The open flags that we use are those
|
||
normally used for a transactional application.
|
||
</p>
|
||
<a id="c_env_init"></a>
|
||
<pre class="programlisting">int
|
||
env_init(DB_ENV *dbenv, const char *home)
|
||
{
|
||
u_int32_t flags;
|
||
int ret;
|
||
|
||
(void)dbenv->set_cachesize(dbenv, 0, CACHESIZE, 0);
|
||
(void)dbenv->set_flags(dbenv, DB_TXN_NOSYNC, 1);
|
||
|
||
flags = DB_CREATE |
|
||
DB_INIT_LOCK |
|
||
DB_INIT_LOG |
|
||
DB_INIT_MPOOL |
|
||
DB_INIT_TXN |
|
||
DB_RECOVER;
|
||
if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0)
|
||
dbenv->err(dbenv, ret, "can't open environment");
|
||
return (ret);
|
||
} </pre>
|
||
</div>
|
||
<div class="sect2" lang="en" xml:lang="en">
|
||
<div class="titlepage">
|
||
<div>
|
||
<div>
|
||
<h3 class="title"><a id="doloop_c"></a>Function: doloop()</h3>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
Having written our <code class="function">main()</code>
|
||
function and utility functions, we now implement
|
||
our application's
|
||
primary data processing function. This
|
||
function 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>
|
||
<a id="c_doloop1"></a>
|
||
<pre class="programlisting">#define BUFSIZE 1024
|
||
int
|
||
doloop(DB_ENV *dbenv)
|
||
{
|
||
DB *dbp;
|
||
DBT key, data;
|
||
char buf[BUFSIZE], *rbuf;
|
||
int ret;
|
||
u_int32_t db_flags;
|
||
|
||
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>
|
||
<a id="c_doloop2"></a>
|
||
<pre class="programlisting"> for (;;) {
|
||
|
||
if (dbp == NULL) {
|
||
if ((ret = db_create(&dbp, dbenv, 0)) != 0)
|
||
return (ret);
|
||
|
||
db_flags = DB_AUTO_COMMIT | DB_CREATE;
|
||
|
||
if ((ret = dbp->open(dbp, NULL, DATABASE,
|
||
NULL, DB_BTREE, db_flags, 0)) != 0) {
|
||
dbenv->err(dbenv, ret, "DB->open");
|
||
goto err;
|
||
}
|
||
} </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> function 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>
|
||
<a id="c_doloop3"></a>
|
||
<pre class="programlisting"> printf("QUOTESERVER > ");
|
||
fflush(stdout);
|
||
|
||
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(dbp, 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(dbenv, "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>
|
||
<a id="c_doloop4"></a>
|
||
<pre class="programlisting"> key.data = buf;
|
||
key.size = (u_int32_t)strlen(buf);
|
||
|
||
data.data = rbuf;
|
||
data.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>
|
||
<a id="c_doloop5"></a>
|
||
<pre class="programlisting"> if ((ret = dbp->put(dbp, NULL, &key, &data, 0)) != 0)
|
||
{
|
||
dbp->err(dbp, ret, "DB->put");
|
||
if (ret != DB_KEYEXIST)
|
||
goto err;
|
||
}
|
||
} </pre>
|
||
<p>
|
||
Finally, we close our database before returning from the
|
||
function.
|
||
</p>
|
||
<a id="c_doloop6"></a>
|
||
<pre class="programlisting">err: if (dbp != NULL)
|
||
(void)dbp->close(dbp, DB_NOSYNC);
|
||
|
||
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>Function: print_stocks()</span>
|
||
|
||
|
||
</h3>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p>
|
||
The <code class="function">print_stocks()</code>
|
||
|
||
<span>function</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>
|
||
<a id="c_printstocks1"></a>
|
||
<pre class="programlisting">/* Displays all stock quote information in the database. */
|
||
int
|
||
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(dbp, NULL, &dbc, 0)) != 0) {
|
||
dbp->err(dbp, ret, "can't open cursor");
|
||
return (ret);
|
||
}
|
||
|
||
memset(&key, 0, sizeof(key));
|
||
memset(&data, 0, sizeof(data));
|
||
|
||
printf("\tSymbol\tPrice\n");
|
||
printf("\t======\t=====\n");
|
||
|
||
for (ret = dbc->get(dbc, &key, &data, DB_FIRST);
|
||
ret == 0;
|
||
ret = dbc->get(dbc, &key, &data, DB_NEXT)) {
|
||
keysize = key.size > MAXKEYSIZE ? MAXKEYSIZE : key.size;
|
||
memcpy(keybuf, key.data, keysize);
|
||
keybuf[keysize] = '\0';
|
||
|
||
datasize = data.size >= MAXDATASIZE ? MAXDATASIZE : data.size;
|
||
memcpy(databuf, data.data, datasize);
|
||
databuf[datasize] = '\0';
|
||
|
||
printf("\t%s\t%s\n", keybuf, databuf);
|
||
}
|
||
printf("\n");
|
||
fflush(stdout);
|
||
|
||
if ((t_ret = dbc->close(dbc)) != 0 && ret == 0)
|
||
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>
|