wvbdbhash.cc

00001 /*
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2003 Net Integration Technologies, Inc.
00004  *
00005  * A hash table container backed by a Berkeley DB (libdb) database.
00006  * See wvondiskhash.h.
00007  */
00008 #include "wvautoconf.h"
00009 
00010 #ifdef WITH_BDB
00011 
00012 #include "wvondiskhash.h"
00013 #include <fcntl.h>
00014 #include <errno.h>
00015 #include <unistd.h>
00016 
00017 #ifdef HAVE_DB_H
00018 #include <db.h>
00019 #else
00020 #ifdef HAVE_DB_185_H
00021 #include <db_185.h>
00022 #endif
00023 #endif
00024 
00025 #include "wvlog.h"
00026 
00027 int comparefunc(const DBT *a, const DBT *b)
00028 {
00029     if (a == NULL && b == NULL) return 0;
00030     if (a == NULL) return 1;
00031     if (b == NULL) return -1;
00032     
00033     size_t minlen = (a->size > b->size) ? b->size : a->size;
00034     int ret = memcmp(a->data, b->data, minlen);
00035     if (ret != 0) return ret;
00036     if (a->size > b->size) return 1;
00037     if (a->size < b->size) return -1;
00038     return 0;
00039 }
00040 
00041 
00042 WvBdbHash::WvBdbHash(WvStringParm _dbfile, bool _persist)
00043 {
00044     dbf = NULL;
00045     opendb(_dbfile, _persist);
00046 }
00047 
00048 
00049 WvBdbHash::~WvBdbHash()
00050 {
00051     closedb();
00052 }
00053 
00054 
00055 void WvBdbHash::opendb(WvStringParm _dbfile, bool _persist)
00056 {
00057     closedb();
00058 
00059     noerr();
00060     
00061     dbfile = _dbfile;
00062     persist_dbfile = _persist;
00063 
00064     BTREEINFO info;
00065     memset(&info, 0, sizeof(info));
00066     info.compare = comparefunc;
00067 
00068     int mode = O_CREAT | O_RDWR;
00069     if (!persist_dbfile) mode |= O_TRUNC;
00070     
00071     dbf = dbopen(!!dbfile ? dbfile.cstr() : NULL,
00072             mode, 0666, DB_BTREE, &info);
00073     if (dbf == NULL) seterr(errno);
00074 }
00075 
00076 
00077 void WvBdbHash::closedb()
00078 {
00079     if (dbf)
00080     {
00081         if (dbf->close(dbf) != 0) seterr(errno);
00082         if (!persist_dbfile && !!dbfile)
00083             ::unlink(dbfile);
00084         dbf = NULL;
00085     }
00086     seterr("The db is closed.");    // this won't overwrite an earlier err
00087 }
00088 
00089 
00090 void WvBdbHash::add(const datum &key, const datum &data, bool replace)
00091 {
00092     if (!isok()) return;
00093     int r = dbf->put(dbf, (DBT *)&key, (DBT *)&data,
00094                     !replace ? R_NOOVERWRITE : 0);
00095     if (r == 1)
00096         seterr("Must set the replace flag to replace existing elements.");
00097     else if (r != 0)
00098         seterr(errno);
00099 }
00100 
00101 
00102 void WvBdbHash::remove(const datum &key)
00103 {
00104     if (!isok()) return;
00105     
00106     datum newkey, data;
00107     newkey = key;
00108     
00109     int ret = dbf->seq(dbf, (DBT *)&newkey, (DBT *)&data, R_CURSOR);
00110     if (!ret)
00111     {
00112         ret = dbf->del(dbf, (DBT *)&newkey, R_CURSOR);
00113     }
00114     
00115     if (ret == 1) seterr("Strange: seq found a key that del didn't recognize");
00116     else if (ret) seterr(errno);
00117 }
00118 
00119 
00120 WvBdbHash::datum WvBdbHash::find(const datum &key)
00121 {
00122     datum ret = {0, 0};
00123     if (!isok()) return ret;
00124 
00125     int r = dbf->get(dbf, (DBT *)&key, (DBT *)&ret, 0);
00126     if (r == 1)
00127     {
00128         // not found - make sure we return an empty datum
00129         ret.dptr = NULL;
00130     }
00131     else if (r != 0)
00132     {
00133         ret.dptr = NULL;
00134         seterr(errno);
00135     }
00136     return ret;
00137 }
00138 
00139 
00140 bool WvBdbHash::exists(const datum &key)
00141 {
00142     if (!isok()) return false;
00143 
00144     datum ret = {0, 0};
00145     int r = dbf->get(dbf, (DBT *)&key, (DBT *)&ret, 0);
00146 
00147     // return true on success
00148     if (r == 0) return true;
00149 
00150     // return false on 1 (not found) or -1 (error)
00151     if (r != 1) seterr(errno);
00152     return false;
00153 }
00154 
00155 
00156 void WvBdbHash::zap()
00157 {
00158     if (!dbfile)
00159     {
00160         // we're broken - nothing we can do
00161         if (!isok())
00162         {
00163             closedb();
00164             return;
00165         }
00166 
00167         // super-slow version
00168         datum key, value;
00169         int r;
00170         while ((r = dbf->seq(dbf, (DBT *)&key, (DBT *)&value, R_FIRST)) == 0)
00171         {
00172             int r2 = dbf->del(dbf, (DBT *)&key, R_CURSOR);
00173             if (r2 == 1) seterr("Strange: seq found a key that del didn't recognize");
00174             else if (r2 != 0) seterr(errno);
00175         }
00176         if (r != 1) seterr(errno);  // 1 = not found
00177     }
00178     else // delete the database file and reopen - much quicker!
00179     {
00180         if (dbf)
00181         {
00182             dbf->close(dbf);    // don't care if it fails
00183             dbf = NULL;
00184         }
00185         int fd = open(dbfile, O_RDWR | O_TRUNC);
00186         if (fd >= 0) ::close(fd);
00187         opendb(dbfile, persist_dbfile);
00188     }
00189 }
00190 
00191 
00192 void WvBdbHash::IterBase::next(datum &curkey, datum &curdata)
00193 {
00194     if (!parent.isok()) return;
00195 
00196     // check if this is the first next() after a rewind()
00197     bool first = !curkey.dptr;
00198     datum wanted = { 0, 0 };
00199     if (first)
00200     {
00201         if (rewindto.dptr)
00202         {
00203             curkey = rewindto;
00204             first = false;
00205         }
00206     }
00207     else
00208     {
00209         wanted.dsize = curkey.dsize;
00210         wanted.dptr = malloc(wanted.dsize);
00211         memcpy(wanted.dptr, curkey.dptr, wanted.dsize);
00212     }
00213 
00214     // always seek for the saved cursor we were just passed, to work around
00215     // bugs in libdb1's seq with btrees.  (As a bonus, this gives us multiple
00216     // iterators for free!)
00217     int r = parent.dbf->seq(parent.dbf, (DBT *)&curkey, (DBT *)&curdata,
00218                 first ? R_FIRST : R_CURSOR);
00219     
00220     if (r == 1)
00221     {
00222         // current key gone, and none higher left: done
00223         curkey.dptr = curdata.dptr = NULL;
00224     }
00225     else if (r != 0)
00226         parent.seterr(errno);
00227 
00228     else if (!first)
00229     {
00230         while (comparefunc((DBT *)&wanted, (DBT *)&curkey) >= 0)
00231         {
00232             // found the exact key or earlier than requested: move forward one
00233             // (yes, libbdb1 can return a key less than requested, despite
00234             // the documentation's claims!)
00235             // This algorithm definitely makes it so inserting the same key
00236             // more than once doesn't work at all.
00237             r = parent.dbf->seq(parent.dbf, (DBT *)&curkey, (DBT *)&curdata,
00238                                  R_NEXT);
00239 
00240             if (r == 1)
00241             {
00242                 // nothing left?  Fine, we're done
00243                 curkey.dptr = curdata.dptr = NULL;
00244                 break;
00245             }
00246             else if (r != 0)
00247                 parent.seterr(errno);
00248         }
00249     }
00250 
00251     // otherwise, curkey is now sitting on the key after the requested one (or
00252     // the very first key), as expected.  (Also, if rewindto is set it should
00253     // be either filled in with the matching btree data or cleared.)  Unless,
00254     // of course, the whole db is borked.
00255     assert(!parent.isok() || !rewindto.dptr || curkey.dptr != rewindto.dptr);
00256     free(wanted.dptr);
00257 }
00258 
00259 #endif /* WITH_BDB */

Generated on Mon Feb 5 10:54:28 2007 for WvStreams by  doxygen 1.5.1