Import BSDDB 4.7.25 (as of svn r89086)

This commit is contained in:
Zachary Ware
2017-09-04 13:40:25 -05:00
parent 4b29e0458f
commit 8f590873d0
4781 changed files with 2241032 additions and 6 deletions

40
examples_c/README Normal file
View File

@@ -0,0 +1,40 @@
# $Id: README,v 12.1 2006/07/07 23:36:14 alanb Exp $
getting_started/
Examples from the Getting Started Guide
bench_001.c Micro-benchmark for the bulk fetch interface.
ex_access.c Using just the DB access methods.
ex_apprec/ Application-specific recovery.
ex_btrec.c Using the BTREE access method with record numbers.
ex_dbclient.c Using DB from an RPC client.
ex_env.c Setting up the DB environment.
ex_lock.c Locking.
ex_mpool.c Shared memory buffer pools.
ex_rep/ Replication. This creates a toy stock quote server
with DB's single-master, multiple-client replication,
with communication over TCP. See ex_rep/README.
ex_sequence.c Sequences.
ex_thread.c Threaded application with multiple readers and writers.
ex_tpcb.c TPC/B.
Ex_tpcb sets up a framework in which to run a TPC/B test.
Database initialization (the -i flag) and running the
benchmark (-n flag) must take place separately (i.e.,
first create the database, then run 1 or more copies of
the benchmark). Furthermore, when running more than one
TPCB process, it is necessary to run the deadlock detector
(db_deadlock), since it is possible for concurrent tpcb
processes to deadlock. For performance measurement, it
will also be beneficial to run the db_checkpoint process
as well.

431
examples_c/bench_001.c Normal file
View File

@@ -0,0 +1,431 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2001,2008 Oracle. All rights reserved.
*
* $Id: bench_001.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
/*
* bench_001 - time bulk fetch interface.
* Without -R builds a btree acording to the arguments.
* With -R runs and times bulk fetches. If -d is specified
* during reads the DB_MULTIPLE interface is used
* otherwise the DB_MULTIPLE_KEY interface is used.
*
* ARGUMENTS:
* -c cachesize [1000 * pagesize]
* -d number of duplicates [none]
* -E don't use environment
* -I Just initialize the environment
* -i number of read iterations [1000000]
* -l length of data item [20]
* -n number of keys [1000000]
* -p pagesize [65536]
* -R perform read test.
* -T incorporate transactions.
*
* COMPILE:
* cc -I /usr/local/BerkeleyDB/include \
* -o bench_001 -O2 bench_001.c /usr/local/BerkeleyDB/lib/libdb.so
*/
#include <sys/types.h>
#include <sys/time.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
extern int getopt(int, char * const *, const char *);
#else
#include <unistd.h>
#endif
#include <db.h>
#define DATABASE "bench_001.db"
int compare_int(DB *, const DBT *, const DBT *);
DB_ENV *db_init(char *, char *, u_int, int);
int fill(DB_ENV *, DB *, int, u_int, int, int);
int get(DB *, int, u_int, int, int, int, int *);
int main(int, char *[]);
void usage(void);
const char
*progname = "bench_001"; /* Program name. */
/*
* db_init --
* Initialize the environment.
*/
DB_ENV *
db_init(home, prefix, cachesize, txn)
char *home, *prefix;
u_int cachesize;
int txn;
{
DB_ENV *dbenv;
u_int32_t flags;
int ret;
if ((ret = db_env_create(&dbenv, 0)) != 0) {
dbenv->err(dbenv, ret, "db_env_create");
return (NULL);
}
dbenv->set_errfile(dbenv, stderr);
dbenv->set_errpfx(dbenv, prefix);
(void)dbenv->set_cachesize(dbenv, 0,
cachesize == 0 ? 50 * 1024 * 1024 : (u_int32_t)cachesize, 0);
flags = DB_CREATE | DB_INIT_MPOOL;
if (txn)
flags |= DB_INIT_TXN | DB_INIT_LOCK;
if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0) {
dbenv->err(dbenv, ret, "DB_ENV->open: %s", home);
(void)dbenv->close(dbenv, 0);
return (NULL);
}
return (dbenv);
}
/*
* get -- loop getting batches of records.
*
*/
int
get(dbp, txn, datalen, num, dups, iter, countp)
DB *dbp;
u_int datalen;
int txn, num, dups, iter, *countp;
{
DBC *dbcp;
DBT key, data;
DB_ENV *dbenv;
DB_TXN *txnp;
u_int32_t flags, len, klen;
int count, i, j, ret;
void *pointer, *dp, *kp;
dbenv = dbp->dbenv;
klen = 0; /* Lint. */
klen = klen;
memset(&key, 0, sizeof(key));
key.data = &j;
key.size = sizeof(j);
memset(&data, 0, sizeof(data));
data.flags = DB_DBT_USERMEM;
data.data = malloc(datalen*1024*1024);
data.ulen = data.size = datalen*1024*1024;
count = 0;
flags = DB_SET;
if (!dups)
flags |= DB_MULTIPLE_KEY;
else
flags |= DB_MULTIPLE;
for (i = 0; i < iter; i++) {
txnp = NULL;
if (txn)
if ((ret =
dbenv->txn_begin(dbenv, NULL, &txnp, 0)) != 0)
goto err;
if ((ret = dbp->cursor(dbp, txnp, &dbcp, 0)) != 0)
goto err;
j = random() % num;
if ((ret = dbcp->get(dbcp, &key, &data, flags)) != 0)
goto err;
DB_MULTIPLE_INIT(pointer, &data);
if (dups)
while (pointer != NULL) {
DB_MULTIPLE_NEXT(pointer, &data, dp, len);
if (dp != NULL)
count++;
}
else
while (pointer != NULL) {
DB_MULTIPLE_KEY_NEXT(pointer,
&data, kp, klen, dp, len);
if (kp != NULL)
count++;
}
if ((ret = dbcp->close(dbcp)) != 0)
goto err;
if (txn)
if ((ret = txnp->commit(txnp, 0)) != 0)
goto err;
}
*countp = count;
return (0);
err: dbp->err(dbp, ret, "get");
return (ret);
}
/*
* fill - fill a db
* Since we open/created the db with transactions (potentially),
* we need to populate it with transactions. We'll bundle the puts
* 10 to a transaction.
*/
#define PUTS_PER_TXN 10
int
fill(dbenv, dbp, txn, datalen, num, dups)
DB_ENV *dbenv;
DB *dbp;
u_int datalen;
int txn, num, dups;
{
DBT key, data;
DB_TXN *txnp;
struct data {
int id;
char str[1];
} *data_val;
int count, i, ret;
/*
* Insert records into the database, where the key is the user
* input and the data is the user input in reverse order.
*/
txnp = NULL;
ret = 0;
count = 0;
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
key.data = &i;
key.size = sizeof(i);
data.data = data_val = malloc(datalen);
memcpy(data_val->str, "0123456789012345678901234567890123456789",
datalen - sizeof(data_val->id));
data.size = datalen;
data.flags = DB_DBT_USERMEM;
for (i = 0; i < num; i++) {
if (txn != 0 && i % PUTS_PER_TXN == 0) {
if (txnp != NULL) {
ret = txnp->commit(txnp, 0);
txnp = NULL;
if (ret != 0)
goto err;
}
if ((ret =
dbenv->txn_begin(dbenv, NULL, &txnp, 0)) != 0)
goto err;
}
data_val->id = 0;
do {
switch (ret = dbp->put(dbp, txnp, &key, &data, 0)) {
case 0:
count++;
break;
default:
dbp->err(dbp, ret, "DB->put");
goto err;
}
} while (++data_val->id < dups);
}
if (txnp != NULL)
ret = txnp->commit(txnp, 0);
printf("%d\n", count);
return (ret);
err: if (txnp != NULL)
(void)txnp->abort(txnp);
return (ret);
}
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
extern int optind;
DB *dbp;
DB_ENV *dbenv;
DB_TXN *txnp;
struct timeval start_time, end_time;
double secs;
u_int cache, datalen, pagesize;
int ch, count, dups, env, init, iter, num;
int ret, rflag, txn;
txnp = NULL;
datalen = 20;
iter = num = 1000000;
env = 1;
dups = init = rflag = txn = 0;
pagesize = 65536;
cache = 1000 * pagesize;
while ((ch = getopt(argc, argv, "c:d:EIi:l:n:p:RT")) != EOF)
switch (ch) {
case 'c':
cache = (u_int)atoi(optarg);
break;
case 'd':
dups = atoi(optarg);
break;
case 'E':
env = 0;
break;
case 'I':
init = 1;
break;
case 'i':
iter = atoi(optarg);
break;
case 'l':
datalen = (u_int)atoi(optarg);
break;
case 'n':
num = atoi(optarg);
break;
case 'p':
pagesize = (u_int)atoi(optarg);
break;
case 'R':
rflag = 1;
break;
case 'T':
txn = 1;
break;
case '?':
default:
usage();
}
argc -= optind;
argv += optind;
/* Remove the previous database. */
if (!rflag) {
if (env)
(void)system("rm -rf BENCH_001; mkdir BENCH_001");
else
(void)unlink(DATABASE);
}
dbenv = NULL;
if (env == 1 &&
(dbenv = db_init("BENCH_001", "bench_001", cache, txn)) == NULL)
return (-1);
if (init)
exit(0);
/* Create and initialize database object, open the database. */
if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
fprintf(stderr,
"%s: db_create: %s\n", progname, db_strerror(ret));
exit(EXIT_FAILURE);
}
dbp->set_errfile(dbp, stderr);
dbp->set_errpfx(dbp, progname);
if ((ret = dbp->set_bt_compare(dbp, compare_int)) != 0) {
dbp->err(dbp, ret, "set_bt_compare");
goto err;
}
if ((ret = dbp->set_pagesize(dbp, pagesize)) != 0) {
dbp->err(dbp, ret, "set_pagesize");
goto err;
}
if (dups && (ret = dbp->set_flags(dbp, DB_DUP)) != 0) {
dbp->err(dbp, ret, "set_flags");
goto err;
}
if (env == 0 && (ret = dbp->set_cachesize(dbp, 0, cache, 0)) != 0) {
dbp->err(dbp, ret, "set_cachesize");
goto err;
}
if ((ret = dbp->set_flags(dbp, DB_DUP)) != 0) {
dbp->err(dbp, ret, "set_flags");
goto err;
}
if (txn != 0)
if ((ret = dbenv->txn_begin(dbenv, NULL, &txnp, 0)) != 0)
goto err;
if ((ret = dbp->open(
dbp, txnp, DATABASE, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) {
dbp->err(dbp, ret, "%s: open", DATABASE);
if (txnp != NULL)
(void)txnp->abort(txnp);
goto err;
}
if (txnp != NULL)
ret = txnp->commit(txnp, 0);
txnp = NULL;
if (ret != 0)
goto err;
if (rflag) {
/* If no environment, fill the cache. */
if (!env && (ret =
get(dbp, txn, datalen, num, dups, iter, &count)) != 0)
goto err;
/* Time the get loop. */
(void)gettimeofday(&start_time, NULL);
if ((ret =
get(dbp, txn, datalen, num, dups, iter, &count)) != 0)
goto err;
(void)gettimeofday(&end_time, NULL);
secs =
(((double)end_time.tv_sec * 1000000 + end_time.tv_usec) -
((double)start_time.tv_sec * 1000000 + start_time.tv_usec))
/ 1000000;
printf("%d records read using %d batches in %.2f seconds: ",
count, iter, secs);
printf("%.0f records/second\n", (double)count / secs);
} else if ((ret = fill(dbenv, dbp, txn, datalen, num, dups)) != 0)
goto err;
/* Close everything down. */
if ((ret = dbp->close(dbp, rflag ? DB_NOSYNC : 0)) != 0) {
fprintf(stderr,
"%s: DB->close: %s\n", progname, db_strerror(ret));
return (1);
}
return (ret);
err: (void)dbp->close(dbp, 0);
return (1);
}
int
compare_int(dbp, a, b)
DB *dbp;
const DBT *a, *b;
{
int ai, bi;
dbp = dbp; /* Lint. */
/*
* Returns:
* < 0 if a < b
* = 0 if a = b
* > 0 if a > b
*/
memcpy(&ai, a->data, sizeof(int));
memcpy(&bi, b->data, sizeof(int));
return (ai - bi);
}
void
usage()
{
(void)fprintf(stderr, "usage: %s %s\n\t%s\n",
progname, "[-EIRT] [-c cachesize] [-d dups]",
"[-i iterations] [-l datalen] [-n keys] [-p pagesize]");
exit(EXIT_FAILURE);
}

470
examples_c/csv/DbRecord.c Normal file
View File

@@ -0,0 +1,470 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: DbRecord.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include "csv.h"
#include "csv_local.h"
#include "csv_extern.h"
static int DbRecord_field(DbRecord *, u_int, void *, datatype);
static int DbRecord_search_field(DbField *, char *, OPERATOR);
static int DbRecord_search_recno(char *, OPERATOR);
/*
* DbRecord_print --
* Display a DbRecord structure.
*/
void
DbRecord_print(DbRecord *recordp, FILE *fp)
{
DbField *f;
void *faddr;
if (fp == NULL)
fp = stdout;
fprintf(fp, "Record: %lu:\n", (u_long)recordp->recno);
for (f = fieldlist; f->name != NULL; ++f) {
faddr = (u_int8_t *)recordp + f->offset;
fprintf(fp, "\t%s: ", f->name);
switch (f->type) {
case NOTSET:
/* NOTREACHED */
abort();
break;
case DOUBLE:
fprintf(fp, "%f\n", *(double *)faddr);
break;
case STRING:
fprintf(fp, "%s\n", *(char **)faddr);
break;
case UNSIGNED_LONG:
fprintf(fp, "%lu\n", *(u_long *)faddr);
break;
}
}
}
/*
* DbRecord_read --
* Read a specific record from the database.
*/
int
DbRecord_read(u_long recno_ulong, DbRecord *recordp)
{
DBT key, data;
u_int32_t recno;
int ret;
/*
* XXX
* This code assumes a record number (typed as u_int32_t) is the same
* size as an unsigned long, and there's no reason to believe that.
*/
recno = recno_ulong;
/*
* Retrieve the requested record from the primary database. Have
* Berkeley DB allocate memory for us, keeps the DB handle thread
* safe.
*
* We have the Berkeley DB library allocate memory for the record,
* which we own and must eventually free. The reason is so we can
* have the string fields in the structure point into the actual
* record, rather than allocating structure local memory to hold them
* and copying them out of the record.
*/
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
key.data = &recno;
key.size = sizeof(recno);
data.flags = DB_DBT_MALLOC;
if ((ret = db->get(db, NULL, &key, &data, 0)) != 0)
return (ret);
if ((ret = DbRecord_init(&key, &data, recordp)) != 0)
return (ret);
return (0);
}
/*
* DbRecord_discard --
* Discard a DbRecord structure.
*/
int
DbRecord_discard(DbRecord *recordp)
{
/* Free the allocated memory. */
free(recordp->raw);
recordp->raw = NULL;
return (0);
}
/*
* DbRecord_init --
* Fill in a DbRecord from the database key/data pair.
*/
int
DbRecord_init(const DBT *key, const DBT *data, DbRecord *recordp)
{
DbField *f;
u_int32_t skip;
void *faddr;
/* Initialize the structure (get the pre-set index values). */
*recordp = DbRecord_base;
/* Fill in the ID and version. */
memcpy(&recordp->recno, key->data, sizeof(u_int32_t));
memcpy(&recordp->version,
(u_int32_t *)data->data + 1, sizeof(u_int32_t));
/* Set up the record references. */
recordp->raw = data->data;
recordp->offset = (u_int32_t *)data->data + 1;
skip = (recordp->field_count + 2) * sizeof(u_int32_t);
recordp->record = (u_int8_t *)data->data + skip;
recordp->record_len = data->size - skip;
for (f = fieldlist; f->name != NULL; ++f) {
faddr = (u_int8_t *)recordp + f->offset;
if (DbRecord_field(
recordp, f->fieldno, faddr, f->type) != 0)
return (1);
}
return (0);
}
/*
* DbRecord_field --
* Fill in an individual field of the DbRecord.
*/
static int
DbRecord_field(
DbRecord *recordp, u_int field, void *addr, datatype type)
{
size_t len;
char number_buf[20];
/*
* The offset table is 0-based, the field numbers are 1-based.
* Correct.
*/
--field;
switch (type) {
case NOTSET:
/* NOTREACHED */
abort();
break;
case STRING:
*((u_char **)addr) = recordp->record + recordp->offset[field];
recordp->record[recordp->offset[field] +
OFFSET_LEN(recordp->offset, field)] = '\0';
break;
case DOUBLE:
case UNSIGNED_LONG:
/* This shouldn't be possible -- 2^32 is only 10 digits. */
len = OFFSET_LEN(recordp->offset, field);
if (len > sizeof(number_buf) - 1) {
dbenv->errx(dbenv,
"record %lu field %lu: numeric field is %lu bytes and too large to copy",
recordp->recno, field, (u_long)len);
return (1);
}
memcpy(number_buf,
recordp->record + recordp->offset[field], len);
number_buf[len] = '\0';
if (type == DOUBLE) {
if (len == 0)
*(double *)addr = 0;
else if (strtod_err(number_buf, (double *)addr) != 0)
goto fmt_err;
} else
if (len == 0)
*(u_long *)addr = 0;
else if (strtoul_err(number_buf, (u_long *)addr) != 0) {
fmt_err: dbenv->errx(dbenv,
"record %lu: numeric field %u error: %s",
recordp->recno, field, number_buf);
return (1);
}
break;
}
return (0);
}
/*
* DbRecord_search_field_name --
* Search, looking for a record by field name.
*/
int
DbRecord_search_field_name(char *field, char *value, OPERATOR op)
{
DbField *f;
for (f = fieldlist; f->name != NULL; ++f)
if (strcasecmp(field, f->name) == 0)
return (DbRecord_search_field(f, value, op));
/* Record numbers aren't handled as fields. */
if (strcasecmp(field, "id") == 0)
return (DbRecord_search_recno(value, op));
dbenv->errx(dbenv, "unknown field name: %s", field);
return (1);
}
/*
* DbRecord_search_field_number --
* Search, looking for a record by field number.
*/
int
DbRecord_search_field_number(u_int32_t fieldno, char *value, OPERATOR op)
{
DbField *f;
for (f = fieldlist; f->name != NULL; ++f)
if (fieldno == f->fieldno)
return (DbRecord_search_field(f, value, op));
dbenv->errx(dbenv, "field number %lu not configured", (u_long)fieldno);
return (1);
}
/*
* DbRecord_search_recno --
* Search, looking for a record by record number.
*/
static int
DbRecord_search_recno(char *value, OPERATOR op)
{
DBC *dbc;
DbRecord record;
DBT key, data;
u_int32_t recno;
u_long recno_ulong;
int ret;
/*
* XXX
* This code assumes a record number (typed as u_int32_t) is the same
* size as an unsigned long, and there's no reason to believe that.
*/
if (strtoul_err(value, &recno_ulong) != 0)
return (1);
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
key.data = &recno;
key.size = sizeof(recno);
if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0)
return (ret);
/*
* Retrieve the first record that interests us. The range depends on
* the operator:
*
* ~ error
* != beginning to end
* < beginning to first match
* <= beginning to last match
* = first match to last match
* > record after last match to end
* >= first match to end
*/
if (op == LT || op == LTEQ || op == NEQ || op == WC || op == NWC)
recno = 1;
else if (op == WC || op == NWC) {
dbenv->errx(dbenv,
"wildcard operator only supported for string fields");
return (1);
} else {
recno = recno_ulong;
if (op == GT)
++recno;
}
if ((ret = dbc->c_get(dbc, &key, &data, DB_SET)) != 0)
goto err;
for (;;) {
if ((ret = DbRecord_init(&key, &data, &record)) != 0)
break;
if (field_cmp_ulong(&record.recno, &recno_ulong, op))
DbRecord_print(&record, NULL);
else
if (op == LT || op == LTEQ || op == EQ)
break;
if ((ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) != 0)
break;
}
err: return (ret == DB_NOTFOUND ? 0 : ret);
}
/*
* DbRecord_search_field --
* Search, looking for a record by field.
*/
static int
DbRecord_search_field(DbField *f, char *value, OPERATOR op)
{
#ifdef HAVE_WILDCARD_SUPPORT
regex_t preq;
#endif
DBC *dbc;
DbRecord record;
DBT key, data, pkey;
double value_double;
u_long value_ulong;
u_int32_t cursor_flags;
int ret, t_ret;
int (*cmp)(void *, void *, OPERATOR);
void *faddr, *valuep;
dbc = NULL;
memset(&key, 0, sizeof(key));
memset(&pkey, 0, sizeof(pkey));
memset(&data, 0, sizeof(data));
/*
* Initialize the comparison function, crack the value. Wild cards
* are always strings, otherwise we follow the field type.
*/
if (op == WC || op == NWC) {
#ifdef HAVE_WILDCARD_SUPPORT
if (f->type != STRING) {
dbenv->errx(dbenv,
"wildcard operator only supported for string fields");
return (1);
}
if (regcomp(&preq, value, 0) != 0) {
dbenv->errx(dbenv, "regcomp of pattern failed");
return (1);
}
valuep = &preq;
cmp = field_cmp_re;
#else
dbenv->errx(dbenv,
"wildcard operators not supported in this build");
return (1);
#endif
} else
switch (f->type) {
case DOUBLE:
if (strtod_err(value, &value_double) != 0)
return (1);
cmp = field_cmp_double;
valuep = &value_double;
key.size = sizeof(double);
break;
case STRING:
valuep = value;
cmp = field_cmp_string;
key.size = strlen(value);
break;
case UNSIGNED_LONG:
if (strtoul_err(value, &value_ulong) != 0)
return (1);
cmp = field_cmp_ulong;
valuep = &value_ulong;
key.size = sizeof(u_long);
break;
default:
case NOTSET:
abort();
/* NOTREACHED */
}
/*
* Retrieve the first record that interests us. The range depends on
* the operator:
*
* ~ beginning to end
* != beginning to end
* < beginning to first match
* <= beginning to last match
* = first match to last match
* > record after last match to end
* >= first match to end
*
* If we have a secondary, set a cursor in the secondary, else set the
* cursor to the beginning of the primary.
*
* XXX
* If the wildcard string has a leading non-magic character we should
* be able to do a range search instead of a full-database search.
*
* Step through records to the first non-match or to the end of the
* database, depending on the operation. If the comparison function
* returns success for a key/data pair, print the pair.
*/
if (f->secondary == NULL || op == NEQ || op == WC || op == NWC) {
if ((ret = db->cursor(db, NULL, &dbc, 0)) != 0)
goto err;
while ((ret = dbc->c_get(dbc, &key, &data, DB_NEXT)) == 0) {
if ((ret = DbRecord_init(&key, &data, &record)) != 0)
break;
faddr = (u_int8_t *)&record + f->offset;
if (cmp(faddr, valuep, op))
DbRecord_print(&record, NULL);
else
if (op == EQ || op == LT || op == LTEQ)
break;
}
} else {
if ((ret =
f->secondary->cursor(f->secondary, NULL, &dbc, 0)) != 0)
goto err;
key.data = valuep;
cursor_flags = op == LT || op == LTEQ ? DB_FIRST : DB_SET_RANGE;
if ((ret =
dbc->c_pget(dbc, &key, &pkey, &data, cursor_flags)) != 0)
goto done;
if (op == GT) {
while ((ret = dbc->c_pget(
dbc, &key, &pkey, &data, DB_NEXT)) == 0) {
if ((ret =
DbRecord_init(&pkey, &data, &record)) != 0)
break;
faddr = (u_int8_t *)&record + f->offset;
if (cmp(faddr, valuep, op) != 0)
break;
}
if (ret != 0)
goto done;
}
do {
if ((ret = DbRecord_init(&pkey, &data, &record)) != 0)
break;
faddr = (u_int8_t *)&record + f->offset;
if (cmp(faddr, valuep, op))
DbRecord_print(&record, NULL);
else
if (op == EQ || op == LT || op == LTEQ)
break;
} while ((ret =
dbc->c_pget(dbc, &key, &pkey, &data, DB_NEXT)) == 0);
}
done: if (ret == DB_NOTFOUND)
ret = 0;
err: if (dbc != NULL && (t_ret = dbc->c_close(dbc)) != 0 && ret == 0)
ret = t_ret;
#ifdef HAVE_WILDCARD_SUPPORT
if (op == WC || op == NWC)
regfree(&preq);
#endif
return (ret);
}

75
examples_c/csv/Makefile Normal file
View File

@@ -0,0 +1,75 @@
# $Id: Makefile,v 1.14 2006/10/27 00:56:44 bostic Exp $
# Berkeley DB installation.
DB_INCLUDE=../../build_unix
LIBS= -L../../build_unix -L../../build_unix/.libs/ -ldb
INC= -I. -I$(DB_INCLUDE)
CFLAGS= $(INC) -g -W -Wall -Wpointer-arith -Wmissing-prototypes
PROGS= csv_code csv_load csv_query
SRCS= DbRecord.c code.c csv_local.c db.c load.c load_main.c query.c \
query_main.c util.c
all: csv_load csv_query
csv_code: code.o
$(CC) -o $@ $? $(LIBS)
LOAD_OBJS=DbRecord.o csv_local.o db.o load.o load_main.o util.o
csv_load: $(LOAD_OBJS)
$(CC) -o $@ $(LOAD_OBJS) $(LIBS)
QUERY_OBJS=DbRecord.o csv_local.o db.o query.o query_main.o util.o
csv_query: $(QUERY_OBJS)
$(CC) -o $@ $(QUERY_OBJS) $(LIBS)
clean distclean realclean:
rm -rf $(PROGS) TESTDIR eBay tags *.o *.core csv_local.[ch]
tags:
rm -f tags
ctags $(SRCS) code.c
DbRecord.o csv_local.o db.o load.o load_main.o query.o: csv_local.h csv.h
query_main.o util.o: csv_local.h csv.h
csv_local.c csv_local.h: csv_code
./csv_code -c csv_local.c -h csv_local.h -f sample.desc
lint_code:
flexelint +fll \
"-e801" \
"-e818" \
"-esym(534,fprintf)" \
"-esym(534,memcpy)" \
"-esym(534,memmove)" \
"-esym(534,memset)" \
"-esym(534,printf)" \
"-wlib(1)" \
-i$(DB_INCLUDE) "-i/usr/include" \
code.c
lint_load:
flexelint +fll \
"-e801" \
"-e818" \
"-esym(534,fprintf)" \
"-esym(534,memcpy)" \
"-esym(534,memmove)" \
"-esym(534,memset)" \
"-esym(534,printf)" \
"-wlib(1)" \
-i$(DB_INCLUDE) "-i/usr/include" \
DbRecord.c csv_local.c db.c load.c load_main.c util.c
lint_query:
flexelint +fll \
"-e801" \
"-e818" \
"-esym(534,fprintf)" \
"-esym(534,memcpy)" \
"-esym(534,memmove)" \
"-esym(534,memset)" \
"-esym(534,printf)" \
"-wlib(1)" \
-i$(DB_INCLUDE) "-i/usr/include" \
DbRecord.c csv_local.c db.c query.c query_main.c util.c

408
examples_c/csv/README Normal file
View File

@@ -0,0 +1,408 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: README,v 1.22 2008/01/08 20:58:23 bostic Exp $
*/
The "comma-separated value" (csv) directory is a suite of three programs:
csv_code: write "helper" code on which to build applications,
csv_load: import csv files into a Berkeley DB database,
csv_query: query databases created by csv_load.
The goal is to allow programmers to easily build applications for using
csv databases.
You can build the three programs, and run a sample application in this
directory.
First, there's the sample.csv file:
Adams,Bob,01/02/03,green,apple,37
Carter,Denise Ann,04/05/06,blue,banana,38
Eidel,Frank,07/08/09,red,cherry,38
Grabel,Harriet,10/11/12,purple,date,40
Indals,Jason,01/03/05,pink,orange,32
Kilt,Laura,07/09/11,yellow,grape,38
Moreno,Nancy,02/04/06,black,strawberry,38
Octon,Patrick,08/10/12,magenta,kiwi,15
The fields are:
Last name,
First name,
Birthdate,
Favorite color,
Favorite fruit,
Age
Second, there's a "description" of that csv file in sample.desc:
version 1 {
LastName string
FirstName string
BirthDate
Color string index
Fruit string index
Age unsigned_long index
}
The DESCRIPTION file maps one-to-one to the fields in the csv file, and
provides a data type for any field the application wants to use. (If
the application doesn't care about a field, don't specify a data type
and the csv code will ignore it.) The string "index" specifies there
should be a secondary index based on the field.
The "field" names in the DESCRIPTION file don't have to be the same as
the ones in the csv file (and, as they may not have embedded spaces,
probably won't be).
To build in the sample directory, on POSIX-like systems, type "make".
This first builds the program csv_code, which it then run, with the file
DESCRIPTION as an input. Running csv_code creates two additional files:
csv_local.c and csv_local.h. Those two files are then used as part of
the build process for two more programs: csv_load and csv_query.
You can load now load the csv file into a Berkeley DB database with the
following command:
% ./csv_load -h TESTDIR < sample.csv
The csv_load command will create a directory and four databases:
primary primary database
Age secondary index on Age field
Color secondary index on Color field
Fruit secondary index on Fruit field
You can then query the database:
% ./csv_query -h TESTDIR
Query: id=2
Record: 2:
LastName: Carter
FirstName: Denise
Color: blue
Fruit: banana
Age: 38
Query: color==green
Record: 1:
LastName: Adams
FirstName: Bob
Color: green
Fruit: apple
Age: 37
and so on.
The csv_code process also creates source code modules that support
building your own applications based on this database. First, there
is the local csv_local.h include file:
/*
* DO NOT EDIT: automatically built by csv_code.
*
* Record structure.
*/
typedef struct __DbRecord {
u_int32_t recno; /* Record number */
/*
* Management fields
*/
void *raw; /* Memory returned by DB */
char *record; /* Raw record */
size_t record_len; /* Raw record length */
u_int32_t field_count; /* Field count */
u_int32_t version; /* Record version */
u_int32_t *offset; /* Offset table */
/*
* Indexed fields
*/
#define CSV_INDX_LASTNAME 1
char *LastName;
#define CSV_INDX_FIRSTNAME 2
char *FirstName;
#define CSV_INDX_COLOR 4
char *Color;
#define CSV_INDX_FRUIT 5
char *Fruit;
#define CSV_INDX_AGE 6
u_long Age;
} DbRecord;
This defines the DbRecord structure that is the primary object for this
csv file. As you can see, the intersting fields in the csv file have
mappings in this structure.
Also, there are routines in the Dbrecord.c file your application can use
to handle DbRecord structures. When you retrieve a record from the
database the DbRecord structure will be filled in based on that record.
Here are the helper routines:
int
DbRecord_print(DbRecord *recordp, FILE *fp)
Display the contents of a DbRecord structure to the specified
output stream.
int
DbRecord_init(const DBT *key, DBT *data, DbRecord *recordp)
Fill in a DbRecord from a returned database key/data pair.
int
DbRecord_read(u_long key, DbRecord *recordp)
Read the specified record (DbRecord_init will be called
to fill in the DbRecord).
int
DbRecord_discard(DbRecord *recordp)
Discard the DbRecord structure (must be called after the
DbRecord_read function), when the application no longer
needs the returned DbRecord.
int
DbRecord_search_field_name(char *field, char *value, OPERATOR op)
Display the DbRecords where the field (named by field) has
the specified relationship to the value. For example:
DbRecord_search_field_name("Age", "35", GT)
would search for records with a "Age" field greater than
35.
int
DbRecord_search_field_number(
u_int32_t fieldno, char *value, OPERATOR op)
Display the DbRecords where the field (named by field)
has the specified relationship to the value. The field
number used as an argument comes from the csv_local.h
file, for example, CSV_INDX_AGE is the field index for
the "Age" field in this csv file. For example:
DbRecord_search_field_number(CSV_INDX_AGE, 35, GT)
would search for records with a "Age" field greater than
35.
Currently, the csv code only supports three types of data:
strings, unsigned longs and doubles. Others can easily be
added.
The usage of the csv_code program is as follows:
usage: csv_code [-v] [-c source-file] [-f input] [-h header-file]
-c output C source code file
-h output C header file
-f input file
-v verbose (defaults to off)
-c A file to which to write the C language code. By default,
the file "csv_local.c" is used.
-f A file to read for a description of the fields in the
csv file. By default, csv_code reads from stdin.
-h A file to which to write the C language header structures.
By default, the file "csv_local.h" is used.
-v The -v verbose flag outputs potentially useful debugging
information.
There are two applications built on top of the code produced by
csv_code, csv_load and csv_query.
The usage of the csv_load program is as follows:
usage: csv_load [-v] [-F format] [-f csv-file] [-h home] [-V version]
-F format (currently supports "excel")
-f input file
-h database environment home directory
-v verbose (defaults to off)
-F See "Input format" below.
-f If an input file is specified using the -f flag, the file
is read and the records in the file are stored into the
database. By default, csv_load reads from stdin.
-h If a database environment home directory is specified
using the -h flag, that directory is used as the
Berkeley DB directory. The default for -h is the
current working directory or the value of the DB_HOME
environment variable.
-V Specify a version number for the input (the default is 1).
-v The -v verbose flag outputs potentially useful debugging
information. It can be specified twice for additional
information.
The usage of csv_query program is as follows:
usage: csv_query [-v] [-c cmd] [-h home]
-c A command to run, otherwise csv_query will enter
interactive mode and prompt for user input.
-h If a database environment home directory is specified
using the -h flag, that directory is used as the
Berkeley DB directory. The default for -h is the
current working directory or the value of the DB_HOME
environment variable.
-v The -v verbose flag outputs potentially useful debugging
information. It can be specified twice for additional
information.
The query program currently supports the following commands:
? Display help screen
exit Exit program
fields Display list of field names
help Display help screen
quit Exit program
version Display database format version
field[op]value Display fields by value (=, !=, <, <=, >, >=, ~, !~)
The "field[op]value" command allows you to specify a field and a
relationship to a value. For example, you could run the query:
csv_query -c "price < 5"
to list all of the records with a "price" field less than "5".
Field names and all string comparisons are case-insensitive.
The operators ~ and !~ do match/no-match based on the IEEE Std 1003.2
(POSIX.2) Basic Regular Expression standard.
As a special case, every database has the field "Id", which matches the
record number of the primary key.
Input format:
The input to the csv_load utility is a text file, containing
lines of comma-separated fields.
Blank lines are ignored. All non-blank lines must be comma-separated
lists of fields.
By default:
<nul> (\000) bytes and unprintable characters are stripped,
input lines are <nl> (\012) separated,
commas cannot be escaped.
If "-F excel" is specified:
<nul> (\000) bytes and unprintable characters are stripped,
input lines are <cr> (\015) separated,
<nl> bytes (\012) characters are stripped from the input,
commas surrounded by double-quote character (") are not
treated as field separators.
Storage format:
Records in the primary database are stored with a 32-bit unsigned
record number as the key.
Key/Data pair 0 is of the format:
[version] 32-bit unsigned int
[field count] 32-bit unsigned int
[raw record] byte array
For example:
[1]
[5]
[field1,field2,field3,field4,field5]
All other Key/Data pairs are of the format:
[version] 32-bit unsigned int
[offset to field 1] 32-bit unsigned int
[offset to field 2] 32-bit unsigned int
[offset to field 3] 32-bit unsigned int
... 32-bit unsigned int
[offset to field N] 32-bit unsigned int
[offset past field N] 32-bit unsigned int
[raw record] byte array
For example:
[1]
[0]
[2]
[5]
[9]
[14]
[19]
[a,ab,abc,abcd,abcde]
012345678901234567890 << byte offsets
0 1 2
So, field 3 of the data can be directly accessed by using
the "offset to field 3", and the length of the field is
the "((offset to field 4) - (offset to field 3)) - 1".
Limits:
The csv program stores the primary key in a 32-bit unsigned
value, limiting the number of records in the database. New
records are inserted after the last existing record, that is,
new records are not inserted into gaps left by any deleted
records. This will limit the total number of records stored in
any database.
Versioning:
Versioning is when a database supports multiple versions of the
records. This is likely to be necessary when dealing with large
applications and databases, as record fields change over time.
The csv application suite does not currently support versions,
although all of the necessary hooks are there.
The way versioning will work is as follows:
The XXX.desc file needs to support multiple version layouts.
The generated C language structure defined should be a superset
of all of the interesting fields from all of the version
layouts, regardless of which versions of the csv records those
fields exist in.
When the csv layer is asked for a record, the record's version
will provide a lookup into a separate database of field lists.
That is, there will be another database which has key/data pairs
where the key is a version number, and the data is the field
list. At that point, it's relatively easy to map the fields
to the structure as is currently done, except that some of the
fields may not be filled in.
To determine if a field is filled in, in the structure, the
application has to have an out-of-band value to put in that
field during DbRecord initialization. If that's a problem, the
alternative would be to add an additional field for each listed
field -- if the additional field is set to 1, the listed field
has been filled in, otherwise it hasn't. The csv code will
support the notion of required fields, so in most cases the
application won't need to check before simply using the field,
it's only if a field isn't required and may be filled in that
the check will be necessary.
TODO:
Csv databases are not portable between machines of different
byte orders. To make them portable, all of the 32-bit unsigned
int fields currently written into the database should be
converted to a standard byte order. This would include the
version number and field count in the column-map record, and the
version and field offsets in the other records.
Add Extended RE string matches.
Add APIs to replace the reading of a schema file, allow users to
fill in a DbRecord structure and do a put on it. (Hard problem:
how to flag fields that aren't filled in.)
Add a second sample file, and write the actual versioning code.

405
examples_c/csv/code.c Normal file
View File

@@ -0,0 +1,405 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: code.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include "csv.h"
typedef struct {
char *name; /* Field name */
char *upper; /* Field name in upper-case */
datatype type; /* Data type */
int indx; /* Index */
} FIELD;
int code_source(void);
int code_header(void);
int desc_dump(void);
int desc_load(void);
char *type_to_string(datatype);
int usage(void);
/*
* Globals
*/
FILE *cfp; /* C source file */
FILE *hfp; /* C source file */
char *progname; /* Program name */
int verbose; /* Verbose flag */
u_int field_cnt; /* Count of fields */
FIELD *fields; /* Field list */
int
main(int argc, char *argv[])
{
int ch;
char *cfile, *hfile;
/* Initialize globals. */
if ((progname = strrchr(argv[0], '/')) == NULL)
progname = argv[0];
else
++progname;
/* Initialize arguments. */
cfile = "csv_local.c"; /* Default header/source files */
hfile = "csv_local.h";
/* Process arguments. */
while ((ch = getopt(argc, argv, "c:f:h:v")) != EOF)
switch (ch) {
case 'c':
cfile = optarg;
break;
case 'f':
if (freopen(optarg, "r", stdin) == NULL) {
fprintf(stderr,
"%s: %s\n", optarg, strerror(errno));
return (EXIT_FAILURE);
}
break;
case 'h':
hfile = optarg;
break;
case 'v':
++verbose;
break;
case '?':
default:
return (usage());
}
argc -= optind;
argv += optind;
if (*argv != NULL)
return (usage());
/* Load records from the input file. */
if (desc_load())
return (EXIT_FAILURE);
/* Dump records for debugging. */
if (verbose && desc_dump())
return (EXIT_FAILURE);
/* Open output files. */
if ((cfp = fopen(cfile, "w")) == NULL) {
fprintf(stderr,
"%s: %s: %s\n", progname, cfile, strerror(errno));
return (EXIT_FAILURE);
}
if ((hfp = fopen(hfile, "w")) == NULL) {
fprintf(stderr,
"%s: %s: %s\n", progname, hfile, strerror(errno));
return (EXIT_FAILURE);
}
/* Build the source and header files. */
if (code_header())
return (EXIT_FAILURE);
if (code_source())
return (EXIT_FAILURE);
return (EXIT_SUCCESS);
}
/*
* desc_load --
* Load a description file.
*/
int
desc_load()
{
u_int field_alloc;
int version;
char *p, *t, save_ch, buf[256];
field_alloc = version = 0;
while (fgets(buf, sizeof(buf), stdin) != NULL) {
if ((p = strchr(buf, '\n')) == NULL) {
fprintf(stderr, "%s: input line too long\n", progname);
return (1);
}
*p = '\0';
/* Skip leading whitespace. */
for (p = buf; isspace(*p); ++p)
;
/* Skip empty lines or lines beginning with '#'. */
if (*p == '\0' || *p == '#')
continue;
/* Get a version. */
if (!version) {
if (strncasecmp(
p, "version", sizeof("version") - 1) == 0) {
version = 1;
continue;
}
fprintf(stderr,
"%s: expected \"version\" line\n", progname);
return (1);
}
/*
* Skip block close -- not currently useful, but when this
* code supports versioned descriptions, it will matter.
*/
if (*p == '}') {
version = 0;
continue;
}
/* Allocate a new field structure as necessary. */
if (field_cnt == field_alloc &&
(fields = realloc(fields, field_alloc += 100)) == NULL) {
fprintf(stderr, "%s: %s\n", progname, strerror(errno));
return (1);
}
/* Find the end of the field name. */
for (t = p; *t != '\0' && !isspace(*t); ++t)
;
save_ch = *t;
*t = '\0';
if ((fields[field_cnt].name = strdup(p)) == NULL ||
(fields[field_cnt].upper = strdup(p)) == NULL) {
fprintf(stderr, "%s: %s\n", progname, strerror(errno));
return (1);
}
*t = save_ch;
p = t;
fields[field_cnt].indx = 0;
fields[field_cnt].type = NOTSET;
for (;;) {
/* Skip to the next field, if any. */
for (; *p != '\0' && isspace(*p); ++p)
;
if (*p == '\0')
break;
/* Find the end of the field. */
for (t = p; *t != '\0' && !isspace(*t); ++t)
;
save_ch = *t;
*t = '\0';
if (strcasecmp(p, "double") == 0)
fields[field_cnt].type = DOUBLE;
else if (strcasecmp(p, "index") == 0)
fields[field_cnt].indx = 1;
else if (strcasecmp(p, "string") == 0)
fields[field_cnt].type = STRING;
else if (strcasecmp(p, "unsigned_long") == 0)
fields[field_cnt].type = UNSIGNED_LONG;
else {
fprintf(stderr,
"%s: unknown keyword: %s\n", progname, p);
return (1);
}
*t = save_ch;
p = t;
}
/* Create a copy of the field name that's upper-case. */
for (p = fields[field_cnt].upper; *p != '\0'; ++p)
if (islower(*p))
*p = (char)toupper(*p);
++field_cnt;
}
if (ferror(stdin)) {
fprintf(stderr, "%s: stdin: %s\n", progname, strerror(errno));
return (1);
}
return (0);
}
/*
* desc_dump --
* Dump a set of FIELD structures.
*/
int
desc_dump()
{
FIELD *f;
u_int i;
for (f = fields, i = 0; i < field_cnt; ++i, ++f) {
fprintf(stderr, "field {%s}: (", f->name);
switch (f->type) {
case NOTSET:
fprintf(stderr, "ignored");
break;
case DOUBLE:
fprintf(stderr, "double");
break;
case STRING:
fprintf(stderr, "string");
break;
case UNSIGNED_LONG:
fprintf(stderr, "unsigned_long");
break;
}
if (f->indx)
fprintf(stderr, ", indexed");
fprintf(stderr, ")\n");
}
return (0);
}
/*
* code_header --
* Print out the C #include file.
*/
int
code_header()
{
FIELD *f;
u_int i;
fprintf(hfp, "/*\n");
fprintf(hfp, " * DO NOT EDIT: automatically built by %s.\n", progname);
fprintf(hfp, " *\n");
fprintf(hfp, " * Record structure.\n");
fprintf(hfp, " */\n");
fprintf(hfp, "typedef struct __DbRecord {\n");
fprintf(hfp, "\tu_int32_t\t recno;\t\t/* Record number */\n");
fprintf(hfp, "\n");
fprintf(hfp, "\t/*\n");
fprintf(hfp, "\t * Management fields\n");
fprintf(hfp, "\t */\n");
fprintf(hfp, "\tvoid\t\t*raw;\t\t/* Memory returned by DB */\n");
fprintf(hfp, "\tu_char\t\t*record;\t/* Raw record */\n");
fprintf(hfp, "\tsize_t\t\t record_len;\t/* Raw record length */\n\n");
fprintf(hfp, "\tu_int32_t\t field_count;\t/* Field count */\n");
fprintf(hfp, "\tu_int32_t\t version;\t/* Record version */\n\n");
fprintf(hfp, "\tu_int32_t\t*offset;\t/* Offset table */\n");
fprintf(hfp, "\n");
fprintf(hfp, "\t/*\n");
fprintf(hfp, "\t * Indexed fields\n");
fprintf(hfp, "\t */\n");
for (f = fields, i = 0; i < field_cnt; ++i, ++f) {
if (f->type == NOTSET)
continue;
if (i != 0)
fprintf(hfp, "\n");
fprintf(hfp, "#define CSV_INDX_%s\t%d\n", f->upper, i + 1);
switch (f->type) {
case NOTSET:
/* NOTREACHED */
abort();
break;
case DOUBLE:
fprintf(hfp, "\tdouble\t\t %s;\n", f->name);
break;
case STRING:
fprintf(hfp, "\tchar\t\t*%s;\n", f->name);
break;
case UNSIGNED_LONG:
fprintf(hfp, "\tu_long\t\t %s;\n", f->name);
break;
}
}
fprintf(hfp, "} DbRecord;\n");
return (0);
}
/*
* code_source --
* Print out the C structure initialization.
*/
int
code_source()
{
FIELD *f;
u_int i;
fprintf(cfp, "/*\n");
fprintf(cfp,
" * DO NOT EDIT: automatically built by %s.\n", progname);
fprintf(cfp, " *\n");
fprintf(cfp, " * Initialized record structure.\n");
fprintf(cfp, " */\n");
fprintf(cfp, "\n");
fprintf(cfp, "#include \"csv.h\"\n");
fprintf(cfp, "#include \"csv_local.h\"\n");
fprintf(cfp, "\n");
fprintf(cfp, "DbRecord DbRecord_base = {\n");
fprintf(cfp, "\t0,\t\t/* Record number */\n");
fprintf(cfp, "\tNULL,\t\t/* Memory returned by DB */\n");
fprintf(cfp, "\tNULL,\t\t/* Raw record */\n");
fprintf(cfp, "\t0,\t\t/* Raw record length */\n");
fprintf(cfp, "\t%d,\t\t/* Field count */\n", field_cnt);
fprintf(cfp, "\t0,\t\t/* Record version */\n");
fprintf(cfp, "\tNULL,\t\t/* Offset table */\n");
fprintf(cfp, "\n");
for (f = fields, i = 0; i < field_cnt; ++i, ++f) {
if (f->type == NOTSET)
continue;
switch (f->type) {
case NOTSET:
abort();
/* NOTREACHED */
break;
case DOUBLE:
case UNSIGNED_LONG:
fprintf(cfp, "\t0,\t\t/* %s */\n", f->name);
break;
case STRING:
fprintf(cfp, "\tNULL,\t\t/* %s */\n", f->name);
break;
}
}
fprintf(cfp, "};\n");
fprintf(cfp, "\n");
fprintf(cfp, "DbField fieldlist[] = {\n");
for (f = fields, i = 0; i < field_cnt; ++i, ++f) {
if (f->type == NOTSET)
continue;
fprintf(cfp, "\t{ \"%s\",", f->name);
fprintf(cfp, " CSV_INDX_%s,", f->upper);
fprintf(cfp, "\n\t %s,", type_to_string(f->type));
fprintf(cfp, " %d,", f->indx ? 1 : 0);
fprintf(cfp, " NULL,");
fprintf(cfp, " FIELD_OFFSET(%s)},\n", f->name);
}
fprintf(cfp, "\t{NULL, 0, STRING, 0, NULL, 0}\n};\n");
return (0);
}
char *
type_to_string(type)
datatype type;
{
switch (type) {
case NOTSET:
return ("NOTSET");
case DOUBLE:
return ("DOUBLE");
case STRING:
return ("STRING");
case UNSIGNED_LONG:
return ("UNSIGNED_LONG");
}
abort();
/* NOTREACHED */
}
int
usage()
{
(void)fprintf(stderr,
"usage: %s [-v] [-c source-file] [-f input] [-h header-file]\n",
progname);
exit(1);
}

101
examples_c/csv/csv.h Normal file
View File

@@ -0,0 +1,101 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: csv.h 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN 1
#include <direct.h>
#include <db.h>
extern int getopt(int, char * const *, const char *);
extern char *optarg;
extern int optind;
#else
#define HAVE_WILDCARD_SUPPORT 1
#include <regex.h>
#include <unistd.h>
#endif
#include "db.h"
/*
* MAP_VERSION
* This code has hooks for versioning, but does not directly support it.
* See the README file for details.
*/
#define MAP_VERSION 1
/*
* Supported formats.
*
* FORMAT_NL: <nl> separated
* FORMAT_EXCEL: Excel dumped flat text.
*/
typedef enum { FORMAT_EXCEL, FORMAT_NL } input_fmt;
/*
* OFFSET_LEN
* The length of any item can be calculated from the two offset fields.
* OFFSET_OOB
* An offset that's illegal, used to detect unavailable fields.
*/
#define OFFSET_LEN(offset, indx) \
(((offset)[(indx) + 1] - (offset)[(indx)]) - 1)
#define OFFSET_OOB 0
/*
* Field comparison operators.
*/
typedef enum { EQ=1, NEQ, GT, GTEQ, LT, LTEQ, WC, NWC } OPERATOR;
/*
* Supported data types.
*/
typedef enum { NOTSET=1, DOUBLE, STRING, UNSIGNED_LONG } datatype;
/*
* C structure that describes the csv fields.
*/
typedef struct {
char *name; /* Field name */
u_int32_t fieldno; /* Field index */
datatype type; /* Data type */
int indx; /* Indexed */
DB *secondary; /* Secondary index handle */
#define FIELD_OFFSET(field) ((size_t)(&(((DbRecord *)0)->field)))
size_t offset; /* DbRecord field offset */
} DbField;
/*
* Globals
*/
extern DB *db; /* Primary database */
extern DbField fieldlist[]; /* Field list */
extern DB_ENV *dbenv; /* Database environment */
extern char *progname; /* Program name */
extern int verbose; /* Program verbosity */
#ifdef _WIN32
#undef strcasecmp
#define strcasecmp _stricmp
#undef strncasecmp
#define strncasecmp _strnicmp
#define mkdir(d, perm) _mkdir(d)
#endif

View File

@@ -0,0 +1,37 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: csv_extern.h 63573 2008-05-23 21:43:21Z trent.nelson $
*/
extern DbRecord DbRecord_base; /* Initialized structure. */
/*
* Prototypes
*/
extern int DbRecord_discard(DbRecord *);
extern int DbRecord_init(const DBT *, const DBT *, DbRecord *);
extern void DbRecord_print(DbRecord *, FILE *);
extern int DbRecord_read(u_long, DbRecord *);
extern int DbRecord_search_field_name(char *, char *, OPERATOR);
extern int DbRecord_search_field_number(u_int, char *, OPERATOR);
extern int compare_double(DB *, const DBT *, const DBT *);
extern int compare_string(DB *, const DBT *, const DBT *);
extern int compare_ulong(DB *, const DBT *, const DBT *);
extern int csv_env_close(void);
extern int csv_env_open(const char *, int);
extern int csv_secondary_close(void);
extern int csv_secondary_open(void);
extern int entry_print(void *, size_t, u_int32_t);
extern int field_cmp_double(void *, void *, OPERATOR);
extern int field_cmp_re(void *, void *, OPERATOR);
extern int field_cmp_string(void *, void *, OPERATOR);
extern int field_cmp_ulong(void *, void *, OPERATOR);
extern int input_load(input_fmt, u_long);
extern int query(char *, int *);
extern int query_interactive(void);
extern int secondary_callback(DB *, const DBT *, const DBT *, DBT *);
extern int strtod_err(char *, double *);
extern int strtoul_err(char *, u_long *);

244
examples_c/csv/db.c Normal file
View File

@@ -0,0 +1,244 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: db.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include "csv.h"
#include "csv_local.h"
#include "csv_extern.h"
static int compare_uint32(DB *, const DBT *, const DBT *);
/*
* csv_env_init --
* Initialize the database environment.
*/
int
csv_env_open(const char *home, int is_rdonly)
{
int ret;
dbenv = NULL;
db = NULL;
/* Create a database environment handle. */
if ((ret = db_env_create(&dbenv, 0)) != 0) {
fprintf(stderr,
"%s: db_env_create: %s\n", progname, db_strerror(ret));
return (1);
}
/*
* Configure Berkeley DB error reporting to stderr, with our program
* name as the prefix.
*/
dbenv->set_errfile(dbenv, stderr);
dbenv->set_errpfx(dbenv, progname);
/*
* The default Berkeley DB cache size is fairly small; configure a
* 1MB cache for now. This value will require tuning in the future.
*/
if ((ret = dbenv->set_cachesize(dbenv, 0, 1048576, 1)) != 0) {
dbenv->err(dbenv, ret, "DB_ENV->set_cachesize");
return (1);
}
/*
* We may be working with an existing environment -- try and join it.
* If that fails, create a new database environment; for now, we only
* need a cache, no logging, locking, or transactions.
*/
if ((ret = dbenv->open(dbenv, home,
DB_JOINENV | DB_USE_ENVIRON, 0)) != 0 &&
(ret = dbenv->open(dbenv, home,
DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE | DB_USE_ENVIRON, 0)) != 0) {
dbenv->err(dbenv, ret, "DB_ENV->open");
return (1);
}
/* Create the primary database handle. */
if ((ret = db_create(&db, dbenv, 0)) != 0) {
dbenv->err(dbenv, ret, "db_create");
return (1);
}
/*
* Records may be relatively large -- use a large page size.
*/
if ((ret = db->set_pagesize(db, 32 * 1024)) != 0) {
dbenv->err(dbenv, ret, "DB->set_pagesize");
return (1);
}
/*
* The primary database uses an integer as its key; on little-endian
* machines, integers sort badly using the default Berkeley DB sort
* function (which is lexicographic). Specify a comparison function
* for the database.
*/
if ((ret = db->set_bt_compare(db, compare_uint32)) != 0) {
dbenv->err(dbenv, ret, "DB->set_bt_compare");
return (1);
}
/* Open the primary database. */
if ((ret = db->open(db, NULL,
"primary", NULL, DB_BTREE, is_rdonly ? 0 : DB_CREATE, 0)) != 0) {
dbenv->err(dbenv, ret, "DB->open: primary");
return (1);
}
/* Open the secondaries. */
if ((ret = csv_secondary_open()) != 0)
return (1);
return (0);
}
/*
* csv_env_close --
* Discard the database environment.
*/
int
csv_env_close()
{
int ret, t_ret;
ret = 0;
/* Close the secondaries. */
ret = csv_secondary_close();
/* Close the primary handle. */
if (db != NULL && (t_ret = db->close(db, 0)) != 0) {
dbenv->err(dbenv, ret, "DB->close");
if (ret == 0)
ret = t_ret;
}
if ((t_ret = dbenv->close(dbenv, 0)) != 0) {
fprintf(stderr,
"%s: DB_ENV->close: %s\n", progname, db_strerror(ret));
if (ret == 0)
ret = t_ret;
}
return (ret);
}
/*
* csv_secondary_open --
* Open any secondary indices.
*/
int
csv_secondary_open()
{
DB *sdb;
DbField *f;
int ret, (*fcmp)(DB *, const DBT *, const DBT *);
/*
* Create secondary database handles.
*/
for (f = fieldlist; f->name != NULL; ++f) {
if (f->indx == 0)
continue;
if ((ret = db_create(&sdb, dbenv, 0)) != 0) {
dbenv->err(dbenv, ret, "db_create");
return (1);
}
sdb->app_private = f;
/* Keys are small, use a relatively small page size. */
if ((ret = sdb->set_pagesize(sdb, 8 * 1024)) != 0) {
dbenv->err(dbenv, ret, "DB->set_pagesize");
return (1);
}
/*
* Sort the database based on the underlying type. Skip
* strings, Berkeley DB defaults to lexicographic sort.
*/
switch (f->type) {
case DOUBLE:
fcmp = compare_double;
break;
case UNSIGNED_LONG:
fcmp = compare_ulong;
break;
case NOTSET:
case STRING:
default:
fcmp = NULL;
break;
}
if (fcmp != NULL &&
(ret = sdb->set_bt_compare(sdb, fcmp)) != 0) {
dbenv->err(dbenv, ret, "DB->set_bt_compare");
return (1);
}
/* Always configure secondaries for sorted duplicates. */
if ((ret = sdb->set_flags(sdb, DB_DUPSORT)) != 0) {
dbenv->err(dbenv, ret, "DB->set_flags");
return (1);
}
if ((ret = sdb->set_dup_compare(sdb, compare_ulong)) != 0) {
dbenv->err(dbenv, ret, "DB->set_dup_compare");
return (1);
}
if ((ret = sdb->open(
sdb, NULL, f->name, NULL, DB_BTREE, DB_CREATE, 0)) != 0) {
dbenv->err(dbenv, ret, "DB->open: %s", f->name);
return (1);
}
if ((ret = sdb->associate(
db, NULL, sdb, secondary_callback, DB_CREATE)) != 0) {
dbenv->err(dbenv, ret, "DB->set_associate");
return (1);
}
f->secondary = sdb;
}
return (0);
}
/*
* csv_secondary_close --
* Close any secondary indices.
*/
int
csv_secondary_close()
{
DbField *f;
int ret, t_ret;
ret = 0;
for (f = fieldlist; f->name != NULL; ++f)
if (f->secondary != NULL && (t_ret =
f->secondary->close(f->secondary, 0)) != 0 && ret == 0)
ret = t_ret;
return (ret);
}
/*
* compare_uint32 --
* Compare two keys.
*/
static int
compare_uint32(DB *db_arg, const DBT *a_arg, const DBT *b_arg)
{
u_int32_t a, b;
db_arg = db_arg; /* Quiet compiler. */
memcpy(&a, a_arg->data, sizeof(u_int32_t));
memcpy(&b, b_arg->data, sizeof(u_int32_t));
return (a > b ? 1 : ((a < b) ? -1 : 0));
}

346
examples_c/csv/load.c Normal file
View File

@@ -0,0 +1,346 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: load.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include "csv.h"
#include "csv_local.h"
#include "csv_extern.h"
typedef enum { GL_OK, GL_EOF, GL_FAIL } getline_status;
static int input_field_count(const char *, size_t, u_int32_t *);
static getline_status
input_getline(char **, size_t *, size_t *);
static int input_put_alloc(u_int32_t **, size_t *, size_t, u_int32_t);
static int input_set_offset(u_int32_t *, char *, size_t, u_int32_t);
static input_fmt ifmt; /* Input format. */
static u_long record_count = 0; /* Input record count for errors. */
static u_long version; /* Version we're loading. */
/*
* input_load --
* Read the input file and load new records into the database.
*/
int
input_load(input_fmt ifmt_arg, u_long version_arg)
{
getline_status gtl_status;
DBT key, data;
DBC *cursor;
u_int32_t field_count, primary_key, *put_line;
size_t input_len, len, put_len;
int is_first, ret;
char *input_line;
field_count = 0; /* Shut the compiler up. */
/* ifmt and version are global to this file. */
ifmt = ifmt_arg;
version = version_arg;
/*
* The primary key for the database is a unique number. Find out the
* last unique number allocated in this database by opening a cursor
* and fetching the last record.
*/
if ((ret = db->cursor(db, NULL, &cursor, 0)) != 0) {
dbenv->err(dbenv, ret, "DB->cursor");
return (1);
}
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
if ((ret = cursor->c_get(cursor, &key, &data, DB_LAST)) != 0)
if (ret == DB_NOTFOUND)
primary_key = 0;
else {
dbenv->err(dbenv, ret, "DB->cursor: DB_LAST");
return (1);
}
else
memcpy(&primary_key, key.data, sizeof(primary_key));
if ((ret = cursor->c_close(cursor)) != 0) {
dbenv->err(dbenv, ret, "DBC->close");
return (1);
}
if (verbose)
dbenv->errx(dbenv,
"maximum existing record in the database is %lu",
(u_long)primary_key);
key.data = &primary_key;
key.size = sizeof(primary_key);
input_line = NULL;
put_line = NULL;
input_len = put_len = 0;
/*
* See the README file for a description of the file input format.
*/
for (is_first = 1; (gtl_status =
input_getline(&input_line, &input_len, &len)) == GL_OK;) {
++record_count;
if (verbose > 1)
dbenv->errx(dbenv, "reading %lu", (u_long)record_count);
/* The first non-blank line of the input is a column map. */
if (is_first) {
is_first = 0;
/* Count the fields we're expecting in the input. */
if (input_field_count(
input_line, len, &field_count) != 0)
return (1);
}
/* Allocate room for the table of offsets. */
if (input_put_alloc(
&put_line, &put_len, len, field_count) != 0)
return (1);
/*
* Build the offset table and create the record we're
* going to store.
*/
if (input_set_offset(put_line,
input_line, len, field_count) != 0)
return (1);
++primary_key;
memcpy(put_line + (field_count + 2), input_line, len);
data.data = put_line;
data.size = (field_count + 2) * sizeof(u_int32_t) + len;
if (verbose > 1)
(void)entry_print(
data.data, data.size, field_count);
/* Load the key/data pair into the database. */
if ((ret = db->put(db, NULL, &key, &data, 0)) != 0) {
dbenv->err(dbenv, ret,
"DB->put: %lu", (u_long)primary_key);
return (1);
}
}
if (gtl_status != GL_EOF)
return (1);
if (verbose)
dbenv->errx(dbenv,
"%lu records read from the input file into the database",
record_count);
/*
* This program isn't transactional, limit the window for corruption.
*/
if ((ret = db->sync(db, 0)) != 0) {
dbenv->err(dbenv, ret, "DB->sync");
return (1);
}
return (0);
}
/*
* input_getline --
* Read in a line of input into a buffer.
*/
static getline_status
input_getline(char **input_linep, size_t *input_lenp, size_t *lenp)
{
size_t input_len, len;
int ch;
char *input_line, *p, *endp;
input_line = *input_linep;
input_len = *input_lenp;
p = input_line;
endp = input_line + input_len;
for (len = 0; (ch = getchar()) != EOF;) {
if (ch == '\0') /* Strip <nul> (\000) bytes. */
continue;
switch (ifmt) {
case FORMAT_NL:
if (ch == '\n')
goto end;
break;
case FORMAT_EXCEL:
/* Strip <nl> (\012) bytes. */
if (ch == '\n')
continue;
/*
* <cr> (\015) bytes terminate lines.
* Skip blank lines.
*/
if (ch == '\015') {
if (len == 0)
continue;
goto end;
}
}
if (input_line == endp) {
input_len += 256;
input_len *= 2;
if ((input_line =
realloc(input_line, input_len)) == NULL) {
dbenv->err(dbenv, errno,
"unable to allocate %lu bytes for record",
(u_long)input_len);
return (GL_FAIL);
}
p = input_line;
endp = p + input_len;
}
if (isprint(ch)) { /* Strip unprintables. */
*p++ = (char)ch;
++len;
}
}
end: if (len == 0)
return (GL_EOF);
*lenp = len;
*input_linep = input_line;
*input_lenp = input_len;
return (GL_OK);
}
/*
* input_field_count --
* Count the fields in the line.
*/
static int
input_field_count(const char *line, size_t len, u_int32_t *field_countp)
{
u_int32_t field_count;
int quoted;
field_count = 1;
/*
* There are N-1 separators for N fields, that is, "a,b,c" is three
* fields, with two comma separators.
*/
switch (ifmt) {
case FORMAT_EXCEL:
quoted = 0;
for (field_count = 1; len > 0; ++line, --len)
if (*line == '"')
quoted = !quoted;
else if (*line == ',' && !quoted)
++field_count;
break;
case FORMAT_NL:
for (field_count = 1; len > 0; ++line, --len)
if (*line == ',')
++field_count;
break;
}
*field_countp = field_count;
if (verbose)
dbenv->errx(dbenv,
"input file made up of %lu fields", (u_int)field_count);
return (0);
}
/*
* input_put_alloc --
* Allocate room for the offset table plus the input.
*/
static int
input_put_alloc(u_int32_t **put_linep,
size_t *put_lenp, size_t len, u_int32_t field_count)
{
size_t total;
total = (field_count + 2) * sizeof(u_int32_t) + len;
if (total > *put_lenp &&
(*put_linep = realloc(*put_linep, *put_lenp += total)) == NULL) {
dbenv->err(dbenv, errno,
"unable to allocate %lu bytes for record",
(u_long)*put_lenp);
return (1);
}
return (0);
}
/*
* input_set_offset --
* Build an offset table and record combination.
*/
static int
input_set_offset(u_int32_t *put_line,
char *input_line, size_t len, u_int32_t field_count)
{
u_int32_t *op;
int quoted;
char *p, *endp;
op = put_line;
/* The first field is the version number. */
*op++ = version;
/*
* Walk the input line, looking for comma separators. It's an error
* to have too many or too few fields.
*/
*op++ = 0;
quoted = 0;
for (p = input_line, endp = input_line + len;; ++p) {
if (ifmt == FORMAT_EXCEL && p < endp) {
if (*p == '"')
quoted = !quoted;
if (quoted)
continue;
}
if (*p == ',' || p == endp) {
if (field_count == 0) {
dbenv->errx(dbenv,
"record %lu: too many fields in the record",
record_count);
return (1);
}
--field_count;
*op++ = (u_int32_t)(p - input_line) + 1;
if (verbose > 1)
dbenv->errx(dbenv,
"offset %lu: {%.*s}", op[-1],
OFFSET_LEN(op, -2), input_line + op[-2]);
/*
* Don't insert a new field if the input lines ends
* in a comma.
*/
if (p == endp || p + 1 == endp)
break;
}
}
*op++ = (u_int32_t)(p - input_line);
if (field_count != 0) {
dbenv->errx(dbenv,
"record %lu: not enough fields in the record",
record_count);
return (1);
}
memcpy(op, input_line, len);
return (0);
}

117
examples_c/csv/load_main.c Normal file
View File

@@ -0,0 +1,117 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: load_main.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include "csv.h"
#include "csv_local.h"
#include "csv_extern.h"
static int usage(void);
/*
* Globals
*/
DB_ENV *dbenv; /* Database environment */
DB *db; /* Primary database */
DB **secondary; /* Secondaries */
int verbose; /* Program verbosity */
char *progname; /* Program name */
int
main(int argc, char *argv[])
{
input_fmt ifmt;
u_long version;
int ch, ret, t_ret;
char *home;
/* Initialize globals. */
dbenv = NULL;
db = NULL;
if ((progname = strrchr(argv[0], '/')) == NULL)
progname = argv[0];
else
++progname;
verbose = 0;
/* Initialize arguments. */
home = NULL;
ifmt = FORMAT_NL;
version = 1;
/* Process arguments. */
while ((ch = getopt(argc, argv, "F:f:h:V:v")) != EOF)
switch (ch) {
case 'f':
if (freopen(optarg, "r", stdin) == NULL) {
fprintf(stderr,
"%s: %s\n", optarg, db_strerror(errno));
return (EXIT_FAILURE);
}
break;
case 'F':
if (strcasecmp(optarg, "excel") == 0) {
ifmt = FORMAT_EXCEL;
break;
}
return (usage());
case 'h':
home = optarg;
break;
case 'V':
if (strtoul_err(optarg, &version))
return (EXIT_FAILURE);
break;
case 'v':
++verbose;
break;
case '?':
default:
return (usage());
}
argc -= optind;
argv += optind;
if (*argv != NULL)
return (usage());
/*
* The home directory may not exist -- try and create it. We don't
* bother to distinguish between failure to create it and it already
* existing, as the database environment open will fail if we aren't
* successful.
*/
if (home == NULL)
home = getenv("DB_HOME");
if (home != NULL)
(void)mkdir(home, S_IRWXU);
/* Create or join the database environment. */
if (csv_env_open(home, 0) != 0)
return (EXIT_FAILURE);
/* Load records into the database. */
ret = input_load(ifmt, version);
/* Close the database environment. */
if ((t_ret = csv_env_close()) != 0 && ret == 0)
ret = t_ret;
return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
/*
* usage --
* Program usage message.
*/
static int
usage(void)
{
(void)fprintf(stderr,
"usage: %s [-v] [-F excel] [-f csv-file] [-h home]\n", progname);
return (EXIT_FAILURE);
}

241
examples_c/csv/query.c Normal file
View File

@@ -0,0 +1,241 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: query.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include "csv.h"
#include "csv_local.h"
#include "csv_extern.h"
static int query_by_field(char *);
static int query_fieldlist(char *);
static int query_help(char *);
static int query_usage(void);
typedef struct _cmdtab {
char *cmd; /* Command name */
int (*f)(char *); /* Underlying function. */
char *help; /* Help message. */
} CMDTAB;
static CMDTAB cmdtab[] = {
{ "?",
query_help,
"?\t\tDisplay help screen" },
{ "exit",
NULL,
"exit\t\tExit program" },
{ "fields",
query_fieldlist,
"fields\t\tDisplay list of field names" },
{ "help",
query_help,
"help\t\tDisplay help screen" },
{ "quit",
NULL,
"quit\t\tExit program" },
{ NULL,
query_by_field,
"field[op]value\tDisplay fields by value (=, !=, <, <=, >, >=, ~, !~)" },
{ NULL, NULL, NULL }
};
/*
* query_interactive --
* Allow the user to interactively query the database.
*/
int
query_interactive()
{
int done;
char *p, input[256];
for (;;) {
printf("Query: ");
(void)fflush(stdout);
if (fgets(input, sizeof(input), stdin) == NULL) {
printf("\n");
if (ferror(stdin)) {
dbenv->err(dbenv, errno,
"error occurred reading from stdin");
return (1);
}
break;
}
if ((p = strchr(input, '\n')) == NULL) {
dbenv->errx(dbenv, "input buffer too small");
return (1);
}
*p = '\0';
if (query(input, &done) != 0)
return (1);
if (done != 0)
break;
}
return (0);
}
/*
* query --
* Process a query.
*/
int
query(char *cmd, int *donep)
{
CMDTAB *p;
if (donep != NULL)
*donep = 0;
for (p = cmdtab; p->cmd != NULL; ++p)
if (p->cmd != NULL &&
strncasecmp(cmd, p->cmd, strlen(p->cmd)) == 0)
break;
if (p->cmd == NULL)
return (query_by_field(cmd));
if (p->f == NULL) {
if (donep != NULL)
*donep = 1;
return (0);
}
return (p->f(cmd));
}
/*
* query_by_field --
* Query the primary database by field.
*/
static int
query_by_field(char *input)
{
OPERATOR operator;
size_t len;
char *field, *op, *value;
/*
* We expect to see "field [op] value" -- figure it out.
*
* Skip leading whitespace.
*/
while (isspace(*input))
++input;
/*
* Find an operator, and it better not start the string.
*/
if ((len = strcspn(field = input, "<>!=~")) == 0)
return (query_usage());
op = field + len;
/* Figure out the operator, and find the start of the value. */
switch (op[0]) {
case '~':
operator = WC;
value = op + 1;
break;
case '!':
if (op[1] == '=') {
operator = NEQ;
value = op + 2;
break;
}
if (op[1] == '~') {
operator = NWC;
value = op + 2;
break;
}
return (query_usage());
case '<':
if (op[1] == '=') {
operator = LTEQ;
value = op + 2;
} else {
operator = LT;
value = op + 1;
}
break;
case '=':
operator = EQ;
if (op[1] == '=')
value = op + 2;
else
value = op + 1;
break;
case '>':
if (op[1] == '=') {
operator = GTEQ;
value = op + 2;
} else {
operator = GT;
value = op + 1;
}
break;
default:
return (query_usage());
}
/* Terminate the field name, and there better be a field name. */
while (--op > input && isspace(*op))
;
if (op == input)
return (query_usage());
op[1] = '\0';
/* Make sure there is a value field. */
while (isspace(*value))
++value;
if (*value == '\0')
return (query_usage());
return (DbRecord_search_field_name(field, value, operator));
}
/*
* query_fieldlist --
* Display list of field names.
*/
static int
query_fieldlist(char *input)
{
DbField *f;
input = input; /* Quiet compiler. */
for (f = fieldlist; f->name != NULL; ++f)
printf("field %3d: %s\n", f->fieldno, f->name);
return (0);
}
/*
* query_help --
* Query command list.
*/
static int
query_help(char *input)
{
CMDTAB *p;
input = input; /* Quiet compiler. */
printf("Query commands:\n");
for (p = cmdtab; p->help != NULL; ++p)
printf("\t%s\n", p->help);
return (0);
}
/*
* query_usage --
* Query usage message.
*/
static int
query_usage(void)
{
fprintf(stderr, "%s: query syntax error\n", progname);
return (query_help(NULL));
}

View File

@@ -0,0 +1,99 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: query_main.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include "csv.h"
#include "csv_local.h"
#include "csv_extern.h"
static int usage(void);
/*
* Globals
*/
DB_ENV *dbenv; /* Database environment */
DB *db; /* Primary database */
int verbose; /* Program verbosity */
char *progname; /* Program name */
int
main(int argc, char *argv[])
{
int ch, done, ret, t_ret;
char **clist, **clp, *home;
/* Initialize globals. */
dbenv = NULL;
db = NULL;
if ((progname = strrchr(argv[0], '/')) == NULL)
progname = argv[0];
else
++progname;
verbose = 0;
/* Initialize arguments. */
home = NULL;
ret = 0;
/* Allocate enough room for command-list arguments. */
if ((clp = clist =
(char **)calloc((size_t)argc + 1, sizeof(char *))) == NULL) {
fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
return (EXIT_FAILURE);
}
/* Process arguments. */
while ((ch = getopt(argc, argv, "c:h:v")) != EOF)
switch (ch) {
case 'c':
*clp++ = optarg;
break;
case 'h':
home = optarg;
break;
case 'v':
++verbose;
break;
case '?':
default:
return (usage());
}
argc -= optind;
argv += optind;
if (*argv != NULL)
return (usage());
/* Create or join the database environment. */
if (csv_env_open(home, 1) != 0)
return (EXIT_FAILURE);
/* Handle the queries. */
if (clp == clist)
ret = query_interactive();
else
for (clp = clist, done = 0; *clp != NULL && !done; ++clp)
if ((ret = query(*clp, &done)) != 0)
break;
/* Close the database environment. */
if ((t_ret = csv_env_close()) != 0 && ret == 0)
ret = t_ret;
return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
/*
* usage --
* Program usage message.
*/
static int
usage(void)
{
(void)fprintf(stderr, "usage: %s [-v] [-c cmd] [-h home]\n", progname);
return (EXIT_FAILURE);
}

View File

@@ -0,0 +1,8 @@
Adams,Bob,01/02/03,green,apple,37
Carter,Denise Ann,04/05/06,blue,banana,38
Eidel,Frank,07/08/09,red,cherry,38
Grabel,Harriet,10/11/12,purple,date,40
Indals,Jason,01/03/05,pink,orange,32
Kilt,Laura,07/09/11,yellow,grape,38
Moreno,Nancy,02/04/06,black,strawberry,38
Octon,Patrick,08/10/12,magenta,kiwi,15
1 Adams Bob 01/02/03 green apple 37
2 Carter Denise Ann 04/05/06 blue banana 38
3 Eidel Frank 07/08/09 red cherry 38
4 Grabel Harriet 10/11/12 purple date 40
5 Indals Jason 01/03/05 pink orange 32
6 Kilt Laura 07/09/11 yellow grape 38
7 Moreno Nancy 02/04/06 black strawberry 38
8 Octon Patrick 08/10/12 magenta kiwi 15

View File

@@ -0,0 +1,10 @@
# $Id: sample.desc,v 1.4 2007/10/16 19:33:14 bostic Exp $
version 1 {
LastName string
FirstName string
BirthDate
Color string index
Fruit string index
Age unsigned_long index
}

309
examples_c/csv/util.c Normal file
View File

@@ -0,0 +1,309 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2005,2008 Oracle. All rights reserved.
*
* $Id: util.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include "csv.h"
#include "csv_local.h"
#include "csv_extern.h"
/*
* entry_print --
* Display the primary database's data item.
*/
int
entry_print(void *data, size_t len, u_int32_t field_count)
{
u_int32_t a, *offset;
u_int i;
char *raw;
memcpy(&a, data, sizeof(u_int32_t));
printf("\tversion: %lu\n", (u_long)a);
offset = (u_int32_t *)data + 1;
if (field_count == 0) {
memcpy(&a, offset++, sizeof(u_int32_t));
printf("\tcolumn map: %lu fields: {%.*s}\n", (u_long)a,
(int)(len - 2 * sizeof(u_int32_t)),
(u_int8_t *)data + 2 * sizeof(u_int32_t));
} else {
raw = (char *)(offset + (field_count + 1));
for (i = 0; i < field_count; ++i) {
memcpy(&a, &offset[i], sizeof(u_int32_t));
len = OFFSET_LEN(offset, i);
printf("\toffset %4lu: len %4lu: {%.*s}\n",
(u_long)offset[i],
(u_long)len, (int)len, raw + a);
}
}
return (0);
}
/*
* strtod_err --
* strtod(3) with error checking.
*/
int
strtod_err(char *input, double *valp)
{
double val;
char *end;
/*
* strtoul requires setting errno to detect errors.
*/
errno = 0;
val = strtod(input, &end);
if (errno == ERANGE) {
dbenv->err(dbenv, ERANGE, "%s", input);
return (1);
}
if (input[0] == '\0' ||
(end[0] != '\0' && end[0] != '\n' && !isspace(end[0]))) {
dbenv->errx(dbenv,
"%s: invalid floating point argument", input);
return (1);
}
*valp = val;
return (0);
}
/*
* strtoul_err --
* strtoul(3) with error checking.
*/
int
strtoul_err(char *input, u_long *valp)
{
u_long val;
char *end;
/*
* strtoul requires setting errno to detect errors.
*/
errno = 0;
val = strtoul(input, &end, 10);
if (errno == ERANGE) {
dbenv->err(dbenv, ERANGE, "%s", input);
return (1);
}
if (input[0] == '\0' ||
(end[0] != '\0' && end[0] != '\n' && !isspace(end[0]))) {
dbenv->errx(dbenv, "%s: invalid unsigned long argument", input);
return (1);
}
*valp = val;
return (0);
}
int
secondary_callback(DB *db_arg, const DBT *key, const DBT *data, DBT *result)
{
DbField *f;
DbRecord record;
void *faddr, *addr;
/* Populate the field. */
if (DbRecord_init(key, data, &record) != 0)
return (-1);
f = db_arg->app_private;
faddr = (u_int8_t *)&record + f->offset;
/*
* If necessary, copy the field into separate memory.
* Set up the result DBT.
*/
switch (f->type) {
case STRING:
result->data = *(char **)faddr;
result->size = strlen(*(char **)faddr) + 1;
break;
case DOUBLE:
if ((addr = malloc(sizeof(double))) == NULL)
return (-1);
result->data = addr;
result->size = sizeof(double);
result->flags = DB_DBT_APPMALLOC;
memcpy(addr, faddr, sizeof(double));
break;
case UNSIGNED_LONG:
if ((addr = malloc(sizeof(u_long))) == NULL)
return (-1);
result->data = addr;
result->size = sizeof(u_long);
result->flags = DB_DBT_APPMALLOC;
memcpy(addr, faddr, sizeof(u_long));
break;
default:
case NOTSET:
abort();
/* NOTREACHED */
}
return (0);
}
/*
* compare_double --
* Compare two keys.
*/
int
compare_double(DB *db_arg, const DBT *a_arg, const DBT *b_arg)
{
double a, b;
db_arg = db_arg; /* Quiet compiler. */
memcpy(&a, a_arg->data, sizeof(double));
memcpy(&b, b_arg->data, sizeof(double));
return (a > b ? 1 : ((a < b) ? -1 : 0));
}
/*
* compare_ulong --
* Compare two keys.
*/
int
compare_ulong(DB *db_arg, const DBT *a_arg, const DBT *b_arg)
{
u_long a, b;
db_arg = db_arg; /* Quiet compiler. */
memcpy(&a, a_arg->data, sizeof(u_long));
memcpy(&b, b_arg->data, sizeof(u_long));
return (a > b ? 1 : ((a < b) ? -1 : 0));
}
/*
* field_cmp_double --
* Compare two double.
*/
int
field_cmp_double(void *a, void *b, OPERATOR op)
{
switch (op) {
case GT:
return (*(double *)a > *(double *)b);
case GTEQ:
return (*(double *)a >= *(double *)b);
case LT:
return (*(double *)a < *(double *)b);
case LTEQ:
return (*(double *)a <= *(double *)b);
case NEQ:
return (*(double *)a != *(double *)b);
case EQ:
return (*(double *)a == *(double *)b);
case WC:
case NWC:
break;
}
abort();
/* NOTREACHED */
}
/*
* field_cmp_re --
* Compare against regular expression.
*/
int
field_cmp_re(void *a, void *b, OPERATOR op)
{
op = op; /* Quiet compiler. */
switch (op) {
#ifdef HAVE_WILDCARD_SUPPORT
case WC:
return (regexec(b, *(char **)a, 0, NULL, 0) == 0);
case NWC:
return (regexec(b, *(char **)a, 0, NULL, 0) != 0);
#else
case WC:
case NWC:
a = a;
b = b; /* Quiet compiler. */
/* FALLTHROUGH */
#endif
case GT:
case GTEQ:
case LT:
case LTEQ:
case NEQ:
case EQ:
break;
}
abort();
/* NOTREACHED */
}
/*
* field_cmp_string --
* Compare two strings.
*/
int
field_cmp_string(void *a, void *b, OPERATOR op)
{
int v;
v = strcasecmp(*(char **)a, b);
switch (op) {
case GT:
return (v > 0 ? 1 : 0);
case GTEQ:
return (v >= 0 ? 1 : 0);
case LT:
return (v < 0 ? 1 : 0);
case LTEQ:
return (v <= 0 ? 1 : 0);
case NEQ:
return (v ? 1 : 0);
case EQ:
return (v ? 0 : 1);
case WC:
case NWC:
break;
}
abort();
/* NOTREACHED */
}
/*
* field_cmp_ulong --
* Compare two ulongs.
*/
int
field_cmp_ulong(void *a, void *b, OPERATOR op)
{
switch (op) {
case GT:
return (*(u_long *)a > *(u_long *)b);
case GTEQ:
return (*(u_long *)a >= *(u_long *)b);
case LT:
return (*(u_long *)a < *(u_long *)b);
case LTEQ:
return (*(u_long *)a <= *(u_long *)b);
case NEQ:
return (*(u_long *)a != *(u_long *)b);
case EQ:
return (*(u_long *)a == *(u_long *)b);
case WC:
case NWC:
break;
}
abort();
/* NOTREACHED */
}

161
examples_c/ex_access.c Normal file
View File

@@ -0,0 +1,161 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1997,2008 Oracle. All rights reserved.
*
* $Id: ex_access.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
extern int getopt(int, char * const *, const char *);
#else
#include <unistd.h>
#endif
#include <db.h>
#define DATABASE "access.db"
int main __P((int, char *[]));
int usage __P((void));
int
main(argc, argv)
int argc;
char *argv[];
{
extern int optind;
DB *dbp;
DBC *dbcp;
DBT key, data;
size_t len;
int ch, ret, rflag;
char *database, *p, *t, buf[1024], rbuf[1024];
const char *progname = "ex_access"; /* Program name. */
rflag = 0;
while ((ch = getopt(argc, argv, "r")) != EOF)
switch (ch) {
case 'r':
rflag = 1;
break;
case '?':
default:
return (usage());
}
argc -= optind;
argv += optind;
/* Accept optional database name. */
database = *argv == NULL ? DATABASE : argv[0];
/* Optionally discard the database. */
if (rflag)
(void)remove(database);
/* Create and initialize database object, open the database. */
if ((ret = db_create(&dbp, NULL, 0)) != 0) {
fprintf(stderr,
"%s: db_create: %s\n", progname, db_strerror(ret));
return (EXIT_FAILURE);
}
dbp->set_errfile(dbp, stderr);
dbp->set_errpfx(dbp, progname);
if ((ret = dbp->set_pagesize(dbp, 1024)) != 0) {
dbp->err(dbp, ret, "set_pagesize");
goto err1;
}
if ((ret = dbp->set_cachesize(dbp, 0, 32 * 1024, 0)) != 0) {
dbp->err(dbp, ret, "set_cachesize");
goto err1;
}
if ((ret = dbp->open(dbp,
NULL, database, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) {
dbp->err(dbp, ret, "%s: open", database);
goto err1;
}
/*
* Insert records into the database, where the key is the user
* input and the data is the user input in reverse order.
*/
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
for (;;) {
printf("input> ");
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) == NULL)
break;
if (strcmp(buf, "exit\n") == 0 || strcmp(buf, "quit\n") == 0)
break;
if ((len = strlen(buf)) <= 1)
continue;
for (t = rbuf, p = buf + (len - 2); p >= buf;)
*t++ = *p--;
*t++ = '\0';
key.data = buf;
data.data = rbuf;
data.size = key.size = (u_int32_t)len - 1;
switch (ret =
dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE)) {
case 0:
break;
default:
dbp->err(dbp, ret, "DB->put");
if (ret != DB_KEYEXIST)
goto err1;
break;
}
}
printf("\n");
/* Acquire a cursor for the database. */
if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) {
dbp->err(dbp, ret, "DB->cursor");
goto err1;
}
/* Initialize the key/data pair so the flags aren't set. */
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
/* Walk through the database and print out the key/data pairs. */
while ((ret = dbcp->get(dbcp, &key, &data, DB_NEXT)) == 0)
printf("%.*s : %.*s\n",
(int)key.size, (char *)key.data,
(int)data.size, (char *)data.data);
if (ret != DB_NOTFOUND) {
dbp->err(dbp, ret, "DBcursor->get");
goto err2;
}
/* Close everything down. */
if ((ret = dbcp->close(dbcp)) != 0) {
dbp->err(dbp, ret, "DBcursor->close");
goto err1;
}
if ((ret = dbp->close(dbp, 0)) != 0) {
fprintf(stderr,
"%s: DB->close: %s\n", progname, db_strerror(ret));
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
err2: (void)dbcp->close(dbcp);
err1: (void)dbp->close(dbp, 0);
return (EXIT_FAILURE);
}
int
usage()
{
(void)fprintf(stderr, "usage: ex_access [-r] [database]\n");
return (EXIT_FAILURE);
}

View File

@@ -0,0 +1,10 @@
# Script to rebuild automatically generated files for ex_apprec.
E=../examples_c/ex_apprec
cd ../../dist
awk -f gen_rec.awk \
-v source_file=$E/ex_apprec_auto.c \
-v header_file=$E/ex_apprec_auto.h \
-v print_file=$E/ex_apprec_autop.c \
-v template_file=$E/ex_apprec_template < $E/ex_apprec.src

View File

@@ -0,0 +1,277 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996,2008 Oracle. All rights reserved.
*
* $Id: ex_apprec.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "db_config.h"
#include "db.h"
#include "db_int.h"
#include "dbinc/db_swap.h"
#include "ex_apprec.h"
int apprec_dispatch __P((DB_ENV *, DBT *, DB_LSN *, db_recops));
int open_env __P((const char *, FILE *, const char *, DB_ENV **));
int verify_absence __P((DB_ENV *, const char *));
int verify_presence __P((DB_ENV *, const char *));
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
DB_ENV *dbenv;
DB_LSN lsn;
DB_TXN *txn;
DBT dirnamedbt;
int ret;
const char *home;
char ch, dirname[256];
const char *progname = "ex_apprec"; /* Program name. */
/* Default home. */
home = "TESTDIR";
while ((ch = getopt(argc, argv, "h:")) != EOF)
switch (ch) {
case 'h':
home = optarg;
break;
default:
fprintf(stderr, "usage: %s [-h home]", progname);
exit(EXIT_FAILURE);
}
printf("Set up environment.\n");
if ((ret = open_env(home, stderr, progname, &dbenv)) != 0)
return (EXIT_FAILURE);
printf("Create a directory in a transaction.\n");
/*
* This application's convention is to log the full directory name,
* including trailing nul.
*/
memset(&dirnamedbt, 0, sizeof(dirnamedbt));
sprintf(dirname, "%s/MYDIRECTORY", home);
dirnamedbt.data = dirname;
dirnamedbt.size = strlen(dirname) + 1;
if ((ret = dbenv->txn_begin(dbenv, NULL, &txn, 0)) != 0) {
dbenv->err(dbenv, ret, "txn_begin");
return (EXIT_FAILURE);
}
/*
* Remember, always log actions before you execute them!
* Since this log record is describing a file system operation and
* we have no control over when file system operations go to disk,
* we need to flush the log record immediately to ensure that the
* log record is on disk before the operation it describes. The
* flush would not be necessary were we doing an operation into the
* BDB mpool and using LSNs that mpool knew about.
*/
memset(&lsn, 0, sizeof(lsn));
if ((ret =
ex_apprec_mkdir_log(dbenv,
txn, &lsn, DB_FLUSH, &dirnamedbt)) != 0) {
dbenv->err(dbenv, ret, "mkdir_log");
return (EXIT_FAILURE);
}
if (mkdir(dirname, 0755) != 0) {
dbenv->err(dbenv, errno, "mkdir");
return (EXIT_FAILURE);
}
printf("Verify the directory's presence: ");
verify_presence(dbenv, dirname);
printf("check.\n");
/* Now abort the transaction and verify that the directory goes away. */
printf("Abort the transaction.\n");
if ((ret = txn->abort(txn)) != 0) {
dbenv->err(dbenv, ret, "txn_abort");
return (EXIT_FAILURE);
}
printf("Verify the directory's absence: ");
verify_absence(dbenv, dirname);
printf("check.\n");
/* Now do the same thing over again, only with a commit this time. */
printf("Create a directory in a transaction.\n");
memset(&dirnamedbt, 0, sizeof(dirnamedbt));
sprintf(dirname, "%s/MYDIRECTORY", home);
dirnamedbt.data = dirname;
dirnamedbt.size = strlen(dirname) + 1;
if ((ret = dbenv->txn_begin(dbenv, NULL, &txn, 0)) != 0) {
dbenv->err(dbenv, ret, "txn_begin");
return (EXIT_FAILURE);
}
memset(&lsn, 0, sizeof(lsn));
if ((ret =
ex_apprec_mkdir_log(dbenv, txn, &lsn, 0, &dirnamedbt)) != 0) {
dbenv->err(dbenv, ret, "mkdir_log");
return (EXIT_FAILURE);
}
if (mkdir(dirname, 0755) != 0) {
dbenv->err(dbenv, errno, "mkdir");
return (EXIT_FAILURE);
}
printf("Verify the directory's presence: ");
verify_presence(dbenv, dirname);
printf("check.\n");
/* Now abort the transaction and verify that the directory goes away. */
printf("Commit the transaction.\n");
if ((ret = txn->commit(txn, 0)) != 0) {
dbenv->err(dbenv, ret, "txn_commit");
return (EXIT_FAILURE);
}
printf("Verify the directory's presence: ");
verify_presence(dbenv, dirname);
printf("check.\n");
printf("Now remove the directory, then run recovery.\n");
if ((ret = dbenv->close(dbenv, 0)) != 0) {
fprintf(stderr, "DB_ENV->close: %s\n", db_strerror(ret));
return (EXIT_FAILURE);
}
if (rmdir(dirname) != 0) {
fprintf(stderr,
"%s: rmdir failed with error %s", progname,
strerror(errno));
}
verify_absence(dbenv, dirname);
/* Opening with DB_RECOVER runs recovery. */
if ((ret = open_env(home, stderr, progname, &dbenv)) != 0)
return (EXIT_FAILURE);
printf("Verify the directory's presence: ");
verify_presence(dbenv, dirname);
printf("check.\n");
/* Close the handle. */
if ((ret = dbenv->close(dbenv, 0)) != 0) {
fprintf(stderr, "DB_ENV->close: %s\n", db_strerror(ret));
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
int
open_env(home, errfp, progname, dbenvp)
const char *home, *progname;
FILE *errfp;
DB_ENV **dbenvp;
{
DB_ENV *dbenv;
int ret;
/*
* Create an environment object and initialize it for error
* reporting.
*/
if ((ret = db_env_create(&dbenv, 0)) != 0) {
fprintf(errfp, "%s: %s\n", progname, db_strerror(ret));
return (ret);
}
dbenv->set_errfile(dbenv, errfp);
dbenv->set_errpfx(dbenv, progname);
/* Set up our custom recovery dispatch function. */
if ((ret = dbenv->set_app_dispatch(dbenv, apprec_dispatch)) != 0) {
dbenv->err(dbenv, ret, "set_app_dispatch");
return (ret);
}
/*
* Open the environment with full transactional support, running
* recovery.
*/
if ((ret =
dbenv->open(dbenv, home, DB_CREATE | DB_RECOVER | DB_INIT_LOCK |
DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN, 0)) != 0) {
dbenv->err(dbenv, ret, "environment open: %s", home);
dbenv->close(dbenv, 0);
return (ret);
}
*dbenvp = dbenv;
return (0);
}
/*
* Sample application dispatch function to handle user-specified log record
* types.
*/
int
apprec_dispatch(dbenv, dbt, lsn, op)
DB_ENV *dbenv;
DBT *dbt;
DB_LSN *lsn;
db_recops op;
{
u_int32_t rectype;
/* Pull the record type out of the log record. */
LOGCOPY_32(dbenv->env, &rectype, dbt->data);
switch (rectype) {
case DB_ex_apprec_mkdir:
return (ex_apprec_mkdir_recover(dbenv, dbt, lsn, op));
default:
/*
* We've hit an unexpected, allegedly user-defined record
* type.
*/
dbenv->errx(dbenv, "Unexpected log record type encountered");
return (EINVAL);
}
}
int
verify_absence(dbenv, dirname)
DB_ENV *dbenv;
const char *dirname;
{
if (access(dirname, F_OK) == 0) {
dbenv->errx(dbenv, "Error--directory present!");
exit(EXIT_FAILURE);
}
return (0);
}
int
verify_presence(dbenv, dirname)
DB_ENV *dbenv;
const char *dirname;
{
if (access(dirname, F_OK) != 0) {
dbenv->errx(dbenv, "Error--directory not present!");
exit(EXIT_FAILURE);
}
return (0);
}

View File

@@ -0,0 +1,24 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002,2008 Oracle. All rights reserved.
*
* $Id: ex_apprec.h 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#ifndef _EX_APPREC_H_
#define _EX_APPREC_H_
#include "ex_apprec_auto.h"
int ex_apprec_mkdir_log
__P((DB_ENV *, DB_TXN *, DB_LSN *, u_int32_t, const DBT *));
int ex_apprec_mkdir_print
__P((DB_ENV *, DBT *, DB_LSN *, db_recops));
int ex_apprec_mkdir_read
__P((DB_ENV *, void *, ex_apprec_mkdir_args **));
int ex_apprec_mkdir_recover
__P((DB_ENV *, DBT *, DB_LSN *, db_recops));
int ex_apprec_init_print __P((DB_ENV *, DB_DISTAB *));
#endif /* !_EX_APPREC_H_ */

View File

@@ -0,0 +1,33 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2002,2008 Oracle. All rights reserved.
*
* $Id: ex_apprec.src,v 12.8 2008/01/08 20:58:24 bostic Exp $
*/
PREFIX ex_apprec
INCLUDE #include "ex_apprec.h"
/*
* This is the source file used to generate the application-specific recovery
* functions used by the ex_apprec example. It should be turned into usable
* source code (including a template for the recovery function itself) by
* invoking changing to the dist directory of the DB distribution and
* running the gen_rec.awk script there as follows:
*
* awk -f ./gen_rec.awk \
* -v source_file=../examples_c/ex_apprec/ex_apprec_auto.c \
* -v header_file=../examples_c/ex_apprec/ex_apprec_auto.h \
* -v template_file=../examples_c/ex_apprec/ex_apprec_template \
* < ../examples_c/ex_apprec/ex_apprec.src
*/
/*
* mkdir: used to create a directory
*
* dirname: relative or absolute pathname of the directory to be created
*/
BEGIN mkdir 42 10000
DBT dirname DBT s
END

View File

@@ -0,0 +1,140 @@
/* Do not edit: automatically built by gen_rec.awk. */
#include "db_config.h"
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include "db.h"
#include "db_int.h"
#include "dbinc/db_swap.h"
#include "ex_apprec.h"
/*
* PUBLIC: int ex_apprec_mkdir_read __P((DB_ENV *, void *,
* PUBLIC: ex_apprec_mkdir_args **));
*/
int
ex_apprec_mkdir_read(dbenv, recbuf, argpp)
DB_ENV *dbenv;
void *recbuf;
ex_apprec_mkdir_args **argpp;
{
ex_apprec_mkdir_args *argp;
u_int8_t *bp;
ENV *env;
env = dbenv->env;
if ((argp = malloc(sizeof(ex_apprec_mkdir_args) + sizeof(DB_TXN))) == NULL)
return (ENOMEM);
bp = recbuf;
argp->txnp = (DB_TXN *)&argp[1];
memset(argp->txnp, 0, sizeof(DB_TXN));
LOGCOPY_32(env, &argp->type, bp);
bp += sizeof(argp->type);
LOGCOPY_32(env, &argp->txnp->txnid, bp);
bp += sizeof(argp->txnp->txnid);
LOGCOPY_TOLSN(env, &argp->prev_lsn, bp);
bp += sizeof(DB_LSN);
memset(&argp->dirname, 0, sizeof(argp->dirname));
LOGCOPY_32(env,&argp->dirname.size, bp);
bp += sizeof(u_int32_t);
argp->dirname.data = bp;
bp += argp->dirname.size;
*argpp = argp;
return (0);
}
/*
* PUBLIC: int ex_apprec_mkdir_log __P((DB_ENV *, DB_TXN *, DB_LSN *,
* PUBLIC: u_int32_t, const DBT *));
*/
int
ex_apprec_mkdir_log(dbenv, txnp, ret_lsnp, flags,
dirname)
DB_ENV *dbenv;
DB_TXN *txnp;
DB_LSN *ret_lsnp;
u_int32_t flags;
const DBT *dirname;
{
DBT logrec;
DB_LSN *lsnp, null_lsn, *rlsnp;
ENV *env;
u_int32_t zero, rectype, txn_num;
u_int npad;
u_int8_t *bp;
int ret;
env = dbenv->env;
rlsnp = ret_lsnp;
rectype = DB_ex_apprec_mkdir;
npad = 0;
ret = 0;
if (txnp == NULL) {
txn_num = 0;
lsnp = &null_lsn;
null_lsn.file = null_lsn.offset = 0;
} else {
/*
* We need to assign begin_lsn while holding region mutex.
* That assignment is done inside the DbEnv->log_put call,
* so pass in the appropriate memory location to be filled
* in by the log_put code.
*/
DB_SET_TXN_LSNP(txnp, &rlsnp, &lsnp);
txn_num = txnp->txnid;
}
logrec.size = sizeof(rectype) + sizeof(txn_num) + sizeof(DB_LSN)
+ sizeof(u_int32_t) + (dirname == NULL ? 0 : dirname->size);
if ((logrec.data = malloc(logrec.size)) == NULL)
return (ENOMEM);
bp = logrec.data;
if (npad > 0)
memset((u_int8_t *)logrec.data + logrec.size - npad, 0, npad);
bp = logrec.data;
LOGCOPY_32(env, bp, &rectype);
bp += sizeof(rectype);
LOGCOPY_32(env, bp, &txn_num);
bp += sizeof(txn_num);
LOGCOPY_FROMLSN(env, bp, lsnp);
bp += sizeof(DB_LSN);
if (dirname == NULL) {
zero = 0;
LOGCOPY_32(env, bp, &zero);
bp += sizeof(u_int32_t);
} else {
LOGCOPY_32(env, bp, &dirname->size);
bp += sizeof(dirname->size);
memcpy(bp, dirname->data, dirname->size);
bp += dirname->size;
}
if ((ret = dbenv->log_put(dbenv, rlsnp, (DBT *)&logrec,
flags | DB_LOG_NOCOPY)) == 0 && txnp != NULL) {
*lsnp = *rlsnp;
if (rlsnp != ret_lsnp)
*ret_lsnp = *rlsnp;
}
#ifdef LOG_DIAGNOSTIC
if (ret != 0)
(void)ex_apprec_mkdir_print(dbenv,
(DBT *)&logrec, ret_lsnp, DB_TXN_PRINT);
#endif
free(logrec.data);
return (ret);
}

View File

@@ -0,0 +1,13 @@
/* Do not edit: automatically built by gen_rec.awk. */
#ifndef ex_apprec_AUTO_H
#define ex_apprec_AUTO_H
#define DB_ex_apprec_mkdir 10000
typedef struct _ex_apprec_mkdir_args {
u_int32_t type;
DB_TXN *txnp;
DB_LSN prev_lsn;
DBT dirname;
} ex_apprec_mkdir_args;
#endif

View File

@@ -0,0 +1,65 @@
/* Do not edit: automatically built by gen_rec.awk. */
#include "db_config.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include "db.h"
#include "ex_apprec.h"
/*
* PUBLIC: int ex_apprec_mkdir_print __P((DB_ENV *, DBT *, DB_LSN *,
* PUBLIC: db_recops));
*/
int
ex_apprec_mkdir_print(dbenv, dbtp, lsnp, notused2)
DB_ENV *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops notused2;
{
ex_apprec_mkdir_args *argp;
int ex_apprec_mkdir_read __P((DB_ENV *, void *, ex_apprec_mkdir_args **));
u_int32_t i;
int ch;
int ret;
notused2 = DB_TXN_PRINT;
if ((ret = ex_apprec_mkdir_read(dbenv, dbtp->data, &argp)) != 0)
return (ret);
(void)printf(
"[%lu][%lu]ex_apprec_mkdir%s: rec: %lu txnp %lx prevlsn [%lu][%lu]\n",
(u_long)lsnp->file, (u_long)lsnp->offset,
(argp->type & DB_debug_FLAG) ? "_debug" : "",
(u_long)argp->type,
(u_long)argp->txnp->txnid,
(u_long)argp->prev_lsn.file, (u_long)argp->prev_lsn.offset);
(void)printf("\tdirname: ");
for (i = 0; i < argp->dirname.size; i++) {
ch = ((u_int8_t *)argp->dirname.data)[i];
printf(isprint(ch) || ch == 0x0a ? "%c" : "%#x ", ch);
}
(void)printf("\n");
(void)printf("\n");
free(argp);
return (0);
}
/*
* PUBLIC: int ex_apprec_init_print __P((DB_ENV *, DB_DISTAB *));
*/
int
ex_apprec_init_print(dbenv, dtabp)
DB_ENV *dbenv;
DB_DISTAB *dtabp;
{
int __db_add_recovery __P((DB_ENV *, DB_DISTAB *,
int (*)(DB_ENV *, DBT *, DB_LSN *, db_recops), u_int32_t));
int ret;
if ((ret = __db_add_recovery(dbenv, dtabp,
ex_apprec_mkdir_print, DB_ex_apprec_mkdir)) != 0)
return (ret);
return (0);
}

View File

@@ -0,0 +1,108 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996,2008 Oracle. All rights reserved.
*
* $Id: ex_apprec_rec.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
/*
* This file is based on the template file ex_apprec_template. Note that
* because ex_apprec_mkdir, like most application-specific recovery functions,
* does not make use of DB-private structures, it has actually been simplified
* significantly.
*/
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <db.h>
#include "ex_apprec.h"
/*
* ex_apprec_mkdir_recover --
* Recovery function for mkdir.
*
* PUBLIC: int ex_apprec_mkdir_recover
* PUBLIC: __P((DB_ENV *, DBT *, DB_LSN *, db_recops));
*/
int
ex_apprec_mkdir_recover(dbenv, dbtp, lsnp, op)
DB_ENV *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops op;
{
ex_apprec_mkdir_args *argp;
int ret;
argp = NULL;
#ifdef DEBUG_RECOVER
ex_apprec_mkdir_print(dbenv, dbtp, lsnp, op);
#endif
if ((ret = ex_apprec_mkdir_read(dbenv, dbtp->data, &argp)) != 0)
goto out;
switch (op) {
case DB_TXN_ABORT:
case DB_TXN_BACKWARD_ROLL:
/*
* If we're aborting, we need to remove the directory if it
* exists. We log the trailing zero in pathnames, so we can
* simply pass the data part of the DBT into rmdir as a string.
* (Note that we don't have any alignment guarantees, but for
* a char * this doesn't matter.)
*
* Ignore all errors other than ENOENT; DB may attempt to undo
* or redo operations without knowing whether they have already
* been done or undone, so we should never assume in a recovery
* function that the task definitely needs doing or undoing.
*/
ret = rmdir(argp->dirname.data);
if (ret != 0 && errno != ENOENT)
dbenv->err(dbenv, ret, "Error in abort of mkdir");
else
ret = 0;
break;
case DB_TXN_FORWARD_ROLL:
/*
* The forward direction is just the opposite; here, we ignore
* EEXIST, because the directory may already exist.
*/
ret = mkdir(argp->dirname.data, 0755);
if (ret != 0 && errno != EEXIST)
dbenv->err(dbenv,
ret, "Error in roll-forward of mkdir");
else
ret = 0;
break;
default:
/*
* We might want to handle DB_TXN_PRINT or DB_TXN_APPLY here,
* too, but we don't try to print the log records and aren't
* using replication, so there's no need to in this example.
*/
dbenv->errx(dbenv, "Unexpected operation type\n");
return (EINVAL);
}
/*
* The recovery function is responsible for returning the LSN of the
* previous log record in this transaction, so that transaction aborts
* can follow the chain backwards.
*
* (If we'd wanted the LSN of this record earlier, we could have
* read it from lsnp, as well--but because we weren't working with
* pages or other objects that store their LSN and base recovery
* decisions on it, we didn't need to.)
*/
*lsnp = argp->prev_lsn;
out: if (argp != NULL)
free(argp);
return (ret);
}

View File

@@ -0,0 +1,70 @@
#include "db.h"
/*
* ex_apprec_mkdir_recover --
* Recovery function for mkdir.
*
* PUBLIC: int ex_apprec_mkdir_recover
* PUBLIC: __P((dbenv *, DBT *, DB_LSN *, db_recops));
*/
int
ex_apprec_mkdir_recover(dbenv, dbtp, lsnp, op)
dbenv *dbenv;
DBT *dbtp;
DB_LSN *lsnp;
db_recops op;
{
ex_apprec_mkdir_args *argp;
int cmp_n, cmp_p, modified, ret;
#ifdef DEBUG_RECOVER
(void)ex_apprec_mkdir_print(dbenv, dbtp, lsnp, op);
#endif
argp = NULL;
if ((ret = ex_apprec_mkdir_read(dbenv, dbtp->data, &argp)) != 0)
goto out;
modified = 0;
cmp_n = 0;
cmp_p = 0;
/*
* The function now needs to calculate cmp_n and cmp_p based
* on whatever is in argp (usually an LSN representing the state
* of an object BEFORE the operation described in this record was
* applied) and whatever other information the function needs,
* e.g., the LSN of the object as it exists now.
*
* cmp_p should be set to 0 if the current state of the object
* is believed to be same as the state of the object BEFORE the
* described operation was applied. For example, if you had an
* LSN in the log record (argp->prevlsn) and a current LSN of the
* object (curlsn), you might want to do:
*
* cmp_p = log_compare(curlsn, argp->prevlsn);
*
* Similarly, cmp_n should be set to 0 if the current state
* of the object reflects the object AFTER this operation has
* been applied. Thus, if you can figure out an object's current
* LSN, yo might set cmp_n as:
*
* cmp_n = log_compare(lsnp, curlsn);
*/
if (cmp_p == 0 && DB_REDO(op)) {
/* Need to redo update described. */
modified = 1;
} else if (cmp_n == 0 && !DB_REDO(op)) {
/* Need to undo update described. */
modified = 1;
}
/* Allow for following LSN pointers through a transaction. */
*lsnp = argp->prev_lsn;
ret = 0;
out: if (argp != NULL)
free(argp);
return (ret);
}

202
examples_c/ex_btrec.c Normal file
View File

@@ -0,0 +1,202 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1997,2008 Oracle. All rights reserved.
*
* $Id: ex_btrec.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#define DATABASE "access.db"
#define WORDLIST "../test/wordlist"
int main __P((void));
int ex_btrec __P((void));
void show __P((const char *, DBT *, DBT *));
int
main()
{
return (ex_btrec() == 1 ? EXIT_FAILURE : EXIT_SUCCESS);
}
int
ex_btrec()
{
DB *dbp;
DBC *dbcp;
DBT key, data;
DB_BTREE_STAT *statp;
FILE *fp;
db_recno_t recno;
size_t len;
int cnt, ret;
char *p, *t, buf[1024], rbuf[1024];
const char *progname = "ex_btrec"; /* Program name. */
/* Open the word database. */
if ((fp = fopen(WORDLIST, "r")) == NULL) {
fprintf(stderr, "%s: open %s: %s\n",
progname, WORDLIST, db_strerror(errno));
return (1);
}
/* Remove the previous database. */
(void)remove(DATABASE);
/* Create and initialize database object, open the database. */
if ((ret = db_create(&dbp, NULL, 0)) != 0) {
fprintf(stderr,
"%s: db_create: %s\n", progname, db_strerror(ret));
return (1);
}
dbp->set_errfile(dbp, stderr);
dbp->set_errpfx(dbp, progname); /* 1K page sizes. */
if ((ret = dbp->set_pagesize(dbp, 1024)) != 0) {
dbp->err(dbp, ret, "set_pagesize");
return (1);
} /* Record numbers. */
if ((ret = dbp->set_flags(dbp, DB_RECNUM)) != 0) {
dbp->err(dbp, ret, "set_flags: DB_RECNUM");
return (1);
}
if ((ret = dbp->open(dbp,
NULL, DATABASE, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) {
dbp->err(dbp, ret, "open: %s", DATABASE);
return (1);
}
/*
* Insert records into the database, where the key is the word
* preceded by its record number, and the data is the same, but
* in reverse order.
*/
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
for (cnt = 1; cnt <= 1000; ++cnt) {
(void)sprintf(buf, "%04d_", cnt);
if (fgets(buf + 4, sizeof(buf) - 4, fp) == NULL)
break;
len = strlen(buf);
for (t = rbuf, p = buf + (len - 2); p >= buf;)
*t++ = *p--;
*t++ = '\0';
key.data = buf;
data.data = rbuf;
data.size = key.size = (u_int32_t)len - 1;
if ((ret =
dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE)) != 0) {
dbp->err(dbp, ret, "DB->put");
if (ret != DB_KEYEXIST)
goto err1;
}
}
/* Close the word database. */
(void)fclose(fp);
/* Print out the number of records in the database. */
if ((ret = dbp->stat(dbp, NULL, &statp, 0)) != 0) {
dbp->err(dbp, ret, "DB->stat");
goto err1;
}
printf("%s: database contains %lu records\n",
progname, (u_long)statp->bt_ndata);
free(statp);
/* Acquire a cursor for the database. */
if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) {
dbp->err(dbp, ret, "DB->cursor");
goto err1;
}
/*
* Prompt the user for a record number, then retrieve and display
* that record.
*/
for (;;) {
/* Get a record number. */
printf("recno #> ");
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) == NULL)
break;
recno = atoi(buf);
/*
* Reset the key each time, the dbp->get() routine returns
* the key and data pair, not just the key!
*/
key.data = &recno;
key.size = sizeof(recno);
if ((ret = dbcp->get(dbcp, &key, &data, DB_SET_RECNO)) != 0)
goto get_err;
/* Display the key and data. */
show("k/d\t", &key, &data);
/* Move the cursor a record forward. */
if ((ret = dbcp->get(dbcp, &key, &data, DB_NEXT)) != 0)
goto get_err;
/* Display the key and data. */
show("next\t", &key, &data);
/*
* Retrieve the record number for the following record into
* local memory.
*/
data.data = &recno;
data.size = sizeof(recno);
data.ulen = sizeof(recno);
data.flags |= DB_DBT_USERMEM;
if ((ret = dbcp->get(dbcp, &key, &data, DB_GET_RECNO)) != 0) {
get_err: dbp->err(dbp, ret, "DBcursor->get");
if (ret != DB_NOTFOUND && ret != DB_KEYEMPTY)
goto err2;
} else
printf("retrieved recno: %lu\n", (u_long)recno);
/* Reset the data DBT. */
memset(&data, 0, sizeof(data));
}
if ((ret = dbcp->close(dbcp)) != 0) {
dbp->err(dbp, ret, "DBcursor->close");
goto err1;
}
if ((ret = dbp->close(dbp, 0)) != 0) {
fprintf(stderr,
"%s: DB->close: %s\n", progname, db_strerror(ret));
return (1);
}
return (0);
err2: (void)dbcp->close(dbcp);
err1: (void)dbp->close(dbp, 0);
return (ret);
}
/*
* show --
* Display a key/data pair.
*/
void
show(msg, key, data)
const char *msg;
DBT *key, *data;
{
printf("%s%.*s : %.*s\n", msg,
(int)key->size, (char *)key->data,
(int)data->size, (char *)data->data);
}

226
examples_c/ex_dbclient.c Normal file
View File

@@ -0,0 +1,226 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996,2008 Oracle. All rights reserved.
*
* $Id: ex_dbclient.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <db.h>
#define DATABASE_HOME "database"
#define DATABASE "access.db"
int db_clientrun __P((DB_ENV *, const char *));
int ex_dbclient __P((const char *));
int ex_dbclient_run __P((const char *, FILE *, const char *, const char *));
int main __P((int, char *[]));
/*
* An example of a program creating/configuring a Berkeley DB environment.
*/
int
main(argc, argv)
int argc;
char *argv[];
{
const char *home;
if (argc != 2) {
fprintf(stderr, "Usage: %s hostname\n", argv[0]);
return (EXIT_FAILURE);
}
/*
* All of the shared database files live in DATABASE_HOME, but
* data files will live in CONFIG_DATA_DIR.
*/
home = DATABASE_HOME;
return (ex_dbclient_run(home,
stderr, argv[1], argv[0]) == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
int
ex_dbclient(host)
const char *host;
{
const char *home;
const char *progname = "ex_dbclient"; /* Program name. */
int ret;
/*
* All of the shared database files live in DATABASE_HOME, but
* data files will live in CONFIG_DATA_DIR.
*/
home = DATABASE_HOME;
if ((ret = ex_dbclient_run(home, stderr, host, progname)) != 0)
return (ret);
return (0);
}
int
ex_dbclient_run(home, errfp, host, progname)
const char *home, *host, *progname;
FILE *errfp;
{
DB_ENV *dbenv;
int ret, retry;
/*
* Create an environment object and initialize it for error
* reporting.
*/
if ((ret = db_env_create(&dbenv, DB_RPCCLIENT)) != 0) {
fprintf(errfp, "%s: %s\n", progname, db_strerror(ret));
return (1);
}
retry = 0;
loop:
while (retry < 5) {
/*
* Set the server host we are talking to.
*/
if ((ret = dbenv->set_rpc_server(dbenv, NULL, host, 10000,
10000, 0)) != 0) {
fprintf(stderr, "Try %d: DB_ENV->set_rpc_server: %s\n",
retry, db_strerror(ret));
retry++;
sleep(15);
} else
break;
}
if (retry >= 5) {
fprintf(stderr,
"DB_ENV->set_rpc_server: %s\n", db_strerror(ret));
dbenv->close(dbenv, 0);
return (1);
}
/*
* We want to specify the shared memory buffer pool cachesize,
* but everything else is the default.
*/
if ((ret = dbenv->set_cachesize(dbenv, 0, 64 * 1024, 0)) != 0) {
dbenv->err(dbenv, ret, "set_cachesize");
dbenv->close(dbenv, 0);
return (1);
}
/*
* We have multiple processes reading/writing these files, so
* we need concurrency control and a shared buffer pool, but
* not logging or transactions.
*/
if ((ret = dbenv->open(dbenv, home,
DB_CREATE | DB_INIT_LOCK | DB_INIT_MPOOL, 0)) != 0) {
dbenv->err(dbenv, ret, "environment open: %s", home);
dbenv->close(dbenv, 0);
if (ret == DB_NOSERVER)
goto loop;
return (1);
}
ret = db_clientrun(dbenv, progname);
printf("db_clientrun returned %d\n", ret);
if (ret == DB_NOSERVER)
goto loop;
/* Close the handle. */
if ((ret = dbenv->close(dbenv, 0)) != 0) {
fprintf(stderr, "DB_ENV->close: %s\n", db_strerror(ret));
return (1);
}
return (0);
}
int
db_clientrun(dbenv, progname)
DB_ENV *dbenv;
const char *progname;
{
DB *dbp;
DBT key, data;
u_int32_t len;
int ret;
char *p, *t, buf[1024], rbuf[1024];
/* Remove the previous database. */
/* Create and initialize database object, open the database. */
if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
fprintf(stderr,
"%s: db_create: %s\n", progname, db_strerror(ret));
return (ret);
}
if ((ret = dbp->set_pagesize(dbp, 1024)) != 0) {
dbp->err(dbp, ret, "set_pagesize");
goto err1;
}
if ((ret = dbp->open(dbp,
NULL, DATABASE, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) {
dbp->err(dbp, ret, "%s: open", DATABASE);
goto err1;
}
/*
* Insert records into the database, where the key is the user
* input and the data is the user input in reverse order.
*/
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
for (;;) {
printf("input> ");
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) == NULL)
break;
if ((len = strlen(buf)) <= 1)
continue;
for (t = rbuf, p = buf + (len - 2); p >= buf;)
*t++ = *p--;
*t++ = '\0';
key.data = buf;
data.data = rbuf;
data.size = key.size = len - 1;
switch (ret =
dbp->put(dbp, NULL, &key, &data, DB_NOOVERWRITE)) {
case 0:
break;
default:
dbp->err(dbp, ret, "DB->put");
if (ret != DB_KEYEXIST)
goto err1;
break;
}
memset(&data, 0, sizeof(DBT));
switch (ret = dbp->get(dbp, NULL, &key, &data, 0)) {
case 0:
printf("%.*s : %.*s\n",
(int)key.size, (char *)key.data,
(int)data.size, (char *)data.data);
break;
default:
dbp->err(dbp, ret, "DB->get");
break;
}
}
if ((ret = dbp->close(dbp, 0)) != 0) {
fprintf(stderr,
"%s: DB->close: %s\n", progname, db_strerror(ret));
return (1);
}
return (0);
err1: (void)dbp->close(dbp, 0);
return (ret);
}

134
examples_c/ex_env.c Normal file
View File

@@ -0,0 +1,134 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1996,2008 Oracle. All rights reserved.
*
* $Id: ex_env.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <db.h>
#ifdef macintosh
#define DATABASE_HOME ":database"
#define CONFIG_DATA_DIR ":database"
#else
#ifdef DB_WIN32
#define DATABASE_HOME "\\tmp\\database"
#define CONFIG_DATA_DIR "\\database\\files"
#else
#define DATABASE_HOME "/tmp/database"
#define CONFIG_DATA_DIR "/database/files"
#endif
#endif
int db_setup __P((const char *, const char *, FILE *, const char *));
int db_teardown __P((const char *, const char *, FILE *, const char *));
int main __P((void));
/*
* An example of a program creating/configuring a Berkeley DB environment.
*/
int
main()
{
const char *data_dir, *home;
const char *progname = "ex_env"; /* Program name. */
/*
* All of the shared database files live in DATABASE_HOME, but
* data files will live in CONFIG_DATA_DIR.
*/
home = DATABASE_HOME;
data_dir = CONFIG_DATA_DIR;
printf("Setup env\n");
if (db_setup(home, data_dir, stderr, progname) != 0)
return (EXIT_FAILURE);
printf("Teardown env\n");
if (db_teardown(home, data_dir, stderr, progname) != 0)
return (EXIT_FAILURE);
return (EXIT_SUCCESS);
}
int
db_setup(home, data_dir, errfp, progname)
const char *home, *data_dir, *progname;
FILE *errfp;
{
DB_ENV *dbenv;
int ret;
/*
* Create an environment object and initialize it for error
* reporting.
*/
if ((ret = db_env_create(&dbenv, 0)) != 0) {
fprintf(errfp, "%s: %s\n", progname, db_strerror(ret));
return (1);
}
dbenv->set_errfile(dbenv, errfp);
dbenv->set_errpfx(dbenv, progname);
/*
* We want to specify the shared memory buffer pool cachesize,
* but everything else is the default.
*/
if ((ret = dbenv->set_cachesize(dbenv, 0, 64 * 1024, 0)) != 0) {
dbenv->err(dbenv, ret, "set_cachesize");
dbenv->close(dbenv, 0);
return (1);
}
/* Databases are in a subdirectory. */
(void)dbenv->set_data_dir(dbenv, data_dir);
/* Open the environment with full transactional support. */
if ((ret = dbenv->open(dbenv, home,
DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN,
0)) != 0) {
dbenv->err(dbenv, ret, "environment open: %s", home);
dbenv->close(dbenv, 0);
return (1);
}
/* Do something interesting... */
/* Close the handle. */
if ((ret = dbenv->close(dbenv, 0)) != 0) {
fprintf(stderr, "DB_ENV->close: %s\n", db_strerror(ret));
return (1);
}
return (0);
}
int
db_teardown(home, data_dir, errfp, progname)
const char *home, *data_dir, *progname;
FILE *errfp;
{
DB_ENV *dbenv;
int ret;
/* Remove the shared database regions. */
if ((ret = db_env_create(&dbenv, 0)) != 0) {
fprintf(errfp, "%s: %s\n", progname, db_strerror(ret));
return (1);
}
dbenv->set_errfile(dbenv, errfp);
dbenv->set_errpfx(dbenv, progname);
(void)dbenv->set_data_dir(dbenv, data_dir);
if ((ret = dbenv->remove(dbenv, home, 0)) != 0) {
fprintf(stderr, "DB_ENV->remove: %s\n", db_strerror(ret));
return (1);
}
return (0);
}

239
examples_c/ex_lock.c Normal file
View File

@@ -0,0 +1,239 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1997,2008 Oracle. All rights reserved.
*
* $Id: ex_lock.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
extern int getopt(int, char * const *, const char *);
#else
#include <unistd.h>
#endif
#include <db.h>
int db_init __P((const char *, u_int32_t, int));
int main __P((int, char *[]));
int usage __P((void));
DB_ENV *dbenv;
const char
*progname = "ex_lock"; /* Program name. */
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
extern int optind;
DBT lock_dbt;
DB_LOCK lock;
DB_LOCK *locks;
db_lockmode_t lock_type;
long held;
size_t len;
u_int32_t locker, maxlocks;
int ch, do_unlink, did_get, i, lockid, lockcount, ret;
const char *home;
char opbuf[16], objbuf[1024], lockbuf[16];
home = "TESTDIR";
maxlocks = 0;
do_unlink = 0;
while ((ch = getopt(argc, argv, "h:m:u")) != EOF)
switch (ch) {
case 'h':
home = optarg;
break;
case 'm':
if ((i = atoi(optarg)) <= 0)
return (usage());
maxlocks = (u_int32_t)i; /* XXX: possible overflow. */
break;
case 'u':
do_unlink = 1;
break;
case '?':
default:
return (usage());
}
argc -= optind;
argv += optind;
if (argc != 0)
return (usage());
/* Initialize the database environment. */
if ((ret = db_init(home, maxlocks, do_unlink)) != 0)
return (ret);
locks = 0;
lockcount = 0;
/*
* Accept lock requests.
*/
if ((ret = dbenv->lock_id(dbenv, &locker)) != 0) {
dbenv->err(dbenv, ret, "unable to get locker id");
(void)dbenv->close(dbenv, 0);
return (EXIT_FAILURE);
}
lockid = -1;
memset(&lock_dbt, 0, sizeof(lock_dbt));
for (held = 0, did_get = 0;;) {
printf("Operation get/release [get]> ");
fflush(stdout);
if (fgets(opbuf, sizeof(opbuf), stdin) == NULL)
break;
if ((len = strlen(opbuf)) <= 1 || strcmp(opbuf, "get\n") == 0) {
/* Acquire a lock. */
printf("input object (text string) to lock> ");
fflush(stdout);
if (fgets(objbuf, sizeof(objbuf), stdin) == NULL)
break;
if ((len = strlen(objbuf)) <= 1)
continue;
do {
printf("lock type read/write [read]> ");
fflush(stdout);
if (fgets(lockbuf,
sizeof(lockbuf), stdin) == NULL)
break;
len = strlen(lockbuf);
} while (len > 1 &&
strcmp(lockbuf, "read\n") != 0 &&
strcmp(lockbuf, "write\n") != 0);
if (len == 1 || strcmp(lockbuf, "read\n") == 0)
lock_type = DB_LOCK_READ;
else
lock_type = DB_LOCK_WRITE;
lock_dbt.data = objbuf;
lock_dbt.size = (u_int32_t)strlen(objbuf);
ret = dbenv->lock_get(dbenv, locker,
DB_LOCK_NOWAIT, &lock_dbt, lock_type, &lock);
if (ret == 0) {
did_get = 1;
lockid = lockcount++;
if (locks == NULL)
locks =
(DB_LOCK *)malloc(sizeof(DB_LOCK));
else
locks = (DB_LOCK *)realloc(locks,
lockcount * sizeof(DB_LOCK));
locks[lockid] = lock;
}
} else {
/* Release a lock. */
do {
printf("input lock to release> ");
fflush(stdout);
if (fgets(objbuf,
sizeof(objbuf), stdin) == NULL)
break;
} while ((len = strlen(objbuf)) <= 1);
lockid = strtol(objbuf, NULL, 16);
if (lockid < 0 || lockid >= lockcount) {
printf("Lock #%d out of range\n", lockid);
continue;
}
lock = locks[lockid];
ret = dbenv->lock_put(dbenv, &lock);
did_get = 0;
}
switch (ret) {
case 0:
printf("Lock #%d %s\n", lockid,
did_get ? "granted" : "released");
held += did_get ? 1 : -1;
break;
case DB_LOCK_NOTGRANTED:
dbenv->err(dbenv, ret, NULL);
break;
case DB_LOCK_DEADLOCK:
dbenv->err(dbenv, ret,
"lock_%s", did_get ? "get" : "put");
break;
default:
dbenv->err(dbenv, ret,
"lock_%s", did_get ? "get" : "put");
(void)dbenv->close(dbenv, 0);
return (EXIT_FAILURE);
}
}
printf("\nClosing lock region %ld locks held\n", held);
if (locks != NULL)
free(locks);
if ((ret = dbenv->close(dbenv, 0)) != 0) {
fprintf(stderr,
"%s: dbenv->close: %s\n", progname, db_strerror(ret));
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
/*
* db_init --
* Initialize the environment.
*/
int
db_init(home, maxlocks, do_unlink)
const char *home;
u_int32_t maxlocks;
int do_unlink;
{
int ret;
if ((ret = db_env_create(&dbenv, 0)) != 0) {
fprintf(stderr, "%s: db_env_create: %s\n",
progname, db_strerror(ret));
return (EXIT_FAILURE);
}
if (do_unlink) {
if ((ret = dbenv->remove(dbenv, home, DB_FORCE)) != 0) {
fprintf(stderr, "%s: dbenv->remove: %s\n",
progname, db_strerror(ret));
return (EXIT_FAILURE);
}
if ((ret = db_env_create(&dbenv, 0)) != 0) {
fprintf(stderr, "%s: db_env_create: %s\n",
progname, db_strerror(ret));
return (EXIT_FAILURE);
}
}
dbenv->set_errfile(dbenv, stderr);
dbenv->set_errpfx(dbenv, progname);
if (maxlocks != 0)
dbenv->set_lk_max_locks(dbenv, maxlocks);
if ((ret =
dbenv->open(dbenv, home, DB_CREATE | DB_INIT_LOCK, 0)) != 0) {
dbenv->err(dbenv, ret, NULL);
(void)dbenv->close(dbenv, 0);
return (EXIT_FAILURE);
}
return (0);
}
int
usage()
{
(void)fprintf(stderr,
"usage: %s [-u] [-h home] [-m maxlocks]\n", progname);
return (EXIT_FAILURE);
}

258
examples_c/ex_mpool.c Normal file
View File

@@ -0,0 +1,258 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1997,2008 Oracle. All rights reserved.
*
* $Id: ex_mpool.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
extern int getopt(int, char * const *, const char *);
#else
#include <unistd.h>
#endif
#include <db.h>
int init __P((const char *, int, int, const char *));
int run __P((int, int, int, int, const char *));
int run_mpool __P((int, int, int, int, const char *));
int main __P((int, char *[]));
int usage __P((const char *));
#define MPOOL "mpool" /* File. */
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
extern int optind;
int cachesize, ch, hits, npages, pagesize;
char *progname;
cachesize = 20 * 1024;
hits = 1000;
npages = 50;
pagesize = 1024;
progname = argv[0];
while ((ch = getopt(argc, argv, "c:h:n:p:")) != EOF)
switch (ch) {
case 'c':
if ((cachesize = atoi(optarg)) < 20 * 1024)
return (usage(progname));
break;
case 'h':
if ((hits = atoi(optarg)) <= 0)
return (usage(progname));
break;
case 'n':
if ((npages = atoi(optarg)) <= 0)
return (usage(progname));
break;
case 'p':
if ((pagesize = atoi(optarg)) <= 0)
return (usage(progname));
break;
case '?':
default:
return (usage(progname));
}
argc -= optind;
argv += optind;
return (run_mpool(pagesize, cachesize,
hits, npages, progname) == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
int
usage(progname)
const char *progname;
{
(void)fprintf(stderr,
"usage: %s [-c cachesize] [-h hits] [-n npages] [-p pagesize]\n",
progname);
return (EXIT_FAILURE);
}
int
run_mpool(pagesize, cachesize, hits, npages, progname)
int pagesize, cachesize, hits, npages;
const char *progname;
{
int ret;
/* Initialize the file. */
if ((ret = init(MPOOL, pagesize, npages, progname)) != 0)
return (ret);
/* Get the pages. */
if ((ret = run(hits, cachesize, pagesize, npages, progname)) != 0)
return (ret);
return (0);
}
/*
* init --
* Create a backing file.
*/
int
init(file, pagesize, npages, progname)
const char *file, *progname;
int pagesize, npages;
{
FILE *fp;
int cnt;
char *p;
/*
* Create a file with the right number of pages, and store a page
* number on each page.
*/
(void)remove(file);
if ((fp = fopen(file, "wb")) == NULL) {
fprintf(stderr,
"%s: %s: %s\n", progname, file, strerror(errno));
return (1);
}
if ((p = (char *)malloc(pagesize)) == NULL) {
fprintf(stderr, "%s: %s\n", progname, strerror(ENOMEM));
return (1);
}
/*
* The pages are numbered from 0, not 1.
*
* Write the index of the page at the beginning of the page in order
* to verify the retrieved page (see run()).
*/
for (cnt = 0; cnt < npages; ++cnt) {
*(db_pgno_t *)p = cnt;
if (fwrite(p, pagesize, 1, fp) != 1) {
fprintf(stderr,
"%s: %s: %s\n", progname, file, strerror(errno));
return (1);
}
}
(void)fclose(fp);
free(p);
return (0);
}
/*
* run --
* Get a set of pages.
*/
int
run(hits, cachesize, pagesize, npages, progname)
int hits, cachesize, pagesize, npages;
const char *progname;
{
DB_ENV *dbenv;
DB_MPOOLFILE *mfp;
db_pgno_t pageno;
int cnt, ret;
void *p;
dbenv = NULL;
mfp = NULL;
printf("%s: cachesize: %d; pagesize: %d; N pages: %d\n",
progname, cachesize, pagesize, npages);
/*
* Open a memory pool, specify a cachesize, output error messages
* to stderr.
*/
if ((ret = db_env_create(&dbenv, 0)) != 0) {
fprintf(stderr,
"%s: db_env_create: %s\n", progname, db_strerror(ret));
return (1);
}
dbenv->set_errfile(dbenv, stderr);
dbenv->set_errpfx(dbenv, progname);
#ifdef HAVE_VXWORKS
if ((ret = dbenv->set_shm_key(dbenv, VXSHM_KEY)) != 0) {
dbenv->err(dbenv, ret, "set_shm_key");
return (1);
}
#endif
/* Set the cachesize. */
if ((ret = dbenv->set_cachesize(dbenv, 0, cachesize, 0)) != 0) {
dbenv->err(dbenv, ret, "set_cachesize");
goto err;
}
/* Open the environment. */
if ((ret = dbenv->open(
dbenv, NULL, DB_CREATE | DB_INIT_MPOOL, 0)) != 0) {
dbenv->err(dbenv, ret, "DB_ENV->open");
goto err;
}
/* Open the file in the environment. */
if ((ret = dbenv->memp_fcreate(dbenv, &mfp, 0)) != 0) {
dbenv->err(dbenv, ret, "DB_ENV->memp_fcreate: %s", MPOOL);
goto err;
}
if ((ret = mfp->open(mfp, MPOOL, 0, 0, pagesize)) != 0) {
dbenv->err(dbenv, ret, "DB_MPOOLFILE->open: %s", MPOOL);
goto err;
}
printf("retrieve %d random pages... ", hits);
srand((u_int)time(NULL));
for (cnt = 0; cnt < hits; ++cnt) {
pageno = rand() % npages;
if ((ret = mfp->get(mfp, &pageno, NULL, 0, &p)) != 0) {
dbenv->err(dbenv, ret,
"unable to retrieve page %lu", (u_long)pageno);
goto err;
}
/* Verify the page's number that was written in init(). */
if (*(db_pgno_t *)p != pageno) {
dbenv->errx(dbenv,
"wrong page retrieved (%lu != %d)",
(u_long)pageno, *(int *)p);
goto err;
}
if ((ret = mfp->put(mfp, p, DB_PRIORITY_UNCHANGED, 0)) != 0) {
dbenv->err(dbenv, ret,
"unable to return page %lu", (u_long)pageno);
goto err;
}
}
printf("successful.\n");
/* Close the file. */
if ((ret = mfp->close(mfp, 0)) != 0) {
dbenv->err(dbenv, ret, "DB_MPOOLFILE->close");
goto err;
}
/* Close the pool. */
if ((ret = dbenv->close(dbenv, 0)) != 0) {
fprintf(stderr,
"%s: db_env_create: %s\n", progname, db_strerror(ret));
return (1);
}
return (0);
err: if (mfp != NULL)
(void)mfp->close(mfp, 0);
if (dbenv != NULL)
(void)dbenv->close(dbenv, 0);
return (1);
}

19
examples_c/ex_rep/README Normal file
View File

@@ -0,0 +1,19 @@
# $Id: README,v 1.1 2006/07/07 23:36:13 alanb Exp $
This is the parent directory for the replication example programs.
The example is a toy stock quote server. There are two versions of
the program: one version uses Berkeley DB's Replication Manager
support, and the other uses the base replication API.
common/ Contains code to implement the basic functions of the
application, to demonstrate that these are largely
independent of which replication API is used.
mgr/ Contains the small amount of code necessary to
configure the application to use Replication Manager.
base/ Contains the sample communications infrastructure, and
other replication support code, to demonstrate some of
the kinds of things that are necessary when using the
base replication API.

View File

@@ -0,0 +1,304 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2001,2008 Oracle. All rights reserved.
*
* $Id: rep_base.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#include "rep_base.h"
/*
* Process globals (we could put these in the machtab I suppose).
*/
int master_eid;
char *myaddr;
unsigned short myport;
static void event_callback __P((DB_ENV *, u_int32_t, void *));
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
DB_ENV *dbenv;
DBT local;
enum { MASTER, CLIENT, UNKNOWN } whoami;
all_args aa;
connect_args ca;
machtab_t *machtab;
thread_t all_thr, conn_thr;
void *astatus, *cstatus;
#ifdef _WIN32
WSADATA wsaData;
#else
struct sigaction sigact;
#endif
repsite_t site, *sitep, self, *selfp;
int maxsites, nsites, ret, priority, totalsites;
char *c, ch;
const char *home, *progname;
APP_DATA my_app_data;
master_eid = DB_EID_INVALID;
my_app_data.elected = 0;
my_app_data.shared_data.is_master = 0; /* assume start out as client */
dbenv = NULL;
whoami = UNKNOWN;
machtab = NULL;
selfp = sitep = NULL;
maxsites = nsites = ret = totalsites = 0;
priority = 100;
home = "TESTDIR";
progname = "ex_rep_base";
if ((ret = create_env(progname, &dbenv)) != 0)
goto err;
dbenv->app_private = &my_app_data;
(void)dbenv->set_event_notify(dbenv, event_callback);
while ((ch = getopt(argc, argv, "Ch:Mm:n:o:p:v")) != EOF)
switch (ch) {
case 'M':
whoami = MASTER;
master_eid = SELF_EID;
break;
case 'C':
whoami = CLIENT;
break;
case 'h':
home = optarg;
break;
case 'm':
if ((myaddr = strdup(optarg)) == NULL) {
fprintf(stderr,
"System error %s\n", strerror(errno));
goto err;
}
self.host = optarg;
self.host = strtok(self.host, ":");
if ((c = strtok(NULL, ":")) == NULL) {
fprintf(stderr, "Bad host specification.\n");
goto err;
}
myport = self.port = (unsigned short)atoi(c);
selfp = &self;
break;
case 'n':
totalsites = atoi(optarg);
break;
case 'o':
site.host = optarg;
site.host = strtok(site.host, ":");
if ((c = strtok(NULL, ":")) == NULL) {
fprintf(stderr, "Bad host specification.\n");
goto err;
}
site.port = atoi(c);
if (sitep == NULL || nsites >= maxsites) {
maxsites = maxsites == 0 ? 10 : 2 * maxsites;
if ((sitep = realloc(sitep,
maxsites * sizeof(repsite_t))) == NULL) {
fprintf(stderr, "System error %s\n",
strerror(errno));
goto err;
}
}
sitep[nsites++] = site;
break;
case 'p':
priority = atoi(optarg);
break;
case 'v':
if ((ret = dbenv->set_verbose(dbenv,
DB_VERB_REPLICATION, 1)) != 0)
goto err;
break;
case '?':
default:
usage(progname);
}
/* Error check command line. */
if (whoami == UNKNOWN) {
fprintf(stderr, "Must specify -M or -C.\n");
goto err;
}
if (selfp == NULL)
usage(progname);
if (home == NULL)
usage(progname);
dbenv->rep_set_priority(dbenv, priority);
#ifdef _WIN32
/* Initialize the Windows sockets DLL. */
if ((ret = WSAStartup(MAKEWORD(2, 2), &wsaData)) != 0) {
fprintf(stderr,
"Unable to initialize Windows sockets: %d\n", ret);
goto err;
}
#else
/*
* Turn off SIGPIPE so that we don't kill processes when they
* happen to lose a connection at the wrong time.
*/
memset(&sigact, 0, sizeof(sigact));
sigact.sa_handler = SIG_IGN;
if ((ret = sigaction(SIGPIPE, &sigact, NULL)) != 0) {
fprintf(stderr,
"Unable to turn off SIGPIPE: %s\n", strerror(ret));
goto err;
}
#endif
/*
* We are hardcoding priorities here that all clients have the
* same priority except for a designated master who gets a higher
* priority.
*/
if ((ret =
machtab_init(&machtab, totalsites)) != 0)
goto err;
my_app_data.comm_infrastructure = machtab;
if ((ret = env_init(dbenv, home)) != 0)
goto err;
/*
* Now sets up comm infrastructure. There are two phases. First,
* we open our port for listening for incoming connections. Then
* we attempt to connect to every host we know about.
*/
(void)dbenv->rep_set_transport(dbenv, SELF_EID, quote_send);
ca.dbenv = dbenv;
ca.home = home;
ca.progname = progname;
ca.machtab = machtab;
ca.port = selfp->port;
if ((ret = thread_create(&conn_thr, NULL, connect_thread, &ca)) != 0) {
dbenv->errx(dbenv, "can't create connect thread");
goto err;
}
aa.dbenv = dbenv;
aa.progname = progname;
aa.home = home;
aa.machtab = machtab;
aa.sites = sitep;
aa.nsites = nsites;
if ((ret = thread_create(&all_thr, NULL, connect_all, &aa)) != 0) {
dbenv->errx(dbenv, "can't create connect-all thread");
goto err;
}
/*
* We have now got the entire communication infrastructure set up.
* It's time to declare ourselves to be a client or master.
*/
if (whoami == MASTER) {
if ((ret = dbenv->rep_start(dbenv, NULL, DB_REP_MASTER)) != 0) {
dbenv->err(dbenv, ret, "dbenv->rep_start failed");
goto err;
}
} else {
memset(&local, 0, sizeof(local));
local.data = myaddr;
local.size = (u_int32_t)strlen(myaddr) + 1;
if ((ret =
dbenv->rep_start(dbenv, &local, DB_REP_CLIENT)) != 0) {
dbenv->err(dbenv, ret, "dbenv->rep_start failed");
goto err;
}
/* Sleep to give ourselves time to find a master. */
sleep(5);
}
if ((ret = doloop(dbenv, &my_app_data.shared_data)) != 0) {
dbenv->err(dbenv, ret, "Main loop failed");
goto err;
}
/* Wait on the connection threads. */
if (thread_join(all_thr, &astatus) || thread_join(conn_thr, &cstatus)) {
ret = -1;
goto err;
}
if ((uintptr_t)astatus != EXIT_SUCCESS ||
(uintptr_t)cstatus != EXIT_SUCCESS) {
ret = -1;
goto err;
}
/*
* We have used the DB_TXN_NOSYNC environment flag for improved
* performance without the usual sacrifice of transactional durability,
* as discussed in the "Transactional guarantees" page of the Reference
* Guide: if one replication site crashes, we can expect the data to
* exist at another site. However, in case we shut down all sites
* gracefully, we push out the end of the log here so that the most
* recent transactions don't mysteriously disappear.
*/
if ((ret = dbenv->log_flush(dbenv, NULL)) != 0)
dbenv->err(dbenv, ret, "log_flush");
err: if (machtab != NULL)
free(machtab);
if (dbenv != NULL)
(void)dbenv->close(dbenv, 0);
#ifdef _WIN32
/* Shut down the Windows sockets DLL. */
(void)WSACleanup();
#endif
return (ret);
}
static void
event_callback(dbenv, which, info)
DB_ENV *dbenv;
u_int32_t which;
void *info;
{
APP_DATA *app = dbenv->app_private;
SHARED_DATA *shared = &app->shared_data;
switch (which) {
case DB_EVENT_REP_CLIENT:
shared->is_master = 0;
break;
case DB_EVENT_REP_ELECTED:
app->elected = 1;
master_eid = SELF_EID;
break;
case DB_EVENT_REP_MASTER:
shared->is_master = 1;
break;
case DB_EVENT_REP_NEWMASTER:
master_eid = *(int*)info;
break;
case DB_EVENT_REP_STARTUPDONE:
/* I don't care about this, for now. */
break;
default:
dbenv->errx(dbenv, "ignoring event %d", which);
}
}

View File

@@ -0,0 +1,141 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2001,2008 Oracle. All rights reserved.
*
* $Id: rep_base.h 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#ifndef _EX_REPQUOTE_H_
#define _EX_REPQUOTE_H_
#include "../common/rep_common.h"
#define SELF_EID 1
typedef struct {
char *host; /* Host name. */
u_int32_t port; /* Port on which to connect to this site. */
} repsite_t;
/* Globals */
typedef struct {
SHARED_DATA shared_data;
int elected;
void *comm_infrastructure;
} APP_DATA;
extern int master_eid;
extern char *myaddr;
extern unsigned short myport;
struct __member; typedef struct __member member_t;
struct __machtab; typedef struct __machtab machtab_t;
/* Arguments for the connect_all thread. */
typedef struct {
DB_ENV *dbenv;
const char *progname;
const char *home;
machtab_t *machtab;
repsite_t *sites;
int nsites;
} all_args;
/* Arguments for the connect_loop thread. */
typedef struct {
DB_ENV *dbenv;
const char * home;
const char * progname;
machtab_t *machtab;
int port;
} connect_args;
#define CACHESIZE (10 * 1024 * 1024)
#define DATABASE "quote.db"
#define MAX_THREADS 25
#define SLEEPTIME 3
#ifndef COMPQUIET
#define COMPQUIET(x,y) x = (y)
#endif
/* Portability macros for basic threading and networking */
#ifdef _WIN32
#include <winsock2.h>
#include <windows.h>
extern int getopt(int, char * const *, const char *);
typedef HANDLE thread_t;
#define thread_create(thrp, attr, func, arg) \
(((*(thrp) = CreateThread(NULL, 0, \
(LPTHREAD_START_ROUTINE)(func), (arg), 0, NULL)) == NULL) ? -1 : 0)
#define thread_join(thr, statusp) \
((WaitForSingleObject((thr), INFINITE) == WAIT_OBJECT_0) && \
GetExitCodeThread((thr), (LPDWORD)(statusp)) ? 0 : -1)
typedef HANDLE mutex_t;
#define mutex_init(m, attr) \
(((*(m) = CreateMutex(NULL, FALSE, NULL)) != NULL) ? 0 : -1)
#define mutex_lock(m) \
((WaitForSingleObject(*(m), INFINITE) == WAIT_OBJECT_0) ? 0 : -1)
#define mutex_unlock(m) (ReleaseMutex(*(m)) ? 0 : -1)
#define sleep(s) Sleep(1000 * (s))
typedef int socklen_t;
typedef SOCKET socket_t;
#define SOCKET_CREATION_FAILURE INVALID_SOCKET
#define readsocket(s, buf, sz) recv((s), (buf), (int)(sz), 0)
#define writesocket(s, buf, sz) send((s), (const char *)(buf), (int)(sz), 0)
#define net_errno WSAGetLastError()
#else /* !_WIN32 */
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <netdb.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
typedef pthread_t thread_t;
#define thread_create(thrp, attr, func, arg) \
pthread_create((thrp), (attr), (func), (arg))
#define thread_join(thr, statusp) pthread_join((thr), (statusp))
typedef pthread_mutex_t mutex_t;
#define mutex_init(m, attr) pthread_mutex_init((m), (attr))
#define mutex_lock(m) pthread_mutex_lock(m)
#define mutex_unlock(m) pthread_mutex_unlock(m)
typedef int socket_t;
#define SOCKET_CREATION_FAILURE -1
#define closesocket(fd) close(fd)
#define net_errno errno
#define readsocket(s, buf, sz) read((s), (buf), (sz))
#define writesocket(s, buf, sz) write((s), (buf), (sz))
#endif
void *connect_all __P((void *args));
void *connect_thread __P((void *args));
int doclient __P((DB_ENV *, const char *, machtab_t *));
int domaster __P((DB_ENV *, const char *));
socket_t get_accepted_socket __P((const char *, int));
socket_t get_connected_socket
__P((machtab_t *, const char *, const char *, int, int *, int *));
int get_next_message __P((socket_t, DBT *, DBT *));
socket_t listen_socket_init __P((const char *, int));
socket_t listen_socket_accept
__P((machtab_t *, const char *, socket_t, int *));
int machtab_getinfo __P((machtab_t *, int, u_int32_t *, int *));
int machtab_init __P((machtab_t **, int));
void machtab_parm __P((machtab_t *, int *, u_int32_t *));
int machtab_rem __P((machtab_t *, int, int));
int quote_send __P((DB_ENV *, const DBT *, const DBT *, const DB_LSN *,
int, u_int32_t));
#endif /* !_EX_REPQUOTE_H_ */

View File

@@ -0,0 +1,467 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2001,2008 Oracle. All rights reserved.
*
* $Id: rep_msg.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#include "rep_base.h"
static int connect_site __P((DB_ENV *, machtab_t *,
const char *, repsite_t *, int *, thread_t *));
static void *elect_thread __P((void *));
static void *hm_loop __P((void *));
typedef struct {
DB_ENV *dbenv;
machtab_t *machtab;
} elect_args;
typedef struct {
DB_ENV *dbenv;
const char *progname;
const char *home;
socket_t fd;
u_int32_t eid;
machtab_t *tab;
} hm_loop_args;
/*
* This is a generic message handling loop that is used both by the
* master to accept messages from a client as well as by clients
* to communicate with other clients.
*/
static void *
hm_loop(args)
void *args;
{
DB_ENV *dbenv;
DB_LSN permlsn;
DBT rec, control;
APP_DATA *app;
const char *c, *home, *progname;
elect_args *ea;
hm_loop_args *ha;
machtab_t *tab;
thread_t elect_thr, *site_thrs, *tmp, tid;
repsite_t self;
u_int32_t timeout;
int eid, n, nsites, nsites_allocd;
int already_open, r, ret, t_ret;
socket_t fd;
void *status;
ea = NULL;
site_thrs = NULL;
nsites_allocd = 0;
nsites = 0;
ha = (hm_loop_args *)args;
dbenv = ha->dbenv;
fd = ha->fd;
home = ha->home;
eid = ha->eid;
progname = ha->progname;
tab = ha->tab;
free(ha);
app = dbenv->app_private;
memset(&rec, 0, sizeof(DBT));
memset(&control, 0, sizeof(DBT));
for (ret = 0; ret == 0;) {
if ((ret = get_next_message(fd, &rec, &control)) != 0) {
/*
* Close this connection; if it's the master call
* for an election.
*/
closesocket(fd);
if ((ret = machtab_rem(tab, eid, 1)) != 0)
break;
/*
* If I'm the master, I just lost a client and this
* thread is done.
*/
if (master_eid == SELF_EID)
break;
/*
* If I was talking with the master and the master
* went away, I need to call an election; else I'm
* done.
*/
if (master_eid != eid)
break;
master_eid = DB_EID_INVALID;
machtab_parm(tab, &n, &timeout);
(void)dbenv->rep_set_timeout(dbenv,
DB_REP_ELECTION_TIMEOUT, timeout);
if ((ret = dbenv->rep_elect(dbenv,
n, (n/2+1), 0)) != 0)
continue;
/*
* Regardless of the results, the site I was talking
* to is gone, so I have nothing to do but exit.
*/
if (app->elected) {
app->elected = 0;
ret = dbenv->rep_start(dbenv,
NULL, DB_REP_MASTER);
}
break;
}
switch (r = dbenv->rep_process_message(dbenv,
&control, &rec, eid, &permlsn)) {
case DB_REP_NEWSITE:
/*
* Check if we got sent connect information and if we
* did, if this is me or if we already have a
* connection to this new site. If we don't,
* establish a new one.
*/
/* No connect info. */
if (rec.size == 0)
break;
/* It's me, do nothing. */
if (strncmp(myaddr, rec.data, rec.size) == 0)
break;
self.host = (char *)rec.data;
self.host = strtok(self.host, ":");
if ((c = strtok(NULL, ":")) == NULL) {
dbenv->errx(dbenv, "Bad host specification");
goto out;
}
self.port = atoi(c);
/*
* We try to connect to the new site. If we can't,
* we treat it as an error since we know that the site
* should be up if we got a message from it (even
* indirectly).
*/
if (nsites == nsites_allocd) {
/* Need to allocate more space. */
if ((tmp = realloc(
site_thrs, (10 + nsites) *
sizeof(thread_t))) == NULL) {
ret = errno;
goto out;
}
site_thrs = tmp;
nsites_allocd += 10;
}
if ((ret = connect_site(dbenv, tab, progname,
&self, &already_open, &tid)) != 0)
goto out;
if (!already_open)
memcpy(&site_thrs
[nsites++], &tid, sizeof(thread_t));
break;
case DB_REP_HOLDELECTION:
if (master_eid == SELF_EID)
break;
/* Make sure that previous election has finished. */
if (ea != NULL) {
if (thread_join(elect_thr, &status) != 0) {
dbenv->errx(dbenv,
"thread join failure");
goto out;
}
ea = NULL;
}
if ((ea = calloc(sizeof(elect_args), 1)) == NULL) {
dbenv->errx(dbenv, "can't allocate memory");
ret = errno;
goto out;
}
ea->dbenv = dbenv;
ea->machtab = tab;
if ((ret = thread_create(&elect_thr,
NULL, elect_thread, (void *)ea)) != 0) {
dbenv->errx(dbenv,
"can't create election thread");
}
break;
case DB_REP_ISPERM:
break;
case 0:
if (app->elected) {
app->elected = 0;
if ((ret = dbenv->rep_start(dbenv,
NULL, DB_REP_MASTER)) != 0) {
dbenv->err(dbenv, ret,
"can't start as master");
goto out;
}
}
break;
default:
dbenv->err(dbenv, r, "DB_ENV->rep_process_message");
break;
}
}
out: if ((t_ret = machtab_rem(tab, eid, 1)) != 0 && ret == 0)
ret = t_ret;
/* Don't close the environment before any children exit. */
if (ea != NULL && thread_join(elect_thr, &status) != 0)
dbenv->errx(dbenv, "can't join election thread");
if (site_thrs != NULL)
while (--nsites >= 0)
if (thread_join(site_thrs[nsites], &status) != 0)
dbenv->errx(dbenv, "can't join site thread");
return ((void *)(uintptr_t)ret);
}
/*
* This is a generic thread that spawns a thread to listen for connections
* on a socket and then spawns off child threads to handle each new
* connection.
*/
void *
connect_thread(args)
void *args;
{
DB_ENV *dbenv;
const char *home, *progname;
hm_loop_args *ha;
connect_args *cargs;
machtab_t *machtab;
thread_t hm_thrs[MAX_THREADS];
void *status;
int i, eid, port, ret;
socket_t fd, ns;
ha = NULL;
cargs = (connect_args *)args;
dbenv = cargs->dbenv;
home = cargs->home;
progname = cargs->progname;
machtab = cargs->machtab;
port = cargs->port;
/*
* Loop forever, accepting connections from new machines,
* and forking off a thread to handle each.
*/
if ((fd = listen_socket_init(progname, port)) < 0) {
ret = errno;
goto err;
}
for (i = 0; i < MAX_THREADS; i++) {
if ((ns = listen_socket_accept(machtab,
progname, fd, &eid)) == SOCKET_CREATION_FAILURE) {
ret = errno;
goto err;
}
if ((ha = calloc(sizeof(hm_loop_args), 1)) == NULL) {
dbenv->errx(dbenv, "can't allocate memory");
ret = errno;
goto err;
}
ha->progname = progname;
ha->home = home;
ha->fd = ns;
ha->eid = eid;
ha->tab = machtab;
ha->dbenv = dbenv;
if ((ret = thread_create(&hm_thrs[i++], NULL,
hm_loop, (void *)ha)) != 0) {
dbenv->errx(dbenv, "can't create thread for site");
goto err;
}
ha = NULL;
}
/* If we fell out, we ended up with too many threads. */
dbenv->errx(dbenv, "Too many threads");
ret = ENOMEM;
/* Do not return until all threads have exited. */
while (--i >= 0)
if (thread_join(hm_thrs[i], &status) != 0)
dbenv->errx(dbenv, "can't join site thread");
err: return (ret == 0 ? (void *)EXIT_SUCCESS : (void *)EXIT_FAILURE);
}
/*
* Open a connection to everyone that we've been told about. If we
* cannot open some connections, keep trying.
*/
void *
connect_all(args)
void *args;
{
DB_ENV *dbenv;
all_args *aa;
const char *home, *progname;
hm_loop_args *ha;
int failed, i, nsites, open, ret, *success;
machtab_t *machtab;
thread_t *hm_thr;
repsite_t *sites;
ha = NULL;
aa = (all_args *)args;
dbenv = aa->dbenv;
progname = aa->progname;
home = aa->home;
machtab = aa->machtab;
nsites = aa->nsites;
sites = aa->sites;
ret = 0;
hm_thr = NULL;
success = NULL;
/* Some implementations of calloc are sad about allocating 0 things. */
if ((success = calloc(nsites > 0 ? nsites : 1, sizeof(int))) == NULL) {
dbenv->err(dbenv, errno, "connect_all");
ret = 1;
goto err;
}
if (nsites > 0 && (hm_thr = calloc(nsites, sizeof(int))) == NULL) {
dbenv->err(dbenv, errno, "connect_all");
ret = 1;
goto err;
}
for (failed = nsites; failed > 0;) {
for (i = 0; i < nsites; i++) {
if (success[i])
continue;
ret = connect_site(dbenv, machtab,
progname, &sites[i], &open, &hm_thr[i]);
/*
* If we couldn't make the connection, this isn't
* fatal to the loop, but we have nothing further
* to do on this machine at the moment.
*/
if (ret == DB_REP_UNAVAIL)
continue;
if (ret != 0)
goto err;
failed--;
success[i] = 1;
/* If the connection is already open, we're done. */
if (ret == 0 && open == 1)
continue;
}
sleep(1);
}
err: if (success != NULL)
free(success);
if (hm_thr != NULL)
free(hm_thr);
return (ret ? (void *)EXIT_FAILURE : (void *)EXIT_SUCCESS);
}
static int
connect_site(dbenv, machtab, progname, site, is_open, hm_thrp)
DB_ENV *dbenv;
machtab_t *machtab;
const char *progname;
repsite_t *site;
int *is_open;
thread_t *hm_thrp;
{
int eid, ret;
socket_t s;
hm_loop_args *ha;
if ((s = get_connected_socket(machtab, progname,
site->host, site->port, is_open, &eid)) < 0)
return (DB_REP_UNAVAIL);
if (*is_open)
return (0);
if ((ha = calloc(sizeof(hm_loop_args), 1)) == NULL) {
dbenv->errx(dbenv, "can't allocate memory");
ret = errno;
goto err;
}
ha->progname = progname;
ha->fd = s;
ha->eid = eid;
ha->tab = machtab;
ha->dbenv = dbenv;
if ((ret = thread_create(hm_thrp, NULL,
hm_loop, (void *)ha)) != 0) {
dbenv->errx(dbenv, "can't create thread for connected site");
goto err1;
}
return (0);
err1: free(ha);
err:
return (ret);
}
/*
* We need to spawn off a new thread in which to hold an election in
* case we are the only thread listening on for messages.
*/
static void *
elect_thread(args)
void *args;
{
DB_ENV *dbenv;
elect_args *eargs;
machtab_t *machtab;
u_int32_t timeout;
int n, ret;
APP_DATA *app;
eargs = (elect_args *)args;
dbenv = eargs->dbenv;
machtab = eargs->machtab;
free(eargs);
app = dbenv->app_private;
machtab_parm(machtab, &n, &timeout);
(void)dbenv->rep_set_timeout(dbenv, DB_REP_ELECTION_TIMEOUT, timeout);
while ((ret = dbenv->rep_elect(dbenv, n, (n/2+1), 0)) != 0)
sleep(2);
if (app->elected) {
app->elected = 0;
if ((ret = dbenv->rep_start(dbenv, NULL, DB_REP_MASTER)) != 0)
dbenv->err(dbenv, ret,
"can't start as master in election thread");
}
return (NULL);
}

View File

@@ -0,0 +1,746 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2001,2008 Oracle. All rights reserved.
*
* $Id: rep_net.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#include "rep_base.h"
#ifndef _SYS_QUEUE_H
/*
* Some *BSD Unix variants include the Queue macros in their libraries and
* these might already have been included. In that case, it would be bad
* to include them again.
*/
#include <dbinc/queue.h> /* !!!: for the LIST_XXX macros. */
#endif
int machtab_add __P((machtab_t *, socket_t, u_int32_t, int, int *));
#ifdef DIAGNOSTIC
void machtab_print __P((machtab_t *));
#endif
ssize_t readn __P((socket_t, void *, size_t));
/*
* This file defines the communication infrastructure for the ex_repquote
* sample application.
*
* This application uses TCP/IP for its communication. In an N-site
* replication group, this means that there are N * N communication
* channels so that every site can communicate with every other site
* (this allows elections to be held when the master fails). We do
* not require that anyone know about all sites when the application
* starts up. In order to communicate, the application should know
* about someone, else it has no idea how to ever get in the game.
*
* Communication is handled via a number of different threads. These
* thread functions are implemented in rep_util.c In this file, we
* define the data structures that maintain the state that describes
* the comm infrastructure, the functions that manipulates this state
* and the routines used to actually send and receive data over the
* sockets.
*/
/*
* The communication infrastructure is represented by a machine table,
* machtab_t, which is essentially a mutex-protected linked list of members
* of the group. The machtab also contains the parameters that are needed
* to call for an election. We hardwire values for these parameters in the
* init function, but these could be set via some configuration setup in a
* real application. We reserve the machine-id 1 to refer to ourselves and
* make the machine-id 0 be invalid.
*/
#define MACHID_INVALID 0
#define MACHID_SELF 1
struct __machtab {
LIST_HEAD(__machlist, __member) machlist;
int nextid;
mutex_t mtmutex;
u_int32_t timeout_time;
int current;
int max;
int nsites;
};
/* Data structure that describes each entry in the machtab. */
struct __member {
u_int32_t hostaddr; /* Host IP address. */
int port; /* Port number. */
int eid; /* Application-specific machine id. */
socket_t fd; /* File descriptor for the socket. */
LIST_ENTRY(__member) links;
/* For linked list of all members we know of. */
};
static int quote_send_broadcast __P((machtab_t *,
const DBT *, const DBT *, u_int32_t));
static int quote_send_one __P((const DBT *, const DBT *, socket_t, u_int32_t));
/*
* machtab_init --
* Initialize the machine ID table.
* XXX Right now we treat the number of sites as the maximum
* number we've ever had on the list at one time. We probably
* want to make that smarter.
*/
int
machtab_init(machtabp, nsites)
machtab_t **machtabp;
int nsites;
{
int ret;
machtab_t *machtab;
if ((machtab = malloc(sizeof(machtab_t))) == NULL) {
fprintf(stderr, "can't allocate memory\n");
return (ENOMEM);
}
LIST_INIT(&machtab->machlist);
/* Reserve eid's 0 and 1. */
machtab->nextid = 2;
machtab->timeout_time = 2 * 1000000; /* 2 seconds. */
machtab->current = machtab->max = 0;
machtab->nsites = nsites;
ret = mutex_init(&machtab->mtmutex, NULL);
*machtabp = machtab;
return (ret);
}
/*
* machtab_add --
* Add a file descriptor to the table of machines, returning
* a new machine ID.
*/
int
machtab_add(machtab, fd, hostaddr, port, idp)
machtab_t *machtab;
socket_t fd;
u_int32_t hostaddr;
int port, *idp;
{
int ret;
member_t *m, *member;
ret = 0;
if ((member = malloc(sizeof(member_t))) == NULL) {
fprintf(stderr, "can't allocate memory\n");
return (ENOMEM);
}
member->fd = fd;
member->hostaddr = hostaddr;
member->port = port;
if ((ret = mutex_lock(&machtab->mtmutex)) != 0) {
fprintf(stderr, "can't lock mutex");
return (ret);
}
for (m = LIST_FIRST(&machtab->machlist);
m != NULL; m = LIST_NEXT(m, links))
if (m->hostaddr == hostaddr && m->port == port)
break;
if (m == NULL) {
member->eid = machtab->nextid++;
LIST_INSERT_HEAD(&machtab->machlist, member, links);
} else
member->eid = m->eid;
if ((ret = mutex_unlock(&machtab->mtmutex)) != 0) {
fprintf(stderr, "can't unlock mutex\n");
return (ret);
}
if (idp != NULL)
*idp = member->eid;
if (m == NULL) {
if (++machtab->current > machtab->max)
machtab->max = machtab->current;
} else {
free(member);
ret = EEXIST;
}
#ifdef DIAGNOSTIC
printf("Exiting machtab_add\n");
machtab_print(machtab);
#endif
return (ret);
}
/*
* machtab_getinfo --
* Return host and port information for a particular machine id.
*/
int
machtab_getinfo(machtab, eid, hostp, portp)
machtab_t *machtab;
int eid;
u_int32_t *hostp;
int *portp;
{
int ret;
member_t *member;
if ((ret = mutex_lock(&machtab->mtmutex)) != 0) {
fprintf(stderr, "can't lock mutex\n");
return (ret);
}
for (member = LIST_FIRST(&machtab->machlist);
member != NULL;
member = LIST_NEXT(member, links))
if (member->eid == eid) {
*hostp = member->hostaddr;
*portp = member->port;
break;
}
if ((ret = mutex_unlock(&machtab->mtmutex)) != 0) {
fprintf(stderr, "can't unlock mutex\n");
return (ret);
}
return (member != NULL ? 0 : EINVAL);
}
/*
* machtab_rem --
* Remove a mapping from the table of machines. Lock indicates
* whether we need to lock the machtab or not (0 indicates we do not
* need to lock; non-zero indicates that we do need to lock).
*/
int
machtab_rem(machtab, eid, lock)
machtab_t *machtab;
int eid;
int lock;
{
int found, ret;
member_t *member;
ret = 0;
if (lock && (ret = mutex_lock(&machtab->mtmutex)) != 0) {
fprintf(stderr, "can't lock mutex\n");
return (ret);
}
for (found = 0, member = LIST_FIRST(&machtab->machlist);
member != NULL;
member = LIST_NEXT(member, links))
if (member->eid == eid) {
found = 1;
LIST_REMOVE(member, links);
(void)closesocket(member->fd);
free(member);
machtab->current--;
break;
}
if (LIST_FIRST(&machtab->machlist) == NULL)
machtab->nextid = 2;
if (lock && (ret = mutex_unlock(&machtab->mtmutex)) != 0)
fprintf(stderr, "can't unlock mutex\n");
#ifdef DIAGNOSTIC
printf("Exiting machtab_rem\n");
machtab_print(machtab);
#endif
return (ret);
}
void
machtab_parm(machtab, nump, timeoutp)
machtab_t *machtab;
int *nump;
u_int32_t *timeoutp;
{
if (machtab->nsites == 0)
*nump = machtab->max;
else
*nump = machtab->nsites;
*timeoutp = machtab->timeout_time;
}
#ifdef DIAGNOSTIC
void
machtab_print(machtab)
machtab_t *machtab;
{
member_t *m;
if (mutex_lock(&machtab->mtmutex) != 0) {
fprintf(stderr, "can't lock mutex\n");
abort();
}
for (m = LIST_FIRST(&machtab->machlist);
m != NULL; m = LIST_NEXT(m, links)) {
printf("IP: %lx Port: %6d EID: %2d FD: %3d\n",
(long)m->hostaddr, m->port, m->eid, m->fd);
}
if (mutex_unlock(&machtab->mtmutex) != 0) {
fprintf(stderr, "can't unlock mutex\n");
abort();
}
}
#endif
/*
* listen_socket_init --
* Initialize a socket for listening on the specified port. Returns
* a file descriptor for the socket, ready for an accept() call
* in a thread that we're happy to let block.
*/
socket_t
listen_socket_init(progname, port)
const char *progname;
int port;
{
socket_t s;
int sockopt;
struct sockaddr_in si;
COMPQUIET(progname, NULL);
if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("can't create listen socket");
return (-1);
}
memset(&si, 0, sizeof(si));
si.sin_family = AF_INET;
si.sin_addr.s_addr = htonl(INADDR_ANY);
si.sin_port = htons((unsigned short)port);
/*
* When using this example for testing, it's common to kill and restart
* regularly. On some systems, this causes bind to fail with "address
* in use" errors unless this option is set.
*/
sockopt = 1;
setsockopt(s, SOL_SOCKET, SO_REUSEADDR,
(const char *)&sockopt, sizeof(sockopt));
if (bind(s, (struct sockaddr *)&si, sizeof(si)) != 0) {
perror("can't bind listen socket");
goto err;
}
if (listen(s, 5) != 0) {
perror("can't establish listen queue");
goto err;
}
return (s);
err: closesocket(s);
return (-1);
}
/*
* listen_socket_accept --
* Accept a connection on a socket. This is essentially just a wrapper
* for accept(3).
*/
socket_t
listen_socket_accept(machtab, progname, s, eidp)
machtab_t *machtab;
const char *progname;
socket_t s;
int *eidp;
{
struct sockaddr_in si;
socklen_t si_len;
int host, ret;
socket_t ns;
u_int16_t port;
COMPQUIET(progname, NULL);
accept_wait:
memset(&si, 0, sizeof(si));
si_len = sizeof(si);
ns = accept(s, (struct sockaddr *)&si, &si_len);
if (ns == SOCKET_CREATION_FAILURE) {
fprintf(stderr, "can't accept incoming connection\n");
return ns;
}
host = ntohl(si.sin_addr.s_addr);
/*
* Sites send their listening port when connections are first
* established, as it will be different from the outgoing port
* for this connection.
*/
if (readn(ns, &port, 2) != 2)
goto err;
port = ntohs(port);
ret = machtab_add(machtab, ns, host, port, eidp);
if (ret == EEXIST) {
closesocket(ns);
goto accept_wait;
} else if (ret != 0)
goto err;
printf("Connected to host %x port %d, eid = %d\n", host, port, *eidp);
return (ns);
err: closesocket(ns);
return SOCKET_CREATION_FAILURE;
}
/*
* get_connected_socket --
* Connect to the specified port of the specified remote machine,
* and return a file descriptor when we have accepted a connection on it.
* Add this connection to the machtab. If we already have a connection
* open to this machine, then don't create another one, return the eid
* of the connection (in *eidp) and set is_open to 1. Return 0.
*/
socket_t
get_connected_socket(machtab, progname, remotehost, port, is_open, eidp)
machtab_t *machtab;
const char *progname, *remotehost;
int port, *is_open, *eidp;
{
int ret;
socket_t s;
struct hostent *hp;
struct sockaddr_in si;
u_int32_t addr;
u_int16_t nport;
*is_open = 0;
if ((hp = gethostbyname(remotehost)) == NULL) {
fprintf(stderr, "%s: host not found: %s\n", progname,
strerror(net_errno));
return (-1);
}
if ((s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
perror("can't create outgoing socket");
return (-1);
}
memset(&si, 0, sizeof(si));
memcpy((char *)&si.sin_addr, hp->h_addr, hp->h_length);
addr = ntohl(si.sin_addr.s_addr);
ret = machtab_add(machtab, s, addr, port, eidp);
if (ret == EEXIST) {
*is_open = 1;
closesocket(s);
return (0);
} else if (ret != 0) {
closesocket(s);
return (-1);
}
si.sin_family = AF_INET;
si.sin_port = htons((unsigned short)port);
if (connect(s, (struct sockaddr *)&si, sizeof(si)) < 0) {
fprintf(stderr, "%s: connection failed: %s\n",
progname, strerror(net_errno));
(void)machtab_rem(machtab, *eidp, 1);
return (-1);
}
/*
* The first thing we send on the socket is our (listening) port
* so the site we are connecting to can register us correctly in
* its machtab.
*/
nport = htons(myport);
writesocket(s, &nport, 2);
return (s);
}
/*
* get_next_message --
* Read a single message from the specified file descriptor, and
* return it in the format used by rep functions (two DBTs and a type).
*
* This function is called in a loop by both clients and masters, and
* the resulting DBTs are manually dispatched to DB_ENV->rep_process_message().
*/
int
get_next_message(fd, rec, control)
socket_t fd;
DBT *rec, *control;
{
size_t nr;
u_int32_t rsize, csize;
u_int8_t *recbuf, *controlbuf;
/*
* The protocol we use on the wire is dead simple:
*
* 4 bytes - rec->size
* (# read above) - rec->data
* 4 bytes - control->size
* (# read above) - control->data
*/
/* Read rec->size. */
nr = readn(fd, &rsize, 4);
if (nr != 4)
return (1);
/* Read the record itself. */
if (rsize > 0) {
if (rec->size < rsize)
rec->data = realloc(rec->data, rsize);
recbuf = rec->data;
nr = readn(fd, recbuf, rsize);
} else {
if (rec->data != NULL)
free(rec->data);
rec->data = NULL;
}
rec->size = rsize;
/* Read control->size. */
nr = readn(fd, &csize, 4);
if (nr != 4)
return (1);
/* Read the control struct itself. */
if (csize > 0) {
controlbuf = control->data;
if (control->size < csize)
controlbuf = realloc(controlbuf, csize);
nr = readn(fd, controlbuf, csize);
if (nr != csize)
return (1);
} else {
if (control->data != NULL)
free(control->data);
controlbuf = NULL;
}
control->data = controlbuf;
control->size = csize;
return (0);
}
/*
* readn --
* Read a full n characters from a file descriptor, unless we get an error
* or EOF.
*/
ssize_t
readn(fd, vptr, n)
socket_t fd;
void *vptr;
size_t n;
{
size_t nleft;
ssize_t nread;
char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nread = readsocket(fd, ptr, nleft)) < 0) {
/*
* Call read() again on interrupted system call;
* on other errors, bail.
*/
if (net_errno == EINTR)
nread = 0;
else {
perror("can't read from socket");
return (-1);
}
} else if (nread == 0)
break; /* EOF */
nleft -= nread;
ptr += nread;
}
return (n - nleft);
}
/*
* quote_send --
* The f_send function for DB_ENV->set_rep_transport.
*/
int
quote_send(dbenv, control, rec, lsnp, eid, flags)
DB_ENV *dbenv;
const DBT *control, *rec;
const DB_LSN *lsnp;
int eid;
u_int32_t flags;
{
int n, ret, t_ret;
socket_t fd;
machtab_t *machtab;
member_t *m;
COMPQUIET(lsnp, NULL);
machtab =
(machtab_t *)((APP_DATA*)dbenv->app_private)->comm_infrastructure;
if (eid == DB_EID_BROADCAST) {
/*
* Right now, we do not require successful transmission.
* I'd like to move this requiring at least one successful
* transmission on PERMANENT requests.
*/
n = quote_send_broadcast(machtab, rec, control, flags);
if (n < 0 /*|| (n == 0 && LF_ISSET(DB_REP_PERMANENT))*/)
return (DB_REP_UNAVAIL);
return (0);
}
if ((ret = mutex_lock(&machtab->mtmutex)) != 0) {
dbenv->errx(dbenv, "can't lock mutex");
return (ret);
}
fd = 0;
for (m = LIST_FIRST(&machtab->machlist); m != NULL;
m = LIST_NEXT(m, links)) {
if (m->eid == eid) {
fd = m->fd;
break;
}
}
if (fd == 0) {
dbenv->err(dbenv, DB_REP_UNAVAIL,
"quote_send: cannot find machine ID %d", eid);
return (DB_REP_UNAVAIL);
}
if ((ret = quote_send_one(rec, control, fd, flags)) != 0)
fprintf(stderr, "socket write error in send() function\n");
if ((t_ret = mutex_unlock(&machtab->mtmutex)) != 0) {
dbenv->errx(dbenv, "can't unlock mutex");
if (ret == 0)
ret = t_ret;
}
return (ret);
}
/*
* quote_send_broadcast --
* Send a message to everybody.
* Returns the number of sites to which this message was successfully
* communicated. A -1 indicates a fatal error.
*/
static int
quote_send_broadcast(machtab, rec, control, flags)
machtab_t *machtab;
const DBT *rec, *control;
u_int32_t flags;
{
int ret, sent;
member_t *m, *next;
if ((ret = mutex_lock(&machtab->mtmutex)) != 0) {
fprintf(stderr, "can't lock mutex\n");
return (ret);
}
sent = 0;
for (m = LIST_FIRST(&machtab->machlist); m != NULL; m = next) {
next = LIST_NEXT(m, links);
if ((ret = quote_send_one(rec, control, m->fd, flags)) != 0) {
fprintf(stderr, "socket write error in broadcast\n");
(void)machtab_rem(machtab, m->eid, 0);
} else
sent++;
}
if (mutex_unlock(&machtab->mtmutex) != 0) {
fprintf(stderr, "can't unlock mutex\n");
return (-1);
}
return (sent);
}
/*
* quote_send_one --
* Send a message to a single machine, given that machine's file
* descriptor.
*
* !!!
* Note that the machtab mutex should be held through this call.
* It doubles as a synchronizer to make sure that two threads don't
* intersperse writes that are part of two single messages.
*/
static int
quote_send_one(rec, control, fd, flags)
const DBT *rec, *control;
socket_t fd;
u_int32_t flags;
{
int retry;
ssize_t bytes_left, nw;
u_int8_t *wp;
COMPQUIET(flags, 0);
/*
* The protocol is simply: write rec->size, write rec->data,
* write control->size, write control->data.
*/
nw = writesocket(fd, (const char *)&rec->size, 4);
if (nw != 4)
return (DB_REP_UNAVAIL);
if (rec->size > 0) {
nw = writesocket(fd, rec->data, rec->size);
if (nw < 0)
return (DB_REP_UNAVAIL);
if (nw != (ssize_t)rec->size) {
/* Try a couple of times to finish the write. */
wp = (u_int8_t *)rec->data + nw;
bytes_left = rec->size - nw;
for (retry = 0; bytes_left > 0 && retry < 3; retry++) {
nw = writesocket(fd, wp, bytes_left);
if (nw < 0)
return (DB_REP_UNAVAIL);
bytes_left -= nw;
wp += nw;
}
if (bytes_left > 0)
return (DB_REP_UNAVAIL);
}
}
nw = writesocket(fd, (const char *)&control->size, 4);
if (nw != 4)
return (DB_REP_UNAVAIL);
if (control->size > 0) {
nw = writesocket(fd, control->data, control->size);
if (nw != (ssize_t)control->size)
return (DB_REP_UNAVAIL);
}
return (0);
}

View File

@@ -0,0 +1,266 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2006,2008 Oracle. All rights reserved.
*
* $Id: rep_common.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#include "rep_common.h"
#define CACHESIZE (10 * 1024 * 1024)
#define DATABASE "quote.db"
#define SLEEPTIME 3
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#define sleep(s) Sleep(1000 * (s))
#endif
static int print_stocks __P((DB *));
static int
print_stocks(dbp)
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:
case DB_LOCK_DEADLOCK:
return (0);
default:
return (ret);
}
}
#define BUFSIZE 1024
int
doloop(dbenv, shared_data)
DB_ENV *dbenv;
SHARED_DATA *shared_data;
{
DB *dbp;
DBT key, data;
char buf[BUFSIZE], *first, *price;
u_int32_t flags;
int ret;
dbp = NULL;
ret = 0;
memset(&key, 0, sizeof(key));
memset(&data, 0, sizeof(data));
for (;;) {
printf("QUOTESERVER%s> ",
shared_data->is_master ? "" : " (read-only)");
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) == NULL)
break;
#define DELIM " \t\n"
if ((first = strtok(&buf[0], DELIM)) == NULL) {
/* Blank input line. */
price = NULL;
} else if ((price = strtok(NULL, DELIM)) == NULL) {
/* Just one input token. */
if (strncmp(buf, "exit", 4) == 0 ||
strncmp(buf, "quit", 4) == 0)
break;
dbenv->errx(dbenv, "Format: TICKER VALUE");
continue;
} else {
/* Normal two-token input line. */
if (first != NULL && !shared_data->is_master) {
dbenv->errx(dbenv, "Can't update at client");
continue;
}
}
if (dbp == NULL) {
if ((ret = db_create(&dbp, dbenv, 0)) != 0)
return (ret);
/* Set page size small so page allocation is cheap. */
if ((ret = dbp->set_pagesize(dbp, 512)) != 0)
goto err;
flags = DB_AUTO_COMMIT;
if (shared_data->is_master)
flags |= DB_CREATE;
if ((ret = dbp->open(dbp,
NULL, DATABASE, NULL, DB_BTREE, flags, 0)) != 0) {
if (ret == ENOENT) {
printf(
"No stock database yet available.\n");
if ((ret = dbp->close(dbp, 0)) != 0) {
dbenv->err(dbenv, ret,
"DB->close");
goto err;
}
dbp = NULL;
continue;
}
if (ret == DB_REP_HANDLE_DEAD ||
ret == DB_LOCK_DEADLOCK) {
dbenv->err(dbenv, ret,
"please retry the operation");
dbp->close(dbp, DB_NOSYNC);
dbp = NULL;
continue;
}
dbenv->err(dbenv, ret, "DB->open");
goto err;
}
}
if (first == NULL)
switch ((ret = print_stocks(dbp))) {
case 0:
break;
case DB_REP_HANDLE_DEAD:
(void)dbp->close(dbp, DB_NOSYNC);
dbp = NULL;
break;
default:
dbp->err(dbp, ret, "Error traversing data");
goto err;
}
else {
key.data = first;
key.size = (u_int32_t)strlen(first);
data.data = price;
data.size = (u_int32_t)strlen(price);
if ((ret = dbp->put(dbp,
NULL, &key, &data, DB_AUTO_COMMIT)) != 0) {
dbp->err(dbp, ret, "DB->put");
goto err;
}
}
}
err: if (dbp != NULL)
(void)dbp->close(dbp, DB_NOSYNC);
return (ret);
}
int
create_env(progname, dbenvp)
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);
}
/* Open and configure an environment. */
int
env_init(dbenv, home)
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_REP | DB_INIT_TXN | DB_RECOVER | DB_THREAD;
if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0)
dbenv->err(dbenv, ret, "can't open environment");
return (ret);
}
/*
* In this application, we specify all communication via the command line. In
* a real application, we would expect that information about the other sites
* in the system would be maintained in some sort of configuration file. The
* critical part of this interface is that we assume at startup that we can
* find out
* 1) what host/port we wish to listen on for connections,
* 2) a (possibly empty) list of other sites we should attempt to connect
* to; and
* 3) what our Berkeley DB home environment is.
*
* These pieces of information are expressed by the following flags.
* -m host:port (required; m stands for me)
* -o host:port (optional; o stands for other; any number of these may be
* specified)
* -h home directory
* -n nsites (optional; number of sites in replication group; defaults to 0
* in which case we try to dynamically compute the number of sites in
* the replication group)
* -p priority (optional: defaults to 100)
* -C or -M start up as client or master (optional)
*/
void
usage(progname)
const char *progname;
{
fprintf(stderr, "usage: %s ", progname);
fprintf(stderr, "[-CM][-h home][-o host:port][-m host:port]%s",
"[-n nsites][-p priority]\n");
exit(EXIT_FAILURE);
}

View File

@@ -0,0 +1,17 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2006,2008 Oracle. All rights reserved.
*
* $Id: rep_common.h 63573 2008-05-23 21:43:21Z trent.nelson $
*/
/* Data shared by both repmgr and base versions of this program. */
typedef struct {
int is_master;
} SHARED_DATA;
int create_env __P((const char *progname, DB_ENV **));
int doloop __P((DB_ENV *, SHARED_DATA *));
int env_init __P((DB_ENV *, const char *));
void usage __P((const char *));

View File

@@ -0,0 +1,201 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2001,2008 Oracle. All rights reserved.
*
* $Id: rep_mgr.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <db.h>
#include "../common/rep_common.h"
typedef struct {
SHARED_DATA shared_data;
} APP_DATA;
const char *progname = "ex_rep";
#ifdef _WIN32
extern int getopt(int, char * const *, const char *);
#endif
static void event_callback __P((DB_ENV *, u_int32_t, void *));
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
DB_ENV *dbenv;
const char *home;
char ch, *host, *portstr;
int ret, totalsites, t_ret, got_listen_address, friend;
u_int16_t port;
APP_DATA my_app_data;
u_int32_t start_policy;
int priority;
my_app_data.shared_data.is_master = 0; /* assume start out as client */
dbenv = NULL;
ret = got_listen_address = 0;
home = "TESTDIR";
if ((ret = create_env(progname, &dbenv)) != 0)
goto err;
dbenv->app_private = &my_app_data;
(void)dbenv->set_event_notify(dbenv, event_callback);
start_policy = DB_REP_ELECTION; /* default */
priority = 100; /* default */
while ((ch = getopt(argc, argv, "Cf:h:Mm:n:o:p:v")) != EOF) {
friend = 0;
switch (ch) {
case 'C':
start_policy = DB_REP_CLIENT;
break;
case 'h':
home = optarg;
break;
case 'M':
start_policy = DB_REP_MASTER;
break;
case 'm':
host = strtok(optarg, ":");
if ((portstr = strtok(NULL, ":")) == NULL) {
fprintf(stderr, "Bad host specification.\n");
goto err;
}
port = (unsigned short)atoi(portstr);
if ((ret = dbenv->repmgr_set_local_site(dbenv,
host, port, 0)) != 0) {
fprintf(stderr,
"Could not set listen address (%d).\n",
ret);
goto err;
}
got_listen_address = 1;
break;
case 'n':
totalsites = atoi(optarg);
if ((ret =
dbenv->rep_set_nsites(dbenv, totalsites)) != 0)
dbenv->err(dbenv, ret, "set_nsites");
break;
case 'f':
friend = 1; /* FALLTHROUGH */
case 'o':
host = strtok(optarg, ":");
if ((portstr = strtok(NULL, ":")) == NULL) {
fprintf(stderr, "Bad host specification.\n");
goto err;
}
port = (unsigned short)atoi(portstr);
if ((ret = dbenv->repmgr_add_remote_site(dbenv, host,
port, NULL, friend ? DB_REPMGR_PEER : 0)) != 0) {
dbenv->err(dbenv, ret,
"Could not add site %s:%d", host,
(int)port);
goto err;
}
break;
case 'p':
priority = atoi(optarg);
break;
case 'v':
if ((ret = dbenv->set_verbose(dbenv,
DB_VERB_REPLICATION, 1)) != 0)
goto err;
break;
case '?':
default:
usage(progname);
}
}
/* Error check command line. */
if ((!got_listen_address) || home == NULL)
usage(progname);
dbenv->rep_set_priority(dbenv, priority);
if ((ret = env_init(dbenv, home)) != 0)
goto err;
if ((ret = dbenv->repmgr_start(dbenv, 3, start_policy)) != 0)
goto err;
if ((ret = doloop(dbenv, &my_app_data.shared_data)) != 0) {
dbenv->err(dbenv, ret, "Client failed");
goto err;
}
/*
* We have used the DB_TXN_NOSYNC environment flag for improved
* performance without the usual sacrifice of transactional durability,
* as discussed in the "Transactional guarantees" page of the Reference
* Guide: if one replication site crashes, we can expect the data to
* exist at another site. However, in case we shut down all sites
* gracefully, we push out the end of the log here so that the most
* recent transactions don't mysteriously disappear.
*/
if ((ret = dbenv->log_flush(dbenv, NULL)) != 0) {
dbenv->err(dbenv, ret, "log_flush");
goto err;
}
err:
if (dbenv != NULL &&
(t_ret = dbenv->close(dbenv, 0)) != 0) {
fprintf(stderr, "failure closing env: %s (%d)\n",
db_strerror(t_ret), t_ret);
if (ret == 0)
ret = t_ret;
}
return (ret);
}
static void
event_callback(dbenv, which, info)
DB_ENV *dbenv;
u_int32_t which;
void *info;
{
APP_DATA *app = dbenv->app_private;
SHARED_DATA *shared = &app->shared_data;
info = NULL; /* Currently unused. */
switch (which) {
case DB_EVENT_REP_CLIENT:
shared->is_master = 0;
break;
case DB_EVENT_REP_MASTER:
shared->is_master = 1;
break;
case DB_EVENT_REP_PERM_FAILED:
printf("insufficient acks\n");
break;
case DB_EVENT_REP_STARTUPDONE: /* FALLTHROUGH */
case DB_EVENT_REP_NEWMASTER:
/* I don't care about these, for now. */
break;
default:
dbenv->errx(dbenv, "ignoring event %d", which);
}
}

133
examples_c/ex_sequence.c Normal file
View File

@@ -0,0 +1,133 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1997,2008 Oracle. All rights reserved.
*
* $Id: ex_sequence.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
extern int getopt(int, char * const *, const char *);
#else
#include <unistd.h>
#endif
#include <db.h>
#define DATABASE "sequence.db"
#define SEQUENCE "my_sequence"
int main __P((int, char *[]));
int usage __P((void));
int
main(argc, argv)
int argc;
char *argv[];
{
extern int optind;
DB *dbp;
DB_SEQUENCE *seq;
DBT key;
int i, ret, rflag;
db_seq_t seqnum;
char ch;
const char *database, *progname = "ex_sequence";
dbp = NULL;
seq = NULL;
rflag = 0;
while ((ch = getopt(argc, argv, "r")) != EOF)
switch (ch) {
case 'r':
rflag = 1;
break;
case '?':
default:
return (usage());
}
argc -= optind;
argv += optind;
/* Accept optional database name. */
database = *argv == NULL ? DATABASE : argv[0];
/* Optionally discard the database. */
if (rflag)
(void)remove(database);
/* Create and initialize database object, open the database. */
if ((ret = db_create(&dbp, NULL, 0)) != 0) {
fprintf(stderr,
"%s: db_create: %s\n", progname, db_strerror(ret));
return (EXIT_FAILURE);
}
dbp->set_errfile(dbp, stderr);
dbp->set_errpfx(dbp, progname);
if ((ret = dbp->open(dbp,
NULL, database, NULL, DB_BTREE, DB_CREATE, 0664)) != 0) {
dbp->err(dbp, ret, "%s: open", database);
goto err;
}
if ((ret = db_sequence_create(&seq, dbp, 0)) != 0) {
dbp->err(dbp, ret, "db_sequence_create");
goto err;
}
memset(&key, 0, sizeof(DBT));
key.data = SEQUENCE;
key.size = (u_int32_t)strlen(SEQUENCE);
if ((ret = seq->open(seq, NULL, &key, DB_CREATE)) != 0) {
dbp->err(dbp, ret, "%s: DB_SEQUENCE->open", SEQUENCE);
goto err;
}
for (i = 0; i < 10; i++) {
if ((ret = seq->get(seq, NULL, 1, &seqnum, 0)) != 0) {
dbp->err(dbp, ret, "DB_SEQUENCE->get");
goto err;
}
/* There's no portable way to print 64-bit numbers. */
#ifdef _WIN32
printf("Got sequence number %I64d\n", (int64_t)seqnum);
#else
printf(
"Got sequence number %llu\n", (unsigned long long)seqnum);
#endif
}
/* Close everything down. */
if ((ret = seq->close(seq, 0)) != 0) {
seq = NULL;
dbp->err(dbp, ret, "DB_SEQUENCE->close");
goto err;
}
if ((ret = dbp->close(dbp, 0)) != 0) {
fprintf(stderr,
"%s: DB->close: %s\n", progname, db_strerror(ret));
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
err: if (seq != NULL)
(void)seq->close(seq, 0);
if (dbp != NULL)
(void)dbp->close(dbp, 0);
return (EXIT_FAILURE);
}
int
usage()
{
(void)fprintf(stderr, "usage: ex_sequence [-r] [database]\n");
return (EXIT_FAILURE);
}

626
examples_c/ex_thread.c Normal file
View File

@@ -0,0 +1,626 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1997,2008 Oracle. All rights reserved.
*
* $Id: ex_thread.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <sys/time.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#ifdef _WIN32
extern int getopt(int, char * const *, const char *);
#else
#include <unistd.h>
#endif
#include <db.h>
/*
* NB: This application is written using POSIX 1003.1b-1993 pthreads
* interfaces, which may not be portable to your system.
*/
extern int sched_yield __P((void)); /* Pthread yield function. */
int db_init __P((const char *));
void *deadlock __P((void *));
void fatal __P((const char *, int, int));
void onint __P((int));
int main __P((int, char *[]));
int reader __P((int));
void stats __P((void));
void *trickle __P((void *));
void *tstart __P((void *));
int usage __P((void));
void word __P((void));
int writer __P((int));
int quit; /* Interrupt handling flag. */
struct _statistics {
int aborted; /* Write. */
int aborts; /* Read/write. */
int adds; /* Write. */
int deletes; /* Write. */
int txns; /* Write. */
int found; /* Read. */
int notfound; /* Read. */
} *perf;
const char
*progname = "ex_thread"; /* Program name. */
#define DATABASE "access.db" /* Database name. */
#define WORDLIST "../test/wordlist" /* Dictionary. */
/*
* We can seriously increase the number of collisions and transaction
* aborts by yielding the scheduler after every DB call. Specify the
* -p option to do this.
*/
int punish; /* -p */
int nlist; /* -n */
int nreaders; /* -r */
int verbose; /* -v */
int nwriters; /* -w */
DB *dbp; /* Database handle. */
DB_ENV *dbenv; /* Database environment. */
int nthreads; /* Total threads. */
char **list; /* Word list. */
/*
* ex_thread --
* Run a simple threaded application of some numbers of readers and
* writers competing for a set of words.
*
* Example UNIX shell script to run this program:
* % rm -rf TESTDIR
* % mkdir TESTDIR
* % ex_thread -h TESTDIR
*/
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
extern int errno, optind;
DB_TXN *txnp;
pthread_t *tids;
int ch, i, ret;
const char *home;
void *retp;
txnp = NULL;
nlist = 1000;
nreaders = nwriters = 4;
home = "TESTDIR";
while ((ch = getopt(argc, argv, "h:pn:r:vw:")) != EOF)
switch (ch) {
case 'h':
home = optarg;
break;
case 'p':
punish = 1;
break;
case 'n':
nlist = atoi(optarg);
break;
case 'r':
nreaders = atoi(optarg);
break;
case 'v':
verbose = 1;
break;
case 'w':
nwriters = atoi(optarg);
break;
case '?':
default:
return (usage());
}
argc -= optind;
argv += optind;
/* Initialize the random number generator. */
srand(getpid() | time(NULL));
/* Register the signal handler. */
(void)signal(SIGINT, onint);
/* Build the key list. */
word();
/* Remove the previous database. */
(void)remove(DATABASE);
/* Initialize the database environment. */
if ((ret = db_init(home)) != 0)
return (ret);
/* Initialize the database. */
if ((ret = db_create(&dbp, dbenv, 0)) != 0) {
dbenv->err(dbenv, ret, "db_create");
(void)dbenv->close(dbenv, 0);
return (EXIT_FAILURE);
}
if ((ret = dbp->set_pagesize(dbp, 1024)) != 0) {
dbp->err(dbp, ret, "set_pagesize");
goto err;
}
if ((ret = dbenv->txn_begin(dbenv, NULL, &txnp, 0)) != 0)
fatal("txn_begin", ret, 1);
if ((ret = dbp->open(dbp, txnp,
DATABASE, NULL, DB_BTREE, DB_CREATE | DB_THREAD, 0664)) != 0) {
dbp->err(dbp, ret, "%s: open", DATABASE);
goto err;
} else {
ret = txnp->commit(txnp, 0);
txnp = NULL;
if (ret != 0)
goto err;
}
nthreads = nreaders + nwriters + 2;
printf("Running: readers %d, writers %d\n", nreaders, nwriters);
fflush(stdout);
/* Create statistics structures, offset by 1. */
if ((perf = calloc(nreaders + nwriters + 1, sizeof(*perf))) == NULL)
fatal(NULL, errno, 1);
/* Create thread ID structures. */
if ((tids = malloc(nthreads * sizeof(pthread_t))) == NULL)
fatal(NULL, errno, 1);
/* Create reader/writer threads. */
for (i = 0; i < nreaders + nwriters; ++i)
if ((ret = pthread_create(
&tids[i], NULL, tstart, (void *)(uintptr_t)i)) != 0)
fatal("pthread_create", ret > 0 ? ret : errno, 1);
/* Create buffer pool trickle thread. */
if (pthread_create(&tids[i], NULL, trickle, &i))
fatal("pthread_create", errno, 1);
++i;
/* Create deadlock detector thread. */
if (pthread_create(&tids[i], NULL, deadlock, &i))
fatal("pthread_create", errno, 1);
/* Wait for the threads. */
for (i = 0; i < nthreads; ++i)
(void)pthread_join(tids[i], &retp);
printf("Exiting\n");
stats();
err: if (txnp != NULL)
(void)txnp->abort(txnp);
(void)dbp->close(dbp, 0);
(void)dbenv->close(dbenv, 0);
return (EXIT_SUCCESS);
}
int
reader(id)
int id;
{
DBT key, data;
int n, ret;
char buf[64];
/*
* DBT's must use local memory or malloc'd memory if the DB handle
* is accessed in a threaded fashion.
*/
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
data.flags = DB_DBT_MALLOC;
/*
* Read-only threads do not require transaction protection, unless
* there's a need for repeatable reads.
*/
while (!quit) {
/* Pick a key at random, and look it up. */
n = rand() % nlist;
key.data = list[n];
key.size = strlen(key.data);
if (verbose) {
sprintf(buf, "reader: %d: list entry %d\n", id, n);
write(STDOUT_FILENO, buf, strlen(buf));
}
switch (ret = dbp->get(dbp, NULL, &key, &data, 0)) {
case DB_LOCK_DEADLOCK: /* Deadlock. */
++perf[id].aborts;
break;
case 0: /* Success. */
++perf[id].found;
free(data.data);
break;
case DB_NOTFOUND: /* Not found. */
++perf[id].notfound;
break;
default:
sprintf(buf,
"reader %d: dbp->get: %s", id, (char *)key.data);
fatal(buf, ret, 0);
}
}
return (0);
}
int
writer(id)
int id;
{
DBT key, data;
DB_TXN *tid;
time_t now, then;
int n, ret;
char buf[256], dbuf[10000];
time(&now);
then = now;
/*
* DBT's must use local memory or malloc'd memory if the DB handle
* is accessed in a threaded fashion.
*/
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
data.data = dbuf;
data.ulen = sizeof(dbuf);
data.flags = DB_DBT_USERMEM;
while (!quit) {
/* Pick a random key. */
n = rand() % nlist;
key.data = list[n];
key.size = strlen(key.data);
if (verbose) {
sprintf(buf, "writer: %d: list entry %d\n", id, n);
write(STDOUT_FILENO, buf, strlen(buf));
}
/* Abort and retry. */
if (0) {
retry: if ((ret = tid->abort(tid)) != 0)
fatal("DB_TXN->abort", ret, 1);
++perf[id].aborts;
++perf[id].aborted;
}
/* Thread #1 prints out the stats every 20 seconds. */
if (id == 1) {
time(&now);
if (now - then >= 20) {
stats();
then = now;
}
}
/* Begin the transaction. */
if ((ret = dbenv->txn_begin(dbenv, NULL, &tid, 0)) != 0)
fatal("txn_begin", ret, 1);
/*
* Get the key. If it doesn't exist, add it. If it does
* exist, delete it.
*/
switch (ret = dbp->get(dbp, tid, &key, &data, 0)) {
case DB_LOCK_DEADLOCK:
goto retry;
case 0:
goto delete;
case DB_NOTFOUND:
goto add;
}
sprintf(buf, "writer: %d: dbp->get", id);
fatal(buf, ret, 1);
/* NOTREACHED */
delete: /* Delete the key. */
switch (ret = dbp->del(dbp, tid, &key, 0)) {
case DB_LOCK_DEADLOCK:
goto retry;
case 0:
++perf[id].deletes;
goto commit;
}
sprintf(buf, "writer: %d: dbp->del", id);
fatal(buf, ret, 1);
/* NOTREACHED */
add: /* Add the key. 1 data item in 30 is an overflow item. */
data.size = 20 + rand() % 128;
if (rand() % 30 == 0)
data.size += 8192;
switch (ret = dbp->put(dbp, tid, &key, &data, 0)) {
case DB_LOCK_DEADLOCK:
goto retry;
case 0:
++perf[id].adds;
goto commit;
default:
sprintf(buf, "writer: %d: dbp->put", id);
fatal(buf, ret, 1);
}
commit: /* The transaction finished, commit it. */
if ((ret = tid->commit(tid, 0)) != 0)
fatal("DB_TXN->commit", ret, 1);
/*
* Every time the thread completes 20 transactions, show
* our progress.
*/
if (++perf[id].txns % 20 == 0) {
sprintf(buf,
"writer: %2d: adds: %4d: deletes: %4d: aborts: %4d: txns: %4d\n",
id, perf[id].adds, perf[id].deletes,
perf[id].aborts, perf[id].txns);
write(STDOUT_FILENO, buf, strlen(buf));
}
/*
* If this thread was aborted more than 5 times before
* the transaction finished, complain.
*/
if (perf[id].aborted > 5) {
sprintf(buf,
"writer: %2d: adds: %4d: deletes: %4d: aborts: %4d: txns: %4d: ABORTED: %2d\n",
id, perf[id].adds, perf[id].deletes,
perf[id].aborts, perf[id].txns, perf[id].aborted);
write(STDOUT_FILENO, buf, strlen(buf));
}
perf[id].aborted = 0;
}
return (0);
}
/*
* stats --
* Display reader/writer thread statistics. To display the statistics
* for the mpool trickle or deadlock threads, use db_stat(1).
*/
void
stats()
{
int id;
char *p, buf[8192];
p = buf + sprintf(buf, "-------------\n");
for (id = 0; id < nreaders + nwriters;)
if (id++ < nwriters)
p += sprintf(p,
"writer: %2d: adds: %4d: deletes: %4d: aborts: %4d: txns: %4d\n",
id, perf[id].adds,
perf[id].deletes, perf[id].aborts, perf[id].txns);
else
p += sprintf(p,
"reader: %2d: found: %5d: notfound: %5d: aborts: %4d\n",
id, perf[id].found,
perf[id].notfound, perf[id].aborts);
p += sprintf(p, "-------------\n");
write(STDOUT_FILENO, buf, p - buf);
}
/*
* db_init --
* Initialize the environment.
*/
int
db_init(home)
const char *home;
{
int ret;
if ((ret = db_env_create(&dbenv, 0)) != 0) {
fprintf(stderr,
"%s: db_env_create: %s\n", progname, db_strerror(ret));
return (EXIT_FAILURE);
}
if (punish)
(void)dbenv->set_flags(dbenv, DB_YIELDCPU, 1);
dbenv->set_errfile(dbenv, stderr);
dbenv->set_errpfx(dbenv, progname);
(void)dbenv->set_cachesize(dbenv, 0, 100 * 1024, 0);
(void)dbenv->set_lg_max(dbenv, 200000);
if ((ret = dbenv->open(dbenv, home,
DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG |
DB_INIT_MPOOL | DB_INIT_TXN | DB_THREAD, 0)) != 0) {
dbenv->err(dbenv, ret, NULL);
(void)dbenv->close(dbenv, 0);
return (EXIT_FAILURE);
}
return (0);
}
/*
* tstart --
* Thread start function for readers and writers.
*/
void *
tstart(arg)
void *arg;
{
pthread_t tid;
u_int id;
id = (uintptr_t)arg + 1;
tid = pthread_self();
if (id <= (u_int)nwriters) {
printf("write thread %d starting: tid: %lu\n", id, (u_long)tid);
fflush(stdout);
writer(id);
} else {
printf("read thread %d starting: tid: %lu\n", id, (u_long)tid);
fflush(stdout);
reader(id);
}
/* NOTREACHED */
return (NULL);
}
/*
* deadlock --
* Thread start function for DB_ENV->lock_detect.
*/
void *
deadlock(arg)
void *arg;
{
struct timeval t;
pthread_t tid;
arg = arg; /* XXX: shut the compiler up. */
tid = pthread_self();
printf("deadlock thread starting: tid: %lu\n", (u_long)tid);
fflush(stdout);
t.tv_sec = 0;
t.tv_usec = 100000;
while (!quit) {
(void)dbenv->lock_detect(dbenv, 0, DB_LOCK_YOUNGEST, NULL);
/* Check every 100ms. */
(void)select(0, NULL, NULL, NULL, &t);
}
return (NULL);
}
/*
* trickle --
* Thread start function for memp_trickle.
*/
void *
trickle(arg)
void *arg;
{
pthread_t tid;
int wrote;
char buf[64];
arg = arg; /* XXX: shut the compiler up. */
tid = pthread_self();
printf("trickle thread starting: tid: %lu\n", (u_long)tid);
fflush(stdout);
while (!quit) {
(void)dbenv->memp_trickle(dbenv, 10, &wrote);
if (verbose) {
sprintf(buf, "trickle: wrote %d\n", wrote);
write(STDOUT_FILENO, buf, strlen(buf));
}
if (wrote == 0) {
sleep(1);
sched_yield();
}
}
return (NULL);
}
/*
* word --
* Build the dictionary word list.
*/
void
word()
{
FILE *fp;
int cnt;
char buf[256];
if ((fp = fopen(WORDLIST, "r")) == NULL)
fatal(WORDLIST, errno, 1);
if ((list = malloc(nlist * sizeof(char *))) == NULL)
fatal(NULL, errno, 1);
for (cnt = 0; cnt < nlist; ++cnt) {
if (fgets(buf, sizeof(buf), fp) == NULL)
break;
if ((list[cnt] = strdup(buf)) == NULL)
fatal(NULL, errno, 1);
}
nlist = cnt; /* In case nlist was larger than possible. */
}
/*
* fatal --
* Report a fatal error and quit.
*/
void
fatal(msg, err, syserr)
const char *msg;
int err, syserr;
{
fprintf(stderr, "%s: ", progname);
if (msg != NULL) {
fprintf(stderr, "%s", msg);
if (syserr)
fprintf(stderr, ": ");
}
if (syserr)
fprintf(stderr, "%s", strerror(err));
fprintf(stderr, "\n");
exit(EXIT_FAILURE);
/* NOTREACHED */
}
/*
* usage --
* Usage message.
*/
int
usage()
{
(void)fprintf(stderr,
"usage: %s [-pv] [-h home] [-n words] [-r readers] [-w writers]\n",
progname);
return (EXIT_FAILURE);
}
/*
* onint --
* Interrupt signal handler.
*/
void
onint(signo)
int signo;
{
signo = 0; /* Quiet compiler. */
quit = 1;
}

717
examples_c/ex_tpcb.c Normal file
View File

@@ -0,0 +1,717 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 1997,2008 Oracle. All rights reserved.
*
* $Id: ex_tpcb.c 63573 2008-05-23 21:43:21Z trent.nelson $
*/
#include <sys/types.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#define NS_PER_MS 1000000 /* Nanoseconds in a millisecond */
#define NS_PER_US 1000 /* Nanoseconds in a microsecond */
#ifdef _WIN32
#include <sys/timeb.h>
extern int getopt(int, char * const *, const char *);
/* Implement a basic high res timer with a POSIX interface for Windows. */
struct timeval {
time_t tv_sec;
long tv_usec;
};
int gettimeofday(struct timeval *tv, struct timezone *tz)
{
struct _timeb now;
_ftime(&now);
tv->tv_sec = now.time;
tv->tv_usec = now.millitm * NS_PER_US;
return (0);
}
#else
#include <unistd.h>
#include <sys/time.h>
#endif
#include <db.h>
typedef enum { ACCOUNT, BRANCH, TELLER } FTYPE;
DB_ENV *db_init __P((const char *, const char *, int, u_int32_t));
int hpopulate __P((DB *, int, int, int, int));
int populate __P((DB *, u_int32_t, u_int32_t, int, const char *));
u_int32_t random_id __P((FTYPE, int, int, int));
u_int32_t random_int __P((u_int32_t, u_int32_t));
int tp_populate __P((DB_ENV *, int, int, int, int, int));
int tp_run __P((DB_ENV *, int, int, int, int, int));
int tp_txn __P((DB_ENV *, DB *, DB *, DB *, DB *, int, int, int, int));
int invarg __P((const char *, int, const char *));
int main __P((int, char *[]));
int usage __P((const char *));
/*
* This program implements a basic TPC/B driver program. To create the
* TPC/B database, run with the -i (init) flag. The number of records
* with which to populate the account, history, branch, and teller tables
* is specified by the a, s, b, and t flags respectively. To run a TPC/B
* test, use the n flag to indicate a number of transactions to run (note
* that you can run many of these processes in parallel to simulate a
* multiuser test run).
*/
#define TELLERS_PER_BRANCH 10
#define ACCOUNTS_PER_TELLER 10000
#define HISTORY_PER_BRANCH 2592000
/*
* The default configuration that adheres to TPCB scaling rules requires
* nearly 3 GB of space. To avoid requiring that much space for testing,
* we set the parameters much lower. If you want to run a valid 10 TPS
* configuration, define VALID_SCALING.
*/
#ifdef VALID_SCALING
#define ACCOUNTS 1000000
#define BRANCHES 10
#define TELLERS 100
#define HISTORY 25920000
#endif
#ifdef TINY
#define ACCOUNTS 1000
#define BRANCHES 10
#define TELLERS 100
#define HISTORY 10000
#endif
#ifdef VERY_TINY
#define ACCOUNTS 500
#define BRANCHES 10
#define TELLERS 50
#define HISTORY 5000
#endif
#if !defined(VALID_SCALING) && !defined(TINY) && !defined(VERY_TINY)
#define ACCOUNTS 100000
#define BRANCHES 10
#define TELLERS 100
#define HISTORY 259200
#endif
#define HISTORY_LEN 100
#define RECLEN 100
#define BEGID 1000000
typedef struct _defrec {
u_int32_t id;
u_int32_t balance;
u_int8_t pad[RECLEN - sizeof(u_int32_t) - sizeof(u_int32_t)];
} defrec;
typedef struct _histrec {
u_int32_t aid;
u_int32_t bid;
u_int32_t tid;
u_int32_t amount;
u_int8_t pad[RECLEN - 4 * sizeof(u_int32_t)];
} histrec;
char *progname = "ex_tpcb"; /* Program name. */
int
main(argc, argv)
int argc;
char *argv[];
{
extern char *optarg;
extern int optind;
DB_ENV *dbenv;
int accounts, branches, seed, tellers, history;
int ch, iflag, mpool, ntxns, ret, txn_no_sync, verbose;
const char *home;
home = "TESTDIR";
accounts = branches = history = tellers = 0;
iflag = mpool = ntxns = txn_no_sync = verbose = 0;
seed = (int)time(NULL);
while ((ch = getopt(argc, argv, "a:b:c:fh:in:S:s:t:v")) != EOF)
switch (ch) {
case 'a': /* Number of account records */
if ((accounts = atoi(optarg)) <= 0)
return (invarg(progname, ch, optarg));
break;
case 'b': /* Number of branch records */
if ((branches = atoi(optarg)) <= 0)
return (invarg(progname, ch, optarg));
break;
case 'c': /* Cachesize in bytes */
if ((mpool = atoi(optarg)) <= 0)
return (invarg(progname, ch, optarg));
break;
case 'f': /* Fast mode: no txn sync. */
txn_no_sync = 1;
break;
case 'h': /* DB home. */
home = optarg;
break;
case 'i': /* Initialize the test. */
iflag = 1;
break;
case 'n': /* Number of transactions */
if ((ntxns = atoi(optarg)) <= 0)
return (invarg(progname, ch, optarg));
break;
case 'S': /* Random number seed. */
if ((seed = atoi(optarg)) <= 0)
return (invarg(progname, ch, optarg));
break;
case 's': /* Number of history records */
if ((history = atoi(optarg)) <= 0)
return (invarg(progname, ch, optarg));
break;
case 't': /* Number of teller records */
if ((tellers = atoi(optarg)) <= 0)
return (invarg(progname, ch, optarg));
break;
case 'v': /* Verbose option. */
verbose = 1;
break;
case '?':
default:
return (usage(progname));
}
argc -= optind;
argv += optind;
srand((u_int)seed);
/* Initialize the database environment. */
if ((dbenv = db_init(home,
progname, mpool, txn_no_sync ? DB_TXN_NOSYNC : 0)) == NULL)
return (EXIT_FAILURE);
accounts = accounts == 0 ? ACCOUNTS : accounts;
branches = branches == 0 ? BRANCHES : branches;
tellers = tellers == 0 ? TELLERS : tellers;
history = history == 0 ? HISTORY : history;
if (verbose)
printf("%ld Accounts, %ld Branches, %ld Tellers, %ld History\n",
(long)accounts, (long)branches,
(long)tellers, (long)history);
if (iflag) {
if (ntxns != 0)
return (usage(progname));
tp_populate(dbenv,
accounts, branches, history, tellers, verbose);
} else {
if (ntxns == 0)
return (usage(progname));
tp_run(dbenv, ntxns, accounts, branches, tellers, verbose);
}
if ((ret = dbenv->close(dbenv, 0)) != 0) {
fprintf(stderr, "%s: dbenv->close failed: %s\n",
progname, db_strerror(ret));
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}
int
invarg(progname, arg, str)
const char *progname;
int arg;
const char *str;
{
(void)fprintf(stderr,
"%s: invalid argument for -%c: %s\n", progname, arg, str);
return (EXIT_FAILURE);
}
int
usage(progname)
const char *progname;
{
const char *a1, *a2;
a1 = "[-fv] [-a accounts] [-b branches]\n";
a2 = "\t[-c cache_size] [-h home] [-S seed] [-s history] [-t tellers]";
(void)fprintf(stderr, "usage: %s -i %s %s\n", progname, a1, a2);
(void)fprintf(stderr,
" %s -n transactions %s %s\n", progname, a1, a2);
return (EXIT_FAILURE);
}
/*
* db_init --
* Initialize the environment.
*/
DB_ENV *
db_init(home, prefix, cachesize, flags)
const char *home, *prefix;
int cachesize;
u_int32_t flags;
{
DB_ENV *dbenv;
u_int32_t local_flags;
int ret;
if ((ret = db_env_create(&dbenv, 0)) != 0) {
fprintf(stderr,
"%s: db_env_create: %s\n", progname, db_strerror(ret));
return (NULL);
}
dbenv->set_errfile(dbenv, stderr);
dbenv->set_errpfx(dbenv, prefix);
(void)dbenv->set_cachesize(dbenv, 0,
cachesize == 0 ? 4 * 1024 * 1024 : (u_int32_t)cachesize, 0);
if (flags & (DB_TXN_NOSYNC))
(void)dbenv->set_flags(dbenv, DB_TXN_NOSYNC, 1);
flags &= ~(DB_TXN_NOSYNC);
local_flags = flags | DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG |
DB_INIT_MPOOL | DB_INIT_TXN;
if ((ret = dbenv->open(dbenv, home, local_flags, 0)) != 0) {
dbenv->err(dbenv, ret, "DB_ENV->open: %s", home);
(void)dbenv->close(dbenv, 0);
return (NULL);
}
return (dbenv);
}
/*
* Initialize the database to the specified number of accounts, branches,
* history records, and tellers.
*/
int
tp_populate(env, accounts, branches, history, tellers, verbose)
DB_ENV *env;
int accounts, branches, history, tellers, verbose;
{
DB *dbp;
u_int32_t balance, idnum, oflags;
u_int32_t end_anum, end_bnum, end_tnum;
u_int32_t start_anum, start_bnum, start_tnum;
int ret;
idnum = BEGID;
balance = 500000;
oflags = DB_CREATE;
if ((ret = db_create(&dbp, env, 0)) != 0) {
env->err(env, ret, "db_create");
return (1);
}
(void)dbp->set_h_nelem(dbp, (u_int32_t)accounts);
if ((ret = dbp->open(dbp, NULL, "account", NULL,
DB_HASH, oflags, 0644)) != 0) {
env->err(env, ret, "DB->open: account");
return (1);
}
start_anum = idnum;
populate(dbp, idnum, balance, accounts, "account");
idnum += accounts;
end_anum = idnum - 1;
if ((ret = dbp->close(dbp, 0)) != 0) {
env->err(env, ret, "DB->close: account");
return (1);
}
if (verbose)
printf("Populated accounts: %ld - %ld\n",
(long)start_anum, (long)end_anum);
/*
* Since the number of branches is very small, we want to use very
* small pages and only 1 key per page, i.e., key-locking instead
* of page locking.
*/
if ((ret = db_create(&dbp, env, 0)) != 0) {
env->err(env, ret, "db_create");
return (1);
}
(void)dbp->set_h_ffactor(dbp, 1);
(void)dbp->set_h_nelem(dbp, (u_int32_t)branches);
(void)dbp->set_pagesize(dbp, 512);
if ((ret = dbp->open(dbp, NULL, "branch", NULL,
DB_HASH, oflags, 0644)) != 0) {
env->err(env, ret, "DB->open: branch");
return (1);
}
start_bnum = idnum;
populate(dbp, idnum, balance, branches, "branch");
idnum += branches;
end_bnum = idnum - 1;
if ((ret = dbp->close(dbp, 0)) != 0) {
env->err(env, ret, "DB->close: branch");
return (1);
}
if (verbose)
printf("Populated branches: %ld - %ld\n",
(long)start_bnum, (long)end_bnum);
/*
* In the case of tellers, we also want small pages, but we'll let
* the fill factor dynamically adjust itself.
*/
if ((ret = db_create(&dbp, env, 0)) != 0) {
env->err(env, ret, "db_create");
return (1);
}
(void)dbp->set_h_ffactor(dbp, 0);
(void)dbp->set_h_nelem(dbp, (u_int32_t)tellers);
(void)dbp->set_pagesize(dbp, 512);
if ((ret = dbp->open(dbp, NULL, "teller", NULL,
DB_HASH, oflags, 0644)) != 0) {
env->err(env, ret, "DB->open: teller");
return (1);
}
start_tnum = idnum;
populate(dbp, idnum, balance, tellers, "teller");
idnum += tellers;
end_tnum = idnum - 1;
if ((ret = dbp->close(dbp, 0)) != 0) {
env->err(env, ret, "DB->close: teller");
return (1);
}
if (verbose)
printf("Populated tellers: %ld - %ld\n",
(long)start_tnum, (long)end_tnum);
if ((ret = db_create(&dbp, env, 0)) != 0) {
env->err(env, ret, "db_create");
return (1);
}
(void)dbp->set_re_len(dbp, HISTORY_LEN);
if ((ret = dbp->open(dbp, NULL, "history", NULL,
DB_RECNO, oflags, 0644)) != 0) {
env->err(env, ret, "DB->open: history");
return (1);
}
hpopulate(dbp, history, accounts, branches, tellers);
if ((ret = dbp->close(dbp, 0)) != 0) {
env->err(env, ret, "DB->close: history");
return (1);
}
return (0);
}
int
populate(dbp, start_id, balance, nrecs, msg)
DB *dbp;
u_int32_t start_id, balance;
int nrecs;
const char *msg;
{
DBT kdbt, ddbt;
defrec drec;
int i, ret;
kdbt.flags = 0;
kdbt.data = &drec.id;
kdbt.size = sizeof(u_int32_t);
ddbt.flags = 0;
ddbt.data = &drec;
ddbt.size = sizeof(drec);
memset(&drec.pad[0], 1, sizeof(drec.pad));
for (i = 0; i < nrecs; i++) {
drec.id = start_id + (u_int32_t)i;
drec.balance = balance;
if ((ret =
(dbp->put)(dbp, NULL, &kdbt, &ddbt, DB_NOOVERWRITE)) != 0) {
dbp->err(dbp,
ret, "Failure initializing %s file\n", msg);
return (1);
}
}
return (0);
}
int
hpopulate(dbp, history, accounts, branches, tellers)
DB *dbp;
int history, accounts, branches, tellers;
{
DBT kdbt, ddbt;
histrec hrec;
db_recno_t key;
int i, ret;
memset(&kdbt, 0, sizeof(kdbt));
memset(&ddbt, 0, sizeof(ddbt));
ddbt.data = &hrec;
ddbt.size = sizeof(hrec);
kdbt.data = &key;
kdbt.size = sizeof(key);
memset(&hrec.pad[0], 1, sizeof(hrec.pad));
hrec.amount = 10;
for (i = 1; i <= history; i++) {
hrec.aid = random_id(ACCOUNT, accounts, branches, tellers);
hrec.bid = random_id(BRANCH, accounts, branches, tellers);
hrec.tid = random_id(TELLER, accounts, branches, tellers);
if ((ret = dbp->put(dbp, NULL, &kdbt, &ddbt, DB_APPEND)) != 0) {
dbp->err(dbp, ret, "dbp->put");
return (1);
}
}
return (0);
}
u_int32_t
random_int(lo, hi)
u_int32_t lo, hi;
{
u_int32_t ret;
int t;
#ifndef RAND_MAX
#define RAND_MAX 0x7fffffff
#endif
t = rand();
ret = (u_int32_t)(((double)t / ((double)(RAND_MAX) + 1)) *
(hi - lo + 1));
ret += lo;
return (ret);
}
u_int32_t
random_id(type, accounts, branches, tellers)
FTYPE type;
int accounts, branches, tellers;
{
u_int32_t min, max, num;
max = min = BEGID;
num = accounts;
switch (type) {
case TELLER:
min += branches;
num = tellers;
/* FALLTHROUGH */
case BRANCH:
if (type == BRANCH)
num = branches;
min += accounts;
/* FALLTHROUGH */
case ACCOUNT:
max = min + num - 1;
}
return (random_int(min, max));
}
int
tp_run(dbenv, n, accounts, branches, tellers, verbose)
DB_ENV *dbenv;
int n, accounts, branches, tellers, verbose;
{
DB *adb, *bdb, *hdb, *tdb;
int failed, ret, txns;
struct timeval start_tv, end_tv;
double start_time, end_time;
adb = bdb = hdb = tdb = NULL;
/*
* Open the database files.
*/
if ((ret = db_create(&adb, dbenv, 0)) != 0) {
dbenv->err(dbenv, ret, "db_create");
goto err;
}
if ((ret = adb->open(adb, NULL, "account", NULL, DB_UNKNOWN,
DB_AUTO_COMMIT, 0)) != 0) {
dbenv->err(dbenv, ret, "DB->open: account");
goto err;
}
if ((ret = db_create(&bdb, dbenv, 0)) != 0) {
dbenv->err(dbenv, ret, "db_create");
goto err;
}
if ((ret = bdb->open(bdb, NULL, "branch", NULL, DB_UNKNOWN,
DB_AUTO_COMMIT, 0)) != 0) {
dbenv->err(dbenv, ret, "DB->open: branch");
goto err;
}
if ((ret = db_create(&hdb, dbenv, 0)) != 0) {
dbenv->err(dbenv, ret, "db_create");
goto err;
}
if ((ret = hdb->open(hdb, NULL, "history", NULL, DB_UNKNOWN,
DB_AUTO_COMMIT, 0)) != 0) {
dbenv->err(dbenv, ret, "DB->open: history");
goto err;
}
if ((ret = db_create(&tdb, dbenv, 0)) != 0) {
dbenv->err(dbenv, ret, "db_create");
goto err;
}
if ((ret = tdb->open(tdb, NULL, "teller", NULL, DB_UNKNOWN,
DB_AUTO_COMMIT, 0)) != 0) {
dbenv->err(dbenv, ret, "DB->open: teller");
goto err;
}
(void)gettimeofday(&start_tv, NULL);
for (txns = n, failed = 0; n-- > 0;)
if ((ret = tp_txn(dbenv, adb, bdb, tdb, hdb,
accounts, branches, tellers, verbose)) != 0)
++failed;
(void)gettimeofday(&end_tv, NULL);
start_time = start_tv.tv_sec + ((start_tv.tv_usec + 0.0)/NS_PER_MS);
end_time = end_tv.tv_sec + ((end_tv.tv_usec + 0.0)/NS_PER_MS);
if (end_time == start_time)
end_time += 1/NS_PER_MS;
printf("%s: %d txns: %d failed, %.3f sec, %.2f TPS\n", progname,
txns, failed, (end_time - start_time),
(txns - failed) / (double)(end_time - start_time));
err: if (adb != NULL)
(void)adb->close(adb, 0);
if (bdb != NULL)
(void)bdb->close(bdb, 0);
if (tdb != NULL)
(void)tdb->close(tdb, 0);
if (hdb != NULL)
(void)hdb->close(hdb, 0);
return (ret == 0 ? 0 : 1);
}
/*
* XXX Figure out the appropriate way to pick out IDs.
*/
int
tp_txn(dbenv, adb, bdb, tdb, hdb, accounts, branches, tellers, verbose)
DB_ENV *dbenv;
DB *adb, *bdb, *tdb, *hdb;
int accounts, branches, tellers, verbose;
{
DBC *acurs, *bcurs, *tcurs;
DBT d_dbt, d_histdbt, k_dbt, k_histdbt;
DB_TXN *t;
db_recno_t key;
defrec rec;
histrec hrec;
int account, branch, teller, ret;
t = NULL;
acurs = bcurs = tcurs = NULL;
/*
* !!!
* This is sample code -- we could move a lot of this into the driver
* to make it faster.
*/
account = random_id(ACCOUNT, accounts, branches, tellers);
branch = random_id(BRANCH, accounts, branches, tellers);
teller = random_id(TELLER, accounts, branches, tellers);
memset(&d_histdbt, 0, sizeof(d_histdbt));
memset(&k_histdbt, 0, sizeof(k_histdbt));
k_histdbt.data = &key;
k_histdbt.size = sizeof(key);
memset(&k_dbt, 0, sizeof(k_dbt));
k_dbt.size = sizeof(int);
memset(&d_dbt, 0, sizeof(d_dbt));
d_dbt.flags = DB_DBT_USERMEM;
d_dbt.data = &rec;
d_dbt.ulen = sizeof(rec);
hrec.aid = account;
hrec.bid = branch;
hrec.tid = teller;
hrec.amount = 10;
/* Request 0 bytes since we're just positioning. */
d_histdbt.flags = DB_DBT_PARTIAL;
/*
* START PER-TRANSACTION TIMING.
*
* Technically, TPCB requires a limit on response time, you only get
* to count transactions that complete within 2 seconds. That's not
* an issue for this sample application -- regardless, here's where
* the transaction begins.
*/
if (dbenv->txn_begin(dbenv, NULL, &t, 0) != 0)
goto err;
if (adb->cursor(adb, t, &acurs, 0) != 0 ||
bdb->cursor(bdb, t, &bcurs, 0) != 0 ||
tdb->cursor(tdb, t, &tcurs, 0) != 0)
goto err;
/* Account record */
k_dbt.data = &account;
if (acurs->get(acurs, &k_dbt, &d_dbt, DB_SET) != 0)
goto err;
rec.balance += 10;
if (acurs->put(acurs, &k_dbt, &d_dbt, DB_CURRENT) != 0)
goto err;
/* Branch record */
k_dbt.data = &branch;
if (bcurs->get(bcurs, &k_dbt, &d_dbt, DB_SET) != 0)
goto err;
rec.balance += 10;
if (bcurs->put(bcurs, &k_dbt, &d_dbt, DB_CURRENT) != 0)
goto err;
/* Teller record */
k_dbt.data = &teller;
if (tcurs->get(tcurs, &k_dbt, &d_dbt, DB_SET) != 0)
goto err;
rec.balance += 10;
if (tcurs->put(tcurs, &k_dbt, &d_dbt, DB_CURRENT) != 0)
goto err;
/* History record */
d_histdbt.flags = 0;
d_histdbt.data = &hrec;
d_histdbt.ulen = sizeof(hrec);
if (hdb->put(hdb, t, &k_histdbt, &d_histdbt, DB_APPEND) != 0)
goto err;
if (acurs->close(acurs) != 0 || bcurs->close(bcurs) != 0 ||
tcurs->close(tcurs) != 0)
goto err;
ret = t->commit(t, 0);
t = NULL;
if (ret != 0)
goto err;
/* END PER-TRANSACTION TIMING. */
return (0);
err: if (acurs != NULL)
(void)acurs->close(acurs);
if (bcurs != NULL)
(void)bcurs->close(bcurs);
if (tcurs != NULL)
(void)tcurs->close(tcurs);
if (t != NULL)
(void)t->abort(t);
if (verbose)
printf("Transaction A=%ld B=%ld T=%ld failed\n",
(long)account, (long)branch, (long)teller);
return (-1);
}

View File

@@ -0,0 +1,274 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2004,2008 Oracle. All rights reserved.
*/
#include "gettingstarted_common.h"
/* Forward declarations */
int usage(void);
int load_vendors_database(STOCK_DBS, char *);
size_t pack_string(char *, char *, size_t);
int load_inventory_database(STOCK_DBS, char *);
int
usage()
{
fprintf(stderr, "example_database_load [-b <path to data files>]");
fprintf(stderr, " [-h <database_home_directory>]\n");
fprintf(stderr, "\tNote: Any path specified must end with your");
fprintf(stderr, " system's path delimiter (/ or \\)\n");
return (-1);
}
/*
* Loads the contents of vendors.txt and inventory.txt into
* Berkeley DB databases. Also causes the itemname secondary
* database to be created and loaded.
*/
int
main(int argc, char *argv[])
{
STOCK_DBS my_stock;
int ch, ret;
size_t size;
char *basename, *inventory_file, *vendor_file;
/* Initialize the STOCK_DBS struct */
initialize_stockdbs(&my_stock);
/* Initialize the base path. */
basename = "./";
/* Parse the command line arguments */
while ((ch = getopt(argc, argv, "b:h:")) != EOF)
switch (ch) {
case 'h':
if (optarg[strlen(optarg)-1] != '/' &&
optarg[strlen(optarg)-1] != '\\')
return (usage());
my_stock.db_home_dir = optarg;
break;
case 'b':
if (basename[strlen(basename)-1] != '/' &&
basename[strlen(basename)-1] != '\\')
return (usage());
basename = optarg;
break;
case '?':
default:
return (usage());
}
/* Identify the files that will hold our databases */
set_db_filenames(&my_stock);
/* Find our input files */
size = strlen(basename) + strlen(INVENTORY_FILE) + 1;
inventory_file = malloc(size);
snprintf(inventory_file, size, "%s%s", basename, INVENTORY_FILE);
size = strlen(basename) + strlen(VENDORS_FILE) + 1;
vendor_file = malloc(size);
snprintf(vendor_file, size, "%s%s", basename, VENDORS_FILE);
/* Open all databases */
ret = databases_setup(&my_stock, "example_database_load", stderr);
if (ret) {
fprintf(stderr, "Error opening databases\n");
databases_close(&my_stock);
return (ret);
}
ret = load_vendors_database(my_stock, vendor_file);
if (ret) {
fprintf(stderr, "Error loading vendors database.\n");
databases_close(&my_stock);
return (ret);
}
ret = load_inventory_database(my_stock, inventory_file);
if (ret) {
fprintf(stderr, "Error loading inventory database.\n");
databases_close(&my_stock);
return (ret);
}
/* close our environment and databases */
databases_close(&my_stock);
printf("Done loading databases.\n");
return (ret);
}
/*
* Loads the contents of the vendors.txt file into
* a database.
*/
int
load_vendors_database(STOCK_DBS my_stock, char *vendor_file)
{
DBT key, data;
char buf[MAXLINE];
FILE *ifp;
VENDOR my_vendor;
/* Load the vendors database */
ifp = fopen(vendor_file, "r");
if (ifp == NULL) {
fprintf(stderr, "Error opening file '%s'\n", vendor_file);
return (-1);
}
while (fgets(buf, MAXLINE, ifp) != NULL) {
/* zero out the structure */
memset(&my_vendor, 0, sizeof(VENDOR));
/* Zero out the DBTs */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
/*
* Scan the line into the structure.
* Convenient, but not particularly safe.
* In a real program, there would be a lot more
* defensive code here.
*/
sscanf(buf,
"%20[^#]#%20[^#]#%20[^#]#%3[^#]#%6[^#]#%13[^#]#%20[^#]#%20[^\n]",
my_vendor.name, my_vendor.street,
my_vendor.city, my_vendor.state,
my_vendor.zipcode, my_vendor.phone_number,
my_vendor.sales_rep, my_vendor.sales_rep_phone);
/* Now that we have our structure we can load it into the database. */
/* Set up the database record's key */
key.data = my_vendor.name;
key.size = (u_int32_t)strlen(my_vendor.name) + 1;
/* Set up the database record's data */
data.data = &my_vendor;
data.size = sizeof(VENDOR);
/*
* Note that given the way we built our struct, there's extra
* bytes in it. Essentially we're using fixed-width fields with
* the unused portion of some fields padded with zeros. This
* is the easiest thing to do, but it does result in a bloated
* database. Look at load_inventory_data() for an example of how
* to avoid this.
*/
/* Put the data into the database */
my_stock.vendor_dbp->put(my_stock.vendor_dbp, 0, &key, &data, 0);
} /* end vendors database while loop */
fclose(ifp);
return (0);
}
/*
* Simple little convenience function that takes a buffer, a string,
* and an offset and copies that string into the buffer at the
* appropriate location. Used to ensure that all our strings
* are contained in a single contiguous chunk of memory.
*/
size_t
pack_string(char *buffer, char *string, size_t start_pos)
{
size_t string_size;
string_size = strlen(string) + 1;
memcpy(buffer+start_pos, string, string_size);
return (start_pos + string_size);
}
/*
* Loads the contents of the inventory.txt file into
* a database. Note that because the itemname
* secondary database is associated to the inventorydb
* (see env_setup() in gettingstarted_common.c), the
* itemname index is automatically created when this
* database is loaded.
*/
int
load_inventory_database(STOCK_DBS my_stock, char *inventory_file)
{
DBT key, data;
char buf[MAXLINE];
char databuf[MAXDATABUF];
size_t bufLen, dataLen;
FILE *ifp;
/*
* Rather than lining everything up nicely in a struct, we're being
* deliberately a bit sloppy here. This function illustrates how to
* store mixed data that might be obtained from various locations
* in your application.
*/
float price;
int quantity;
char category[MAXFIELD], name[MAXFIELD];
char vendor[MAXFIELD], sku[MAXFIELD];
/* Load the inventory database */
ifp = fopen(inventory_file, "r");
if (ifp == NULL) {
fprintf(stderr, "Error opening file '%s'\n", inventory_file);
return (-1);
}
while (fgets(buf, MAXLINE, ifp) != NULL) {
/*
* Scan the line into the appropriate buffers and variables.
* Convenient, but not particularly safe. In a real
* program, there would be a lot more defensive code here.
*/
sscanf(buf,
"%20[^#]#%20[^#]#%f#%i#%20[^#]#%20[^\n]",
name, sku, &price, &quantity, category, vendor);
/*
* Now pack it into a single contiguous memory location for
* storage.
*/
memset(databuf, 0, MAXDATABUF);
bufLen = 0;
dataLen = 0;
dataLen = sizeof(float);
memcpy(databuf, &price, dataLen);
bufLen += dataLen;
dataLen = sizeof(int);
memcpy(databuf + bufLen, &quantity, dataLen);
bufLen += dataLen;
bufLen = pack_string(databuf, name, bufLen);
bufLen = pack_string(databuf, sku, bufLen);
bufLen = pack_string(databuf, category, bufLen);
bufLen = pack_string(databuf, vendor, bufLen);
/* Zero out the DBTs */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
/* The key is the item's SKU */
key.data = sku;
key.size = (u_int32_t)strlen(sku) + 1;
/* The data is the information that we packed into databuf. */
data.data = databuf;
data.size = (u_int32_t)bufLen;
/* Put the data into the database */
my_stock.vendor_dbp->put(my_stock.inventory_dbp, 0, &key, &data, 0);
} /* end vendors database while loop */
/* Cleanup */
fclose(ifp);
return (0);
}

View File

@@ -0,0 +1,275 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2004,2008 Oracle. All rights reserved.
*/
#include "gettingstarted_common.h"
/* Forward declarations */
int usage(void);
char *show_inventory_item(void *);
int show_all_records(STOCK_DBS *);
int show_records(STOCK_DBS *, char *);
int show_vendor_record(char *, DB *);
int
usage()
{
fprintf(stderr, "example_database_read [-i <item name>]");
fprintf(stderr, " [-h <database home>]\n");
fprintf(stderr,
"\tNote: Any path specified to the -h parameter must end\n");
fprintf(stderr, " with your system's path delimiter (/ or \\)\n");
return (-1);
}
/*
* Searches for a inventory item based on that item's name. The search is
* performed using the item name secondary database. Displays all
* inventory items that use the specified name, as well as the vendor
* associated with that inventory item.
*
* If no item name is provided, then all inventory items are displayed.
*/
int
main(int argc, char *argv[])
{
STOCK_DBS my_stock;
int ch, ret;
char *itemname;
/* Initialize the STOCK_DBS struct */
initialize_stockdbs(&my_stock);
/* Parse the command line arguments */
itemname = NULL;
while ((ch = getopt(argc, argv, "h:i:?")) != EOF)
switch (ch) {
case 'h':
if (optarg[strlen(optarg)-1] != '/' &&
optarg[strlen(optarg)-1] != '\\')
return (usage());
my_stock.db_home_dir = optarg;
break;
case 'i':
itemname = optarg;
break;
case '?':
default:
return (usage());
}
/* Identify the files that hold our databases */
set_db_filenames(&my_stock);
/* Open all databases */
ret = databases_setup(&my_stock, "example_database_read", stderr);
if (ret != 0) {
fprintf(stderr, "Error opening databases\n");
databases_close(&my_stock);
return (ret);
}
if (itemname == NULL)
ret = show_all_records(&my_stock);
else
ret = show_records(&my_stock, itemname);
/* close our databases */
databases_close(&my_stock);
return (ret);
}
int show_all_records(STOCK_DBS *my_stock)
{
DBC *inventory_cursorp;
DBT key, data;
char *the_vendor;
int exit_value, ret;
/* Initialize our DBTs. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
/* Get a cursor to the inventory db */
my_stock->inventory_dbp->cursor(my_stock->inventory_dbp, NULL,
&inventory_cursorp, 0);
/*
* Iterate over the inventory database, from the first record
* to the last, displaying each in turn.
*/
exit_value = 0;
while ((ret =
inventory_cursorp->get(inventory_cursorp, &key, &data, DB_NEXT)) == 0)
{
the_vendor = show_inventory_item(data.data);
ret = show_vendor_record(the_vendor, my_stock->vendor_dbp);
if (ret) {
exit_value = ret;
break;
}
}
/* Close the cursor */
inventory_cursorp->close(inventory_cursorp);
return (exit_value);
}
/*
* Search for an inventory item given its name (using the inventory item
* secondary database) and display that record and any duplicates that may
* exist.
*/
int
show_records(STOCK_DBS *my_stock, char *itemname)
{
DBC *itemname_cursorp;
DBT key, data;
char *the_vendor;
int ret, exit_value;
/* Initialize our DBTs. */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
/* Get a cursor to the itemname db */
my_stock->itemname_sdbp->cursor(my_stock->itemname_sdbp, NULL,
&itemname_cursorp, 0);
/*
* Get the search key. This is the name on the inventory
* record that we want to examine.
*/
key.data = itemname;
key.size = (u_int32_t)strlen(itemname) + 1;
/*
* Position our cursor to the first record in the secondary
* database that has the appropriate key.
*/
exit_value = 0;
ret = itemname_cursorp->get(itemname_cursorp, &key, &data, DB_SET);
if (!ret) {
do {
/*
* Show the inventory record and the vendor responsible
* for this inventory item.
*/
the_vendor = show_inventory_item(data.data);
ret = show_vendor_record(the_vendor, my_stock->vendor_dbp);
if (ret) {
exit_value = ret;
break;
}
/*
* Our secondary allows duplicates, so we need to loop over
* the next duplicate records and show them all. This is done
* because an inventory item's name is not a unique value.
*/
} while (itemname_cursorp->get(itemname_cursorp, &key, &data,
DB_NEXT_DUP) == 0);
} else {
printf("No records found for '%s'\n", itemname);
}
/* Close the cursor */
itemname_cursorp->close(itemname_cursorp);
return (exit_value);
}
/*
* Shows an inventory item. How we retrieve the inventory
* item values from the provided buffer is strictly dependent
* on the order that those items were originally stored in the
* DBT. See load_inventory_database in example_database_load
* for how this was done.
*/
char *
show_inventory_item(void *vBuf)
{
float price;
int quantity;
size_t buf_pos;
char *category, *name, *sku, *vendor_name;
char *buf = (char *)vBuf;
price = *((float *)buf);
buf_pos = sizeof(float);
quantity = *((int *)(buf + buf_pos));
buf_pos += sizeof(int);
name = buf + buf_pos;
buf_pos += strlen(name) + 1;
sku = buf + buf_pos;
buf_pos += strlen(sku) + 1;
category = buf + buf_pos;
buf_pos += strlen(category) + 1;
vendor_name = buf + buf_pos;
printf("name: %s\n", name);
printf("\tSKU: %s\n", sku);
printf("\tCategory: %s\n", category);
printf("\tPrice: %.2f\n", price);
printf("\tQuantity: %i\n", quantity);
printf("\tVendor:\n");
return (vendor_name);
}
/*
* Shows a vendor record. Each vendor record is an instance of
* a vendor structure. See load_vendor_database() in
* example_database_load for how this structure was originally
* put into the database.
*/
int
show_vendor_record(char *vendor_name, DB *vendor_dbp)
{
DBT key, data;
VENDOR my_vendor;
int ret;
/* Zero our DBTs */
memset(&key, 0, sizeof(DBT));
memset(&data, 0, sizeof(DBT));
/* Set the search key to the vendor's name */
key.data = vendor_name;
key.size = (u_int32_t)strlen(vendor_name) + 1;
/*
* Make sure we use the memory we set aside for the VENDOR
* structure rather than the memory that DB allocates.
* Some systems may require structures to be aligned in memory
* in a specific way, and DB may not get it right.
*/
data.data = &my_vendor;
data.ulen = sizeof(VENDOR);
data.flags = DB_DBT_USERMEM;
/* Get the record */
ret = vendor_dbp->get(vendor_dbp, NULL, &key, &data, 0);
if (ret != 0) {
vendor_dbp->err(vendor_dbp, ret, "Error searching for vendor: '%s'",
vendor_name);
return (ret);
} else {
printf("\t\t%s\n", my_vendor.name);
printf("\t\t%s\n", my_vendor.street);
printf("\t\t%s, %s\n", my_vendor.city, my_vendor.state);
printf("\t\t%s\n\n", my_vendor.zipcode);
printf("\t\t%s\n\n", my_vendor.phone_number);
printf("\t\tContact: %s\n", my_vendor.sales_rep);
printf("\t\t%s\n", my_vendor.sales_rep_phone);
}
return (0);
}

View File

@@ -0,0 +1,239 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2004,2008 Oracle. All rights reserved.
*/
#include "gettingstarted_common.h"
int get_item_name(DB *, const DBT *, const DBT *, 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)
{
u_int offset;
dbp = NULL; /* Not needed, unused. */
pkey = NULL;
/*
* First, obtain the buffer location where 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 float) and the quantity (an int).
*
* See load_inventory_database() in example_database_load.c
* for how we packed the inventory information into the
* data DBT.
*/
offset = sizeof(float) + sizeof(int);
/* Check to make sure there's data */
if (pdata->size < offset)
return (-1); /* Returning non-zero means that the
* secondary record is not created/updated.
*/
/* Now set the secondary key's data to be the item name */
memset(skey, 0, sizeof(DBT));
skey->data = (u_int8_t *)pdata->data + offset;
skey->size = (u_int32_t)strlen(skey->data) + 1;
return (0);
}
/* Opens a database */
int
open_database(DB **dbpp, const char *file_name,
const char *program_name, FILE *error_file_pointer,
int is_secondary)
{
DB *dbp; /* For convenience */
u_int32_t open_flags;
int ret;
/* Initialize the DB handle */
ret = db_create(&dbp, NULL, 0);
if (ret != 0) {
fprintf(error_file_pointer, "%s: %s\n", program_name,
db_strerror(ret));
return (ret);
}
/* Point to the memory malloc'd by db_create() */
*dbpp = dbp;
/* Set up error handling for this database */
dbp->set_errfile(dbp, error_file_pointer);
dbp->set_errpfx(dbp, program_name);
/*
* If this is a secondary database, then we want to allow
* sorted duplicates.
*/
if (is_secondary) {
ret = dbp->set_flags(dbp, DB_DUPSORT);
if (ret != 0) {
dbp->err(dbp, ret, "Attempt to set DUPSORT flags failed.",
file_name);
return (ret);
}
}
/* Set the open flags */
open_flags = DB_CREATE; /* Allow database creation */
/* Now open the database */
ret = dbp->open(dbp, /* Pointer to the database */
NULL, /* Txn pointer */
file_name, /* File name */
NULL, /* Logical db name */
DB_BTREE, /* Database type (using btree) */
open_flags, /* Open flags */
0); /* File mode. Using defaults */
if (ret != 0) {
dbp->err(dbp, ret, "Database '%s' open failed.", file_name);
return (ret);
}
return (0);
}
/* opens all databases */
int
databases_setup(STOCK_DBS *my_stock, const char *program_name,
FILE *error_file_pointer)
{
int ret;
/* Open the vendor database */
ret = open_database(&(my_stock->vendor_dbp),
my_stock->vendor_db_name,
program_name, error_file_pointer,
PRIMARY_DB);
if (ret != 0)
/*
* Error reporting is handled in open_database() so just return
* the return code.
*/
return (ret);
/* Open the inventory database */
ret = open_database(&(my_stock->inventory_dbp),
my_stock->inventory_db_name,
program_name, error_file_pointer,
PRIMARY_DB);
if (ret != 0)
/*
* Error reporting is handled in open_database() so just return
* the return code.
*/
return (ret);
/*
* Open the itemname secondary database. This is used to
* index the product names found in the inventory
* database.
*/
ret = open_database(&(my_stock->itemname_sdbp),
my_stock->itemname_db_name,
program_name, error_file_pointer,
SECONDARY_DB);
if (ret != 0)
/*
* Error reporting is handled in open_database() so just return
* the return code.
*/
return (0);
/*
* Associate the itemname db with its primary db
* (inventory db).
*/
my_stock->inventory_dbp->associate(
my_stock->inventory_dbp, /* Primary db */
NULL, /* txn id */
my_stock->itemname_sdbp, /* Secondary db */
get_item_name, /* Secondary key creator */
0); /* Flags */
printf("databases opened successfully\n");
return (0);
}
/* Initializes the STOCK_DBS struct.*/
void
initialize_stockdbs(STOCK_DBS *my_stock)
{
my_stock->db_home_dir = DEFAULT_HOMEDIR;
my_stock->inventory_dbp = NULL;
my_stock->vendor_dbp = NULL;
my_stock->itemname_sdbp = NULL;
my_stock->vendor_db_name = NULL;
my_stock->inventory_db_name = NULL;
my_stock->itemname_db_name = NULL;
}
/* Identify all the files that will hold our databases. */
void
set_db_filenames(STOCK_DBS *my_stock)
{
size_t size;
/* Create the Inventory DB file name */
size = strlen(my_stock->db_home_dir) + strlen(INVENTORYDB) + 1;
my_stock->inventory_db_name = malloc(size);
snprintf(my_stock->inventory_db_name, size, "%s%s",
my_stock->db_home_dir, INVENTORYDB);
/* Create the Vendor DB file name */
size = strlen(my_stock->db_home_dir) + strlen(VENDORDB) + 1;
my_stock->vendor_db_name = malloc(size);
snprintf(my_stock->vendor_db_name, size, "%s%s",
my_stock->db_home_dir, VENDORDB);
/* Create the itemname DB file name */
size = strlen(my_stock->db_home_dir) + strlen(ITEMNAMEDB) + 1;
my_stock->itemname_db_name = malloc(size);
snprintf(my_stock->itemname_db_name, size, "%s%s",
my_stock->db_home_dir, ITEMNAMEDB);
}
/* Closes all the databases and secondary databases. */
int
databases_close(STOCK_DBS *my_stock)
{
int ret;
/*
* Note that closing a database automatically flushes its cached data
* to disk, so no sync is required here.
*/
if (my_stock->itemname_sdbp != NULL) {
ret = my_stock->itemname_sdbp->close(my_stock->itemname_sdbp, 0);
if (ret != 0)
fprintf(stderr, "Itemname database close failed: %s\n",
db_strerror(ret));
}
if (my_stock->inventory_dbp != NULL) {
ret = my_stock->inventory_dbp->close(my_stock->inventory_dbp, 0);
if (ret != 0)
fprintf(stderr, "Inventory database close failed: %s\n",
db_strerror(ret));
}
if (my_stock->vendor_dbp != NULL) {
ret = my_stock->vendor_dbp->close(my_stock->vendor_dbp, 0);
if (ret != 0)
fprintf(stderr, "Vendor database close failed: %s\n",
db_strerror(ret));
}
printf("databases closed.\n");
return (0);
}

View File

@@ -0,0 +1,59 @@
/*-
* See the file LICENSE for redistribution information.
*
* Copyright (c) 2004,2008 Oracle. All rights reserved.
*/
#include <db.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
extern int getopt(int, char * const *, const char *);
extern char *optarg;
#define snprintf _snprintf
#else
#include <unistd.h>
#endif
#define DEFAULT_HOMEDIR "./"
#define INVENTORY_FILE "inventory.txt"
#define VENDORS_FILE "vendors.txt"
#define INVENTORYDB "inventoryDB.db"
#define ITEMNAMEDB "itemnameDB.db"
#define MAXDATABUF 1024
#define MAXFIELD 20
#define MAXLINE 150
#define PRIMARY_DB 0
#define SECONDARY_DB 1
#define VENDORDB "vendorDB.db"
typedef struct stock_dbs {
DB *inventory_dbp; /* Database containing inventory information */
DB *vendor_dbp; /* Database containing vendor information */
DB *itemname_sdbp; /* Index based on the item name index */
char *db_home_dir; /* Directory containing the database files */
char *itemname_db_name; /* Itemname secondary database */
char *inventory_db_name; /* Name of the inventory database */
char *vendor_db_name; /* Name of the vendor database */
} STOCK_DBS;
typedef struct vendor {
char name[MAXFIELD]; /* Vendor name */
char street[MAXFIELD]; /* Street name and number */
char city[MAXFIELD]; /* City */
char state[3]; /* Two-digit US state code */
char zipcode[6]; /* US zipcode */
char phone_number[13]; /* Vendor phone number */
char sales_rep[MAXFIELD]; /* Name of sales representative */
char sales_rep_phone[MAXFIELD]; /* Sales rep's phone number */
} VENDOR;
/* Function prototypes */
int databases_close(STOCK_DBS *);
int databases_setup(STOCK_DBS *, const char *, FILE *);
void initialize_stockdbs(STOCK_DBS *);
int open_database(DB **, const char *, const char *, FILE *, int);
void set_db_filenames(STOCK_DBS *my_stock);

View File

@@ -0,0 +1,800 @@
Oranges#OranfruiRu6Ghr#0.71#451#fruits#TriCounty Produce
Oranges#OranfruiXRPFn1#0.73#263#fruits#Simply Fresh
Oranges#OranfruiLEuzQj#0.69#261#fruits#Off the Vine
Apples#ApplfruiZls4Du#1.20#472#fruits#TriCounty Produce
Apples#Applfrui8fewZe#1.21#402#fruits#Simply Fresh
Apples#ApplfruiXoT6xG#1.20#728#fruits#Off the Vine
Bananas#BanafruipIlluX#0.50#207#fruits#TriCounty Produce
Bananas#BanafruiEQhWuj#0.50#518#fruits#Simply Fresh
Bananas#BanafruimpRgPO#0.50#741#fruits#Off the Vine
Almonds#AlmofruiPPCLz8#0.55#600#fruits#TriCounty Produce
Almonds#AlmofruidMyKmp#0.54#745#fruits#Simply Fresh
Almonds#Almofrui7K0xzH#0.53#405#fruits#Off the Vine
Allspice#AllsfruibJGK4R#0.94#669#fruits#TriCounty Produce
Allspice#Allsfruilfvoeg#0.94#244#fruits#Simply Fresh
Allspice#Allsfruio12BOS#0.95#739#fruits#Off the Vine
Apricot#AprifruijphEpM#0.89#560#fruits#TriCounty Produce
Apricot#AprifruiU1zIDn#0.91#980#fruits#Simply Fresh
Apricot#AprifruichcwYS#0.95#668#fruits#Off the Vine
Avocado#AvocfruiwYYomu#0.99#379#fruits#TriCounty Produce
Avocado#AvocfruiT6IwWE#1.02#711#fruits#Simply Fresh
Avocado#AvocfruisbK1h5#0.97#856#fruits#Off the Vine
Bael Fruit#BaelfruilAU7Hj#0.41#833#fruits#TriCounty Produce
Bael Fruit#BaelfruiX2KvqV#0.40#770#fruits#Simply Fresh
Bael Fruit#Baelfruidjne4e#0.39#778#fruits#Off the Vine
Betel Nut#BetefruiQYdHqQ#0.34#926#fruits#TriCounty Produce
Betel Nut#Betefrui32BKAz#0.37#523#fruits#Simply Fresh
Betel Nut#BetefruisaWzY4#0.34#510#fruits#Off the Vine
Black Walnut#BlacfruiXxIuMU#0.57#923#fruits#TriCounty Produce
Black Walnut#BlacfruiZXgY9t#0.59#312#fruits#Simply Fresh
Black Walnut#BlacfruikWO0vz#0.60#877#fruits#Off the Vine
Blueberry#BluefruiCbxb4t#1.02#276#fruits#TriCounty Produce
Blueberry#BluefruiBuCfgO#1.03#522#fruits#Simply Fresh
Blueberry#Bluefruixz8MkE#1.01#278#fruits#Off the Vine
Boysenberry#BoysfruizxyMuz#1.05#239#fruits#TriCounty Produce
Boysenberry#Boysfrui3hTRQu#1.09#628#fruits#Simply Fresh
Boysenberry#BoysfruinpLvr3#1.02#349#fruits#Off the Vine
Breadnut#Breafrui0kDPs6#0.31#558#fruits#TriCounty Produce
Breadnut#Breafrui44s3og#0.32#879#fruits#Simply Fresh
Breadnut#BreafruiwyLKhJ#0.30#407#fruits#Off the Vine
Cactus#Cactfruiyo2ddH#0.56#601#fruits#TriCounty Produce
Cactus#CactfruixTOLv5#0.54#477#fruits#Simply Fresh
Cactus#Cactfrui4ioUav#0.55#896#fruits#Off the Vine
California Wild Grape#CalifruiZsWAa6#0.78#693#fruits#TriCounty Produce
California Wild Grape#Califruid84xyt#0.83#293#fruits#Simply Fresh
California Wild Grape#CalifruiLSJFoJ#0.81#543#fruits#Off the Vine
Cashew#CashfruihaOFVP#0.37#221#fruits#TriCounty Produce
Cashew#Cashfruizzcw1E#0.38#825#fruits#Simply Fresh
Cashew#CashfruiqtMe2Q#0.38#515#fruits#Off the Vine
Chico Sapote#ChicfruiY534SX#0.47#216#fruits#TriCounty Produce
Chico Sapote#ChicfruiSqL3Lc#0.45#476#fruits#Simply Fresh
Chico Sapote#ChicfruiurzIp4#0.47#200#fruits#Off the Vine
Chinese Jello#ChinfruiyRg75u#0.64#772#fruits#TriCounty Produce
Chinese Jello#ChinfruiuIUj0X#0.65#624#fruits#Simply Fresh
Chinese Jello#ChinfruiwXbRrL#0.67#719#fruits#Off the Vine
Common Guava#Commfruib6znSI#0.80#483#fruits#TriCounty Produce
Common Guava#Commfrui6eUivL#0.81#688#fruits#Simply Fresh
Common Guava#CommfruibWKnz3#0.84#581#fruits#Off the Vine
Crabapple#CrabfruioY2L63#0.94#582#fruits#TriCounty Produce
Crabapple#Crabfruijxcxyt#0.94#278#fruits#Simply Fresh
Crabapple#CrabfruibvWd8K#0.95#213#fruits#Off the Vine
Cranberry#CranfruiJxmKr5#0.83#923#fruits#TriCounty Produce
Cranberry#CranfruiPlklAF#0.84#434#fruits#Simply Fresh
Cranberry#Cranfrui3G5XL9#0.84#880#fruits#Off the Vine
Damson Plum#DamsfruibMRMwe#0.98#782#fruits#TriCounty Produce
Damson Plum#DamsfruiV6wFLk#1.03#400#fruits#Simply Fresh
Damson Plum#DamsfruiLhqFrQ#0.98#489#fruits#Off the Vine
Date Palm#DatefruigS31GU#1.14#315#fruits#TriCounty Produce
Date Palm#DatefruipKPaJK#1.09#588#fruits#Simply Fresh
Date Palm#Datefrui5fTyNS#1.14#539#fruits#Off the Vine
Dragon's Eye#DragfruirGJ3aI#0.28#315#fruits#TriCounty Produce
Dragon's Eye#DragfruiBotxqt#0.27#705#fruits#Simply Fresh
Dragon's Eye#DragfruiPsSnV9#0.29#482#fruits#Off the Vine
East Indian Wine Palm#EastfruiNXFJuG#0.43#992#fruits#TriCounty Produce
East Indian Wine Palm#Eastfruiq06fRr#0.40#990#fruits#Simply Fresh
East Indian Wine Palm#Eastfrui4QUwl2#0.43#351#fruits#Off the Vine
English Walnut#EnglfruiBMtHtW#1.04#787#fruits#TriCounty Produce
English Walnut#EnglfruiHmVzxV#1.03#779#fruits#Simply Fresh
English Walnut#Englfrui18Tc9n#1.06#339#fruits#Off the Vine
False Mangosteen#FalsfruibkmYqH#0.66#971#fruits#TriCounty Produce
False Mangosteen#FalsfruipBsbcX#0.68#250#fruits#Simply Fresh
False Mangosteen#FalsfruiPrFfhe#0.70#386#fruits#Off the Vine
Fried Egg Tree#FriefruiihHUdc#0.29#649#fruits#TriCounty Produce
Fried Egg Tree#FriefruimdD1rf#0.28#527#fruits#Simply Fresh
Fried Egg Tree#FriefruivyAzYq#0.29#332#fruits#Off the Vine
Genipap#GenifruiDtKusQ#0.62#986#fruits#TriCounty Produce
Genipap#GenifruiXq32eP#0.61#326#fruits#Simply Fresh
Genipap#Genifruiphwwyq#0.61#794#fruits#Off the Vine
Ginger#GingfruiQLbRZI#0.28#841#fruits#TriCounty Produce
Ginger#GingfruiS8kK4p#0.29#432#fruits#Simply Fresh
Ginger#GingfruioL3Y4S#0.27#928#fruits#Off the Vine
Grapefruit#Grapfruih86Zxh#1.07#473#fruits#TriCounty Produce
Grapefruit#GrapfruiwL1v0N#1.08#878#fruits#Simply Fresh
Grapefruit#GrapfruihmJzWm#1.02#466#fruits#Off the Vine
Hackberry#HackfruiQjomN7#0.22#938#fruits#TriCounty Produce
Hackberry#HackfruiWS0eKp#0.20#780#fruits#Simply Fresh
Hackberry#Hackfrui0MIv6J#0.21#345#fruits#Off the Vine
Honey Locust#HonefruiebXGRc#1.08#298#fruits#TriCounty Produce
Honey Locust#HonefruiPSqILB#1.00#427#fruits#Simply Fresh
Honey Locust#Honefrui6UXtvW#1.03#422#fruits#Off the Vine
Japanese Plum#JapafruihTmoYR#0.40#658#fruits#TriCounty Produce
Japanese Plum#JapafruifGqz0l#0.40#700#fruits#Simply Fresh
Japanese Plum#JapafruiufWkLx#0.39#790#fruits#Off the Vine
Jojoba#JojofruisE0wTh#0.97#553#fruits#TriCounty Produce
Jojoba#JojofruiwiYLp2#1.02#969#fruits#Simply Fresh
Jojoba#JojofruigMD1ej#0.96#899#fruits#Off the Vine
Jostaberry#JostfruiglsEGV#0.50#300#fruits#TriCounty Produce
Jostaberry#JostfruiV3oo1h#0.52#423#fruits#Simply Fresh
Jostaberry#JostfruiUBerur#0.53#562#fruits#Off the Vine
Kangaroo Apple#KangfruiEQknz8#0.60#661#fruits#TriCounty Produce
Kangaroo Apple#KangfruiNabdFq#0.60#377#fruits#Simply Fresh
Kangaroo Apple#Kangfrui7hky1i#0.60#326#fruits#Off the Vine
Ken's Red#Ken'fruinPUSIm#0.21#337#fruits#TriCounty Produce
Ken's Red#Ken'fruiAoZlpl#0.21#902#fruits#Simply Fresh
Ken's Red#Ken'frui5rmbd4#0.22#972#fruits#Off the Vine
Ketembilla#Ketefrui3yAKxQ#0.31#303#fruits#TriCounty Produce
Ketembilla#KetefruiROn6F5#0.34#283#fruits#Simply Fresh
Ketembilla#Ketefrui16Rsts#0.33#887#fruits#Off the Vine
King Orange#KingfruisOFzWk#0.74#429#fruits#TriCounty Produce
King Orange#KingfruiBmzRJT#0.74#500#fruits#Simply Fresh
King Orange#KingfruiGsrgRX#0.78#994#fruits#Off the Vine
Kola Nut#KolafruiBbtAuw#0.58#991#fruits#TriCounty Produce
Kola Nut#KolafruirbnLVS#0.62#733#fruits#Simply Fresh
Kola Nut#Kolafrui1ItXJx#0.58#273#fruits#Off the Vine
Kuko#Kukofrui6YH5Ds#0.41#647#fruits#TriCounty Produce
Kuko#Kukofrui7WZaZK#0.39#241#fruits#Simply Fresh
Kuko#Kukofruig9MQFT#0.40#204#fruits#Off the Vine
Kumquat#KumqfruiT6WKQL#0.73#388#fruits#TriCounty Produce
Kumquat#KumqfruidLiFLU#0.70#393#fruits#Simply Fresh
Kumquat#KumqfruiL6zhQX#0.71#994#fruits#Off the Vine
Kwai Muk#KwaifruiQK1zOE#1.10#249#fruits#TriCounty Produce
Kwai Muk#KwaifruifbCRlT#1.14#657#fruits#Simply Fresh
Kwai Muk#Kwaifruipe7T2m#1.09#617#fruits#Off the Vine
Lanzone#LanzfruijsPf1v#0.34#835#fruits#TriCounty Produce
Lanzone#LanzfruibU3QoL#0.34#404#fruits#Simply Fresh
Lanzone#LanzfruiYgHwv6#0.34#237#fruits#Off the Vine
Lemon#Lemofrui4Tgsg2#0.46#843#fruits#TriCounty Produce
Lemon#LemofruivK6qvj#0.43#207#fruits#Simply Fresh
Lemon#LemofruiXSXqJ0#0.44#910#fruits#Off the Vine
Lemon Grass#LemofruiVFgVh5#0.40#575#fruits#TriCounty Produce
Lemon Grass#LemofruiWIelvi#0.41#386#fruits#Simply Fresh
Lemon Grass#LemofruiGVAow0#0.39#918#fruits#Off the Vine
Lilly-pilly#LillfruiEQnW1m#1.21#974#fruits#TriCounty Produce
Lilly-pilly#LillfruiMqVuR5#1.23#303#fruits#Simply Fresh
Lilly-pilly#LillfruiVGH9p4#1.17#512#fruits#Off the Vine
Ling Nut#LingfruiGtOf8X#0.85#540#fruits#TriCounty Produce
Ling Nut#LingfruiuP0Jf9#0.83#200#fruits#Simply Fresh
Ling Nut#LingfruiuO5qf5#0.81#319#fruits#Off the Vine
Lipote#LipofruisxD2Qc#0.85#249#fruits#TriCounty Produce
Lipote#LipofruiHNdIqL#0.85#579#fruits#Simply Fresh
Lipote#LipofruiSQ2pKK#0.83#472#fruits#Off the Vine
Litchee#Litcfrui1R6Ydz#0.99#806#fruits#TriCounty Produce
Litchee#LitcfruiwtDM79#1.01#219#fruits#Simply Fresh
Litchee#LitcfruilpPZbC#1.05#419#fruits#Off the Vine
Longan#LongfruiEI0lWF#1.02#573#fruits#TriCounty Produce
Longan#LongfruiPQxxSF#1.04#227#fruits#Simply Fresh
Longan#LongfruisdI812#0.99#993#fruits#Off the Vine
Love-in-a-mist#LovefruiKYPW70#0.69#388#fruits#TriCounty Produce
Love-in-a-mist#LovefruiHrgjDa#0.67#478#fruits#Simply Fresh
Love-in-a-mist#LovefruipSOWVz#0.71#748#fruits#Off the Vine
Lychee#LychfruiicVLnY#0.38#276#fruits#TriCounty Produce
Lychee#LychfruiGY6yJr#0.38#602#fruits#Simply Fresh
Lychee#LychfruiTzDCq2#0.40#572#fruits#Off the Vine
Mabolo#MabofruiSY8RQS#0.97#263#fruits#TriCounty Produce
Mabolo#MabofruiOWWk0n#0.98#729#fruits#Simply Fresh
Mabolo#MabofruixQLOTF#0.98#771#fruits#Off the Vine
Macadamia Nut#MacafruiZppJPw#1.22#888#fruits#TriCounty Produce
Macadamia Nut#MacafruiI7XFMV#1.24#484#fruits#Simply Fresh
Macadamia Nut#Macafrui4x8bxV#1.20#536#fruits#Off the Vine
Madagascar Plum#MadafruiVj5fDf#1.14#596#fruits#TriCounty Produce
Madagascar Plum#MadafruivJhAFI#1.15#807#fruits#Simply Fresh
Madagascar Plum#Madafrui7MTe1x#1.17#355#fruits#Off the Vine
Magnolia Vine#MagnfruiigN4Y1#1.17#321#fruits#TriCounty Produce
Magnolia Vine#MagnfruicKtiHd#1.15#353#fruits#Simply Fresh
Magnolia Vine#MagnfruiLPDSCp#1.23#324#fruits#Off the Vine
Mamey#Mamefrui5rjLF6#0.36#683#fruits#TriCounty Produce
Mamey#MamefruiM6ndnR#0.38#404#fruits#Simply Fresh
Mamey#Mamefruiq9KntD#0.36#527#fruits#Off the Vine
Mandarin Orange#MandfruiRKpmKL#0.42#352#fruits#TriCounty Produce
Mandarin Orange#Mandfrui1V0KLG#0.42#548#fruits#Simply Fresh
Mandarin Orange#Mandfruig2o9Fg#0.41#686#fruits#Off the Vine
Marany Nut#MarafruiqkrwoJ#1.14#273#fruits#TriCounty Produce
Marany Nut#MarafruiCGKpke#1.12#482#fruits#Simply Fresh
Marany Nut#MarafruiB1YE5x#1.09#412#fruits#Off the Vine
Marula#MarufruiXF4biH#0.22#403#fruits#TriCounty Produce
Marula#MarufruidZiVKZ#0.23#317#fruits#Simply Fresh
Marula#MarufruiIS8BEp#0.21#454#fruits#Off the Vine
Mayhaw#MayhfruiCSrm7k#0.24#220#fruits#TriCounty Produce
Mayhaw#MayhfruiNRDzWs#0.25#710#fruits#Simply Fresh
Mayhaw#MayhfruiIUCyEg#0.24#818#fruits#Off the Vine
Meiwa Kumquat#MeiwfruiYhv3AY#0.21#997#fruits#TriCounty Produce
Meiwa Kumquat#MeiwfruiyzQFNR#0.22#347#fruits#Simply Fresh
Meiwa Kumquat#Meiwfruict4OUp#0.21#923#fruits#Off the Vine
Mexican Barberry#Mexifrui2P2dXi#0.28#914#fruits#TriCounty Produce
Mexican Barberry#MexifruiywUTMI#0.29#782#fruits#Simply Fresh
Mexican Barberry#MexifruijPHu5X#0.29#367#fruits#Off the Vine
Meyer Lemon#Meyefruin9901J#0.38#824#fruits#TriCounty Produce
Meyer Lemon#MeyefruiNeQpjO#0.37#617#fruits#Simply Fresh
Meyer Lemon#MeyefruiYEVznZ#0.37#741#fruits#Off the Vine
Mississippi Honeyberry#Missfruipb5iW3#0.95#595#fruits#TriCounty Produce
Mississippi Honeyberry#MissfruiINiDbB#0.96#551#fruits#Simply Fresh
Mississippi Honeyberry#MissfruiNUQ82a#0.93#396#fruits#Off the Vine
Monkey Pot#MonkfruiXlTW4j#0.90#896#fruits#TriCounty Produce
Monkey Pot#Monkfrui1p7a4h#0.88#344#fruits#Simply Fresh
Monkey Pot#Monkfrui4eKggb#0.92#917#fruits#Off the Vine
Monos Plum#Monofrui0Mv9aV#1.11#842#fruits#TriCounty Produce
Monos Plum#Monofrui6iTGQY#1.14#570#fruits#Simply Fresh
Monos Plum#MonofruiNu2uGH#1.13#978#fruits#Off the Vine
Moosewood#MoosfruiMXEGex#0.86#969#fruits#TriCounty Produce
Moosewood#Moosfrui8805mB#0.86#963#fruits#Simply Fresh
Moosewood#MoosfruiOsnDFL#0.88#594#fruits#Off the Vine
Natal Orange#NatafruitB8Kh2#0.42#332#fruits#TriCounty Produce
Natal Orange#NatafruiOhqRrd#0.42#982#fruits#Simply Fresh
Natal Orange#NatafruiRObMf6#0.41#268#fruits#Off the Vine
Nectarine#NectfruilNfeD8#0.36#601#fruits#TriCounty Produce
Nectarine#NectfruiQfjt6b#0.35#818#fruits#Simply Fresh
Nectarine#Nectfrui5U7U96#0.37#930#fruits#Off the Vine
Neem Tree#NeemfruiCruEMF#0.24#222#fruits#TriCounty Produce
Neem Tree#NeemfruiGv0pv5#0.24#645#fruits#Simply Fresh
Neem Tree#NeemfruiUFPVfk#0.25#601#fruits#Off the Vine
New Zealand Spinach#New fruihDIgec#0.87#428#fruits#TriCounty Produce
New Zealand Spinach#New fruiaoR9TP#0.87#630#fruits#Simply Fresh
New Zealand Spinach#New fruiy8LBul#0.94#570#fruits#Off the Vine
Olosapo#OlosfruiGXvaMm#0.76#388#fruits#TriCounty Produce
Olosapo#OlosfruiESlpB3#0.76#560#fruits#Simply Fresh
Olosapo#OlosfruiFNEkER#0.76#962#fruits#Off the Vine
Oregon Grape#OregfruiWxhzrf#1.14#892#fruits#TriCounty Produce
Oregon Grape#OregfruiMgjHUn#1.20#959#fruits#Simply Fresh
Oregon Grape#OregfruiC5UCxX#1.17#419#fruits#Off the Vine
Otaheite Apple#OtahfruilT0iFj#0.21#579#fruits#TriCounty Produce
Otaheite Apple#Otahfrui92PyMY#0.22#857#fruits#Simply Fresh
Otaheite Apple#OtahfruiLGD1EH#0.20#807#fruits#Off the Vine
Oyster Plant#OystfruimGxOsj#0.77#835#fruits#TriCounty Produce
Oyster Plant#Oystfrui1kudBX#0.81#989#fruits#Simply Fresh
Oyster Plant#OystfruiaX3uO2#0.80#505#fruits#Off the Vine
Panama Berry#PanafruiZG0Vp4#1.19#288#fruits#TriCounty Produce
Panama Berry#PanafruiobvXPE#1.21#541#fruits#Simply Fresh
Panama Berry#PanafruipaW8F3#1.16#471#fruits#Off the Vine
Peach Tomato#PeacfruiQpovYH#1.20#475#fruits#TriCounty Produce
Peach Tomato#PeacfruixYXLTN#1.18#655#fruits#Simply Fresh
Peach Tomato#PeacfruiILDYAp#1.23#876#fruits#Off the Vine
Peanut#Peanfruiy8M7pt#0.69#275#fruits#TriCounty Produce
Peanut#PeanfruiEimbED#0.65#307#fruits#Simply Fresh
Peanut#Peanfruic452Vc#0.68#937#fruits#Off the Vine
Peanut Butter Fruit#PeanfruixEDt9Y#0.27#628#fruits#TriCounty Produce
Peanut Butter Fruit#PeanfruiST0T0R#0.27#910#fruits#Simply Fresh
Peanut Butter Fruit#Peanfrui7jeRN2#0.27#938#fruits#Off the Vine
Pear#PearfruiB5YmSJ#0.20#945#fruits#TriCounty Produce
Pear#PearfruiA93XZx#0.21#333#fruits#Simply Fresh
Pear#PearfruioNKiIf#0.21#715#fruits#Off the Vine
Pecan#PecafruiiTIv1Z#0.26#471#fruits#TriCounty Produce
Pecan#PecafruiMGkqla#0.26#889#fruits#Simply Fresh
Pecan#Pecafrui1szYz2#0.25#929#fruits#Off the Vine
Purple Passion Fruit#Purpfrui4mMGkD#1.04#914#fruits#TriCounty Produce
Purple Passion Fruit#Purpfrui5XOW3K#1.06#423#fruits#Simply Fresh
Purple Passion Fruit#PurpfruifDTAgW#1.05#549#fruits#Off the Vine
Red Mulberry#Red fruiVLOXIW#1.24#270#fruits#TriCounty Produce
Red Mulberry#Red fruiXNXt4a#1.21#836#fruits#Simply Fresh
Red Mulberry#Red fruiUseWLG#1.21#795#fruits#Off the Vine
Red Princess#Red fruigJLR4V#0.23#829#fruits#TriCounty Produce
Red Princess#Red fruinVKps5#0.23#558#fruits#Simply Fresh
Red Princess#Red frui0jl9mg#0.24#252#fruits#Off the Vine
Striped Screw Pine#StrifruiUKzjoU#0.60#226#fruits#TriCounty Produce
Striped Screw Pine#StrifruivWLDzH#0.64#685#fruits#Simply Fresh
Striped Screw Pine#StrifruiiF7CGH#0.60#983#fruits#Off the Vine
Tapioca#Tapifruib4LCqt#0.40#955#fruits#TriCounty Produce
Tapioca#TapifruiwgQLj9#0.41#889#fruits#Simply Fresh
Tapioca#TapifruiZ6Igg3#0.41#655#fruits#Off the Vine
Tavola#Tavofrui0k9XOt#1.16#938#fruits#TriCounty Produce
Tavola#Tavofrui8DuRxL#1.08#979#fruits#Simply Fresh
Tavola#TavofruiNZEuJZ#1.16#215#fruits#Off the Vine
Tea#TeafruiL0357s#1.11#516#fruits#TriCounty Produce
Tea#TeafruiD5soTf#1.13#970#fruits#Simply Fresh
Tea#TeafruiOWq4oO#1.19#357#fruits#Off the Vine
Ugli Fruit#UglifruipKNCpf#0.24#501#fruits#TriCounty Produce
Ugli Fruit#UglifruifbDrzc#0.24#642#fruits#Simply Fresh
Ugli Fruit#Uglifruiwx8or4#0.24#280#fruits#Off the Vine
Vegetable Brain#VegefruieXLBoc#0.73#355#fruits#TriCounty Produce
Vegetable Brain#Vegefruik5FSdl#0.71#498#fruits#Simply Fresh
Vegetable Brain#VegefruiKBfzN0#0.72#453#fruits#Off the Vine
White Walnut#Whitfruit3oVHL#0.30#501#fruits#TriCounty Produce
White Walnut#WhitfruiHygydw#0.30#913#fruits#Simply Fresh
White Walnut#WhitfruieNtplo#0.30#401#fruits#Off the Vine
Wood Apple#WoodfruijVPRqA#0.68#501#fruits#TriCounty Produce
Wood Apple#Woodfrui4Zk69T#0.68#616#fruits#Simply Fresh
Wood Apple#WoodfruiuSLHZK#0.70#474#fruits#Off the Vine
Yellow Horn#Yellfrui5igjjf#1.18#729#fruits#TriCounty Produce
Yellow Horn#Yellfrui0DiPqa#1.13#517#fruits#Simply Fresh
Yellow Horn#Yellfrui0ljvqC#1.14#853#fruits#Off the Vine
Yellow Sapote#YellfruilGmCfq#0.93#204#fruits#TriCounty Produce
Yellow Sapote#Yellfrui4J2mke#0.88#269#fruits#Simply Fresh
Yellow Sapote#Yellfrui6PuXaL#0.86#575#fruits#Off the Vine
Ylang-ylang#Ylanfrui3rmByO#0.76#429#fruits#TriCounty Produce
Ylang-ylang#YlanfruiA80Nkq#0.76#886#fruits#Simply Fresh
Ylang-ylang#YlanfruinUEm5d#0.72#747#fruits#Off the Vine
Zapote Blanco#ZapofruisZ5sMA#0.67#428#fruits#TriCounty Produce
Zapote Blanco#ZapofruilKxl7N#0.65#924#fruits#Simply Fresh
Zapote Blanco#ZapofruiAe6Eu1#0.68#255#fruits#Off the Vine
Zulu Nut#Zulufrui469K4k#0.71#445#fruits#TriCounty Produce
Zulu Nut#ZulufruiWbz6vU#0.71#653#fruits#Simply Fresh
Zulu Nut#Zulufrui0LJnWK#0.71#858#fruits#Off the Vine
Artichoke#ArtivegeIuqmS4#0.71#282#vegetables#The Pantry
Artichoke#Artivegebljjnf#0.69#66#vegetables#TriCounty Produce
Artichoke#ArtivegeTa2lcF#0.70#618#vegetables#Off the Vine
Asparagus#AspavegezC0cDl#0.23#70#vegetables#The Pantry
Asparagus#AspavegeM1q5Kt#0.24#546#vegetables#TriCounty Produce
Asparagus#AspavegeXWbCb8#0.24#117#vegetables#Off the Vine
Basil#Basivegev08fzf#0.31#213#vegetables#The Pantry
Basil#BasivegeF3Uha7#0.29#651#vegetables#TriCounty Produce
Basil#BasivegeqR8SHC#0.31#606#vegetables#Off the Vine
Bean#BeanvegegCFUOp#0.27#794#vegetables#The Pantry
Bean#BeanvegeqMSEVq#0.27#468#vegetables#TriCounty Produce
Bean#Beanvege4IGUwX#0.27#463#vegetables#Off the Vine
Beet#BeetvegedEv4Ic#0.35#120#vegetables#The Pantry
Beet#Beetvegegi1bz1#0.35#540#vegetables#TriCounty Produce
Beet#BeetvegemztZcN#0.36#386#vegetables#Off the Vine
Blackeyed Pea#Blacvege3TPldr#0.86#133#vegetables#The Pantry
Blackeyed Pea#Blacvege3Zqnep#0.88#67#vegetables#TriCounty Produce
Blackeyed Pea#Blacvege3khffZ#0.90#790#vegetables#Off the Vine
Cabbage#CabbvegeY0c4Fw#0.82#726#vegetables#The Pantry
Cabbage#CabbvegeoaK7Co#0.85#439#vegetables#TriCounty Produce
Cabbage#CabbvegeVvO646#0.82#490#vegetables#Off the Vine
Carrot#CarrvegeEbI0sw#0.45#717#vegetables#The Pantry
Carrot#CarrvegeEZndWL#0.49#284#vegetables#TriCounty Produce
Carrot#CarrvegewUkHao#0.47#122#vegetables#Off the Vine
Cauliflower#Caulvege1CPeNG#0.68#756#vegetables#The Pantry
Cauliflower#CaulvegedrPqib#0.66#269#vegetables#TriCounty Produce
Cauliflower#CaulvegeT6cka8#0.65#728#vegetables#Off the Vine
Chayote#ChayvegePRReGE#0.14#233#vegetables#The Pantry
Chayote#Chayvegep058f7#0.14#88#vegetables#TriCounty Produce
Chayote#ChayvegeoxO40S#0.14#611#vegetables#Off the Vine
Corn#CornvegeukXkv6#0.72#632#vegetables#The Pantry
Corn#CornvegePnPREC#0.72#609#vegetables#TriCounty Produce
Corn#CornvegeO0GwoQ#0.70#664#vegetables#Off the Vine
Cucumber#CucuvegeEqQeA7#0.94#499#vegetables#The Pantry
Cucumber#CucuvegewmKbJ1#0.94#738#vegetables#TriCounty Produce
Cucumber#CucuvegeUW6JaA#0.94#565#vegetables#Off the Vine
Cantaloupe#CantvegeIHs9vJ#0.66#411#vegetables#The Pantry
Cantaloupe#CantvegeEaDdST#0.66#638#vegetables#TriCounty Produce
Cantaloupe#CantvegewWQEa0#0.64#682#vegetables#Off the Vine
Carraway#CarrvegewuL4Ma#0.32#740#vegetables#The Pantry
Carraway#CarrvegeyiWfBj#0.32#265#vegetables#TriCounty Produce
Carraway#CarrvegeMjb1i9#0.31#732#vegetables#Off the Vine
Celeriac#CelevegeoTBicd#0.74#350#vegetables#The Pantry
Celeriac#CelevegeCNABoZ#0.70#261#vegetables#TriCounty Produce
Celeriac#Celevege9LUeww#0.70#298#vegetables#Off the Vine
Celery#Celevegej40ZCc#0.59#740#vegetables#The Pantry
Celery#CelevegerYlVRy#0.58#734#vegetables#TriCounty Produce
Celery#Celevege67eimC#0.58#619#vegetables#Off the Vine
Chervil#ChervegeuH4Dge#0.09#502#vegetables#The Pantry
Chervil#Chervegea1OyKO#0.09#299#vegetables#TriCounty Produce
Chervil#Chervegeq56gMO#0.09#474#vegetables#Off the Vine
Chicory#Chicvege79qoQ8#0.09#709#vegetables#The Pantry
Chicory#ChicvegeTSVBQq#0.10#477#vegetables#TriCounty Produce
Chicory#Chicvege6qpcyi#0.10#282#vegetables#Off the Vine
Chinese Cabbage#ChinvegeFNsSRn#0.78#408#vegetables#The Pantry
Chinese Cabbage#Chinvege2ldNr3#0.80#799#vegetables#TriCounty Produce
Chinese Cabbage#ChinvegeK3R2Td#0.80#180#vegetables#Off the Vine
Chinese Beans#ChinvegebxbyPy#0.45#654#vegetables#The Pantry
Chinese Beans#ChinvegewKGwgx#0.45#206#vegetables#TriCounty Produce
Chinese Beans#ChinvegevVjzC0#0.47#643#vegetables#Off the Vine
Chines Kale#ChinvegeCfdkss#0.70#239#vegetables#The Pantry
Chines Kale#Chinvege6V6Dne#0.65#548#vegetables#TriCounty Produce
Chines Kale#ChinvegeB7vE3x#0.66#380#vegetables#Off the Vine
Chinese Radish#ChinvegeXcM4eq#0.22#190#vegetables#The Pantry
Chinese Radish#ChinvegeTdUBqN#0.22#257#vegetables#TriCounty Produce
Chinese Radish#ChinvegeMXMms8#0.22#402#vegetables#Off the Vine
Chinese Mustard#ChinvegeRDdpdl#0.33#149#vegetables#The Pantry
Chinese Mustard#ChinvegeABDhNd#0.32#320#vegetables#TriCounty Produce
Chinese Mustard#Chinvege8NPwa2#0.34#389#vegetables#Off the Vine
Cilantro#CilavegeQXBEsW#0.60#674#vegetables#The Pantry
Cilantro#CilavegeRgjkUG#0.60#355#vegetables#TriCounty Produce
Cilantro#CilavegelT2msu#0.59#464#vegetables#Off the Vine
Collard#CollvegesTGGNw#0.32#745#vegetables#The Pantry
Collard#CollvegeAwdor5#0.32#124#vegetables#TriCounty Produce
Collard#CollvegeQe900L#0.30#796#vegetables#Off the Vine
Coriander#CorivegeXxp4xY#0.26#560#vegetables#The Pantry
Coriander#Corivege9xBAT0#0.27#321#vegetables#TriCounty Produce
Coriander#CorivegeCfNjBx#0.27#709#vegetables#Off the Vine
Dandelion#DandvegeJNcnbr#0.11#285#vegetables#The Pantry
Dandelion#DandvegeGwBkHZ#0.11#733#vegetables#TriCounty Produce
Dandelion#DandvegeZfwVqn#0.11#57#vegetables#Off the Vine
Daikon Radish#DaikvegeHHsd7M#0.61#743#vegetables#The Pantry
Daikon Radish#DaikvegeIu17yC#0.62#459#vegetables#TriCounty Produce
Daikon Radish#DaikvegePzFjqf#0.63#296#vegetables#Off the Vine
Eggplant#EggpvegeKJtydN#0.55#200#vegetables#The Pantry
Eggplant#EggpvegeQMKrNs#0.53#208#vegetables#TriCounty Produce
Eggplant#EggpvegeN0WnSo#0.51#761#vegetables#Off the Vine
English Pea#Englvegea1ytIn#0.40#457#vegetables#The Pantry
English Pea#EnglvegerU9Vty#0.37#263#vegetables#TriCounty Produce
English Pea#EnglvegeCmkd3y#0.39#430#vegetables#Off the Vine
Fennel#Fennvegebz2UM7#0.76#545#vegetables#The Pantry
Fennel#FennvegeQzjtZ3#0.78#795#vegetables#TriCounty Produce
Fennel#FennvegeXSrW61#0.75#79#vegetables#Off the Vine
Garlic#GarlvegesR2yel#0.76#478#vegetables#The Pantry
Garlic#GarlvegeEQvt8W#0.77#349#vegetables#TriCounty Produce
Garlic#GarlvegedljBdK#0.80#708#vegetables#Off the Vine
Ginger#GingvegeMNiTc2#0.88#563#vegetables#The Pantry
Ginger#Gingvegeq366Sn#0.89#738#vegetables#TriCounty Produce
Ginger#GingvegeznyyVj#0.89#598#vegetables#Off the Vine
Horseradish#HorsvegemSwISt#0.12#622#vegetables#The Pantry
Horseradish#HorsvegetCOS0x#0.11#279#vegetables#TriCounty Produce
Horseradish#Horsvegew6XXaS#0.12#478#vegetables#Off the Vine
Japanese Eggplant#JapavegeTdKDCL#0.57#539#vegetables#The Pantry
Japanese Eggplant#JapavegevsJfGa#0.58#782#vegetables#TriCounty Produce
Japanese Eggplant#JapavegeCIrIxd#0.57#777#vegetables#Off the Vine
Jerusalem Artichoke#Jeruvege928cr0#0.13#231#vegetables#The Pantry
Jerusalem Artichoke#JeruvegeC2v086#0.14#123#vegetables#TriCounty Produce
Jerusalem Artichoke#JeruvegeehCYzi#0.14#196#vegetables#Off the Vine
Jicama#JicavegeRWYj9n#0.75#79#vegetables#The Pantry
Jicama#JicavegeGk5LKH#0.71#292#vegetables#TriCounty Produce
Jicama#JicavegeUjpaX1#0.70#308#vegetables#Off the Vine
Kale#Kalevegext6RNT#0.55#765#vegetables#The Pantry
Kale#KalevegeFsp17B#0.53#107#vegetables#TriCounty Produce
Kale#KalevegeAffBTS#0.57#573#vegetables#Off the Vine
Kiwifruit#KiwivegeloZBKJ#0.60#769#vegetables#The Pantry
Kiwifruit#KiwivegenCQAHw#0.59#307#vegetables#TriCounty Produce
Kiwifruit#Kiwivege0Gi3P2#0.59#235#vegetables#Off the Vine
Kohlrabi#KohlvegeJFKZDl#0.26#406#vegetables#The Pantry
Kohlrabi#Kohlvege32UTAj#0.28#613#vegetables#TriCounty Produce
Kohlrabi#KohlvegejNQC1M#0.28#326#vegetables#Off the Vine
Leek#Leekvege5iaFtg#0.70#580#vegetables#The Pantry
Leek#Leekvegei9Wxbz#0.68#188#vegetables#TriCounty Produce
Leek#LeekvegewY4mAc#0.70#473#vegetables#Off the Vine
Lettuce#LettvegesK9wDR#0.55#716#vegetables#The Pantry
Lettuce#LettvegeWzMyCM#0.57#83#vegetables#TriCounty Produce
Lettuce#LettvegeHgfGG8#0.56#268#vegetables#Off the Vine
Melons#Melovege6t93WF#0.11#252#vegetables#The Pantry
Melons#Melovegeq9kz7T#0.12#558#vegetables#TriCounty Produce
Melons#Melovege9kLTXN#0.12#382#vegetables#Off the Vine
Mushroom#MushvegeSq53h8#0.59#365#vegetables#The Pantry
Mushroom#Mushvegedq6lYP#0.59#444#vegetables#TriCounty Produce
Mushroom#Mushvege8o27D2#0.55#467#vegetables#Off the Vine
Okra#OkravegeTszQSL#0.55#62#vegetables#The Pantry
Okra#OkravegeJBWmfh#0.58#165#vegetables#TriCounty Produce
Okra#OkravegeD6tF9n#0.55#77#vegetables#Off the Vine
Onion#OniovegejwimQo#0.80#186#vegetables#The Pantry
Onion#OniovegeUOwwks#0.80#417#vegetables#TriCounty Produce
Onion#OniovegezcRDrc#0.80#435#vegetables#Off the Vine
Oregano#OregvegetlU7Ez#0.71#119#vegetables#The Pantry
Oregano#Oregvege9h9ZKy#0.70#173#vegetables#TriCounty Produce
Oregano#OregvegebXr0PJ#0.70#773#vegetables#Off the Vine
Parsley#ParsvegeXFEjjN#0.83#502#vegetables#The Pantry
Parsley#ParsvegejAg5C4#0.80#454#vegetables#TriCounty Produce
Parsley#ParsvegehAtH2H#0.84#523#vegetables#Off the Vine
Parsnip#Parsvegee9Lp6D#0.46#626#vegetables#The Pantry
Parsnip#ParsvegeSxXHSA#0.47#411#vegetables#TriCounty Produce
Parsnip#Parsvegea0stPf#0.44#403#vegetables#Off the Vine
Pea#Peavegecq4SxR#0.18#342#vegetables#The Pantry
Pea#Peavege46Gdp9#0.18#255#vegetables#TriCounty Produce
Pea#Peavegeov1gc5#0.18#251#vegetables#Off the Vine
Pepper#PeppvegeUcBYRp#0.33#52#vegetables#The Pantry
Pepper#PeppvegeB60btP#0.35#107#vegetables#TriCounty Produce
Pepper#PeppvegeG4tP3e#0.34#481#vegetables#Off the Vine
Pigeon Pea#Pigevegec5bAtm#0.94#391#vegetables#The Pantry
Pigeon Pea#Pigevegeb93eLi#0.91#447#vegetables#TriCounty Produce
Pigeon Pea#PigevegejEBDRa#0.89#259#vegetables#Off the Vine
Irish Potato#IrisvegeJNQqby#0.72#355#vegetables#The Pantry
Irish Potato#Irisvegewq1PLd#0.72#601#vegetables#TriCounty Produce
Irish Potato#IrisvegeAfFLdO#0.68#740#vegetables#Off the Vine
Pumpkin#PumpvegeiYsPR8#0.25#776#vegetables#The Pantry
Pumpkin#PumpvegelqP1Kh#0.25#189#vegetables#TriCounty Produce
Pumpkin#Pumpvegeb3nQU5#0.26#207#vegetables#Off the Vine
Radish#RadivegeNwwSBJ#0.16#613#vegetables#The Pantry
Radish#Radivege0tIBnL#0.16#779#vegetables#TriCounty Produce
Radish#RadivegeNLqJCf#0.16#731#vegetables#Off the Vine
Rhubarb#RhubvegeREfOti#0.12#301#vegetables#The Pantry
Rhubarb#Rhubvege4Jc3b7#0.12#557#vegetables#TriCounty Produce
Rhubarb#RhubvegeaXqF7H#0.12#378#vegetables#Off the Vine
Rosemary#Rosevege16QStc#0.73#380#vegetables#The Pantry
Rosemary#RosevegeNf6Oem#0.75#622#vegetables#TriCounty Produce
Rosemary#RosevegeFgsOyN#0.74#631#vegetables#Off the Vine
Rutabaga#RutavegecUYfQ3#0.55#676#vegetables#The Pantry
Rutabaga#RutavegejOG5DF#0.55#273#vegetables#TriCounty Produce
Rutabaga#RutavegewEVjzV#0.53#452#vegetables#Off the Vine
Salsify#SalsvegeViS9HF#0.11#537#vegetables#The Pantry
Salsify#Salsvegemd3HAL#0.11#753#vegetables#TriCounty Produce
Salsify#SalsvegeuRCnmq#0.10#787#vegetables#Off the Vine
Savory#Savovegee4DRWl#0.21#456#vegetables#The Pantry
Savory#SavovegerZ90Xm#0.21#642#vegetables#TriCounty Produce
Savory#Savovegeje7yy7#0.22#328#vegetables#Off the Vine
Sesame#Sesavege4NAWZE#0.84#54#vegetables#The Pantry
Sesame#SesavegeMTc9IN#0.84#458#vegetables#TriCounty Produce
Sesame#SesavegegOwAjo#0.83#125#vegetables#Off the Vine
Shallots#ShalvegeUO2pDO#0.26#599#vegetables#The Pantry
Shallots#ShalvegeY1sekb#0.27#647#vegetables#TriCounty Produce
Shallots#ShalvegeSDC8VY#0.27#369#vegetables#Off the Vine
Sugar Snap Peas#SugavegepUZDTl#0.47#308#vegetables#The Pantry
Sugar Snap Peas#Sugavege1XyzNH#0.48#205#vegetables#TriCounty Produce
Sugar Snap Peas#SugavegeJuaG7f#0.46#348#vegetables#Off the Vine
Soybean#SoybvegeqxSVRL#0.70#639#vegetables#The Pantry
Soybean#SoybvegezEMjOG#0.68#423#vegetables#TriCounty Produce
Soybean#SoybvegebanSFq#0.67#268#vegetables#Off the Vine
Spaghetti Squash#SpagvegeMNO1yC#0.12#753#vegetables#The Pantry
Spaghetti Squash#SpagvegeilpUaD#0.13#604#vegetables#TriCounty Produce
Spaghetti Squash#SpagvegeAOoZNX#0.13#431#vegetables#Off the Vine
Spinach#SpinvegeegXXou#0.10#742#vegetables#The Pantry
Spinach#SpinvegeVcqXL6#0.11#708#vegetables#TriCounty Produce
Spinach#SpinvegetZ26DN#0.11#625#vegetables#Off the Vine
Sweet Potato#SweevegepNDQWb#0.94#720#vegetables#The Pantry
Sweet Potato#Sweevegepnw7Tm#0.90#377#vegetables#TriCounty Produce
Sweet Potato#Sweevegeyk0C82#0.89#242#vegetables#Off the Vine
Swiss Chard#SwisvegeksalTA#0.54#545#vegetables#The Pantry
Swiss Chard#SwisvegeKm2Kze#0.54#472#vegetables#TriCounty Produce
Swiss Chard#SwisvegehteuMk#0.56#142#vegetables#Off the Vine
Taro#Tarovege3fpGV6#0.87#155#vegetables#The Pantry
Taro#TarovegerZkmof#0.86#371#vegetables#TriCounty Produce
Taro#TarovegeXKPuzc#0.89#443#vegetables#Off the Vine
Tarragon#TarrvegeCzVC6U#0.18#491#vegetables#The Pantry
Tarragon#TarrvegesIkEfS#0.17#65#vegetables#TriCounty Produce
Tarragon#TarrvegerZsKFP#0.18#180#vegetables#Off the Vine
Thyme#Thymvege8Rv72c#0.41#442#vegetables#The Pantry
Thyme#ThymvegeJoUdQS#0.42#237#vegetables#TriCounty Produce
Thyme#ThymvegeRck5uO#0.43#491#vegetables#Off the Vine
Tomato#Tomavegey0NHGK#0.31#60#vegetables#The Pantry
Tomato#TomavegeKAjRUn#0.30#630#vegetables#TriCounty Produce
Tomato#TomavegePZOHlH#0.30#70#vegetables#Off the Vine
Turnip#TurnvegeRVQiV5#0.44#580#vegetables#The Pantry
Turnip#TurnvegeVjIX9D#0.45#743#vegetables#TriCounty Produce
Turnip#TurnvegelFhvuJ#0.44#219#vegetables#Off the Vine
Watercress#WatevegelwzPLQ#0.54#230#vegetables#The Pantry
Watercress#Watevege8oeDCT#0.54#774#vegetables#TriCounty Produce
Watercress#Watevegexr8L1t#0.55#185#vegetables#Off the Vine
Watermelon#WatevegeL83MRH#0.19#698#vegetables#The Pantry
Watermelon#WatevegeR2S4Dq#0.21#488#vegetables#TriCounty Produce
Watermelon#WatevegepFPXQu#0.21#439#vegetables#Off the Vine
Kamote#KamovegegdON75#0.13#218#vegetables#The Pantry
Kamote#KamovegevupDBf#0.13#98#vegetables#TriCounty Produce
Kamote#KamovegeSQX7IA#0.14#703#vegetables#Off the Vine
Alogbati#AlogvegeB1WaJU#0.41#775#vegetables#The Pantry
Alogbati#AlogvegeVr5cPP#0.40#789#vegetables#TriCounty Produce
Alogbati#AlogvegeyTUQzy#0.40#416#vegetables#Off the Vine
Ampalaya#AmpavegemR9fSd#0.85#107#vegetables#The Pantry
Ampalaya#AmpavegeJDu9Im#0.90#676#vegetables#TriCounty Produce
Ampalaya#AmpavegepL8GH5#0.86#728#vegetables#Off the Vine
Dahon ng sili#Dahovege6X9grk#0.11#369#vegetables#The Pantry
Dahon ng sili#DahovegeiHZjQT#0.11#141#vegetables#TriCounty Produce
Dahon ng sili#DahovegeoCDAH8#0.12#517#vegetables#Off the Vine
Gabi#GabivegeVm4Xk3#0.44#396#vegetables#The Pantry
Gabi#Gabivegeu6woqK#0.42#722#vegetables#TriCounty Produce
Gabi#GabivegezcA7q1#0.42#394#vegetables#Off the Vine
Kabute#Kabuvege6Tqrif#0.16#123#vegetables#The Pantry
Kabute#KabuvegeA3uYdG#0.15#183#vegetables#TriCounty Produce
Kabute#KabuvegeXW6ZiI#0.16#624#vegetables#Off the Vine
Kamoteng Kahoy#KamovegeAdW37X#0.42#782#vegetables#The Pantry
Kamoteng Kahoy#KamovegetFlqpC#0.42#515#vegetables#TriCounty Produce
Kamoteng Kahoy#KamovegeMvxoLn#0.40#166#vegetables#Off the Vine
Kangkong#KangvegeSFTvEz#0.35#759#vegetables#The Pantry
Kangkong#KangvegeRLR6gL#0.34#695#vegetables#TriCounty Produce
Kangkong#Kangvege9BFo14#0.35#783#vegetables#Off the Vine
Labanos#Labavege3qrWJL#0.94#514#vegetables#The Pantry
Labanos#LabavegekgVWDH#0.89#210#vegetables#TriCounty Produce
Labanos#LabavegeiVPgMx#0.89#207#vegetables#Off the Vine
Labong#LabovegeX3O8yz#0.85#722#vegetables#The Pantry
Labong#LabovegeI1wSEs#0.87#472#vegetables#TriCounty Produce
Labong#LabovegeOPiQht#0.85#740#vegetables#Off the Vine
Malunggay#MaluvegeHkwAFm#0.30#252#vegetables#The Pantry
Malunggay#Maluvegez6TiSY#0.30#245#vegetables#TriCounty Produce
Malunggay#MaluvegewzY37D#0.31#405#vegetables#Off the Vine
Munggo#MungvegeqeuwGw#0.25#362#vegetables#The Pantry
Munggo#MungvegeNhqWvL#0.26#360#vegetables#TriCounty Produce
Munggo#MungvegeGxNxQC#0.25#555#vegetables#Off the Vine
Pechay#PechvegezDeHFZ#0.36#401#vegetables#The Pantry
Pechay#Pechvegehi4Fcx#0.35#723#vegetables#TriCounty Produce
Pechay#Pechvege8Pq8Eo#0.36#141#vegetables#Off the Vine
Sigarilyas#SigavegeMJrtlV#0.88#335#vegetables#The Pantry
Sigarilyas#SigavegeLhsoOB#0.87#768#vegetables#TriCounty Produce
Sigarilyas#SigavegeS6RJcA#0.93#356#vegetables#Off the Vine
Sitaw#Sitavege0hMi9z#0.65#153#vegetables#The Pantry
Sitaw#Sitavegeez1g6N#0.67#561#vegetables#TriCounty Produce
Sitaw#Sitavege0BCNeF#0.66#674#vegetables#Off the Vine
Talong#TalovegevZjVK6#0.10#530#vegetables#The Pantry
Talong#TalovegexX4MRw#0.09#305#vegetables#TriCounty Produce
Talong#TalovegeO3U2ze#0.10#126#vegetables#Off the Vine
Toge#TogevegeYelJUw#0.54#449#vegetables#The Pantry
Toge#Togevegeilr1xK#0.54#274#vegetables#TriCounty Produce
Toge#Togevegesvjnyn#0.51#316#vegetables#Off the Vine
Ube#UbevegeoPnxvb#0.56#397#vegetables#The Pantry
Ube#Ubevege2CNyve#0.55#450#vegetables#TriCounty Produce
Ube#UbevegeC43sVj#0.55#263#vegetables#Off the Vine
Upo#UpovegecOGRqC#0.22#404#vegetables#The Pantry
Upo#Upovegekjl2wl#0.22#541#vegetables#TriCounty Produce
Upo#UpovegemTTTwI#0.23#459#vegetables#Off the Vine
Edamame#EdamvegeVYtk8z#0.79#296#vegetables#The Pantry
Edamame#Edamvege608vXi#0.78#700#vegetables#TriCounty Produce
Edamame#Edamvege1jiqGY#0.75#115#vegetables#Off the Vine
Hairy melon#HairvegeFYFHIw#0.71#789#vegetables#The Pantry
Hairy melon#HairvegeS7AAqI#0.72#302#vegetables#TriCounty Produce
Hairy melon#HairvegeO6WJHL#0.72#444#vegetables#Off the Vine
Burdock#BurdvegeyLstLV#0.56#761#vegetables#The Pantry
Burdock#BurdvegeZsqAjT#0.56#582#vegetables#TriCounty Produce
Burdock#BurdvegeycF7mo#0.55#566#vegetables#Off the Vine
Snake gourd#SnakvegesfHGvt#0.92#626#vegetables#The Pantry
Snake gourd#SnakvegedlNiBk#0.92#669#vegetables#TriCounty Produce
Snake gourd#Snakvegec5n1UM#0.92#143#vegetables#Off the Vine
Wasabi#Wasavege5P5pZp#0.67#751#vegetables#The Pantry
Wasabi#Wasavege6EEE9r#0.68#559#vegetables#TriCounty Produce
Wasabi#Wasavege1ve7TY#0.65#61#vegetables#Off the Vine
Yam#YamvegeRN9ONH#0.57#438#vegetables#The Pantry
Yam#YamvegeWjdzeA#0.56#564#vegetables#TriCounty Produce
Yam#YamvegeI1AnyI#0.56#456#vegetables#Off the Vine
Apple Fritters#AppldessDj96hw#6.12#16#desserts#Mom's Kitchen
Apple Fritters#AppldessrN1kvM#6.06#7#desserts#The Baking Pan
Banana Split#Banadess7tpjkJ#10.86#10#desserts#Mom's Kitchen
Banana Split#Banadessfif758#11.07#14#desserts#The Baking Pan
Blueberry Boy Bait#BluedesseX2LVU#3.72#16#desserts#Mom's Kitchen
Blueberry Boy Bait#Bluedess9zLhaH#3.93#9#desserts#The Baking Pan
Candied Cranberries#CanddessjW92p3#1.77#9#desserts#Mom's Kitchen
Candied Cranberries#CanddesskhtVoQ#1.72#0#desserts#The Baking Pan
Daiquiri Souffle#DaiqdessebnYcy#9.54#15#desserts#Mom's Kitchen
Daiquiri Souffle#DaiqdessfM1DnX#9.72#6#desserts#The Baking Pan
Bananas Flambe#BanadesscczumD#6.94#12#desserts#Mom's Kitchen
Bananas Flambe#Banadess8qNfxd#7.07#16#desserts#The Baking Pan
Pie, Apple#Pie,desshcSHhT#7.88#11#desserts#Mom's Kitchen
Pie, Apple#Pie,dessTbiwDp#7.88#15#desserts#The Baking Pan
Pie, Pumpkin#Pie,desswhPBPB#6.00#20#desserts#Mom's Kitchen
Pie, Pumpkin#Pie,dessDg3NWl#6.24#19#desserts#The Baking Pan
Pie, Blueberry#Pie,dessw9VdgD#2.14#3#desserts#Mom's Kitchen
Pie, Blueberry#Pie,dessiSjZKD#2.12#1#desserts#The Baking Pan
Pie, Pecan#Pie,dess2NqhNR#12.70#20#desserts#Mom's Kitchen
Pie, Pecan#Pie,dessB1LfcE#12.33#12#desserts#The Baking Pan
Pie, Cranberry Apple#Pie,dess1mL7IS#10.16#7#desserts#Mom's Kitchen
Pie, Cranberry Apple#Pie,dessmDhkUA#10.16#11#desserts#The Baking Pan
Pie, Banana Cream#Pie,dessH80DuG#7.35#6#desserts#Mom's Kitchen
Pie, Banana Cream#Pie,dessf1YvFb#7.08#11#desserts#The Baking Pan
Pie, Key Lime#Pie,desshtli5N#4.85#2#desserts#Mom's Kitchen
Pie, Key Lime#Pie,dessMwQkKm#5.13#1#desserts#The Baking Pan
Pie, Lemon Meringue#Pie,dess9naVkX#3.74#7#desserts#Mom's Kitchen
Pie, Lemon Meringue#Pie,dessKYcNML#3.67#5#desserts#The Baking Pan
Pie, Caramel#Pie,dessSUuiIU#2.27#9#desserts#Mom's Kitchen
Pie, Caramel#Pie,dessvo8uHh#2.33#4#desserts#The Baking Pan
Pie, Raspberry#Pie,dessUHhMlS#2.36#0#desserts#Mom's Kitchen
Pie, Raspberry#Pie,dessJflbf5#2.36#2#desserts#The Baking Pan
Ice Cream, Chocolate#Ice desseXuyxx#1.44#9#desserts#Mom's Kitchen
Ice Cream, Chocolate#Ice dessASBohf#1.41#13#desserts#The Baking Pan
Ice Cream, Vanilla#Ice dessYnzbbt#11.92#19#desserts#Mom's Kitchen
Ice Cream, Vanilla#Ice dessUBBKp8#11.58#10#desserts#The Baking Pan
Ice Cream, Strawberry#Ice dessfTwKhD#1.90#14#desserts#Mom's Kitchen
Ice Cream, Strawberry#Ice dessaO9Fxf#1.99#6#desserts#The Baking Pan
Ice Cream, Rocky Road#Ice dessyIri3P#13.10#20#desserts#Mom's Kitchen
Ice Cream, Rocky Road#Ice dessZuLr8F#13.48#13#desserts#The Baking Pan
Ice Cream, Mint Chocolate Chip#Ice dessV1IGG7#5.75#4#desserts#Mom's Kitchen
Ice Cream, Mint Chocolate Chip#Ice dessX1gEQ4#5.64#1#desserts#The Baking Pan
Ice Cream Sundae#Ice dessbhlAXt#5.62#11#desserts#Mom's Kitchen
Ice Cream Sundae#Ice dessByapxl#5.72#16#desserts#The Baking Pan
Cobbler, Peach#CobbdessYUGeOB#10.14#20#desserts#Mom's Kitchen
Cobbler, Peach#CobbdessXfEtUK#10.43#16#desserts#The Baking Pan
Cobbler, Berry-Pecan#Cobbdessx3htak#5.36#12#desserts#Mom's Kitchen
Cobbler, Berry-Pecan#Cobbdesse4FUVI#5.41#8#desserts#The Baking Pan
Cobbler, Blueberry#CobbdessbiI0oF#3.78#11#desserts#Mom's Kitchen
Cobbler, Blueberry#CobbdessMXxbBN#3.57#2#desserts#The Baking Pan
Cobbler, Cherry#CobbdessNSa8QW#12.58#0#desserts#Mom's Kitchen
Cobbler, Cherry#CobbdessA1dADa#12.10#10#desserts#The Baking Pan
Cobbler, Huckleberry#Cobbdess3t6O8d#3.99#18#desserts#Mom's Kitchen
Cobbler, Huckleberry#CobbdessGI9euK#3.88#0#desserts#The Baking Pan
Cobbler, Rhubarb#Cobbdess22X40Z#9.54#0#desserts#Mom's Kitchen
Cobbler, Rhubarb#CobbdessPfnCT0#9.27#18#desserts#The Baking Pan
Cobbler, Strawberry#CobbdessI78188#12.43#0#desserts#Mom's Kitchen
Cobbler, Strawberry#CobbdessH3LdgQ#12.20#3#desserts#The Baking Pan
Cobbler, Zucchini#Cobbdess5rK4dP#11.24#3#desserts#Mom's Kitchen
Cobbler, Zucchini#Cobbdess4Ez8kS#10.51#10#desserts#The Baking Pan
Brownies#BrowdessmogdTl#7.62#9#desserts#Mom's Kitchen
Brownies#Browdess84Qc1z#7.55#9#desserts#The Baking Pan
Fudge Bar#Fudgdess8iXSyf#11.72#6#desserts#Mom's Kitchen
Fudge Bar#FudgdessakU1Id#12.29#5#desserts#The Baking Pan
Cookies, Oatmeal#Cookdessnq9Oya#2.84#15#desserts#Mom's Kitchen
Cookies, Oatmeal#CookdessBhgp7p#2.68#10#desserts#The Baking Pan
Cookies, Chocolate Chip#CookdessRVszsZ#12.73#17#desserts#Mom's Kitchen
Cookies, Chocolate Chip#CookdessSOoHmT#12.26#19#desserts#The Baking Pan
Cookies, Peanut Butter#Cookdess2UcMI2#7.82#5#desserts#Mom's Kitchen
Cookies, Peanut Butter#Cookdess1cILme#7.46#20#desserts#The Baking Pan
Mousse, Chocolate#MousdessDpN4sQ#6.25#20#desserts#Mom's Kitchen
Mousse, Chocolate#Mousdess8FyFT8#5.96#1#desserts#The Baking Pan
Mousse, Blueberry Maple#MousdessacwrkO#7.28#7#desserts#Mom's Kitchen
Mousse, Blueberry Maple#MousdessbiCMFg#7.21#12#desserts#The Baking Pan
Mousse, Chocolate Banana#MousdessIeW4qz#5.13#2#desserts#Mom's Kitchen
Mousse, Chocolate Banana#Mousdess1De9oL#5.08#19#desserts#The Baking Pan
Mousse, Cherry#Mousdesss1bF8H#13.05#20#desserts#Mom's Kitchen
Mousse, Cherry#Mousdess0ujevx#12.43#1#desserts#The Baking Pan
Mousse, Eggnog#MousdessZ38hXj#9.07#10#desserts#Mom's Kitchen
Mousse, Eggnog#Mousdesshs05ST#8.81#8#desserts#The Baking Pan
Mousse, Strawberry#MousdessHCDlBK#5.58#3#desserts#Mom's Kitchen
Mousse, Strawberry#MousdessSZ4PyW#5.36#6#desserts#The Baking Pan
Sherbet, Cantaloupe#Sherdess3DCxUg#3.11#9#desserts#Mom's Kitchen
Sherbet, Cantaloupe#Sherdesscp2VIz#2.99#7#desserts#The Baking Pan
Sherbet, Lemon Milk#Sherdess1JVFOS#7.57#9#desserts#Mom's Kitchen
Sherbet, Lemon Milk#SherdessC865vu#7.57#0#desserts#The Baking Pan
Sherbet, Orange Crush#Sherdess8W8Mb9#4.32#18#desserts#Mom's Kitchen
Sherbet, Orange Crush#SherdessxmVJBF#4.16#10#desserts#The Baking Pan
Sherbet, Blueberry#SherdessFAgxqp#3.46#9#desserts#Mom's Kitchen
Sherbet, Blueberry#SherdessMPL87u#3.60#6#desserts#The Baking Pan
Sherbet, Raspberry#Sherdesse86ugA#6.08#1#desserts#Mom's Kitchen
Sherbet, Raspberry#Sherdesslc1etR#5.85#12#desserts#The Baking Pan
Sherbet, Strawberry#SherdessFwv09m#4.63#17#desserts#Mom's Kitchen
Sherbet, Strawberry#SherdessKB0H7q#4.81#20#desserts#The Baking Pan
Tart, Apple#TartdessrsTyXA#3.35#18#desserts#Mom's Kitchen
Tart, Apple#Tartdessp7pyiy#3.13#11#desserts#The Baking Pan
Tart, Almond#TartdessC7FARL#6.62#10#desserts#Mom's Kitchen
Tart, Almond#Tartdess1V1A1c#6.68#13#desserts#The Baking Pan
Tart, Blueberry#TartdesssQZRXX#10.28#10#desserts#Mom's Kitchen
Tart, Blueberry#TartdessUSJSuc#10.28#9#desserts#The Baking Pan
Tart, Chocolate-Pear#Tartdess2pdOE4#5.67#17#desserts#Mom's Kitchen
Tart, Chocolate-Pear#TartdessL3aEDd#5.51#9#desserts#The Baking Pan
Tart, Lemon Fudge#Tartdess9DhZUT#3.88#3#desserts#Mom's Kitchen
Tart, Lemon Fudge#TartdesshzLOWt#3.96#13#desserts#The Baking Pan
Tart, Pecan#TartdessvSbXzd#11.80#3#desserts#Mom's Kitchen
Tart, Pecan#Tartdess6YXJec#11.04#13#desserts#The Baking Pan
Tart, Pineapple#TartdesseMfJFe#9.01#18#desserts#Mom's Kitchen
Tart, Pineapple#TartdessA2Wftr#8.44#13#desserts#The Baking Pan
Tart, Pear#Tartdess4a1BUc#10.09#2#desserts#Mom's Kitchen
Tart, Pear#TartdessNw8YPG#10.68#5#desserts#The Baking Pan
Tart, Raspberry#TartdessAVnpP6#6.18#7#desserts#Mom's Kitchen
Tart, Raspberry#TartdessfVxZFf#5.95#9#desserts#The Baking Pan
Tart, Strawberry#Tartdess4IUcZW#4.75#8#desserts#Mom's Kitchen
Tart, Strawberry#Tartdess2BeEDb#4.61#17#desserts#The Baking Pan
Tart, Raspberry#TartdesshyBd24#1.85#5#desserts#Mom's Kitchen
Tart, Raspberry#Tartdess5fqxgy#1.94#20#desserts#The Baking Pan
Trifle, Berry#TrifdessmEkbU2#12.48#19#desserts#Mom's Kitchen
Trifle, Berry#TrifdessAV9Ix8#12.60#18#desserts#The Baking Pan
Trifle, American#TrifdesscsdSCd#4.70#17#desserts#Mom's Kitchen
Trifle, American#TrifdessTArskm#4.35#11#desserts#The Baking Pan
Trifle, English#TrifdessX87q8T#8.20#9#desserts#Mom's Kitchen
Trifle, English#Trifdess52l955#8.12#11#desserts#The Baking Pan
Trifle, Orange#TrifdesslUwxwe#9.74#15#desserts#Mom's Kitchen
Trifle, Orange#TrifdessFrfCHP#10.22#1#desserts#The Baking Pan
Trifle, Pumpkin#TrifdessJKFN96#4.72#7#desserts#Mom's Kitchen
Trifle, Pumpkin#TrifdessMNw4EV#4.95#16#desserts#The Baking Pan
Trifle, Scottish#TrifdessFa0JdK#13.63#0#desserts#Mom's Kitchen
Trifle, Scottish#TrifdessAAUQCN#14.03#6#desserts#The Baking Pan
Trifle, Sherry#TrifdesscuttJg#4.42#5#desserts#Mom's Kitchen
Trifle, Sherry#TrifdesspRGpfP#4.21#19#desserts#The Baking Pan
Trifle, Strawberry#TrifdessAd5TpV#3.58#11#desserts#Mom's Kitchen
Trifle, Strawberry#Trifdess1rtW0A#3.58#3#desserts#The Baking Pan
Trifle, Scotch Whiskey#Trifdess2zJsGi#5.44#5#desserts#Mom's Kitchen
Trifle, Scotch Whiskey#TrifdessL8nuI6#5.18#5#desserts#The Baking Pan
Cheesecake, Amaretto#CheedessOJBqfD#11.89#5#desserts#Mom's Kitchen
Cheesecake, Amaretto#CheedessVnDf14#11.89#9#desserts#The Baking Pan
Cheesecake, Apple#Cheedessuks1YK#11.22#15#desserts#Mom's Kitchen
Cheesecake, Apple#CheedessMYKaKK#11.01#14#desserts#The Baking Pan
Cheesecake, Apricot#CheedessKUxTYY#12.34#16#desserts#Mom's Kitchen
Cheesecake, Apricot#CheedessMvB1pr#11.88#18#desserts#The Baking Pan
Cheesecake, Australian#CheedessQ9WAIn#2.70#9#desserts#Mom's Kitchen
Cheesecake, Australian#CheedessE6Jyjc#2.53#14#desserts#The Baking Pan
Cheesecake, Arkansas#CheedessTbqzmw#6.98#9#desserts#Mom's Kitchen
Cheesecake, Arkansas#CheedesstWJZfC#6.66#5#desserts#The Baking Pan
Cheesecake, Blueberry#Cheedessyo51KL#8.07#11#desserts#Mom's Kitchen
Cheesecake, Blueberry#Cheedess4Hz7P4#8.62#5#desserts#The Baking Pan
Cheesecake, Cherry#CheedessEahRkC#4.40#14#desserts#Mom's Kitchen
Cheesecake, Cherry#Cheedess3Nx4jZ#4.65#3#desserts#The Baking Pan
Cheesecake, Cran-Raspberry#CheedessrJsr9i#13.47#20#desserts#Mom's Kitchen
Cheesecake, Cran-Raspberry#CheedesshcuXCy#14.00#6#desserts#The Baking Pan
Cheesecake, German Chocolate#CheedesswayvJL#12.03#16#desserts#Mom's Kitchen
Cheesecake, German Chocolate#CheedessebTAeB#11.58#0#desserts#The Baking Pan
Cheesecake, Turtle#CheedessLqgeIA#12.19#6#desserts#Mom's Kitchen
Cheesecake, Turtle#CheedessvyNohA#12.07#19#desserts#The Baking Pan
Brownies, Apple#BrowdessIDW1Cc#5.44#12#desserts#Mom's Kitchen
Brownies, Apple#BrowdessyRMrAH#5.14#12#desserts#The Baking Pan
Brownies, Fudge#BrowdessmIHIFJ#5.19#8#desserts#Mom's Kitchen
Brownies, Fudge#BrowdessqewJ38#5.10#17#desserts#The Baking Pan
Brownies, Almond Macaroon#BrowdessniK7QI#10.57#3#desserts#Mom's Kitchen
Brownies, Almond Macaroon#BrowdessgkXURH#10.36#17#desserts#The Baking Pan
Brownies, Butterscotch#BrowdesslpA06E#7.16#13#desserts#Mom's Kitchen
Brownies, Butterscotch#BrowdessK5hofE#7.30#6#desserts#The Baking Pan
Brownies, Caramel#BrowdessVGfoA8#3.07#3#desserts#Mom's Kitchen
Brownies, Caramel#Browdess5jvVMM#3.13#11#desserts#The Baking Pan
Brownies, Cherry#Browdessyoa66A#3.39#17#desserts#Mom's Kitchen
Brownies, Cherry#BrowdessIg2JuF#3.39#11#desserts#The Baking Pan
Brownies, Chocolate Chip#Browdessb9dc59#6.18#10#desserts#Mom's Kitchen
Brownies, Chocolate Chip#BrowdessvW4nOx#6.43#14#desserts#The Baking Pan
Brownies, Coconut#BrowdessWPHrVR#3.06#15#desserts#Mom's Kitchen
Brownies, Coconut#BrowdessGVBlML#2.86#11#desserts#The Baking Pan
Brownies, Cream Cheese#Browdess1OyRay#12.74#4#desserts#Mom's Kitchen
Brownies, Cream Cheese#Browdess2fRsNv#12.61#19#desserts#The Baking Pan
Brownies, Fudge Mint#Browdessl7DP7k#11.45#14#desserts#Mom's Kitchen
Brownies, Fudge Mint#Browdessv70VKQ#11.34#16#desserts#The Baking Pan
Brownies, Mint Chip#BrowdessDDMvF7#1.81#15#desserts#Mom's Kitchen
Brownies, Mint Chip#Browdess0j9PBD#1.84#9#desserts#The Baking Pan
Cake, Angel Food#CakedessEaqGaE#11.18#3#desserts#Mom's Kitchen
Cake, Angel Food#CakedessJyAyFe#11.18#14#desserts#The Baking Pan
Cake, Chocolate#CakedessKLXFbn#10.11#7#desserts#Mom's Kitchen
Cake, Chocolate#CakedessfNP5Hg#9.91#14#desserts#The Baking Pan
Cake, Carrot#CakedessUTgMoV#4.20#13#desserts#Mom's Kitchen
Cake, Carrot#CakedessQdkaYg#4.00#3#desserts#The Baking Pan
Cake, Lemon Blueberry#CakedessepkeEW#11.73#16#desserts#Mom's Kitchen
Cake, Lemon Blueberry#CakedessHTKyQs#12.42#16#desserts#The Baking Pan
Cake Triple Fudge#CakedessiZ75lR#7.92#7#desserts#Mom's Kitchen
Cake Triple Fudge#CakedessWRrSXP#8.00#15#desserts#The Baking Pan
Cake, Walnut#CakedessveYVXZ#10.83#17#desserts#Mom's Kitchen
Cake, Walnut#Cakedesse22rT5#11.04#7#desserts#The Baking Pan
Cake, French Apple#CakedessjA2Kxv#1.95#0#desserts#Mom's Kitchen
Cake, French Apple#CakedessNBHCk0#1.86#20#desserts#The Baking Pan
Cake, Fig#CakedessOncX4y#6.82#3#desserts#Mom's Kitchen
Cake, Fig#CakedessTJtffn#7.08#10#desserts#The Baking Pan
Cake, Maple#CakedessnoGPRF#3.04#11#desserts#Mom's Kitchen
Cake, Maple#CakedessfVattM#3.22#4#desserts#The Baking Pan
Cake, Devil's Food#CakedessiXcDCt#4.73#7#desserts#Mom's Kitchen
Cake, Devil's Food#CakedessnBZk45#4.82#6#desserts#The Baking Pan
Cake, Double-Lemon#CakedesskeS0Vd#3.46#9#desserts#Mom's Kitchen
Cake, Double-Lemon#Cakedess50vx53#3.60#6#desserts#The Baking Pan
Sorbet, Blackberry#SorbdessQoa0CE#9.88#15#desserts#Mom's Kitchen
Sorbet, Blackberry#SorbdessqoOYzv#9.78#9#desserts#The Baking Pan

View File

@@ -0,0 +1,6 @@
TriCounty Produce#309 S. Main Street#Middle Town#MN#55432#763 555 5761#Mort Dufresne#763 555 5765
Simply Fresh#15612 Bogart Lane#Harrigan#WI#53704#420 333 3912#Cheryl Swedberg#420 333 3952
Off the Vine#133 American Ct.#Centennial#IA#52002#563 121 3800#Bob King#563 121 3800 x54
The Pantry#1206 N. Creek Way#Middle Town#MN#55432#763 555 3391#Sully Beckstrom#763 555 3391
Mom's Kitchen#53 Yerman Ct.#Middle Town#MN#55432#763 554 9200#Maggie Kultgen#763 554 9200 x12
The Baking Pan#1415 53rd Ave.#Dutchin#MN#56304#320 442 2277#Mike Roan#320 442 6879

View File

@@ -0,0 +1,461 @@
/* File: txn_guide.c */
/* We assume an ANSI-compatible compiler */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#ifdef _WIN32
#include <windows.h>
#define PATHD '\\'
extern int getopt(int, char * const *, const char *);
extern char *optarg;
typedef HANDLE thread_t;
#define thread_create(thrp, attr, func, arg) \
(((*(thrp) = CreateThread(NULL, 0, \
(LPTHREAD_START_ROUTINE)(func), (arg), 0, NULL)) == NULL) ? -1 : 0)
#define thread_join(thr, statusp) \
((WaitForSingleObject((thr), INFINITE) == WAIT_OBJECT_0) && \
GetExitCodeThread((thr), (LPDWORD)(statusp)) ? 0 : -1)
typedef HANDLE mutex_t;
#define mutex_init(m, attr) \
(((*(m) = CreateMutex(NULL, FALSE, NULL)) != NULL) ? 0 : -1)
#define mutex_lock(m) \
((WaitForSingleObject(*(m), INFINITE) == WAIT_OBJECT_0) ? 0 : -1)
#define mutex_unlock(m) (ReleaseMutex(*(m)) ? 0 : -1)
#else
#include <pthread.h>
#include <unistd.h>
#define PATHD '/'
typedef pthread_t thread_t;
#define thread_create(thrp, attr, func, arg) \
pthread_create((thrp), (attr), (func), (arg))
#define thread_join(thr, statusp) pthread_join((thr), (statusp))
typedef pthread_mutex_t mutex_t;
#define mutex_init(m, attr) pthread_mutex_init((m), (attr))
#define mutex_lock(m) pthread_mutex_lock(m)
#define mutex_unlock(m) pthread_mutex_unlock(m)
#endif
/* Run 5 writers threads at a time. */
#define NUMWRITERS 5
/*
* Printing of a thread_t is implementation-specific, so we
* create our own thread IDs for reporting purposes.
*/
int global_thread_num;
mutex_t thread_num_lock;
/* Forward declarations */
int count_records(DB *, DB_TXN *);
int open_db(DB **, const char *, const char *, DB_ENV *, u_int32_t);
int usage(void);
void *writer_thread(void *);
/* Usage function */
int
usage()
{
fprintf(stderr, " [-h <database_home_directory>]\n");
return (EXIT_FAILURE);
}
int
main(int argc, char *argv[])
{
/* Initialize our handles */
DB *dbp = NULL;
DB_ENV *envp = NULL;
thread_t writer_threads[NUMWRITERS];
int ch, i, ret, ret_t;
u_int32_t env_flags;
char *db_home_dir;
/* Application name */
const char *prog_name = "txn_guide";
/* Database file name */
const char *file_name = "mydb.db";
/* Parse the command line arguments */
#ifdef _WIN32
db_home_dir = ".\\";
#else
db_home_dir = "./";
#endif
while ((ch = getopt(argc, argv, "h:")) != EOF)
switch (ch) {
case 'h':
db_home_dir = optarg;
break;
case '?':
default:
return (usage());
}
/* Create the environment */
ret = db_env_create(&envp, 0);
if (ret != 0) {
fprintf(stderr, "Error creating environment handle: %s\n",
db_strerror(ret));
goto err;
}
/*
* Indicate that we want db to perform lock detection internally.
* Also indicate that the transaction with the fewest number of
* write locks will receive the deadlock notification in
* the event of a deadlock.
*/
ret = envp->set_lk_detect(envp, DB_LOCK_MINWRITE);
if (ret != 0) {
fprintf(stderr, "Error setting lock detect: %s\n",
db_strerror(ret));
goto err;
}
env_flags =
DB_CREATE | /* Create the environment if it does not exist */
DB_RECOVER | /* Run normal recovery. */
DB_INIT_LOCK | /* Initialize the locking subsystem */
DB_INIT_LOG | /* Initialize the logging subsystem */
DB_INIT_TXN | /* Initialize the transactional subsystem. This
* also turns on logging. */
DB_INIT_MPOOL | /* Initialize the memory pool (in-memory cache) */
DB_THREAD; /* Cause the environment to be free-threaded */
/* Now actually open the environment */
ret = envp->open(envp, db_home_dir, env_flags, 0);
if (ret != 0) {
fprintf(stderr, "Error opening environment: %s\n",
db_strerror(ret));
goto err;
}
/*
* If we had utility threads (for running checkpoints or
* deadlock detection, for example) we would spawn those
* here. However, for a simple example such as this,
* that is not required.
*/
/* Open the database */
ret = open_db(&dbp, prog_name, file_name,
envp, DB_DUPSORT);
if (ret != 0)
goto err;
/* Initialize a mutex. Used to help provide thread ids. */
(void)mutex_init(&thread_num_lock, NULL);
/* Start the writer threads. */
for (i = 0; i < NUMWRITERS; i++)
(void)thread_create(
&writer_threads[i], NULL, writer_thread, (void *)dbp);
/* Join the writers */
for (i = 0; i < NUMWRITERS; i++)
(void)thread_join(writer_threads[i], NULL);
err:
/* Close our database handle, if it was opened. */
if (dbp != NULL) {
ret_t = dbp->close(dbp, 0);
if (ret_t != 0) {
fprintf(stderr, "%s database close failed: %s\n",
file_name, db_strerror(ret_t));
ret = ret_t;
}
}
/* Close our environment, if it was opened. */
if (envp != NULL) {
ret_t = envp->close(envp, 0);
if (ret_t != 0) {
fprintf(stderr, "environment close failed: %s\n",
db_strerror(ret_t));
ret = ret_t;
}
}
/* Final status message and return. */
printf("I'm all done.\n");
return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
/*
* A function that performs a series of writes to a
* Berkeley DB database. The information written
* to the database is largely nonsensical, but the
* mechanism of transactional commit/abort and
* deadlock detection is illustrated here.
*/
void *
writer_thread(void *args)
{
static char *key_strings[] = {
"key 1", "key 2", "key 3", "key 4", "key 5",
"key 6", "key 7", "key 8", "key 9", "key 10"
};
DB *dbp;
DB_ENV *envp;
DBT key, value;
DB_TXN *txn;
int i, j, payload, ret, thread_num;
int retry_count, max_retries = 20; /* Max retry on a deadlock */
dbp = (DB *)args;
envp = dbp->get_env(dbp);
/* Get the thread number */
(void)mutex_lock(&thread_num_lock);
global_thread_num++;
thread_num = global_thread_num;
(void)mutex_unlock(&thread_num_lock);
/* Initialize the random number generator */
srand(thread_num);
/* Write 50 times and then quit */
for (i = 0; i < 50; i++) {
retry_count = 0; /* Used for deadlock retries */
/*
* Some think it is bad form to loop with a goto statement, but
* we do it anyway because it is the simplest and clearest way
* to achieve our abort/retry operation.
*/
retry:
/* Begin our transaction. We group multiple writes in
* this thread under a single transaction so as to
* (1) show that you can atomically perform multiple writes
* at a time, and (2) to increase the chances of a
* deadlock occurring so that we can observe our
* deadlock detection at work.
*
* Normally we would want to avoid the potential for deadlocks,
* so for this workload the correct thing would be to perform our
* puts with autocommit. But that would excessively simplify our
* example, so we do the "wrong" thing here instead.
*/
ret = envp->txn_begin(envp, NULL, &txn, 0);
if (ret != 0) {
envp->err(envp, ret, "txn_begin failed");
return ((void *)EXIT_FAILURE);
}
for (j = 0; j < 10; j++) {
/* Set up our key and values DBTs */
memset(&key, 0, sizeof(DBT));
key.data = key_strings[j];
key.size = (u_int32_t)strlen(key_strings[j]) + 1;
memset(&value, 0, sizeof(DBT));
payload = rand() + i;
value.data = &payload;
value.size = sizeof(int);
/* Perform the database put. */
switch (ret = dbp->put(dbp, txn, &key, &value, 0)) {
case 0:
break;
/*
* Our database is configured for sorted duplicates,
* so there is a potential for a KEYEXIST error return.
* If we get one, simply ignore it and continue on.
*
* Note that you will see KEYEXIST errors only after you
* have run this program at least once.
*/
case DB_KEYEXIST:
printf("Got keyexists.\n");
break;
/*
* Here's where we perform deadlock detection. If
* DB_LOCK_DEADLOCK is returned by the put operation,
* then this thread has been chosen to break a deadlock.
* It must abort its operation, and optionally retry the
* put.
*/
case DB_LOCK_DEADLOCK:
/*
* First thing that we MUST do is abort the
* transaction.
*/
(void)txn->abort(txn);
/*
* Now we decide if we want to retry the operation.
* If we have retried less than max_retries,
* increment the retry count and goto retry.
*/
if (retry_count < max_retries) {
printf("Writer %i: Got DB_LOCK_DEADLOCK.\n",
thread_num);
printf("Writer %i: Retrying write operation.\n",
thread_num);
retry_count++;
goto retry;
}
/*
* Otherwise, just give up.
*/
printf("Writer %i: ", thread_num);
printf("Got DB_LOCK_DEADLOCK and out of retries.\n");
printf("Writer %i: Giving up.\n", thread_num);
return ((void *)EXIT_FAILURE);
/*
* If a generic error occurs, we simply abort the
* transaction and exit the thread completely.
*/
default:
envp->err(envp, ret, "db put failed");
ret = txn->abort(txn);
if (ret != 0)
envp->err(envp, ret,
"txn abort failed");
return ((void *)EXIT_FAILURE);
} /** End case statement **/
} /** End for loop **/
/*
* print the number of records found in the database.
* See count_records() for usage information.
*/
printf("Thread %i. Record count: %i\n", thread_num,
count_records(dbp, NULL));
/*
* If all goes well, we can commit the transaction and
* exit the thread.
*/
ret = txn->commit(txn, 0);
if (ret != 0) {
envp->err(envp, ret, "txn commit failed");
return ((void *)EXIT_FAILURE);
}
}
return ((void *)EXIT_SUCCESS);
}
/*
* This simply counts the number of records contained in the
* database and returns the result. You can use this function
* in three ways:
*
* First call it with an active txn handle.
* Secondly, configure the cursor for uncommitted reads (this
* is what the example currently does).
* Third, call count_records AFTER the writer has committed
* its transaction.
*
* If you do none of these things, the writer thread will
* self-deadlock.
*
* Note that this function exists only for illustrative purposes.
* A more straight-forward way to count the number of records in
* a database is to use DB->stat() or DB->stat_print().
*/
int
count_records(DB *dbp, DB_TXN *txn)
{
DBT key, value;
DBC *cursorp;
int count, ret;
cursorp = NULL;
count = 0;
/* Get the cursor */
ret = dbp->cursor(dbp, txn, &cursorp,
DB_READ_UNCOMMITTED);
if (ret != 0) {
dbp->err(dbp, ret,
"count_records: cursor open failed.");
goto cursor_err;
}
/* Get the key DBT used for the database read */
memset(&key, 0, sizeof(DBT));
memset(&value, 0, sizeof(DBT));
do {
ret = cursorp->get(cursorp, &key, &value, DB_NEXT);
switch (ret) {
case 0:
count++;
break;
case DB_NOTFOUND:
break;
default:
dbp->err(dbp, ret,
"Count records unspecified error");
goto cursor_err;
}
} while (ret == 0);
cursor_err:
if (cursorp != NULL) {
ret = cursorp->close(cursorp);
if (ret != 0) {
dbp->err(dbp, ret,
"count_records: cursor close failed.");
}
}
return (count);
}
/* Open a Berkeley DB database */
int
open_db(DB **dbpp, const char *progname, const char *file_name,
DB_ENV *envp, u_int32_t extra_flags)
{
int ret;
u_int32_t open_flags;
DB *dbp;
/* Initialize the DB handle */
ret = db_create(&dbp, envp, 0);
if (ret != 0) {
fprintf(stderr, "%s: %s\n", progname,
db_strerror(ret));
return (EXIT_FAILURE);
}
/* Point to the memory malloc'd by db_create() */
*dbpp = dbp;
if (extra_flags != 0) {
ret = dbp->set_flags(dbp, extra_flags);
if (ret != 0) {
dbp->err(dbp, ret,
"open_db: Attempt to set extra flags failed.");
return (EXIT_FAILURE);
}
}
/* Now open the database */
open_flags = DB_CREATE | /* Allow database creation */
DB_READ_UNCOMMITTED | /* Allow dirty reads */
DB_AUTO_COMMIT; /* Allow autocommit */
ret = dbp->open(dbp, /* Pointer to the database */
NULL, /* Txn pointer */
file_name, /* File name */
NULL, /* Logical db name */
DB_BTREE, /* Database type (using btree) */
open_flags, /* Open flags */
0); /* File mode. Using defaults */
if (ret != 0) {
dbp->err(dbp, ret, "Database '%s' open failed",
file_name);
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}

View File

@@ -0,0 +1,451 @@
/* File: txn_guide_inmemory.c */
/* We assume an ANSI-compatible compiler */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <db.h>
#ifdef _WIN32
#include <windows.h>
#define PATHD '\\'
extern int getopt(int, char * const *, const char *);
extern char *optarg;
typedef HANDLE thread_t;
#define thread_create(thrp, attr, func, arg) \
(((*(thrp) = CreateThread(NULL, 0, \
(LPTHREAD_START_ROUTINE)(func), (arg), 0, NULL)) == NULL) ? -1 : 0)
#define thread_join(thr, statusp) \
((WaitForSingleObject((thr), INFINITE) == WAIT_OBJECT_0) && \
GetExitCodeThread((thr), (LPDWORD)(statusp)) ? 0 : -1)
typedef HANDLE mutex_t;
#define mutex_init(m, attr) \
(((*(m) = CreateMutex(NULL, FALSE, NULL)) != NULL) ? 0 : -1)
#define mutex_lock(m) \
((WaitForSingleObject(*(m), INFINITE) == WAIT_OBJECT_0) ? 0 : -1)
#define mutex_unlock(m) (ReleaseMutex(*(m)) ? 0 : -1)
#else
#include <pthread.h>
#include <unistd.h>
#define PATHD '/'
typedef pthread_t thread_t;
#define thread_create(thrp, attr, func, arg) \
pthread_create((thrp), (attr), (func), (arg))
#define thread_join(thr, statusp) pthread_join((thr), (statusp))
typedef pthread_mutex_t mutex_t;
#define mutex_init(m, attr) pthread_mutex_init((m), (attr))
#define mutex_lock(m) pthread_mutex_lock(m)
#define mutex_unlock(m) pthread_mutex_unlock(m)
#endif
/* Run 5 writers threads at a time. */
#define NUMWRITERS 5
/*
* Printing of a thread_t is implementation-specific, so we
* create our own thread IDs for reporting purposes.
*/
int global_thread_num;
mutex_t thread_num_lock;
/* Forward declarations */
int count_records(DB *, DB_TXN *);
int open_db(DB **, const char *, const char *, DB_ENV *, u_int32_t);
void *writer_thread(void *);
int
main(void)
{
/* Initialize our handles */
DB *dbp = NULL;
DB_ENV *envp = NULL;
thread_t writer_threads[NUMWRITERS];
int i, ret, ret_t;
u_int32_t env_flags;
/* Application name */
const char *prog_name = "txn_guide_inmemory";
/* Create the environment */
ret = db_env_create(&envp, 0);
if (ret != 0) {
fprintf(stderr, "Error creating environment handle: %s\n",
db_strerror(ret));
goto err;
}
env_flags =
DB_CREATE | /* Create the environment if it does not exist */
DB_INIT_LOCK | /* Initialize the locking subsystem */
DB_INIT_LOG | /* Initialize the logging subsystem */
DB_INIT_TXN | /* Initialize the transactional subsystem. This
* also turns on logging. */
DB_INIT_MPOOL | /* Initialize the memory pool (in-memory cache) */
DB_PRIVATE | /* Region files are backed by heap memory. */
DB_THREAD; /* Cause the environment to be free-threaded */
/* Specify in-memory logging */
ret = envp->log_set_config(envp, DB_LOG_IN_MEMORY, 1);
if (ret != 0) {
fprintf(stderr, "Error setting log subsystem to in-memory: %s\n",
db_strerror(ret));
goto err;
}
/*
* Specify the size of the in-memory log buffer.
*/
ret = envp->set_lg_bsize(envp, 10 * 1024 * 1024);
if (ret != 0) {
fprintf(stderr, "Error increasing the log buffer size: %s\n",
db_strerror(ret));
goto err;
}
/*
* Specify the size of the in-memory cache.
*/
ret = envp->set_cachesize(envp, 0, 10 * 1024 * 1024, 1);
if (ret != 0) {
fprintf(stderr, "Error increasing the cache size: %s\n",
db_strerror(ret));
goto err;
}
/*
* Indicate that we want db to perform lock detection internally.
* Also indicate that the transaction with the fewest number of
* write locks will receive the deadlock notification in
* the event of a deadlock.
*/
ret = envp->set_lk_detect(envp, DB_LOCK_MINWRITE);
if (ret != 0) {
fprintf(stderr, "Error setting lock detect: %s\n",
db_strerror(ret));
goto err;
}
/* Now actually open the environment */
ret = envp->open(envp, NULL, env_flags, 0);
if (ret != 0) {
fprintf(stderr, "Error opening environment: %s\n",
db_strerror(ret));
goto err;
}
/*
* If we had utility threads (for running checkpoints or
* deadlock detection, for example) we would spawn those
* here. However, for a simple example such as this,
* that is not required.
*/
/* Open the database */
ret = open_db(&dbp, prog_name, NULL,
envp, DB_DUPSORT);
if (ret != 0)
goto err;
/* Initialize a mutex. Used to help provide thread ids. */
(void)mutex_init(&thread_num_lock, NULL);
/* Start the writer threads. */
for (i = 0; i < NUMWRITERS; i++)
(void)thread_create(
&writer_threads[i], NULL, writer_thread, (void *)dbp);
/* Join the writers */
for (i = 0; i < NUMWRITERS; i++)
(void)thread_join(writer_threads[i], NULL);
err:
/* Close our database handle, if it was opened. */
if (dbp != NULL) {
ret_t = dbp->close(dbp, 0);
if (ret_t != 0) {
fprintf(stderr, "%s database close failed.\n",
db_strerror(ret_t));
ret = ret_t;
}
}
/* Close our environment, if it was opened. */
if (envp != NULL) {
ret_t = envp->close(envp, 0);
if (ret_t != 0) {
fprintf(stderr, "environment close failed: %s\n",
db_strerror(ret_t));
ret = ret_t;
}
}
/* Final status message and return. */
printf("I'm all done.\n");
return (ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
}
/*
* A function that performs a series of writes to a
* Berkeley DB database. The information written
* to the database is largely nonsensical, but the
* mechanism of transactional commit/abort and
* deadlock detection is illustrated here.
*/
void *
writer_thread(void *args)
{
static char *key_strings[] = {
"key 1", "key 2", "key 3", "key 4", "key 5",
"key 6", "key 7", "key 8", "key 9", "key 10"
};
DB *dbp;
DB_ENV *envp;
DBT key, value;
DB_TXN *txn;
int i, j, payload, ret, thread_num;
int retry_count, max_retries = 20; /* Max retry on a deadlock */
dbp = (DB *)args;
envp = dbp->get_env(dbp);
/* Get the thread number */
(void)mutex_lock(&thread_num_lock);
global_thread_num++;
thread_num = global_thread_num;
(void)mutex_unlock(&thread_num_lock);
/* Initialize the random number generator */
srand(thread_num);
/* Write 50 times and then quit */
for (i = 0; i < 50; i++) {
retry_count = 0; /* Used for deadlock retries */
/*
* Some think it is bad form to loop with a goto statement, but
* we do it anyway because it is the simplest and clearest way
* to achieve our abort/retry operation.
*/
retry:
/* Begin our transaction. We group multiple writes in
* this thread under a single transaction so as to
* (1) show that you can atomically perform multiple writes
* at a time, and (2) to increase the chances of a
* deadlock occurring so that we can observe our
* deadlock detection at work.
*
* Normally we would want to avoid the potential for deadlocks,
* so for this workload the correct thing would be to perform our
* puts with autocommit. But that would excessively simplify our
* example, so we do the "wrong" thing here instead.
*/
ret = envp->txn_begin(envp, NULL, &txn, 0);
if (ret != 0) {
envp->err(envp, ret, "txn_begin failed");
return ((void *)EXIT_FAILURE);
}
for (j = 0; j < 10; j++) {
/* Set up our key and values DBTs */
memset(&key, 0, sizeof(DBT));
key.data = key_strings[j];
key.size = (u_int32_t)strlen(key_strings[j]) + 1;
memset(&value, 0, sizeof(DBT));
payload = rand() + i;
value.data = &payload;
value.size = sizeof(int);
/* Perform the database put. */
switch (ret = dbp->put(dbp, txn, &key, &value, 0)) {
case 0:
break;
/*
* Here's where we perform deadlock detection. If
* DB_LOCK_DEADLOCK is returned by the put operation,
* then this thread has been chosen to break a deadlock.
* It must abort its operation, and optionally retry the
* put.
*/
case DB_LOCK_DEADLOCK:
/*
* First thing that we MUST do is abort the
* transaction.
*/
(void)txn->abort(txn);
/*
* Now we decide if we want to retry the operation.
* If we have retried less than max_retries,
* increment the retry count and goto retry.
*/
if (retry_count < max_retries) {
printf("Writer %i: Got DB_LOCK_DEADLOCK.\n",
thread_num);
printf("Writer %i: Retrying write operation.\n",
thread_num);
retry_count++;
goto retry;
}
/*
* Otherwise, just give up.
*/
printf("Writer %i: ", thread_num);
printf("Got DB_LOCK_DEADLOCK and out of retries.\n");
printf("Writer %i: Giving up.\n", thread_num);
return ((void *)EXIT_FAILURE);
/*
* If a generic error occurs, we simply abort the
* transaction and exit the thread completely.
*/
default:
envp->err(envp, ret, "db put failed");
ret = txn->abort(txn);
if (ret != 0)
envp->err(envp, ret, "txn abort failed");
return ((void *)EXIT_FAILURE);
} /** End case statement **/
} /** End for loop **/
/*
* print the number of records found in the database.
* See count_records() for usage information.
*/
printf("Thread %i. Record count: %i\n", thread_num,
count_records(dbp, txn));
/*
* If all goes well, we can commit the transaction and
* exit the thread.
*/
ret = txn->commit(txn, 0);
if (ret != 0) {
envp->err(envp, ret, "txn commit failed");
return ((void *)EXIT_FAILURE);
}
}
return ((void *)EXIT_SUCCESS);
}
/*
* This simply counts the number of records contained in the
* database and returns the result. You can use this function
* in three ways:
*
* First call it with an active txn handle (this is what the
* example currently does).
*
* Secondly, configure the cursor for uncommitted reads.
*
* Third, call count_records AFTER the writer has committed
* its transaction.
*
* If you do none of these things, the writer thread will
* self-deadlock.
*
* Note that this function exists only for illustrative purposes.
* A more straight-forward way to count the number of records in
* a database is to use DB->stat() or DB->stat_print().
*/
int
count_records(DB *dbp, DB_TXN *txn)
{
DBT key, value;
DBC *cursorp;
int count, ret;
cursorp = NULL;
count = 0;
/* Get the cursor */
ret = dbp->cursor(dbp, txn, &cursorp, 0);
if (ret != 0) {
dbp->err(dbp, ret,
"count_records: cursor open failed.");
goto cursor_err;
}
/* Get the key DBT used for the database read */
memset(&key, 0, sizeof(DBT));
memset(&value, 0, sizeof(DBT));
do {
ret = cursorp->get(cursorp, &key, &value, DB_NEXT);
switch (ret) {
case 0:
count++;
break;
case DB_NOTFOUND:
break;
default:
dbp->err(dbp, ret,
"Count records unspecified error");
goto cursor_err;
}
} while (ret == 0);
cursor_err:
if (cursorp != NULL) {
ret = cursorp->close(cursorp);
if (ret != 0) {
dbp->err(dbp, ret,
"count_records: cursor close failed.");
}
}
return (count);
}
/* Open a Berkeley DB database */
int
open_db(DB **dbpp, const char *progname, const char *file_name,
DB_ENV *envp, u_int32_t extra_flags)
{
int ret;
u_int32_t open_flags;
DB *dbp;
/* Initialize the DB handle */
ret = db_create(&dbp, envp, 0);
if (ret != 0) {
fprintf(stderr, "%s: %s\n", progname,
db_strerror(ret));
return (EXIT_FAILURE);
}
/* Point to the memory malloc'd by db_create() */
*dbpp = dbp;
if (extra_flags != 0) {
ret = dbp->set_flags(dbp, extra_flags);
if (ret != 0) {
dbp->err(dbp, ret,
"open_db: Attempt to set extra flags failed.");
return (EXIT_FAILURE);
}
}
/* Now open the database */
open_flags = DB_CREATE | /* Allow database creation */
DB_THREAD |
DB_AUTO_COMMIT; /* Allow autocommit */
ret = dbp->open(dbp, /* Pointer to the database */
NULL, /* Txn pointer */
file_name, /* File name */
NULL, /* Logical db name */
DB_BTREE, /* Database type (using btree) */
open_flags, /* Open flags */
0); /* File mode. Using defaults */
if (ret != 0) {
dbp->err(dbp, ret, "Database open failed");
return (EXIT_FAILURE);
}
return (EXIT_SUCCESS);
}