diff options
author | tim@threads.polyesthetic.msg <> | 2001-03-04 19:42:05 -0500 |
---|---|---|
committer | tim@threads.polyesthetic.msg <> | 2001-03-04 19:42:05 -0500 |
commit | 89dad52004ecba5a380aeebb0e2a9beaae88eb86 (patch) | |
tree | 9dd732e08dba156ee3d7635caedc0dc3107ecac6 /bdb/examples_cxx | |
parent | 639a1069d313843288ba6d9cb54b290073a748a7 (diff) | |
download | mariadb-git-89dad52004ecba5a380aeebb0e2a9beaae88eb86.tar.gz |
Import changeset
Diffstat (limited to 'bdb/examples_cxx')
-rw-r--r-- | bdb/examples_cxx/AccessExample.cpp | 151 | ||||
-rw-r--r-- | bdb/examples_cxx/BtRecExample.cpp | 247 | ||||
-rw-r--r-- | bdb/examples_cxx/EnvExample.cpp | 122 | ||||
-rw-r--r-- | bdb/examples_cxx/LockExample.cpp | 236 | ||||
-rw-r--r-- | bdb/examples_cxx/MpoolExample.cpp | 210 | ||||
-rw-r--r-- | bdb/examples_cxx/TpcbExample.cpp | 666 |
6 files changed, 1632 insertions, 0 deletions
diff --git a/bdb/examples_cxx/AccessExample.cpp b/bdb/examples_cxx/AccessExample.cpp new file mode 100644 index 00000000000..ae885aa8388 --- /dev/null +++ b/bdb/examples_cxx/AccessExample.cpp @@ -0,0 +1,151 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: AccessExample.cpp,v 11.7 2000/12/06 18:58:23 bostic Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <iostream.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#ifndef _MSC_VER +#include <unistd.h> +#endif +#endif + +#include <iomanip.h> +#include <db_cxx.h> + +class AccessExample +{ +public: + AccessExample(); + void run(); + +private: + static const char FileName[]; + + // no need for copy and assignment + AccessExample(const AccessExample &); + void operator = (const AccessExample &); +}; + +static void usage(); // forward + +int main(int argc, char *argv[]) +{ + if (argc > 1) { + usage(); + } + + // Use a try block just to report any errors. + // An alternate approach to using exceptions is to + // use error models (see DbEnv::set_error_model()) so + // that error codes are returned for all Berkeley DB methods. + // + try { + AccessExample app; + app.run(); + return 0; + } + catch (DbException &dbe) { + cerr << "AccessExample: " << dbe.what() << "\n"; + return 1; + } +} + +static void usage() +{ + cerr << "usage: AccessExample\n"; + exit(1); +} + +const char AccessExample::FileName[] = "access.db"; + +AccessExample::AccessExample() +{ +} + +void AccessExample::run() +{ + // Remove the previous database. + (void)unlink(FileName); + + // Create the database object. + // There is no environment for this simple example. + Db db(0, 0); + + db.set_error_stream(&cerr); + db.set_errpfx("AccessExample"); + db.set_pagesize(1024); /* Page size: 1K. */ + db.set_cachesize(0, 32 * 1024, 0); + db.open(FileName, NULL, DB_BTREE, DB_CREATE, 0664); + + // + // Insert records into the database, where the key is the user + // input and the data is the user input in reverse order. + // + char buf[1024]; + char rbuf[1024]; + char *t; + char *p; + int ret; + int len; + + for (;;) { + cout << "input> "; + cout.flush(); + + cin.getline(buf, sizeof(buf)); + if (cin.eof()) + break; + + if ((len = strlen(buf)) <= 0) + continue; + for (t = rbuf, p = buf + (len - 1); p >= buf;) + *t++ = *p--; + *t++ = '\0'; + + Dbt key(buf, len + 1); + Dbt data(rbuf, len + 1); + + ret = db.put(0, &key, &data, DB_NOOVERWRITE); + if (ret == DB_KEYEXIST) { + cout << "Key " << buf << " already exists.\n"; + } + } + cout << "\n"; + + // We put a try block around this section of code + // to ensure that our database is properly closed + // in the event of an error. + // + try { + // Acquire a cursor for the table. + Dbc *dbcp; + db.cursor(NULL, &dbcp, 0); + + // Walk through the table, printing the key/data pairs. + Dbt key; + Dbt data; + while (dbcp->get(&key, &data, DB_NEXT) == 0) { + char *key_string = (char *)key.get_data(); + char *data_string = (char *)data.get_data(); + cout << key_string << " : " << data_string << "\n"; + } + dbcp->close(); + } + catch (DbException &dbe) { + cerr << "AccessExample: " << dbe.what() << "\n"; + } + + db.close(0); +} diff --git a/bdb/examples_cxx/BtRecExample.cpp b/bdb/examples_cxx/BtRecExample.cpp new file mode 100644 index 00000000000..98d9626b969 --- /dev/null +++ b/bdb/examples_cxx/BtRecExample.cpp @@ -0,0 +1,247 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: BtRecExample.cpp,v 11.6 2000/02/19 20:57:59 bostic Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> +#include <errno.h> +#include <iostream.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include <iomanip.h> +#include <db_cxx.h> + +#define DATABASE "access.db" +#define WORDLIST "../test/wordlist" + +void usage(); +extern "C" int getopt(int, char * const *, const char *); + +char *progname = "BtRecExample"; // Program name. + +class BtRecExample +{ +public: + BtRecExample(FILE *fp); + ~BtRecExample(); + void run(); + void stats(); + void show(char *msg, Dbt *key, Dbt *data); + +private: + Db *dbp; + Dbc *dbcp; +}; + +BtRecExample::BtRecExample(FILE *fp) +{ + char *p, *t, buf[1024], rbuf[1024]; + int ret; + + // Remove the previous database. + (void)unlink(DATABASE); + + dbp = new Db(NULL, 0); + + dbp->set_error_stream(&cerr); + dbp->set_errpfx(progname); + dbp->set_pagesize(1024); // 1K page sizes. + + dbp->set_flags(DB_RECNUM); // Record numbers. + dbp->open(DATABASE, NULL, DB_BTREE, DB_CREATE, 0664); + + // + // 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. + // + + for (int cnt = 1; cnt <= 1000; ++cnt) { + (void)sprintf(buf, "%04d_", cnt); + if (fgets(buf + 4, sizeof(buf) - 4, fp) == NULL) + break; + u_int32_t len = strlen(buf); + buf[len - 1] = '\0'; + for (t = rbuf, p = buf + (len - 2); p >= buf;) + *t++ = *p--; + *t++ = '\0'; + + // As a convenience for printing, we include the null terminator + // in the stored data. + // + Dbt key(buf, len); + Dbt data(rbuf, len); + + if ((ret = dbp->put(NULL, &key, &data, DB_NOOVERWRITE)) != 0) { + dbp->err(ret, "Db::put"); + if (ret != DB_KEYEXIST) + throw DbException(ret); + } + } +} + +BtRecExample::~BtRecExample() +{ + if (dbcp != 0) + dbcp->close(); + dbp->close(0); + delete dbp; +} + +// +// Print out the number of records in the database. +// +void BtRecExample::stats() +{ + DB_BTREE_STAT *statp; + + dbp->stat(&statp, NULL, 0); + cout << progname << ": database contains " + << (u_long)statp->bt_ndata << " records\n"; + + // Note: must use free, not delete. + // This struct is allocated by C. + // + free(statp); +} + +void BtRecExample::run() +{ + db_recno_t recno; + int ret; + char buf[1024]; + + // Acquire a cursor for the database. + dbp->cursor(NULL, &dbcp, 0); + + // + // Prompt the user for a record number, then retrieve and display + // that record. + // + for (;;) { + // Get a record number. + cout << "recno #> "; + cout.flush(); + if (fgets(buf, sizeof(buf), stdin) == NULL) + break; + recno = atoi(buf); + + // + // Start with a fresh key each time, + // the dbp->get() routine returns + // the key and data pair, not just the key! + // + Dbt key(&recno, sizeof(recno)); + Dbt data; + + if ((ret = dbcp->get(&key, &data, DB_SET_RECNO)) != 0) { + dbp->err(ret, "DBcursor->get"); + throw DbException(ret); + } + + // Display the key and data. + show("k/d\t", &key, &data); + + // Move the cursor a record forward. + if ((ret = dbcp->get(&key, &data, DB_NEXT)) != 0) { + dbp->err(ret, "DBcursor->get"); + throw DbException(ret); + } + + // Display the key and data. + show("next\t", &key, &data); + + // + // Retrieve the record number for the following record into + // local memory. + // + data.set_data(&recno); + data.set_size(sizeof(recno)); + data.set_ulen(sizeof(recno)); + data.set_flags(data.get_flags() | DB_DBT_USERMEM); + + if ((ret = dbcp->get(&key, &data, DB_GET_RECNO)) != 0) { + if (ret != DB_NOTFOUND && ret != DB_KEYEMPTY) { + dbp->err(ret, "DBcursor->get"); + throw DbException(ret); + } + } + else { + cout << "retrieved recno: " << (u_long)recno << "\n"; + } + } + + dbcp->close(); + dbcp = NULL; +} + +// +// show -- +// Display a key/data pair. +// +void BtRecExample::show(char *msg, Dbt *key, Dbt *data) +{ + cout << msg << (char *)key->get_data() + << " : " << (char *)data->get_data() << "\n"; +} + +int +main(int argc, char *argv[]) +{ + extern char *optarg; + extern int optind; + FILE *fp; + int ch; + + while ((ch = getopt(argc, argv, "")) != EOF) + switch (ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + // Open the word database. + if ((fp = fopen(WORDLIST, "r")) == NULL) { + fprintf(stderr, "%s: open %s: %s\n", + progname, WORDLIST, db_strerror(errno)); + exit (1); + } + + try { + BtRecExample app(fp); + + // Close the word database. + (void)fclose(fp); + fp = NULL; + + app.stats(); + app.run(); + } + catch (DbException &dbe) { + cerr << "Exception: " << dbe.what() << "\n"; + return dbe.get_errno(); + } + + return (0); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: %s\n", progname); + exit(1); +} diff --git a/bdb/examples_cxx/EnvExample.cpp b/bdb/examples_cxx/EnvExample.cpp new file mode 100644 index 00000000000..bef1f3d1ace --- /dev/null +++ b/bdb/examples_cxx/EnvExample.cpp @@ -0,0 +1,122 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: EnvExample.cpp,v 11.12 2000/10/27 20:32:00 dda Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <iostream.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include <db_cxx.h> + +#ifdef macintosh +#define DATABASE_HOME ":database" +#define CONFIG_DATA_DIR ":database" +#else +#ifdef DB_WIN32 +#define DATABASE_HOME "\\tmp\\database" +#define CONFIG_DATA_DIR "\\database\\files" +#else +#define DATABASE_HOME "/tmp/database" +#define CONFIG_DATA_DIR "/database/files" +#endif +#endif + +void db_setup(char *, char *, ostream&); +void db_teardown(char *, char *, ostream&); + +char *progname = "EnvExample"; /* Program name. */ + +// +// An example of a program creating/configuring a Berkeley DB environment. +// +int +main(int, char **) +{ + // + // Note: it may be easiest to put all Berkeley DB operations in a + // try block, as seen here. Alternatively, you can change the + // ErrorModel in the DbEnv so that exceptions are never thrown + // and check error returns from all methods. + // + try { + char *data_dir, *home; + + // + // All of the shared database files live in /home/database, + // but data files live in /database. + // + home = DATABASE_HOME; + data_dir = CONFIG_DATA_DIR; + + cout << "Setup env\n"; + db_setup(DATABASE_HOME, data_dir, cerr); + + cout << "Teardown env\n"; + db_teardown(DATABASE_HOME, data_dir, cerr); + return 0; + } + catch (DbException &dbe) { + cerr << "AccessExample: " << dbe.what() << "\n"; + return 1; + } +} + +// Note that any of the db calls can throw DbException +void +db_setup(char *home, char *data_dir, ostream& err_stream) +{ + // + // Create an environment object and initialize it for error + // reporting. + // + DbEnv *dbenv = new DbEnv(0); + dbenv->set_error_stream(&err_stream); + dbenv->set_errpfx(progname); + + // + // We want to specify the shared memory buffer pool cachesize, + // but everything else is the default. + // + dbenv->set_cachesize(0, 64 * 1024, 0); + + // Databases are in a subdirectory. + (void)dbenv->set_data_dir(data_dir); + + // Open the environment with full transactional support. + dbenv->open(DATABASE_HOME, + DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN, 0); + + // Do something interesting... + + // Close the handle. + dbenv->close(0); +} + +void +db_teardown(char *home, char *data_dir, ostream& err_stream) +{ + // Remove the shared database regions. + DbEnv *dbenv = new DbEnv(0); + + dbenv->set_error_stream(&err_stream); + dbenv->set_errpfx(progname); + + (void)dbenv->set_data_dir(data_dir); + dbenv->remove(home, 0); + delete dbenv; +} diff --git a/bdb/examples_cxx/LockExample.cpp b/bdb/examples_cxx/LockExample.cpp new file mode 100644 index 00000000000..cfab2868098 --- /dev/null +++ b/bdb/examples_cxx/LockExample.cpp @@ -0,0 +1,236 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: LockExample.cpp,v 11.8 2001/01/04 14:23:30 dda Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <iostream.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#include <db_cxx.h> + +char *progname = "LockExample"; // Program name. + +// +// An example of a program using DBLock and related classes. +// +class LockExample : public DbEnv +{ +public: + void run(); + + LockExample(const char *home, u_int32_t maxlocks, int do_unlink); + +private: + static const char FileName[]; + + // no need for copy and assignment + LockExample(const LockExample &); + void operator = (const LockExample &); +}; + +static void usage(); // forward + +int +main(int argc, char *argv[]) +{ + const char *home; + int do_unlink; + u_int32_t maxlocks; + int i; + + home = "TESTDIR"; + maxlocks = 0; + do_unlink = 0; + for (int argnum = 1; argnum < argc; ++argnum) { + if (strcmp(argv[argnum], "-h") == 0) { + if (++argnum >= argc) + usage(); + home = argv[argnum]; + } + else if (strcmp(argv[argnum], "-m") == 0) { + if (++argnum >= argc) + usage(); + if ((i = atoi(argv[argnum])) <= 0) + usage(); + maxlocks = (u_int32_t)i; /* XXX: possible overflow. */ + } + else if (strcmp(argv[argnum], "-u") == 0) { + do_unlink = 1; + } + else { + usage(); + } + } + + try { + if (do_unlink) { + // Create an environment that immediately + // removes all files. + LockExample tmp(home, maxlocks, do_unlink); + } + + LockExample app(home, maxlocks, do_unlink); + app.run(); + app.close(0); + return 0; + } + catch (DbException &dbe) { + cerr << "LockExample: " << dbe.what() << "\n"; + return 1; + } +} + +LockExample::LockExample(const char *home, u_int32_t maxlocks, int do_unlink) +: DbEnv(0) +{ + int ret; + + if (do_unlink) { + if ((ret = remove(home, DB_FORCE)) != 0) { + cerr << progname << ": DbEnv::remove: " + << strerror(errno) << "\n"; + exit (1); + } + } + else { + set_error_stream(&cerr); + set_errpfx("LockExample"); + if (maxlocks != 0) + set_lk_max_locks(maxlocks); + open(home, DB_CREATE | DB_INIT_LOCK, 0); + } +} + +void LockExample::run() +{ + long held; + u_int32_t len, locker; + int did_get, ret; + DbLock *locks = 0; + int lockcount = 0; + char objbuf[1024]; + int lockid = 0; + + // + // Accept lock requests. + // + lock_id(&locker); + for (held = 0;;) { + cout << "Operation get/release [get]> "; + cout.flush(); + + char opbuf[16]; + cin.getline(opbuf, sizeof(opbuf)); + if (cin.eof()) + break; + if ((len = strlen(opbuf)) <= 1 || strcmp(opbuf, "get") == 0) { + // Acquire a lock. + cout << "input object (text string) to lock> "; + cout.flush(); + cin.getline(objbuf, sizeof(objbuf)); + if (cin.eof()) + break; + if ((len = strlen(objbuf)) <= 0) + continue; + + char lockbuf[16]; + do { + cout << "lock type read/write [read]> "; + cout.flush(); + cin.getline(lockbuf, sizeof(lockbuf)); + if (cin.eof()) + break; + len = strlen(lockbuf); + } while (len >= 1 && + strcmp(lockbuf, "read") != 0 && + strcmp(lockbuf, "write") != 0); + + db_lockmode_t lock_type; + if (len <= 1 || strcmp(lockbuf, "read") == 0) + lock_type = DB_LOCK_READ; + else + lock_type = DB_LOCK_WRITE; + + Dbt dbt(objbuf, strlen(objbuf)); + + DbLock lock; + ret = lock_get(locker, DB_LOCK_NOWAIT, &dbt, + lock_type, &lock); + did_get = 1; + lockid = lockcount++; + if (locks == NULL) { + locks = new DbLock[1]; + } + else { + DbLock *newlocks = new DbLock[lockcount]; + for (int lockno = 0; lockno < lockid; lockno++) { + newlocks[lockno] = locks[lockno]; + } + delete locks; + locks = newlocks; + } + locks[lockid] = lock; + } else { + // Release a lock. + do { + cout << "input lock to release> "; + cout.flush(); + cin.getline(objbuf, sizeof(objbuf)); + if (cin.eof()) + break; + } while ((len = strlen(objbuf)) <= 0); + lockid = strtol(objbuf, NULL, 16); + if (lockid < 0 || lockid >= lockcount) { + cout << "Lock #" << lockid << " out of range\n"; + continue; + } + DbLock lock = locks[lockid]; + ret = lock.put(this); + did_get = 0; + } + + switch (ret) { + case 0: + cout << "Lock #" << lockid << " " + << (did_get ? "granted" : "released") + << "\n"; + held += did_get ? 1 : -1; + break; + case DB_LOCK_NOTGRANTED: + cout << "Lock not granted\n"; + break; + case DB_LOCK_DEADLOCK: + cerr << "LockExample: lock_" + << (did_get ? "get" : "put") + << ": " << "returned DEADLOCK"; + break; + default: + cerr << "LockExample: lock_get: %s", + strerror(errno); + } + } + cout << "\n"; + cout << "Closing lock region " << held << " locks held\n"; + if (locks != 0) + delete locks; +} + +static void +usage() +{ + cerr << "usage: LockExample [-u] [-h home] [-m maxlocks]\n"; + exit(1); +} diff --git a/bdb/examples_cxx/MpoolExample.cpp b/bdb/examples_cxx/MpoolExample.cpp new file mode 100644 index 00000000000..cf0f5f7e6a4 --- /dev/null +++ b/bdb/examples_cxx/MpoolExample.cpp @@ -0,0 +1,210 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: MpoolExample.cpp,v 11.9 2000/10/27 20:32:01 dda Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#include <errno.h> +#include <fcntl.h> +#include <iostream.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#endif + +#include <db_cxx.h> + +#define MPOOL "mpool" + +void init(char *, int, int); +void run(DB_ENV *, int, int, int); + +static void usage(); + +char *progname = "MpoolExample"; // Program name. + +class MpoolExample : public DbEnv +{ +public: + MpoolExample(); + void initdb(const char *home, int cachesize); + void run(int hits, int pagesize, int npages); + +private: + static const char FileName[]; + + // no need for copy and assignment + MpoolExample(const MpoolExample &); + void operator = (const MpoolExample &); +}; + +int main(int argc, char *argv[]) +{ + int cachesize = 20 * 1024; + int hits = 1000; + int npages = 50; + int pagesize = 1024; + + for (int i = 1; i < argc; ++i) { + if (strcmp(argv[i], "-c") == 0) { + if ((cachesize = atoi(argv[++i])) < 20 * 1024) + usage(); + } + else if (strcmp(argv[i], "-h") == 0) { + if ((hits = atoi(argv[++i])) <= 0) + usage(); + } + else if (strcmp(argv[i], "-n") == 0) { + if ((npages = atoi(argv[++i])) <= 0) + usage(); + } + else if (strcmp(argv[i], "-p") == 0) { + if ((pagesize = atoi(argv[++i])) <= 0) + usage(); + } + else { + usage(); + } + } + + // Initialize the file. + init(MPOOL, pagesize, npages); + + try { + MpoolExample app; + + cout << progname + << ": cachesize: " << cachesize + << "; pagesize: " << pagesize + << "; N pages: " << npages << "\n"; + + app.initdb(NULL, cachesize); + app.run(hits, pagesize, npages); + cout << "MpoolExample: completed\n"; + return 0; + } + catch (DbException &dbe) { + cerr << "MpoolExample: " << dbe.what() << "\n"; + return 1; + } +} + +// +// init -- +// Create a backing file. +// +void +init(char *file, int pagesize, int npages) +{ + // + // Create a file with the right number of pages, and store a page + // number on each page. + // + int fd; + int flags = O_CREAT | O_RDWR | O_TRUNC; +#ifdef DB_WIN32 + flags |= O_BINARY; +#endif + if ((fd = open(file, flags, 0666)) < 0) { + cerr << "MpoolExample: " << file << ": " << strerror(errno) << "\n"; + exit(1); + } + char *p = new char[pagesize]; + memset(p, 0, pagesize); + + // The pages are numbered from 0. + for (int cnt = 0; cnt <= npages; ++cnt) { + *(db_pgno_t *)p = cnt; + if (write(fd, p, pagesize) != pagesize) { + cerr << "MpoolExample: " << file + << ": " << strerror(errno) << "\n"; + exit(1); + } + } + delete [] p; +} + +static void +usage() +{ + cerr << "usage: MpoolExample [-c cachesize] " + << "[-h hits] [-n npages] [-p pagesize]\n"; + exit(1); +} + +// Note: by using DB_CXX_NO_EXCEPTIONS, we get explicit error returns +// from various methods rather than exceptions so we can report more +// information with each error. +// +MpoolExample::MpoolExample() +: DbEnv(DB_CXX_NO_EXCEPTIONS) +{ +} + +void MpoolExample::initdb(const char *home, int cachesize) +{ + set_error_stream(&cerr); + set_errpfx("MpoolExample"); + set_cachesize(0, cachesize, 0); + + open(home, DB_CREATE | DB_INIT_MPOOL, 0); +} + +// +// run -- +// Get a set of pages. +// +void +MpoolExample::run(int hits, int pagesize, int npages) +{ + db_pgno_t pageno; + int cnt; + void *p; + + // Open the file in the pool. + DbMpoolFile *dbmfp; + + DbMpoolFile::open(this, MPOOL, 0, 0, pagesize, NULL, &dbmfp); + + cout << "retrieve " << hits << " random pages... "; + + srand((unsigned int)time(NULL)); + for (cnt = 0; cnt < hits; ++cnt) { + pageno = (rand() % npages) + 1; + if ((errno = dbmfp->get(&pageno, 0, &p)) != 0) { + cerr << "MpoolExample: unable to retrieve page " + << (unsigned long)pageno << ": " + << strerror(errno) << "\n"; + exit(1); + } + if (*(db_pgno_t *)p != pageno) { + cerr << "MpoolExample: wrong page retrieved (" + << (unsigned long)pageno << " != " + << *(int *)p << ")\n"; + exit(1); + } + if ((errno = dbmfp->put(p, 0)) != 0) { + cerr << "MpoolExample: unable to return page " + << (unsigned long)pageno << ": " + << strerror(errno) << "\n"; + exit(1); + } + } + + cout << "successful.\n"; + + // Close the pool. + if ((errno = close(0)) != 0) { + cerr << "MpoolExample: " << strerror(errno) << "\n"; + exit(1); + } +} diff --git a/bdb/examples_cxx/TpcbExample.cpp b/bdb/examples_cxx/TpcbExample.cpp new file mode 100644 index 00000000000..f4ca72df8e3 --- /dev/null +++ b/bdb/examples_cxx/TpcbExample.cpp @@ -0,0 +1,666 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997, 1998, 1999, 2000 + * Sleepycat Software. All rights reserved. + * + * $Id: TpcbExample.cpp,v 11.14 2000/10/27 20:32:01 dda Exp $ + */ + +#include "db_config.h" + +#ifndef NO_SYSTEM_INCLUDES +#include <sys/types.h> + +#if TIME_WITH_SYS_TIME +#include <sys/time.h> +#include <time.h> +#else +#if HAVE_SYS_TIME_H +#include <sys/time.h> +#else +#include <time.h> +#endif +#endif + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#endif + +#ifdef DB_WIN32 +#include <sys/types.h> +#include <sys/timeb.h> +#endif + +#include <iostream.h> +#include <iomanip.h> +#include <db_cxx.h> + +typedef enum { ACCOUNT, BRANCH, TELLER } FTYPE; + +void errExit(int err, const char *); // show err as errno and exit + +void invarg(int, char *); +u_int32_t random_id(FTYPE, u_int32_t, u_int32_t, u_int32_t); +u_int32_t random_int(u_int32_t, u_int32_t); +static void usage(void); + +int verbose; +char *progname = "TpcbExample"; // Program name. + +class TpcbExample : public DbEnv +{ +public: + void populate(int, int, int, int); + void run(int, int, int, int); + int txn(Db *, Db *, Db *, Db *, + int, int, int); + void populateHistory(Db *, int, u_int32_t, u_int32_t, u_int32_t); + void populateTable(Db *, u_int32_t, u_int32_t, int, char *); + + // Note: the constructor creates a DbEnv(), which is + // not fully initialized until the DbEnv::open() method + // is called. + // + TpcbExample(const char *home, int cachesize, + int initializing, int flags); + +private: + static const char FileName[]; + + // no need for copy and assignment + TpcbExample(const TpcbExample &); + void operator = (const TpcbExample &); +}; + +// +// 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 100 +#define ACCOUNTS_PER_TELLER 1000 +#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 + +#if !defined(VALID_SCALING) && !defined(TINY) +#define ACCOUNTS 100000 +#define BRANCHES 10 +#define TELLERS 100 +#define HISTORY 259200 +#endif + +#define HISTORY_LEN 100 +#define RECLEN 100 +#define BEGID 1000000 + +struct Defrec { + u_int32_t id; + u_int32_t balance; + u_int8_t pad[RECLEN - sizeof(u_int32_t) - sizeof(u_int32_t)]; +}; + +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)]; +}; + +int +main(int argc, char *argv[]) +{ + unsigned long seed; + int accounts, branches, tellers, history; + int iflag, mpool, ntxns, txn_no_sync; + char *home, *endarg; + + home = "TESTDIR"; + accounts = branches = history = tellers = 0; + txn_no_sync = 0; + mpool = ntxns = 0; + verbose = 0; + iflag = 0; + seed = (unsigned long)getpid(); + + for (int i = 1; i < argc; ++i) { + + if (strcmp(argv[i], "-a") == 0) { + // Number of account records + if ((accounts = atoi(argv[++i])) <= 0) + invarg('a', argv[i]); + } + else if (strcmp(argv[i], "-b") == 0) { + // Number of branch records + if ((branches = atoi(argv[++i])) <= 0) + invarg('b', argv[i]); + } + else if (strcmp(argv[i], "-c") == 0) { + // Cachesize in bytes + if ((mpool = atoi(argv[++i])) <= 0) + invarg('c', argv[i]); + } + else if (strcmp(argv[i], "-f") == 0) { + // Fast mode: no txn sync. + txn_no_sync = 1; + } + else if (strcmp(argv[i], "-h") == 0) { + // DB home. + home = argv[++i]; + } + else if (strcmp(argv[i], "-i") == 0) { + // Initialize the test. + iflag = 1; + } + else if (strcmp(argv[i], "-n") == 0) { + // Number of transactions + if ((ntxns = atoi(argv[++i])) <= 0) + invarg('n', argv[i]); + } + else if (strcmp(argv[i], "-S") == 0) { + // Random number seed. + seed = strtoul(argv[++i], &endarg, 0); + if (*endarg != '\0') + invarg('S', argv[i]); + } + else if (strcmp(argv[i], "-s") == 0) { + // Number of history records + if ((history = atoi(argv[++i])) <= 0) + invarg('s', argv[i]); + } + else if (strcmp(argv[i], "-t") == 0) { + // Number of teller records + if ((tellers = atoi(argv[++i])) <= 0) + invarg('t', argv[i]); + } + else if (strcmp(argv[i], "-v") == 0) { + // Verbose option. + verbose = 1; + } + else { + usage(); + } + } + + srand((unsigned int)seed); + + accounts = accounts == 0 ? ACCOUNTS : accounts; + branches = branches == 0 ? BRANCHES : branches; + tellers = tellers == 0 ? TELLERS : tellers; + history = history == 0 ? HISTORY : history; + + if (verbose) + cout << (long)accounts << " Accounts " + << (long)branches << " Branches " + << (long)tellers << " Tellers " + << (long)history << " History\n"; + + try { + // Initialize the database environment. + // Must be done in within a try block, unless you + // change the error model in the environment options. + // + TpcbExample app(home, mpool, iflag, txn_no_sync ? DB_TXN_NOSYNC : 0); + + if (iflag) { + if (ntxns != 0) + usage(); + app.populate(accounts, branches, history, tellers); + } + else { + if (ntxns == 0) + usage(); + app.run(ntxns, accounts, branches, tellers); + } + + app.close(0); + return 0; + } + catch (DbException &dbe) { + cerr << "TpcbExample: " << dbe.what() << "\n"; + return 1; + } +} + +void +invarg(int arg, char *str) +{ + cerr << "TpcbExample: invalid argument for -" + << (char)arg << ": " << str << "\n"; + exit(1); +} + +static void +usage() +{ + cerr << "usage: TpcbExample [-fiv] [-a accounts] [-b branches]\n" + << " [-c cachesize] [-h home] [-n transactions ]\n" + << " [-S seed] [-s history] [-t tellers]\n"; + exit(1); +} + +TpcbExample::TpcbExample(const char *home, int cachesize, + int initializing, int flags) +: DbEnv(0) +{ + u_int32_t local_flags; + + set_error_stream(&cerr); + set_errpfx("TpcbExample"); + (void)set_cachesize(0, cachesize == 0 ? + 4 * 1024 * 1024 : (u_int32_t)cachesize, 0); + + local_flags = flags | DB_CREATE | DB_INIT_MPOOL; + if (!initializing) + local_flags |= DB_INIT_TXN | DB_INIT_LOCK | DB_INIT_LOG; + open(home, local_flags, 0); +} + +// +// Initialize the database to the specified number of accounts, branches, +// history records, and tellers. +// +void +TpcbExample::populate(int accounts, int branches, int history, int tellers) +{ + Db *dbp; + + int err; + u_int32_t balance, idnum; + u_int32_t end_anum, end_bnum, end_tnum; + u_int32_t start_anum, start_bnum, start_tnum; + + idnum = BEGID; + balance = 500000; + + dbp = new Db(this, 0); + dbp->set_h_nelem((unsigned int)accounts); + + if ((err = dbp->open("account", NULL, DB_HASH, + DB_CREATE | DB_TRUNCATE, 0644)) != 0) { + errExit(err, "Open of account file failed"); + } + + start_anum = idnum; + populateTable(dbp, idnum, balance, accounts, "account"); + idnum += accounts; + end_anum = idnum - 1; + if ((err = dbp->close(0)) != 0) { + errExit(err, "Account file close failed"); + } + delete dbp; + if (verbose) + cout << "Populated accounts: " + << (long)start_anum << " - " << (long)end_anum << "\n"; + + dbp = new Db(this, 0); + // + // Since the number of branches is very small, we want to use very + // small pages and only 1 key per page. This is the poor-man's way + // of getting key locking instead of page locking. + // + dbp->set_h_ffactor(1); + dbp->set_h_nelem((unsigned int)branches); + dbp->set_pagesize(512); + + if ((err = dbp->open("branch", NULL, DB_HASH, + DB_CREATE | DB_TRUNCATE, 0644)) != 0) { + errExit(err, "Branch file create failed"); + } + start_bnum = idnum; + populateTable(dbp, idnum, balance, branches, "branch"); + idnum += branches; + end_bnum = idnum - 1; + if ((err = dbp->close(0)) != 0) { + errExit(err, "Close of branch file failed"); + } + delete dbp; + + if (verbose) + cout << "Populated branches: " + << (long)start_bnum << " - " << (long)end_bnum << "\n"; + + dbp = new Db(this, 0); + // + // In the case of tellers, we also want small pages, but we'll let + // the fill factor dynamically adjust itself. + // + dbp->set_h_ffactor(0); + dbp->set_h_nelem((unsigned int)tellers); + dbp->set_pagesize(512); + + if ((err = dbp->open("teller", NULL, DB_HASH, + DB_CREATE | DB_TRUNCATE, 0644)) != 0) { + errExit(err, "Teller file create failed"); + } + + start_tnum = idnum; + populateTable(dbp, idnum, balance, tellers, "teller"); + idnum += tellers; + end_tnum = idnum - 1; + if ((err = dbp->close(0)) != 0) { + errExit(err, "Close of teller file failed"); + } + delete dbp; + if (verbose) + cout << "Populated tellers: " + << (long)start_tnum << " - " << (long)end_tnum << "\n"; + + dbp = new Db(this, 0); + dbp->set_re_len(HISTORY_LEN); + if ((err = dbp->open("history", NULL, DB_RECNO, + DB_CREATE | DB_TRUNCATE, 0644)) != 0) { + errExit(err, "Create of history file failed"); + } + + populateHistory(dbp, history, accounts, branches, tellers); + if ((err = dbp->close(0)) != 0) { + errExit(err, "Close of history file failed"); + } + delete dbp; +} + +void +TpcbExample::populateTable(Db *dbp, + u_int32_t start_id, u_int32_t balance, + int nrecs, char *msg) +{ + Defrec drec; + memset(&drec.pad[0], 1, sizeof(drec.pad)); + + Dbt kdbt(&drec.id, sizeof(u_int32_t)); + Dbt ddbt(&drec, sizeof(drec)); + + for (int i = 0; i < nrecs; i++) { + drec.id = start_id + (u_int32_t)i; + drec.balance = balance; + int err; + if ((err = + dbp->put(NULL, &kdbt, &ddbt, DB_NOOVERWRITE)) != 0) { + cerr << "Failure initializing " << msg << " file: " + << strerror(err) << "\n"; + exit(1); + } + } +} + +void +TpcbExample::populateHistory(Db *dbp, int nrecs, + u_int32_t accounts, u_int32_t branches, u_int32_t tellers) +{ + Histrec hrec; + memset(&hrec.pad[0], 1, sizeof(hrec.pad)); + hrec.amount = 10; + db_recno_t key; + + Dbt kdbt(&key, sizeof(u_int32_t)); + Dbt ddbt(&hrec, sizeof(hrec)); + + for (int i = 1; i <= nrecs; 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); + + int err; + key = (db_recno_t)i; + if ((err = dbp->put(NULL, &kdbt, &ddbt, DB_APPEND)) != 0) { + errExit(err, "Failure initializing history file"); + } + } +} + +u_int32_t +random_int(u_int32_t lo, u_int32_t hi) +{ + u_int32_t ret; + int t; + + t = rand(); + ret = (u_int32_t)(((double)t / ((double)(RAND_MAX) + 1)) * + (hi - lo + 1)); + ret += lo; + return (ret); +} + +u_int32_t +random_id(FTYPE type, u_int32_t accounts, u_int32_t branches, u_int32_t 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)); +} + +void +TpcbExample::run(int n, int accounts, int branches, int tellers) +{ + Db *adb, *bdb, *hdb, *tdb; + double gtps, itps; + int failed, ifailed, ret, txns; + time_t starttime, curtime, lasttime; +#ifndef DB_WIN32 + pid_t pid; + + pid = getpid(); +#else + int pid; + + pid = 0; +#endif + + // + // Open the database files. + // + + int err; + adb = new Db(this, 0); + if ((err = adb->open("account", NULL, DB_UNKNOWN, 0, 0)) != 0) + errExit(err, "Open of account file failed"); + + bdb = new Db(this, 0); + if ((err = bdb->open("branch", NULL, DB_UNKNOWN, 0, 0)) != 0) + errExit(err, "Open of branch file failed"); + + tdb = new Db(this, 0); + if ((err = tdb->open("teller", NULL, DB_UNKNOWN, 0, 0)) != 0) + errExit(err, "Open of teller file failed"); + + hdb = new Db(this, 0); + if ((err = hdb->open("history", NULL, DB_UNKNOWN, 0, 0)) != 0) + errExit(err, "Open of history file failed"); + + txns = failed = ifailed = 0; + starttime = time(NULL); + lasttime = starttime; + while (n-- > 0) { + txns++; + ret = txn(adb, bdb, tdb, hdb, accounts, branches, tellers); + if (ret != 0) { + failed++; + ifailed++; + } + if (n % 5000 == 0) { + curtime = time(NULL); + gtps = (double)(txns - failed) / (curtime - starttime); + itps = (double)(5000 - ifailed) / (curtime - lasttime); + + // We use printf because it provides much simpler + // formatting than iostreams. + // + printf("[%d] %d txns %d failed ", (int)pid, + txns, failed); + printf("%6.2f TPS (gross) %6.2f TPS (interval)\n", + gtps, itps); + lasttime = curtime; + ifailed = 0; + } + } + + (void)adb->close(0); + (void)bdb->close(0); + (void)tdb->close(0); + (void)hdb->close(0); + + cout << (long)txns << " transactions begun " + << (long)failed << " failed\n"; +} + +// +// XXX Figure out the appropriate way to pick out IDs. +// +int +TpcbExample::txn(Db *adb, Db *bdb, Db *tdb, Db *hdb, + int accounts, int branches, int tellers) +{ + Dbc *acurs = NULL; + Dbc *bcurs = NULL; + Dbc *tcurs = NULL; + DbTxn *t = NULL; + + db_recno_t key; + Defrec rec; + Histrec hrec; + int account, branch, teller; + + Dbt d_dbt; + Dbt d_histdbt; + Dbt k_dbt; + Dbt k_histdbt(&key, sizeof(key)); + + // + // XXX We could move a lot of this into the driver to make this + // faster. + // + account = random_id(ACCOUNT, accounts, branches, tellers); + branch = random_id(BRANCH, accounts, branches, tellers); + teller = random_id(TELLER, accounts, branches, tellers); + + k_dbt.set_size(sizeof(int)); + + d_dbt.set_flags(DB_DBT_USERMEM); + d_dbt.set_data(&rec); + d_dbt.set_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.set_flags(DB_DBT_PARTIAL); + + // START TIMING + if (txn_begin(NULL, &t, 0) != 0) + goto err; + + if (adb->cursor(t, &acurs, 0) != 0 || + bdb->cursor(t, &bcurs, 0) != 0 || + tdb->cursor(t, &tcurs, 0) != 0) + goto err; + + // Account record + k_dbt.set_data(&account); + if (acurs->get(&k_dbt, &d_dbt, DB_SET) != 0) + goto err; + rec.balance += 10; + if (acurs->put(&k_dbt, &d_dbt, DB_CURRENT) != 0) + goto err; + + // Branch record + k_dbt.set_data(&branch); + if (bcurs->get(&k_dbt, &d_dbt, DB_SET) != 0) + goto err; + rec.balance += 10; + if (bcurs->put(&k_dbt, &d_dbt, DB_CURRENT) != 0) + goto err; + + // Teller record + k_dbt.set_data(&teller); + if (tcurs->get(&k_dbt, &d_dbt, DB_SET) != 0) + goto err; + rec.balance += 10; + if (tcurs->put(&k_dbt, &d_dbt, DB_CURRENT) != 0) + goto err; + + // History record + d_histdbt.set_flags(0); + d_histdbt.set_data(&hrec); + d_histdbt.set_ulen(sizeof(hrec)); + if (hdb->put(t, &k_histdbt, &d_histdbt, DB_APPEND) != 0) + goto err; + + if (acurs->close() != 0 || bcurs->close() != 0 || + tcurs->close() != 0) + goto err; + + if (t->commit(0) != 0) + goto err; + + // END TIMING + return (0); + +err: + if (acurs != NULL) + (void)acurs->close(); + if (bcurs != NULL) + (void)bcurs->close(); + if (tcurs != NULL) + (void)tcurs->close(); + if (t != NULL) + (void)t->abort(); + + if (verbose) + cout << "Transaction A=" << (long)account + << " B=" << (long)branch + << " T=" << (long)teller << " failed\n"; + return (-1); +} + +void errExit(int err, const char *s) +{ + cerr << progname << ": "; + if (s != NULL) { + cerr << s << ": "; + } + cerr << strerror(err) << "\n"; + exit(1); +} |