diff options
author | Jesse Morgan <jesse@jesterpm.net> | 2016-12-17 21:28:53 -0800 |
---|---|---|
committer | Jesse Morgan <jesse@jesterpm.net> | 2016-12-17 21:28:53 -0800 |
commit | 54df2afaa61c6a03cbb4a33c9b90fa572b6d07b8 (patch) | |
tree | 18147b92b969d25ffbe61935fb63035cac820dd0 /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')
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, <status) || + 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); +} |