summaryrefslogtreecommitdiff
path: root/db-4.8.30/examples_c
diff options
context:
space:
mode:
authorJesse Morgan <jesse@jesterpm.net>2016-12-17 21:28:53 -0800
committerJesse Morgan <jesse@jesterpm.net>2016-12-17 21:28:53 -0800
commit54df2afaa61c6a03cbb4a33c9b90fa572b6d07b8 (patch)
tree18147b92b969d25ffbe61935fb63035cac820dd0 /db-4.8.30/examples_c
Berkeley DB 4.8 with rust build script for linux.
Diffstat (limited to 'db-4.8.30/examples_c')
-rw-r--r--db-4.8.30/examples_c/README40
-rw-r--r--db-4.8.30/examples_c/bench_001.c431
-rw-r--r--db-4.8.30/examples_c/csv/DbRecord.c470
-rw-r--r--db-4.8.30/examples_c/csv/Makefile75
-rw-r--r--db-4.8.30/examples_c/csv/README408
-rw-r--r--db-4.8.30/examples_c/csv/code.c405
-rw-r--r--db-4.8.30/examples_c/csv/csv.h101
-rw-r--r--db-4.8.30/examples_c/csv/csv_extern.h37
-rw-r--r--db-4.8.30/examples_c/csv/db.c244
-rw-r--r--db-4.8.30/examples_c/csv/load.c346
-rw-r--r--db-4.8.30/examples_c/csv/load_main.c117
-rw-r--r--db-4.8.30/examples_c/csv/query.c241
-rw-r--r--db-4.8.30/examples_c/csv/query_main.c99
-rw-r--r--db-4.8.30/examples_c/csv/sample.csv8
-rw-r--r--db-4.8.30/examples_c/csv/sample.desc10
-rw-r--r--db-4.8.30/examples_c/csv/util.c309
-rw-r--r--db-4.8.30/examples_c/ex_access.c161
-rw-r--r--db-4.8.30/examples_c/ex_apprec/auto_rebuild10
-rw-r--r--db-4.8.30/examples_c/ex_apprec/ex_apprec.c278
-rw-r--r--db-4.8.30/examples_c/ex_apprec/ex_apprec.h24
-rw-r--r--db-4.8.30/examples_c/ex_apprec/ex_apprec.src33
-rw-r--r--db-4.8.30/examples_c/ex_apprec/ex_apprec_auto.c140
-rw-r--r--db-4.8.30/examples_c/ex_apprec/ex_apprec_auto.h13
-rw-r--r--db-4.8.30/examples_c/ex_apprec/ex_apprec_autop.c65
-rw-r--r--db-4.8.30/examples_c/ex_apprec/ex_apprec_rec.c108
-rw-r--r--db-4.8.30/examples_c/ex_apprec/ex_apprec_template70
-rw-r--r--db-4.8.30/examples_c/ex_btrec.c202
-rw-r--r--db-4.8.30/examples_c/ex_dbclient.c226
-rw-r--r--db-4.8.30/examples_c/ex_env.c183
-rw-r--r--db-4.8.30/examples_c/ex_lock.c239
-rw-r--r--db-4.8.30/examples_c/ex_mpool.c258
-rw-r--r--db-4.8.30/examples_c/ex_rep/README19
-rw-r--r--db-4.8.30/examples_c/ex_rep/base/rep_base.c247
-rw-r--r--db-4.8.30/examples_c/ex_rep/base/rep_base.h117
-rw-r--r--db-4.8.30/examples_c/ex_rep/base/rep_msg.c467
-rw-r--r--db-4.8.30/examples_c/ex_rep/base/rep_net.c749
-rw-r--r--db-4.8.30/examples_c/ex_rep/common/rep_common.c672
-rw-r--r--db-4.8.30/examples_c/ex_rep/common/rep_common.h81
-rw-r--r--db-4.8.30/examples_c/ex_rep/mgr/rep_mgr.c204
-rw-r--r--db-4.8.30/examples_c/ex_rep_gsg/rep_mgr_gsg.c403
-rw-r--r--db-4.8.30/examples_c/ex_rep_gsg/simple_txn.c255
-rw-r--r--db-4.8.30/examples_c/ex_sequence.c133
-rw-r--r--db-4.8.30/examples_c/ex_stream.c222
-rw-r--r--db-4.8.30/examples_c/ex_thread.c626
-rw-r--r--db-4.8.30/examples_c/ex_tpcb.c718
-rw-r--r--db-4.8.30/examples_c/getting_started/example_database_load.c274
-rw-r--r--db-4.8.30/examples_c/getting_started/example_database_read.c275
-rw-r--r--db-4.8.30/examples_c/getting_started/gettingstarted_common.c239
-rw-r--r--db-4.8.30/examples_c/getting_started/gettingstarted_common.h59
-rw-r--r--db-4.8.30/examples_c/getting_started/inventory.txt800
-rw-r--r--db-4.8.30/examples_c/getting_started/vendors.txt6
-rw-r--r--db-4.8.30/examples_c/txn_guide/txn_guide.c471
-rw-r--r--db-4.8.30/examples_c/txn_guide/txn_guide_inmemory.c461
53 files changed, 12849 insertions, 0 deletions
diff --git a/db-4.8.30/examples_c/README b/db-4.8.30/examples_c/README
new file mode 100644
index 0000000..34e92e0
--- /dev/null
+++ b/db-4.8.30/examples_c/README
@@ -0,0 +1,40 @@
+# $Id$
+
+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.
diff --git a/db-4.8.30/examples_c/bench_001.c b/db-4.8.30/examples_c/bench_001.c
new file mode 100644
index 0000000..e181173
--- /dev/null
+++ b/db-4.8.30/examples_c/bench_001.c
@@ -0,0 +1,431 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+/*
+ * 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);
+}
diff --git a/db-4.8.30/examples_c/csv/DbRecord.c b/db-4.8.30/examples_c/csv/DbRecord.c
new file mode 100644
index 0000000..56af9d4
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/DbRecord.c
@@ -0,0 +1,470 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/csv/Makefile b/db-4.8.30/examples_c/csv/Makefile
new file mode 100644
index 0000000..9d46cc7
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/Makefile
@@ -0,0 +1,75 @@
+# $Id$
+
+# 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
diff --git a/db-4.8.30/examples_c/csv/README b/db-4.8.30/examples_c/csv/README
new file mode 100644
index 0000000..6a5fd13
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/README
@@ -0,0 +1,408 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+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.
diff --git a/db-4.8.30/examples_c/csv/code.c b/db-4.8.30/examples_c/csv/code.c
new file mode 100644
index 0000000..a6a3878
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/code.c
@@ -0,0 +1,405 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/csv/csv.h b/db-4.8.30/examples_c/csv/csv.h
new file mode 100644
index 0000000..fb0a66a
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/csv.h
@@ -0,0 +1,101 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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
diff --git a/db-4.8.30/examples_c/csv/csv_extern.h b/db-4.8.30/examples_c/csv/csv_extern.h
new file mode 100644
index 0000000..8bd0653
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/csv_extern.h
@@ -0,0 +1,37 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+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 *);
diff --git a/db-4.8.30/examples_c/csv/db.c b/db-4.8.30/examples_c/csv/db.c
new file mode 100644
index 0000000..efa9208
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/db.c
@@ -0,0 +1,244 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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));
+}
diff --git a/db-4.8.30/examples_c/csv/load.c b/db-4.8.30/examples_c/csv/load.c
new file mode 100644
index 0000000..34d8cc1
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/load.c
@@ -0,0 +1,346 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/csv/load_main.c b/db-4.8.30/examples_c/csv/load_main.c
new file mode 100644
index 0000000..4ff4dd4
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/load_main.c
@@ -0,0 +1,117 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/csv/query.c b/db-4.8.30/examples_c/csv/query.c
new file mode 100644
index 0000000..c6eee8c
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/query.c
@@ -0,0 +1,241 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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));
+}
diff --git a/db-4.8.30/examples_c/csv/query_main.c b/db-4.8.30/examples_c/csv/query_main.c
new file mode 100644
index 0000000..30376cd
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/query_main.c
@@ -0,0 +1,99 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/csv/sample.csv b/db-4.8.30/examples_c/csv/sample.csv
new file mode 100644
index 0000000..b3f0970
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/sample.csv
@@ -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
diff --git a/db-4.8.30/examples_c/csv/sample.desc b/db-4.8.30/examples_c/csv/sample.desc
new file mode 100644
index 0000000..65aa939
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/sample.desc
@@ -0,0 +1,10 @@
+# $Id$
+
+version 1 {
+ LastName string
+ FirstName string
+ BirthDate
+ Color string index
+ Fruit string index
+ Age unsigned_long index
+}
diff --git a/db-4.8.30/examples_c/csv/util.c b/db-4.8.30/examples_c/csv/util.c
new file mode 100644
index 0000000..eed03f0
--- /dev/null
+++ b/db-4.8.30/examples_c/csv/util.c
@@ -0,0 +1,309 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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 */
+}
diff --git a/db-4.8.30/examples_c/ex_access.c b/db-4.8.30/examples_c/ex_access.c
new file mode 100644
index 0000000..fa12a9e
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_access.c
@@ -0,0 +1,161 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/ex_apprec/auto_rebuild b/db-4.8.30/examples_c/ex_apprec/auto_rebuild
new file mode 100644
index 0000000..aadcf64
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_apprec/auto_rebuild
@@ -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
diff --git a/db-4.8.30/examples_c/ex_apprec/ex_apprec.c b/db-4.8.30/examples_c/ex_apprec/ex_apprec.c
new file mode 100644
index 0000000..c1440c8
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_apprec/ex_apprec.c
@@ -0,0 +1,278 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1996-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#include "db_config.h"
+
+#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.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);
+}
diff --git a/db-4.8.30/examples_c/ex_apprec/ex_apprec.h b/db-4.8.30/examples_c/ex_apprec/ex_apprec.h
new file mode 100644
index 0000000..39bdb67
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_apprec/ex_apprec.h
@@ -0,0 +1,24 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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_ */
diff --git a/db-4.8.30/examples_c/ex_apprec/ex_apprec.src b/db-4.8.30/examples_c/ex_apprec/ex_apprec.src
new file mode 100644
index 0000000..8bdba5d
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_apprec/ex_apprec.src
@@ -0,0 +1,33 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+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
diff --git a/db-4.8.30/examples_c/ex_apprec/ex_apprec_auto.c b/db-4.8.30/examples_c/ex_apprec/ex_apprec_auto.c
new file mode 100644
index 0000000..ae5114d
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_apprec/ex_apprec_auto.c
@@ -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);
+}
+
diff --git a/db-4.8.30/examples_c/ex_apprec/ex_apprec_auto.h b/db-4.8.30/examples_c/ex_apprec/ex_apprec_auto.h
new file mode 100644
index 0000000..a4b4c63
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_apprec/ex_apprec_auto.h
@@ -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
diff --git a/db-4.8.30/examples_c/ex_apprec/ex_apprec_autop.c b/db-4.8.30/examples_c/ex_apprec/ex_apprec_autop.c
new file mode 100644
index 0000000..dcd9ce8
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_apprec/ex_apprec_autop.c
@@ -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);
+}
diff --git a/db-4.8.30/examples_c/ex_apprec/ex_apprec_rec.c b/db-4.8.30/examples_c/ex_apprec/ex_apprec_rec.c
new file mode 100644
index 0000000..338b454
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_apprec/ex_apprec_rec.c
@@ -0,0 +1,108 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1996-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+/*
+ * 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);
+}
diff --git a/db-4.8.30/examples_c/ex_apprec/ex_apprec_template b/db-4.8.30/examples_c/ex_apprec/ex_apprec_template
new file mode 100644
index 0000000..55182f6
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_apprec/ex_apprec_template
@@ -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);
+}
+
diff --git a/db-4.8.30/examples_c/ex_btrec.c b/db-4.8.30/examples_c/ex_btrec.c
new file mode 100644
index 0000000..8ea3189
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_btrec.c
@@ -0,0 +1,202 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/ex_dbclient.c b/db-4.8.30/examples_c/ex_dbclient.c
new file mode 100644
index 0000000..caffce0
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_dbclient.c
@@ -0,0 +1,226 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1996-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/ex_env.c b/db-4.8.30/examples_c/ex_env.c
new file mode 100644
index 0000000..997ff7b
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_env.c
@@ -0,0 +1,183 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1996-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#include <sys/types.h>
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+extern int getopt(int, char * const *, const char *);
+#else
+#include <unistd.h>
+#endif
+
+#include <db.h>
+
+int db_setup __P((const char *, const char *, FILE *, const char *));
+int db_teardown __P((const char *, const char *, FILE *, const char *));
+static int usage __P((void));
+
+const char *progname = "ex_env"; /* Program name. */
+
+/*
+ * An example of a program creating/configuring a Berkeley DB environment.
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ const char *data_dir, *home;
+
+ int ch;
+ /*
+ * All of the shared database files live in home, but
+ * data files will live in data_dir.
+ */
+ home = "TESTDIR";
+ data_dir = "data";
+ while ((ch = getopt(argc, argv, "h:d:")) != EOF)
+ switch (ch) {
+ case 'h':
+ home = optarg;
+ break;
+ case 'd':
+ data_dir = optarg;
+ break;
+ case '?':
+ default:
+ return (usage());
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ return (usage());
+
+ 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;
+ DB *dbp;
+ 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, 0644)) != 0) {
+ dbenv->err(dbenv, ret, "environment open: %s", home);
+ dbenv->close(dbenv, 0);
+ return (1);
+ }
+
+ /*
+ * Open a database in the environment to verify the data_dir
+ * has been set correctly.
+ * Create a database object and initialize it for error
+ * reporting.
+ */
+ if ((ret = db_create(&dbp, dbenv, 0)) != 0){
+ fprintf(errfp, "%s: %s\n", progname, db_strerror(ret));
+ return (1);
+ }
+
+ /* Open a database with DB_BTREE access method. */
+ if ((ret = dbp->open(dbp, NULL, "exenv_db1.db", NULL,
+ DB_BTREE, DB_CREATE,0644)) != 0){
+ fprintf(stderr, "database open: %s\n", db_strerror(ret));
+ return (1);
+ }
+
+ /* Close the database handle. */
+ if ((ret = dbp->close(dbp, 0)) != 0) {
+ fprintf(stderr, "database close: %s\n", db_strerror(ret));
+ return (1);
+ }
+
+ /* Close the environment 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);
+
+ /* Remove the environment. */
+ if ((ret = dbenv->remove(dbenv, home, 0)) != 0) {
+ fprintf(stderr, "DB_ENV->remove: %s\n", db_strerror(ret));
+ return (1);
+ }
+ return (0);
+}
+
+static int
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: %s [-h home] [-d data_dir]\n", progname);
+ return (EXIT_FAILURE);
+}
diff --git a/db-4.8.30/examples_c/ex_lock.c b/db-4.8.30/examples_c/ex_lock.c
new file mode 100644
index 0000000..40f169f
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_lock.c
@@ -0,0 +1,239 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/ex_mpool.c b/db-4.8.30/examples_c/ex_mpool.c
new file mode 100644
index 0000000..3ec41d5
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_mpool.c
@@ -0,0 +1,258 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/ex_rep/README b/db-4.8.30/examples_c/ex_rep/README
new file mode 100644
index 0000000..7f20e13
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_rep/README
@@ -0,0 +1,19 @@
+# $Id$
+
+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.
diff --git a/db-4.8.30/examples_c/ex_rep/base/rep_base.c b/db-4.8.30/examples_c/ex_rep/base/rep_base.c
new file mode 100644
index 0000000..87273dc
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_rep/base/rep_base.c
@@ -0,0 +1,247 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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;
+
+const char *progname = "ex_rep_base";
+
+static void event_callback __P((DB_ENV *, u_int32_t, void *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ DB_ENV *dbenv;
+ SETUP_DATA setup_info;
+ DBT local;
+ all_args aa;
+ connect_args ca;
+ supthr_args supa;
+ machtab_t *machtab;
+ thread_t all_thr, ckp_thr, conn_thr, lga_thr;
+ void *astatus, *cstatus;
+#ifdef _WIN32
+ WSADATA wsaData;
+#else
+ struct sigaction sigact;
+#endif
+ APP_DATA my_app_data;
+ int ret;
+
+ memset(&setup_info, 0, sizeof(SETUP_DATA));
+ setup_info.progname = progname;
+ master_eid = DB_EID_INVALID;
+ memset(&my_app_data, 0, sizeof(APP_DATA));
+ dbenv = NULL;
+ machtab = NULL;
+ ret = 0;
+
+ if ((ret = create_env(progname, &dbenv)) != 0)
+ goto err;
+ dbenv->app_private = &my_app_data;
+ (void)dbenv->set_event_notify(dbenv, event_callback);
+
+ /* Parse command line and perform common replication setup. */
+ if ((ret = common_rep_setup(dbenv, argc, argv, &setup_info)) != 0)
+ goto err;
+
+ if (setup_info.role == MASTER)
+ master_eid = SELF_EID;
+
+ myaddr = strdup(setup_info.self.host);
+ myport = setup_info.self.port;
+
+#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, setup_info.nsites)) != 0)
+ goto err;
+ my_app_data.comm_infrastructure = machtab;
+
+ if ((ret = env_init(dbenv, setup_info.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 = setup_info.home;
+ ca.progname = progname;
+ ca.machtab = machtab;
+ ca.port = setup_info.self.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 = setup_info.home;
+ aa.machtab = machtab;
+ aa.sites = setup_info.site_list;
+ aa.nsites = setup_info.remotesites;
+ if ((ret = thread_create(&all_thr, NULL, connect_all, &aa)) != 0) {
+ dbenv->errx(dbenv, "can't create connect-all thread");
+ goto err;
+ }
+
+ /* Start checkpoint and log archive threads. */
+ supa.dbenv = dbenv;
+ supa.shared = &my_app_data.shared_data;
+ if ((ret = start_support_threads(dbenv, &supa, &ckp_thr, &lga_thr))
+ != 0)
+ 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 (setup_info.role == 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;
+ }
+
+ /* Finish checkpoint and log archive threads. */
+ if ((ret = finish_support_threads(&ckp_thr, &lga_thr)) != 0)
+ 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;
+ shared->in_client_sync = 1;
+ break;
+
+ case DB_EVENT_REP_ELECTED:
+ app->elected = 1;
+ master_eid = SELF_EID;
+ break;
+
+ case DB_EVENT_REP_MASTER:
+ shared->is_master = 1;
+ shared->in_client_sync = 0;
+ break;
+
+ case DB_EVENT_REP_NEWMASTER:
+ master_eid = *(int*)info;
+ shared->in_client_sync = 1;
+ break;
+
+ case DB_EVENT_REP_STARTUPDONE:
+ shared->in_client_sync = 0;
+ break;
+
+ default:
+ dbenv->errx(dbenv, "ignoring event %d", which);
+ }
+}
diff --git a/db-4.8.30/examples_c/ex_rep/base/rep_base.h b/db-4.8.30/examples_c/ex_rep/base/rep_base.h
new file mode 100644
index 0000000..4bfeb29
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_rep/base/rep_base.h
@@ -0,0 +1,117 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#ifndef _EX_REPQUOTE_H_
+#define _EX_REPQUOTE_H_
+
+#include "../common/rep_common.h"
+
+#define SELF_EID 1
+
+/* 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
+
+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)
+
+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_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 *));
+void *connect_thread __P((void *));
+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_ */
diff --git a/db-4.8.30/examples_c/ex_rep/base/rep_msg.c b/db-4.8.30/examples_c/ex_rep/base/rep_msg.c
new file mode 100644
index 0000000..445fc30
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_rep/base/rep_msg.c
@@ -0,0 +1,467 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/ex_rep/base/rep_net.c b/db-4.8.30/examples_c/ex_rep/base/rep_net.c
new file mode 100644
index 0000000..350300d
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_rep/base/rep_net.c
@@ -0,0 +1,749 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+ if ((recbuf = rec->data) == NULL)
+ return (1);
+ 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);
+ if (controlbuf == NULL)
+ return (1);
+ 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);
+}
diff --git a/db-4.8.30/examples_c/ex_rep/common/rep_common.c b/db-4.8.30/examples_c/ex_rep/common/rep_common.c
new file mode 100644
index 0000000..c98ea98
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_rep/common/rep_common.c
@@ -0,0 +1,672 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2006-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <db.h>
+
+#include "rep_common.h"
+
+#define CACHESIZE (10 * 1024 * 1024)
+#define DATABASE "quote.db"
+#define SLEEPTIME 3
+
+static int print_stocks __P((DB *));
+
+/*
+ * Perform command line parsing and common replication setup for the repmgr
+ * and base replication example programs.
+ */
+int
+common_rep_setup(dbenv, argc, argv, setup_info)
+ DB_ENV *dbenv;
+ int argc;
+ char *argv[];
+ SETUP_DATA *setup_info;
+{
+ repsite_t site;
+ extern char *optarg;
+ char ch, *portstr;
+ int ack_policy, got_self, is_repmgr, maxsites, priority, ret;
+
+ got_self = is_repmgr = maxsites = ret = site.peer = 0;
+
+ priority = 100;
+ ack_policy = DB_REPMGR_ACKS_QUORUM;
+ setup_info->role = UNKNOWN;
+ if (strncmp(setup_info->progname, "ex_rep_mgr", 10) == 0)
+ is_repmgr = 1;
+
+ /*
+ * Replication setup calls that are only needed if a command
+ * line option is specified are made within this while/switch
+ * statement. Replication setup calls that should be made
+ * whether or not a command line option is specified are after
+ * this while/switch statement.
+ */
+ while ((ch = getopt(argc, argv, "a:bCh:l:Mn:p:R:r:v")) != EOF) {
+ switch (ch) {
+ case 'a':
+ if (!is_repmgr)
+ usage(is_repmgr, setup_info->progname);
+ if (strncmp(optarg, "all", 3) == 0)
+ ack_policy = DB_REPMGR_ACKS_ALL;
+ else if (strncmp(optarg, "quorum", 6) != 0)
+ usage(is_repmgr, setup_info->progname);
+ break;
+ case 'b':
+ /*
+ * Configure bulk transfer to send groups of records
+ * to clients in a single network transfer. This is
+ * useful for master sites and clients participating
+ * in client-to-client synchronization.
+ */
+ if ((ret = dbenv->rep_set_config(dbenv,
+ DB_REP_CONF_BULK, 1)) != 0) {
+ dbenv->err(dbenv, ret,
+ "Could not configure bulk transfer.\n");
+ goto err;
+ }
+ break;
+ case 'C':
+ setup_info->role = CLIENT;
+ break;
+ case 'h':
+ setup_info->home = optarg;
+ break;
+ case 'l':
+ setup_info->self.host = strtok(optarg, ":");
+ if ((portstr = strtok(NULL, ":")) == NULL) {
+ fprintf(stderr, "Bad host specification.\n");
+ goto err;
+ }
+ setup_info->self.port = (unsigned short)atoi(portstr);
+ setup_info->self.peer = 0;
+ got_self = 1;
+ break;
+ case 'M':
+ setup_info->role = MASTER;
+ break;
+ case 'n':
+ setup_info->nsites = atoi(optarg);
+ /*
+ * For repmgr, set the total number of sites in the
+ * replication group. This is used by repmgr internal
+ * election processing. For base replication, nsites
+ * is simply passed back to main for use in its
+ * communications and election processing.
+ */
+ if (is_repmgr && setup_info->nsites > 0 &&
+ (ret = dbenv->rep_set_nsites(dbenv,
+ setup_info->nsites)) != 0) {
+ dbenv->err(dbenv, ret,
+ "Could not set nsites.\n");
+ goto err;
+ }
+ break;
+ case 'p':
+ priority = atoi(optarg);
+ break;
+ case 'R':
+ if (!is_repmgr)
+ usage(is_repmgr, setup_info->progname);
+ site.peer = 1; /* FALLTHROUGH */
+ case 'r':
+ site.host = optarg;
+ site.host = strtok(site.host, ":");
+ if ((portstr = strtok(NULL, ":")) == NULL) {
+ fprintf(stderr, "Bad host specification.\n");
+ goto err;
+ }
+ site.port = (unsigned short)atoi(portstr);
+ if (setup_info->site_list == NULL ||
+ setup_info->remotesites >= maxsites) {
+ maxsites = maxsites == 0 ? 10 : 2 * maxsites;
+ if ((setup_info->site_list =
+ realloc(setup_info->site_list,
+ maxsites * sizeof(repsite_t))) == NULL) {
+ fprintf(stderr, "System error %s\n",
+ strerror(errno));
+ goto err;
+ }
+ }
+ (setup_info->site_list)[(setup_info->remotesites)++] =
+ site;
+ site.peer = 0;
+ break;
+ case 'v':
+ if ((ret = dbenv->set_verbose(dbenv,
+ DB_VERB_REPLICATION, 1)) != 0)
+ goto err;
+ break;
+ case '?':
+ default:
+ usage(is_repmgr, setup_info->progname);
+ }
+ }
+
+ /* Error check command line. */
+ if (!got_self || setup_info->home == NULL)
+ usage(is_repmgr, setup_info->progname);
+ if (!is_repmgr && setup_info->role == UNKNOWN) {
+ fprintf(stderr, "Must specify -M or -C.\n");
+ goto err;
+ }
+
+ /*
+ * Set replication group election priority for this environment.
+ * An election first selects the site with the most recent log
+ * records as the new master. If multiple sites have the most
+ * recent log records, the site with the highest priority value
+ * is selected as master.
+ */
+ if ((ret = dbenv->rep_set_priority(dbenv, priority)) != 0) {
+ dbenv->err(dbenv, ret, "Could not set priority.\n");
+ goto err;
+ }
+
+ /*
+ * For repmgr, set the policy that determines how master and client
+ * sites handle acknowledgement of replication messages needed for
+ * permanent records. The default policy of "quorum" requires only
+ * a quorum of electable peers sufficient to ensure a permanent
+ * record remains durable if an election is held. The "all" option
+ * requires all clients to acknowledge a permanent replication
+ * message instead.
+ */
+ if (is_repmgr &&
+ (ret = dbenv->repmgr_set_ack_policy(dbenv, ack_policy)) != 0) {
+ dbenv->err(dbenv, ret, "Could not set ack policy.\n");
+ goto err;
+ }
+
+ /*
+ * Set the threshold for the minimum and maximum time the client
+ * waits before requesting retransmission of a missing message.
+ * Base these values on the performance and load characteristics
+ * of the master and client host platforms as well as the round
+ * trip message time.
+ */
+ if ((ret = dbenv->rep_set_request(dbenv, 20000, 500000)) != 0) {
+ dbenv->err(dbenv, ret,
+ "Could not set client_retransmission defaults.\n");
+ goto err;
+ }
+
+ /*
+ * Configure deadlock detection to ensure that any deadlocks
+ * are broken by having one of the conflicting lock requests
+ * rejected. DB_LOCK_DEFAULT uses the lock policy specified
+ * at environment creation time or DB_LOCK_RANDOM if none was
+ * specified.
+ */
+ if ((ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT)) != 0) {
+ dbenv->err(dbenv, ret,
+ "Could not configure deadlock detection.\n");
+ goto err;
+ }
+
+ /* The following base replication features may also be useful to your
+ * application. See Berkeley DB documentation for more details.
+ * - Master leases: Provide stricter consistency for data reads
+ * on a master site.
+ * - Timeouts: Customize the amount of time Berkeley DB waits
+ * for such things as an election to be concluded or a master
+ * lease to be granted.
+ * - Delayed client synchronization: Manage the master site's
+ * resources by spreading out resource-intensive client
+ * synchronizations.
+ * - Blocked client operations: Return immediately with an error
+ * instead of waiting indefinitely if a client operation is
+ * blocked by an ongoing client synchronization.
+ */
+
+err:
+ return (ret);
+}
+
+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);
+ }
+}
+
+/* Start checkpoint and log archive support threads. */
+int
+start_support_threads(dbenv, sup_args, ckp_thr, lga_thr)
+ DB_ENV *dbenv;
+ supthr_args *sup_args;
+ thread_t *ckp_thr;
+ thread_t *lga_thr;
+{
+ int ret;
+
+ ret = 0;
+ if ((ret = thread_create(ckp_thr, NULL, checkpoint_thread,
+ sup_args)) != 0) {
+ dbenv->errx(dbenv, "can't create checkpoint thread");
+ goto err;
+ }
+ if ((ret = thread_create(lga_thr, NULL, log_archive_thread,
+ sup_args)) != 0)
+ dbenv->errx(dbenv, "can't create log archive thread");
+err:
+ return (ret);
+
+}
+
+/* Wait for checkpoint and log archive support threads to finish. */
+int
+finish_support_threads(ckp_thr, lga_thr)
+ thread_t *ckp_thr;
+ thread_t *lga_thr;
+{
+ void *ctstatus, *ltstatus;
+ int ret;
+
+ ret = 0;
+ if (thread_join(*lga_thr, &ltstatus) ||
+ thread_join(*ckp_thr, &ctstatus)) {
+ ret = -1;
+ goto err;
+ }
+ if ((uintptr_t)ltstatus != EXIT_SUCCESS ||
+ (uintptr_t)ctstatus != EXIT_SUCCESS)
+ ret = -1;
+err:
+ 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) {
+ /*
+ * This makes the checkpoint and log
+ * archive threads stop.
+ */
+ shared_data->app_finished = 1;
+ 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);
+
+ flags = DB_AUTO_COMMIT;
+ /*
+ * Open database with DB_CREATE only if this is
+ * a master database. A client database uses
+ * polling to attempt to open the database without
+ * DB_CREATE until it is successful.
+ *
+ * This DB_CREATE polling logic can be simplified
+ * under some circumstances. For example, if the
+ * application can be sure a database is already
+ * there, it would never need to open it with
+ * DB_CREATE.
+ */
+ 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) {
+ /*
+ * If this is a client in the middle of
+ * synchronizing with the master, the client data
+ * is possibly stale and won't be displayed until
+ * client synchronization is finished. It is also
+ * possible to display the stale data if this is
+ * acceptable to the application.
+ */
+ if (shared_data->in_client_sync)
+ printf(
+"Cannot read data during client synchronization - please try again.\n");
+ else
+ 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.
+ * -a all|quorum (optional; repmgr only, a stands for ack policy)
+ * -b (optional, b stands for bulk)
+ * -C or -M start up as client or master (optional for repmgr, required
+ * for base example)
+ * -h home directory (required)
+ * -l host:port (required; l stands for local)
+ * -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)
+ * -r host:port (optional; r stands for remote; any number of these may be
+ * specified)
+ * -R host:port (optional; repmgr only, remote peer)
+ * -v (optional; v stands for verbose)
+ */
+void
+usage(is_repmgr, progname)
+ const int is_repmgr;
+ const char *progname;
+{
+ fprintf(stderr, "usage: %s ", progname);
+ if (is_repmgr)
+ fprintf(stderr, "[-CM]-h home -l host:port[-r host:port]%s%s",
+ "[-R host:port][-a all|quorum][-b][-n nsites]",
+ "[-p priority][-v]\n");
+ else
+ fprintf(stderr, "-CM -h home -l host:port[-r host:port]%s",
+ "[-b][-n nsites][-p priority][-v]\n");
+ exit(EXIT_FAILURE);
+}
+
+/*
+ * This is a very simple thread that performs checkpoints at a fixed
+ * time interval. For a master site, the time interval is one minute
+ * plus the duration of the checkpoint_delay timeout (30 seconds by
+ * default.) For a client site, the time interval is one minute.
+ */
+void *
+checkpoint_thread(args)
+ void *args;
+{
+ DB_ENV *dbenv;
+ SHARED_DATA *shared;
+ supthr_args *ca;
+ int i, ret;
+
+ ca = (supthr_args *)args;
+ dbenv = ca->dbenv;
+ shared = ca->shared;
+
+ for (;;) {
+ /*
+ * Wait for one minute, polling once per second to see if
+ * application has finished. When application has finished,
+ * terminate this thread.
+ */
+ for (i = 0; i < 60; i++) {
+ sleep(1);
+ if (shared->app_finished == 1)
+ return ((void *)EXIT_SUCCESS);
+ }
+
+ /* Perform a checkpoint. */
+ if ((ret = dbenv->txn_checkpoint(dbenv, 0, 0, 0)) != 0) {
+ dbenv->err(dbenv, ret,
+ "Could not perform checkpoint.\n");
+ return ((void *)EXIT_FAILURE);
+ }
+ }
+}
+
+/*
+ * This is a simple log archive thread. Once per minute, it removes all but
+ * the most recent 3 logs that are safe to remove according to a call to
+ * DB_ENV->log_archive().
+ *
+ * Log cleanup is needed to conserve disk space, but aggressive log cleanup
+ * can cause more frequent client initializations if a client lags too far
+ * behind the current master. This can happen in the event of a slow client,
+ * a network partition, or a new master that has not kept as many logs as the
+ * previous master.
+ *
+ * The approach in this routine balances the need to mitigate against a
+ * lagging client by keeping a few more of the most recent unneeded logs
+ * with the need to conserve disk space by regularly cleaning up log files.
+ * Use of automatic log removal (DB_ENV->log_set_config() DB_LOG_AUTO_REMOVE
+ * flag) is not recommended for replication due to the risk of frequent
+ * client initializations.
+ */
+void *
+log_archive_thread(args)
+ void *args;
+{
+ DB_ENV *dbenv;
+ SHARED_DATA *shared;
+ char **begin, **list;
+ supthr_args *la;
+ int i, listlen, logs_to_keep, minlog, ret;
+
+ la = (supthr_args *)args;
+ dbenv = la->dbenv;
+ shared = la->shared;
+ logs_to_keep = 3;
+
+ for (;;) {
+ /*
+ * Wait for one minute, polling once per second to see if
+ * application has finished. When application has finished,
+ * terminate this thread.
+ */
+ for (i = 0; i < 60; i++) {
+ sleep(1);
+ if (shared->app_finished == 1)
+ return ((void *)EXIT_SUCCESS);
+ }
+
+ /* Get the list of unneeded log files. */
+ if ((ret = dbenv->log_archive(dbenv, &list, DB_ARCH_ABS))
+ != 0) {
+ dbenv->err(dbenv, ret,
+ "Could not get log archive list.");
+ return ((void *)EXIT_FAILURE);
+ }
+ if (list != NULL) {
+ listlen = 0;
+ /* Get the number of logs in the list. */
+ for (begin = list; *begin != NULL; begin++, listlen++);
+ /*
+ * Remove all but the logs_to_keep most recent
+ * unneeded log files.
+ */
+ minlog = listlen - logs_to_keep;
+ for (begin = list, i= 0; i < minlog; list++, i++) {
+ if ((ret = unlink(*list)) != 0) {
+ dbenv->err(dbenv, ret,
+ "logclean: remove %s", *list);
+ dbenv->errx(dbenv,
+ "logclean: Error remove %s", *list);
+ free(begin);
+ return ((void *)EXIT_FAILURE);
+ }
+ }
+ free(begin);
+ }
+ }
+}
diff --git a/db-4.8.30/examples_c/ex_rep/common/rep_common.h b/db-4.8.30/examples_c/ex_rep/common/rep_common.h
new file mode 100644
index 0000000..d81b9d2
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_rep/common/rep_common.h
@@ -0,0 +1,81 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2006-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+/* User-specified role an environment should play in the replication group. */
+typedef enum { MASTER, CLIENT, UNKNOWN } ENV_ROLE;
+
+/* User-specified information about a replication site. */
+typedef struct {
+ char *host; /* Host name. */
+ u_int32_t port; /* Port on which to connect to this site. */
+ int peer; /* Whether remote site is repmgr peer. */
+} repsite_t;
+
+/* Data used for common replication setup. */
+typedef struct {
+ const char *progname;
+ char *home;
+ int nsites;
+ int remotesites;
+ ENV_ROLE role;
+ repsite_t self;
+ repsite_t *site_list;
+} SETUP_DATA;
+
+/* Data shared by both repmgr and base versions of this program. */
+typedef struct {
+ int is_master;
+ int app_finished;
+ int in_client_sync;
+} SHARED_DATA;
+
+/* Arguments for support threads. */
+typedef struct {
+ DB_ENV *dbenv;
+ SHARED_DATA *shared;
+} supthr_args;
+
+/* Portability macros for basic threading & timing */
+#ifdef _WIN32
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+#include <winsock2.h>
+#define snprintf _snprintf
+#define sleep(s) Sleep(1000 * (s))
+
+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)
+
+#else /* !_WIN32 */
+#include <sys/time.h>
+#include <pthread.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))
+
+#endif
+
+void *checkpoint_thread __P((void *));
+int common_rep_setup __P((DB_ENV *, int, char *[], SETUP_DATA *));
+int create_env __P((const char *, DB_ENV **));
+int doloop __P((DB_ENV *, SHARED_DATA *));
+int env_init __P((DB_ENV *, const char *));
+int finish_support_threads __P((thread_t *, thread_t *));
+void *log_archive_thread __P((void *));
+int start_support_threads __P((DB_ENV *, supthr_args *, thread_t *,
+ thread_t *));
+void usage __P((const int, const char *));
diff --git a/db-4.8.30/examples_c/ex_rep/mgr/rep_mgr.c b/db-4.8.30/examples_c/ex_rep/mgr/rep_mgr.c
new file mode 100644
index 0000000..9a726d3
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_rep/mgr/rep_mgr.c
@@ -0,0 +1,204 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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_mgr";
+
+static void event_callback __P((DB_ENV *, u_int32_t, void *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ DB_ENV *dbenv;
+ SETUP_DATA setup_info;
+ repsite_t *site_list;
+ APP_DATA my_app_data;
+ thread_t ckp_thr, lga_thr;
+ supthr_args sup_args;
+ u_int32_t start_policy;
+ int i, ret, t_ret;
+
+ memset(&setup_info, 0, sizeof(SETUP_DATA));
+ setup_info.progname = progname;
+ memset(&my_app_data, 0, sizeof(APP_DATA));
+ dbenv = NULL;
+ ret = 0;
+
+ start_policy = DB_REP_ELECTION;
+
+ if ((ret = create_env(progname, &dbenv)) != 0)
+ goto err;
+ dbenv->app_private = &my_app_data;
+ (void)dbenv->set_event_notify(dbenv, event_callback);
+
+ /* Parse command line and perform common replication setup. */
+ if ((ret = common_rep_setup(dbenv, argc, argv, &setup_info)) != 0)
+ goto err;
+
+ /* Perform repmgr-specific setup based on command line options. */
+ if (setup_info.role == MASTER)
+ start_policy = DB_REP_MASTER;
+ else if (setup_info.role == CLIENT)
+ start_policy = DB_REP_CLIENT;
+ if ((ret = dbenv->repmgr_set_local_site(dbenv, setup_info.self.host,
+ setup_info.self.port, 0)) != 0) {
+ fprintf(stderr, "Could not set listen address (%d).\n", ret);
+ goto err;
+ }
+ site_list = setup_info.site_list;
+ for (i = 0; i < setup_info.remotesites; i++) {
+ if ((ret = dbenv->repmgr_add_remote_site(dbenv,
+ site_list[i].host, site_list[i].port, NULL,
+ site_list[i].peer ? DB_REPMGR_PEER : 0)) != 0) {
+ dbenv->err(dbenv, ret,
+ "Could not add site %s:%d", site_list[i].host,
+ (int)site_list[i].port);
+ goto err;
+ }
+ }
+
+ /*
+ * Configure heartbeat timeouts so that repmgr monitors the
+ * health of the TCP connection. Master sites broadcast a heartbeat
+ * at the frequency specified by the DB_REP_HEARTBEAT_SEND timeout.
+ * Client sites wait for message activity the length of the
+ * DB_REP_HEARTBEAT_MONITOR timeout before concluding that the
+ * connection to the master is lost. The DB_REP_HEARTBEAT_MONITOR
+ * timeout should be longer than the DB_REP_HEARTBEAT_SEND timeout.
+ */
+ if ((ret = dbenv->rep_set_timeout(dbenv, DB_REP_HEARTBEAT_SEND,
+ 5000000)) != 0)
+ dbenv->err(dbenv, ret,
+ "Could not set heartbeat send timeout.\n");
+ if ((ret = dbenv->rep_set_timeout(dbenv, DB_REP_HEARTBEAT_MONITOR,
+ 10000000)) != 0)
+ dbenv->err(dbenv, ret,
+ "Could not set heartbeat monitor timeout.\n");
+
+ /*
+ * The following repmgr features may also be useful to your
+ * application. See Berkeley DB documentation for more details.
+ * - Two-site strict majority rule - In a two-site replication
+ * group, require both sites to be available to elect a new
+ * master.
+ * - Timeouts - Customize the amount of time repmgr waits
+ * for such things as waiting for acknowledgements or attempting
+ * to reconnect to other sites.
+ * - Site list - return a list of sites currently known to repmgr.
+ */
+
+ if ((ret = env_init(dbenv, setup_info.home)) != 0)
+ goto err;
+
+ /* Start checkpoint and log archive threads. */
+ sup_args.dbenv = dbenv;
+ sup_args.shared = &my_app_data.shared_data;
+ if ((ret = start_support_threads(dbenv, &sup_args, &ckp_thr,
+ &lga_thr)) != 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;
+ }
+
+ /* Finish checkpoint and log archive threads. */
+ if ((ret = finish_support_threads(&ckp_thr, &lga_thr)) != 0)
+ 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;
+ shared->in_client_sync = 1;
+ break;
+
+ case DB_EVENT_REP_MASTER:
+ shared->is_master = 1;
+ shared->in_client_sync = 0;
+ break;
+
+ case DB_EVENT_REP_NEWMASTER:
+ shared->in_client_sync = 1;
+ break;
+
+ case DB_EVENT_REP_PERM_FAILED:
+ /*
+ * Did not get enough acks to guarantee transaction
+ * durability based on the configured ack policy. This
+ * transaction will be flushed to the master site's
+ * local disk storage for durability.
+ */
+ printf(
+ "Insufficient acknowledgements to guarantee transaction durability.\n");
+ break;
+
+ case DB_EVENT_REP_STARTUPDONE:
+ shared->in_client_sync = 0;
+ break;
+
+ default:
+ dbenv->errx(dbenv, "ignoring event %d", which);
+ }
+}
diff --git a/db-4.8.30/examples_c/ex_rep_gsg/rep_mgr_gsg.c b/db-4.8.30/examples_c/ex_rep_gsg/rep_mgr_gsg.c
new file mode 100644
index 0000000..f10776a
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_rep_gsg/rep_mgr_gsg.c
@@ -0,0 +1,403 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2006-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+/*
+ * NOTE: This example is a simplified version of the rep_mgr.c
+ * example that can be found in the db/examples_c/ex_rep/mgr directory.
+ *
+ * This example is intended only as an aid in learning Replication Manager
+ * concepts. It is not complete in that many features are not exercised
+ * in it, nor are many error conditions properly handled.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include <db.h>
+
+#ifdef _WIN32
+extern int getopt(int, char * const *, const char *);
+#endif
+
+#define CACHESIZE (10 * 1024 * 1024)
+#define DATABASE "quote.db"
+#define SLEEPTIME 3
+
+typedef struct {
+ int is_master;
+} APP_DATA;
+
+const char *progname = "ex_rep_gsg_repmgr";
+
+int create_env(const char *, DB_ENV **);
+int env_init(DB_ENV *, const char *);
+int doloop (DB_ENV *);
+int print_stocks(DB *);
+static void event_callback(DB_ENV *, u_int32_t, void *);
+
+/* Usage function */
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s ", progname);
+ fprintf(stderr, "-h home -l host:port -n nsites\n");
+ fprintf(stderr, "\t\t[-r host:port][-p priority]\n");
+ fprintf(stderr, "where:\n");
+ fprintf(stderr, "\t-h identifies the environment home directory ");
+ fprintf(stderr, "(required).\n");
+ fprintf(stderr, "\t-l identifies the host and port used by this ");
+ fprintf(stderr, "site (required).\n");
+ fprintf(stderr, "\t-n identifies the number of sites in this ");
+ fprintf(stderr, "replication group (required).\n");
+ fprintf(stderr, "\t-r identifies another site participating in ");
+ fprintf(stderr, "this replication group\n");
+ fprintf(stderr, "\t-p identifies the election priority used by ");
+ fprintf(stderr, "this replica.\n");
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ DB_ENV *dbenv;
+ extern char *optarg;
+ const char *home;
+ char ch, *host, *portstr;
+ int local_is_set, ret, totalsites;
+ u_int16_t port;
+ /* Used to track whether this is a replica or a master. */
+ APP_DATA my_app_data;
+
+ dbenv = NULL;
+ ret = local_is_set = totalsites = 0;
+ home = NULL;
+
+ my_app_data.is_master = 0; /* Assume that we start as a replica */
+
+ if ((ret = create_env(progname, &dbenv)) != 0)
+ goto err;
+
+ /* Make APP_DATA available through the environment handle. */
+ dbenv->app_private = &my_app_data;
+
+ /* Default priority is 100. */
+ dbenv->rep_set_priority(dbenv, 100);
+ /* Permanent messages require at least one ack. */
+ dbenv->repmgr_set_ack_policy(dbenv, DB_REPMGR_ACKS_ONE);
+ /* Give 500 microseconds to receive the ack. */
+ dbenv->rep_set_timeout(dbenv, DB_REP_ACK_TIMEOUT, 500);
+
+ /* Collect the command line options. */
+ while ((ch = getopt(argc, argv, "h:l:n:p:r:")) != EOF)
+ switch (ch) {
+ case 'h':
+ home = optarg;
+ break;
+ /* Set the host and port used by this environment. */
+ case 'l':
+ host = strtok(optarg, ":");
+ if ((portstr = strtok(NULL, ":")) == NULL) {
+ fprintf(stderr, "Bad host specification.\n");
+ goto err;
+ }
+ port = (unsigned short)atoi(portstr);
+ if (dbenv->repmgr_set_local_site(dbenv, host, port, 0) != 0) {
+ fprintf(stderr,
+ "Could not set local address %s.\n", host);
+ goto err;
+ }
+ local_is_set = 1;
+ break;
+ /* Set the number of sites in this replication group. */
+ case 'n':
+ totalsites = atoi(optarg);
+ if ((ret = dbenv->rep_set_nsites(dbenv, totalsites)) != 0)
+ dbenv->err(dbenv, ret, "set_nsites");
+ break;
+ /* Set this replica's election priority. */
+ case 'p':
+ dbenv->rep_set_priority(dbenv, atoi(optarg));
+ break;
+ /* Identify another site in the replication group. */
+ case 'r':
+ host = strtok(optarg, ":");
+ if ((portstr = strtok(NULL, ":")) == NULL) {
+ fprintf(stderr, "Bad host specification.\n");
+ goto err;
+ }
+ port = (unsigned short)atoi(portstr);
+ if (dbenv->repmgr_add_remote_site(dbenv, host, port, 0, 0) != 0) {
+ fprintf(stderr,
+ "Could not add site %s.\n", host);
+ goto err;
+ }
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ /* Error check command line. */
+ if (home == NULL || !local_is_set || !totalsites)
+ usage();
+
+ if ((ret = env_init(dbenv, home)) != 0)
+ goto err;
+
+ if ((ret = dbenv->repmgr_start(dbenv, 3, DB_REP_ELECTION)) != 0)
+ goto err;
+
+ if ((ret = doloop(dbenv)) != 0) {
+ dbenv->err(dbenv, ret, "Application failed");
+ goto err;
+ }
+
+err: if (dbenv != NULL)
+ (void)dbenv->close(dbenv, 0);
+
+ return (ret);
+
+}
+
+/* Create and configure an environment handle. */
+int
+create_env(const char *progname, DB_ENV **dbenvp)
+{
+ DB_ENV *dbenv;
+ int ret;
+
+ if ((ret = db_env_create(&dbenv, 0)) != 0) {
+ fprintf(stderr, "can't create env handle: %s\n",
+ db_strerror(ret));
+ return (ret);
+ }
+
+ dbenv->set_errfile(dbenv, stderr);
+ dbenv->set_errpfx(dbenv, progname);
+ (void)dbenv->set_event_notify(dbenv, event_callback);
+
+ *dbenvp = dbenv;
+ return (0);
+}
+
+/* Open and configure an environment. */
+int
+env_init(DB_ENV *dbenv, const char *home)
+{
+ u_int32_t flags;
+ int ret;
+
+ (void)dbenv->set_cachesize(dbenv, 0, CACHESIZE, 0);
+ (void)dbenv->set_flags(dbenv, DB_TXN_NOSYNC, 1);
+
+ flags = DB_CREATE |
+ DB_INIT_LOCK |
+ DB_INIT_LOG |
+ DB_INIT_MPOOL |
+ DB_INIT_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);
+}
+
+/*
+ * A callback used to determine whether the local environment is a
+ * replica or a master. This is called by the Replication Manager
+ * when the local environment changes state.
+ */
+static void
+event_callback(DB_ENV *dbenv, u_int32_t which, void *info)
+{
+ APP_DATA *app = dbenv->app_private;
+
+ info = NULL; /* Currently unused. */
+
+ switch (which) {
+ case DB_EVENT_REP_MASTER:
+ app->is_master = 1;
+ break;
+
+ case DB_EVENT_REP_CLIENT:
+ app->is_master = 0;
+ break;
+
+ case DB_EVENT_REP_STARTUPDONE: /* FALLTHROUGH */
+ case DB_EVENT_REP_NEWMASTER:
+ /* Ignore. */
+ break;
+
+ default:
+ dbenv->errx(dbenv, "ignoring event %d", which);
+ }
+}
+
+/*
+ * Provides the main data processing function for our application.
+ * This function provides a command line prompt to which the user
+ * can provide a ticker string and a stock price. Once a value is
+ * entered to the application, the application writes the value to
+ * the database and then displays the entire database.
+ */
+#define BUFSIZE 1024
+
+int
+doloop(DB_ENV *dbenv)
+{
+ DB *dbp;
+ APP_DATA *app_data;
+ DBT key, data;
+ char buf[BUFSIZE], *rbuf;
+ int ret;
+ u_int32_t flags;
+
+ dbp = NULL;
+ ret = 0;
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ app_data = dbenv->app_private;
+
+ for (;;) {
+ if (dbp == NULL) {
+ if ((ret = db_create(&dbp, dbenv, 0)) != 0)
+ return (ret);
+
+ flags = DB_AUTO_COMMIT;
+ if (app_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;
+ sleep(SLEEPTIME);
+ continue;
+ }
+ dbenv->err(dbenv, ret, "DB->open");
+ goto err;
+ }
+ }
+
+ printf("QUOTESERVER%s> ",
+ app_data->is_master ? "" : " (read-only)");
+ fflush(stdout);
+
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ break;
+ if (strtok(&buf[0], " \t\n") == NULL) {
+ switch ((ret = print_stocks(dbp))) {
+ case 0:
+ continue;
+ case DB_REP_HANDLE_DEAD:
+ (void)dbp->close(dbp, DB_NOSYNC);
+ dbp = NULL;
+ dbenv->errx(dbenv, "Got a dead replication handle");
+ continue;
+ default:
+ dbp->err(dbp, ret, "Error traversing data");
+ goto err;
+ }
+ }
+ rbuf = strtok(NULL, " \t\n");
+ if (rbuf == NULL || rbuf[0] == '\0') {
+ if (strncmp(buf, "exit", 4) == 0 ||
+ strncmp(buf, "quit", 4) == 0)
+ break;
+ dbenv->errx(dbenv, "Format: TICKER VALUE");
+ continue;
+ }
+
+ if (!app_data->is_master) {
+ dbenv->errx(dbenv, "Can't update at client");
+ continue;
+ }
+
+ key.data = buf;
+ key.size = (u_int32_t)strlen(buf);
+
+ data.data = rbuf;
+ data.size = (u_int32_t)strlen(rbuf);
+
+ if ((ret = dbp->put(dbp,
+ NULL, &key, &data, 0)) != 0) {
+ dbp->err(dbp, ret, "DB->put");
+ goto err;
+ }
+ }
+
+err: if (dbp != NULL)
+ (void)dbp->close(dbp, DB_NOSYNC);
+
+ return (ret);
+}
+
+/* Display all the stock quote information in the database. */
+int
+print_stocks(DB *dbp)
+{
+ DBC *dbc;
+ DBT key, data;
+#define MAXKEYSIZE 10
+#define MAXDATASIZE 20
+ char keybuf[MAXKEYSIZE + 1], databuf[MAXDATASIZE + 1];
+ int ret, t_ret;
+ u_int32_t keysize, datasize;
+
+ if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) {
+ dbp->err(dbp, ret, "can't open cursor");
+ return (ret);
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ printf("\tSymbol\tPrice\n");
+ printf("\t======\t=====\n");
+
+ for (ret = dbc->c_get(dbc, &key, &data, DB_FIRST);
+ ret == 0;
+ ret = dbc->c_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->c_close(dbc)) != 0 && ret == 0)
+ ret = t_ret;
+
+ switch (ret) {
+ case 0:
+ case DB_NOTFOUND:
+ return (0);
+ case DB_LOCK_DEADLOCK:
+ return (0);
+ default:
+ return (ret);
+ }
+}
+
diff --git a/db-4.8.30/examples_c/ex_rep_gsg/simple_txn.c b/db-4.8.30/examples_c/ex_rep_gsg/simple_txn.c
new file mode 100644
index 0000000..cb36532
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_rep_gsg/simple_txn.c
@@ -0,0 +1,255 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2006-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#ifndef _WIN32
+#include <unistd.h>
+#endif
+
+#include <db.h>
+
+#ifdef _WIN32
+extern int getopt(int, char * const *, const char *);
+#endif
+
+#define CACHESIZE (10 * 1024 * 1024)
+#define DATABASE "quote.db"
+
+const char *progname = "ex_rep_gsg_simple";
+
+int create_env(const char *, DB_ENV **);
+int env_init(DB_ENV *, const char *);
+int doloop (DB_ENV *);
+int print_stocks(DB *);
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s ", progname);
+ fprintf(stderr, "-h home\n");
+ exit(EXIT_FAILURE);
+}
+
+int
+main(int argc, char *argv[])
+{
+ extern char *optarg;
+ DB_ENV *dbenv;
+ const char *home;
+ char ch;
+ int ret;
+
+ dbenv = NULL;
+
+ ret = 0;
+ home = NULL;
+
+ /* Create and configure the environment handle. */
+ if ((ret = create_env(progname, &dbenv)) != 0)
+ goto err;
+
+ /* Collect the command line options. */
+ while ((ch = getopt(argc, argv, "h:")) != EOF)
+ switch (ch) {
+ case 'h':
+ home = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ /* Error check command line. */
+ if (home == NULL)
+ usage();
+
+ /* Open the environment. */
+ if ((ret = env_init(dbenv, home)) != 0)
+ goto err;
+
+ if ((ret = doloop(dbenv)) != 0) {
+ dbenv->err(dbenv, ret, "Application failed");
+ goto err;
+ }
+
+err: if (dbenv != NULL)
+ (void)dbenv->close(dbenv, 0);
+
+ return (ret);
+}
+
+/* Create and configure an environment handle. */
+int
+create_env(const char *progname, DB_ENV **dbenvp)
+{
+ DB_ENV *dbenv;
+ int ret;
+
+ if ((ret = db_env_create(&dbenv, 0)) != 0) {
+ fprintf(stderr, "can't create env handle: %s\n",
+ db_strerror(ret));
+ return (ret);
+ }
+
+ dbenv->set_errfile(dbenv, stderr);
+ dbenv->set_errpfx(dbenv, progname);
+
+ *dbenvp = dbenv;
+ return (0);
+}
+
+/* Open and configure an environment. */
+int
+env_init(DB_ENV *dbenv, const char *home)
+{
+ u_int32_t flags;
+ int ret;
+
+ (void)dbenv->set_cachesize(dbenv, 0, CACHESIZE, 0);
+ (void)dbenv->set_flags(dbenv, DB_TXN_NOSYNC, 1);
+
+ flags = DB_CREATE |
+ DB_INIT_LOCK |
+ DB_INIT_LOG |
+ DB_INIT_MPOOL |
+ DB_INIT_TXN |
+ DB_RECOVER;
+ if ((ret = dbenv->open(dbenv, home, flags, 0)) != 0)
+ dbenv->err(dbenv, ret, "can't open environment");
+ return (ret);
+}
+
+/*
+ * Provides the main data processing function for our application.
+ * This function provides a command line prompt to which the user
+ * can provide a ticker string and a stock price. Once a value is
+ * entered to the application, the application writes the value to
+ * the database and then displays the entire database.
+ */
+#define BUFSIZE 1024
+int
+doloop(DB_ENV *dbenv)
+{
+ DB *dbp;
+ DBT key, data;
+ char buf[BUFSIZE], *rbuf;
+ int ret;
+ u_int32_t db_flags;
+
+ dbp = NULL;
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ ret = 0;
+
+ for (;;) {
+ if (dbp == NULL) {
+ if ((ret = db_create(&dbp, dbenv, 0)) != 0)
+ return (ret);
+
+ db_flags = DB_AUTO_COMMIT | DB_CREATE;
+
+ if ((ret = dbp->open(dbp,
+ NULL, DATABASE, NULL, DB_BTREE, db_flags, 0)) != 0) {
+ dbenv->err(dbenv, ret, "DB->open");
+ goto err;
+ }
+ }
+
+ printf("QUOTESERVER> ");
+ fflush(stdout);
+
+ if (fgets(buf, sizeof(buf), stdin) == NULL)
+ break;
+ if (strtok(&buf[0], " \t\n") == NULL) {
+ switch ((ret = print_stocks(dbp))) {
+ case 0:
+ continue;
+ default:
+ dbp->err(dbp, ret, "Error traversing data");
+ goto err;
+ }
+ }
+ rbuf = strtok(NULL, " \t\n");
+ if (rbuf == NULL || rbuf[0] == '\0') {
+ if (strncmp(buf, "exit", 4) == 0 ||
+ strncmp(buf, "quit", 4) == 0)
+ break;
+ dbenv->errx(dbenv, "Format: TICKER VALUE");
+ continue;
+ }
+
+ key.data = buf;
+ key.size = (u_int32_t)strlen(buf);
+
+ data.data = rbuf;
+ data.size = (u_int32_t)strlen(rbuf);
+
+ if ((ret = dbp->put(dbp, NULL, &key, &data, 0)) != 0)
+ {
+ dbp->err(dbp, ret, "DB->put");
+ if (ret != DB_KEYEXIST)
+ goto err;
+ }
+ }
+err: if (dbp != NULL)
+ (void)dbp->close(dbp, DB_NOSYNC);
+
+ return (ret);
+}
+
+/* Display all the stock quote information in the database. */
+int
+print_stocks(DB *dbp)
+{
+ DBC *dbc;
+ DBT key, data;
+#define MAXKEYSIZE 10
+#define MAXDATASIZE 20
+ char keybuf[MAXKEYSIZE + 1], databuf[MAXDATASIZE + 1];
+ int ret, t_ret;
+ u_int32_t keysize, datasize;
+
+ if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) {
+ dbp->err(dbp, ret, "can't open cursor");
+ return (ret);
+ }
+
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+
+ printf("\tSymbol\tPrice\n");
+ printf("\t======\t=====\n");
+
+ for (ret = dbc->c_get(dbc, &key, &data, DB_FIRST);
+ ret == 0;
+ ret = dbc->c_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->c_close(dbc)) != 0 && ret == 0)
+ ret = t_ret;
+
+ switch (ret) {
+ case 0:
+ case DB_NOTFOUND:
+ return (0);
+ default:
+ return (ret);
+ }
+}
diff --git a/db-4.8.30/examples_c/ex_sequence.c b/db-4.8.30/examples_c/ex_sequence.c
new file mode 100644
index 0000000..059d5a9
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_sequence.c
@@ -0,0 +1,133 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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);
+}
diff --git a/db-4.8.30/examples_c/ex_stream.c b/db-4.8.30/examples_c/ex_stream.c
new file mode 100644
index 0000000..ab9814d
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_stream.c
@@ -0,0 +1,222 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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 "stream.db"
+#define CHUNK_SIZE 500
+#define DATA_SIZE CHUNK_SIZE * 100
+
+int main __P((int, char *[]));
+int usage __P((void));
+int invarg __P((const char *, int, const char *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+ DB *dbp;
+ DBC *dbcp;
+ DBT key, data;
+ DBTYPE db_type;
+ int ch, chunk_sz, chunk_off, data_sz, i, ret, rflag;
+ int page_sz;
+ char *database, *buf;
+ const char *progname = "ex_stream"; /* Program name. */
+
+ chunk_sz = CHUNK_SIZE;
+ data_sz = DATA_SIZE;
+ chunk_off = page_sz = rflag = 0;
+ db_type = DB_BTREE;
+ while ((ch = getopt(argc, argv, "c:d:p:t:")) != EOF)
+ switch (ch) {
+ case 'c':
+ if ((chunk_sz = atoi(optarg)) <= 0)
+ return (invarg(progname, ch, optarg));
+ break;
+ case 'd':
+ if ((data_sz = atoi(optarg)) <= 0)
+ return (invarg(progname, ch, optarg));
+ break;
+ case 'p':
+ if ((page_sz = atoi(optarg)) <= 0 ||
+ page_sz % 2 != 0 || page_sz < 512 ||
+ page_sz > 64 * 1024)
+ return (invarg(progname, ch, optarg));
+ break;
+ case 't':
+ switch (optarg[0]) {
+ case 'b':
+ db_type = DB_BTREE;
+ break;
+ case 'h':
+ db_type = DB_HASH;
+ break;
+ case 'r':
+ db_type = DB_RECNO;
+ break;
+ default:
+ return (invarg(progname, ch, optarg));
+ break;
+ }
+ break;
+ case '?':
+ default:
+ return (usage());
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* Accept optional database name. */
+ database = *argv == NULL ? DATABASE : argv[0];
+
+ if (chunk_sz > data_sz) {
+ fprintf(stderr,
+"Chunk size must be less than and a factor of the data size\n");
+
+ return (usage());
+ }
+
+ /* Discard any existing 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 (EXIT_FAILURE);
+ }
+ dbp->set_errfile(dbp, stderr);
+ dbp->set_errpfx(dbp, progname);
+ if (page_sz != 0 && (ret = dbp->set_pagesize(dbp, page_sz)) != 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_type, DB_CREATE, 0664)) != 0) {
+ dbp->err(dbp, ret, "%s: open", database);
+ goto err1;
+ }
+
+ /* Ensure the data size is a multiple of the chunk size. */
+ data_sz = data_sz - (data_sz % chunk_sz);
+
+ /* Initialize the key/data pair for a streaming insert. */
+ memset(&key, 0, sizeof(key));
+ memset(&data, 0, sizeof(data));
+ key.data = &chunk_sz; /* Our key value does not really matter. */
+ key.size = sizeof(int);
+ data.ulen = data_sz;
+ data.size = chunk_sz;
+ data.data = buf = malloc(data_sz);
+ data.flags = DB_DBT_USERMEM | DB_DBT_PARTIAL;
+
+ /* Populate the data with something. */
+ for (i = 0; i < data_sz; ++i)
+ buf[i] = (char)('a' + i % ('z' - 'a'));
+
+ if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) {
+ dbp->err(dbp, ret, "DB->cursor");
+ goto err1;
+ }
+ for (chunk_off = 0; chunk_off < data_sz; chunk_off += chunk_sz) {
+ data.size = chunk_sz;
+ if ((ret = dbcp->put(dbcp, &key, &data,
+ (chunk_off == 0 ? DB_KEYFIRST : DB_CURRENT)) != 0)) {
+ dbp->err(dbp, ret, "DBCursor->put");
+ goto err2;
+ }
+ data.doff += chunk_sz;
+ }
+ if ((ret = dbcp->close(dbcp)) != 0) {
+ dbp->err(dbp, ret, "DBcursor->close");
+ goto err1;
+ }
+
+ memset(data.data, 0, data.ulen);
+ /* Retrieve the data item in chunks. */
+ if ((ret = dbp->cursor(dbp, NULL, &dbcp, 0)) != 0) {
+ dbp->err(dbp, ret, "DB->cursor");
+ goto err1;
+ }
+ data.doff = 0;
+ data.dlen = chunk_sz;
+ memset(data.data, 0, data.ulen);
+
+ /*
+ * Loop over the item, retrieving a chunk at a time.
+ * The requested chunk will be stored at the start of data.data.
+ */
+ for (chunk_off = 0; chunk_off < data_sz; chunk_off += chunk_sz) {
+ if ((ret = dbcp->get(dbcp, &key, &data,
+ (chunk_off == 0 ? DB_SET : DB_CURRENT)) != 0)) {
+ dbp->err(dbp, ret, "DBCursor->get");
+ goto err2;
+ }
+ data.doff += chunk_sz;
+ }
+
+ 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
+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()
+{
+ (void)fprintf(stderr,
+"usage: ex_stream [-c int] [-d int] [-p int] [-t char] [database]\n");
+ (void)fprintf(stderr, "Where options are:\n");
+ (void)fprintf(stderr, "\t-c set the chunk size.\n");
+ (void)fprintf(stderr, "\t-d set the total record size.\n");
+ (void)fprintf(stderr,
+ "\t-t choose a database type btree (b), hash (h) or recno (r)\n");
+
+ return (EXIT_FAILURE);
+}
diff --git a/db-4.8.30/examples_c/ex_thread.c b/db-4.8.30/examples_c/ex_thread.c
new file mode 100644
index 0000000..cc523a2
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_thread.c
@@ -0,0 +1,626 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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;
+}
diff --git a/db-4.8.30/examples_c/ex_tpcb.c b/db-4.8.30/examples_c/ex_tpcb.c
new file mode 100644
index 0000000..565eeb9
--- /dev/null
+++ b/db-4.8.30/examples_c/ex_tpcb.c
@@ -0,0 +1,718 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+#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_lk_detect(dbenv, DB_LOCK_DEFAULT);
+ (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);
+}
diff --git a/db-4.8.30/examples_c/getting_started/example_database_load.c b/db-4.8.30/examples_c/getting_started/example_database_load.c
new file mode 100644
index 0000000..7074396
--- /dev/null
+++ b/db-4.8.30/examples_c/getting_started/example_database_load.c
@@ -0,0 +1,274 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 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);
+}
diff --git a/db-4.8.30/examples_c/getting_started/example_database_read.c b/db-4.8.30/examples_c/getting_started/example_database_read.c
new file mode 100644
index 0000000..d963032
--- /dev/null
+++ b/db-4.8.30/examples_c/getting_started/example_database_read.c
@@ -0,0 +1,275 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 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);
+}
diff --git a/db-4.8.30/examples_c/getting_started/gettingstarted_common.c b/db-4.8.30/examples_c/getting_started/gettingstarted_common.c
new file mode 100644
index 0000000..a095d13
--- /dev/null
+++ b/db-4.8.30/examples_c/getting_started/gettingstarted_common.c
@@ -0,0 +1,239 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 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 (ret);
+
+ /*
+ * 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);
+}
diff --git a/db-4.8.30/examples_c/getting_started/gettingstarted_common.h b/db-4.8.30/examples_c/getting_started/gettingstarted_common.h
new file mode 100644
index 0000000..8213a13
--- /dev/null
+++ b/db-4.8.30/examples_c/getting_started/gettingstarted_common.h
@@ -0,0 +1,59 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 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);
diff --git a/db-4.8.30/examples_c/getting_started/inventory.txt b/db-4.8.30/examples_c/getting_started/inventory.txt
new file mode 100644
index 0000000..d6b6876
--- /dev/null
+++ b/db-4.8.30/examples_c/getting_started/inventory.txt
@@ -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
diff --git a/db-4.8.30/examples_c/getting_started/vendors.txt b/db-4.8.30/examples_c/getting_started/vendors.txt
new file mode 100644
index 0000000..528e1b1
--- /dev/null
+++ b/db-4.8.30/examples_c/getting_started/vendors.txt
@@ -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
diff --git a/db-4.8.30/examples_c/txn_guide/txn_guide.c b/db-4.8.30/examples_c/txn_guide/txn_guide.c
new file mode 100644
index 0000000..1051c6e
--- /dev/null
+++ b/db-4.8.30/examples_c/txn_guide/txn_guide.c
@@ -0,0 +1,471 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+/* 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;
+
+/* Wrap Windows thread API to make it look POSIXey. */
+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) && \
+ ((statusp == NULL) ? 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);
+}
diff --git a/db-4.8.30/examples_c/txn_guide/txn_guide_inmemory.c b/db-4.8.30/examples_c/txn_guide/txn_guide_inmemory.c
new file mode 100644
index 0000000..15b61a0
--- /dev/null
+++ b/db-4.8.30/examples_c/txn_guide/txn_guide_inmemory.c
@@ -0,0 +1,461 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+/* 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;
+
+/* Wrap Windows thread API to make it look POSIXey. */
+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) && \
+ ((statusp == NULL) ? 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);
+}