Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members | Related Pages

wvbdbhash.cc

Go to the documentation of this file.
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 wvbdbhash.h.
00007  */
00008 #include "wvautoconf.h"
00009 
00010 #ifdef WITH_BDB
00011 
00012 #include "wvbdbhash.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 WvBdbHashBase::WvBdbHashBase(WvStringParm _dbfile, bool _persist)
00043 {
00044     dbf = NULL;
00045     opendb(_dbfile, _persist);
00046 }
00047 
00048 
00049 WvBdbHashBase::~WvBdbHashBase()
00050 {
00051     closedb();
00052 }
00053 
00054 
00055 void WvBdbHashBase::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, mode, 0666, DB_BTREE,
00072             &info);
00073     if (!dbf) seterr(errno);
00074 }
00075 
00076 
00077 void WvBdbHashBase::closedb()
00078 {
00079     if (dbf)
00080     {
00081         if (!dbf->close(dbf)) 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 WvBdbHashBase::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 WvBdbHashBase::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 WvBdbHashBase::datum WvBdbHashBase::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 WvBdbHashBase::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 WvBdbHashBase::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 WvBdbHashBase::IterBase::IterBase(WvBdbHashBase &_bdbhash)
00193     : bdbhash(_bdbhash)
00194 {
00195     rewindto.dsize = 0;
00196     rewindto.dptr = NULL;
00197 }
00198 
00199 
00200 WvBdbHashBase::IterBase::~IterBase()
00201 {
00202     free(rewindto.dptr);
00203 }
00204 
00205 
00206 void WvBdbHashBase::IterBase::rewind()
00207 {
00208     free(rewindto.dptr);
00209     rewindto.dptr = NULL;
00210 }
00211 
00212 
00213 void WvBdbHashBase::IterBase::rewind(const datum &firstkey, datum &curkey,
00214         datum &curdata)
00215 {
00216     // save the firstkey and clear the current one
00217     free(rewindto.dptr);
00218     rewindto.dsize = firstkey.dsize;
00219     rewindto.dptr = malloc(rewindto.dsize);
00220     memcpy(rewindto.dptr, firstkey.dptr, rewindto.dsize);
00221     curkey.dptr = curdata.dptr = NULL;
00222 }
00223 
00224 
00225 void WvBdbHashBase::IterBase::next(datum &curkey, datum &curdata)
00226 {
00227     if (!bdbhash.isok()) return;
00228 
00229     // check if this is the first next() after a rewind()
00230     bool first = !curkey.dptr;
00231     datum wanted = { 0, 0 };
00232     if (first)
00233     {
00234         if (rewindto.dptr)
00235         {
00236             curkey = rewindto;
00237             first = false;
00238         }
00239     }
00240     else
00241     {
00242         wanted.dsize = curkey.dsize;
00243         wanted.dptr = malloc(wanted.dsize);
00244         memcpy(wanted.dptr, curkey.dptr, wanted.dsize);
00245     }
00246 
00247     // always seek for the saved cursor we were just passed, to work around
00248     // bugs in libdb1's seq with btrees.  (As a bonus, this gives us multiple
00249     // iterators for free!)
00250     int r = bdbhash.dbf->seq(bdbhash.dbf, (DBT *)&curkey, (DBT *)&curdata,
00251                 first ? R_FIRST : R_CURSOR);
00252     if (r == 1)
00253     {
00254         // current key gone, and none higher left: done
00255         curkey.dptr = curdata.dptr = NULL;
00256     }
00257     else if (r != 0)
00258         bdbhash.seterr(errno);
00259     
00260     else if (!first)
00261     {
00262         while (comparefunc((DBT *)&wanted, (DBT *)&curkey) >= 0)
00263         {
00264             // found the exact key or earlier than requested: move forward one
00265             // (yes, libbdb1 can return a key less than requested, despite
00266             // the documentation's claims!)
00267             // This algorithm definitely makes it so inserting the same key
00268             // more than once doesn't work at all.
00269             r = bdbhash.dbf->seq(bdbhash.dbf, (DBT *)&curkey, (DBT *)&curdata,
00270                                  R_NEXT);
00271 
00272             if (r == 1)
00273             {
00274                 // nothing left?  Fine, we're done
00275                 curkey.dptr = curdata.dptr = NULL;
00276                 break;
00277             }
00278             else if (r != 0)
00279                 bdbhash.seterr(errno);
00280         }
00281     }
00282 
00283     // otherwise, curkey is now sitting on the key after the requested one (or
00284     // the very first key), as expected.  (Also, if rewindto is set it should
00285     // be either filled in with the matching btree data or cleared.)  Unless,
00286     // of course, the whole db is borked.
00287     assert(!bdbhash.isok() || !rewindto.dptr || curkey.dptr != rewindto.dptr);
00288     free(wanted.dptr);
00289 }
00290 
00291 
00292 void WvBdbHashBase::IterBase::xunlink(const datum &curkey)
00293 {
00294     bdbhash.remove(curkey);
00295 }
00296 
00297 
00298 void WvBdbHashBase::IterBase::update(const datum &curkey, const datum &data)
00299 {
00300     bdbhash.add(curkey, data, true);
00301 }
00302 
00303 #endif
00304 

Generated on Wed Dec 15 15:08:10 2004 for WvStreams by  doxygen 1.3.9.1