libmoost
/home/mhx/git/github/libmoost/include/moost/kvds/detail/kvds_bdb_linux.hpp
Go to the documentation of this file.
00001 /* vim:set ts=3 sw=3 sts=3 et: */
00028 
00029 
00030 
00031 
00032 
00033 
00034 
00035 
00036 #ifndef MOOST_KVDS_KVDSBDB_LINUX_HPP__
00037 #define MOOST_KVDS_KVDSBDB_LINUX_HPP__
00038 
00039 #include <cstdlib>
00040 #include <cstring>
00041 #include <stdexcept>
00042 #include <sstream>
00043 #include <limits>
00044 
00045 #include <db_cxx.h>
00046 
00047 #include <boost/shared_ptr.hpp>
00048 
00049 #include "../../compiler/attributes/unused.hpp"
00050 #include "../ikvds.hpp"
00051 
00056 #define KVDSDBDX__(rval, cmd) \
00057    try \
00058    { \
00059       rval = cmd; \
00060    } \
00061    catch(DbException const & e) \
00062    { \
00063       rval = e.get_errno(); \
00064    }
00065 
00066 namespace moost { namespace kvds {
00067 
00068    typedef std::vector<char> byte_array_t;
00069 
00072    template <typename T>
00073    struct ScopedFree
00074    {
00075       ScopedFree() : p(0) {}
00076       ~ScopedFree() { free(p); }
00077 
00078       T * p;
00079    };
00080 
00093 
00095    struct ConstDbt : Dbt
00096    {
00097       ConstDbt(void const * data, size_t size) :
00098          Dbt(const_cast<void *>(data), size)
00099       {
00100       }
00101 
00103       void set_data(void const * data);
00104    };
00105 
00107    struct UsermemDbt : Dbt
00108    {
00109       UsermemDbt(void * data, size_t size) :
00110          Dbt(data, size)
00111       {
00112          set_flags(DB_DBT_USERMEM);
00113       }
00114 
00115       UsermemDbt()
00116       {
00117          set_flags(DB_DBT_USERMEM);
00118       }
00119    };
00120 
00122    struct MallocDbt : Dbt
00123    {
00124       MallocDbt()
00125       {
00126          set_flags(DB_DBT_MALLOC);
00127       }
00128 
00129       ~MallocDbt()
00130       {
00131          free(get_data());
00132       }
00133 
00135       void set_data(void const * data);
00136    };
00137 
00139 
00141    template <DBTYPE dbtypeT>
00142    class KvdsBdb : public IKvds
00143    {
00144    public:
00145       typedef boost::shared_ptr<Db> store_type;
00146 
00147       KvdsBdb() : pitr_(0)
00148       {
00149       }
00150 
00151       ~KvdsBdb()
00152       {
00153          try
00154          {
00155             close();
00156          }
00157          catch(...) { /* ignore */ }
00158       }
00159 
00160       void open (char const dsname [], bool newdb = false)
00161       {
00162          if(pdb_) { throw std::runtime_error("The store is already open"); }
00163 
00164          pdb_.reset(new Db(0, 0)); 
00165 
00166          int const OMODE = (newdb ? (DB_TRUNCATE | DB_CREATE) : DB_CREATE);
00167 
00168          int rval = -1;
00169          KVDSDBDX__(rval, pdb_->open(0, dsname, 0, dbtypeT, OMODE, 0));
00170 
00171          if(rval != 0) { throw std::runtime_error("Failed to open DBD file"); }
00172       }
00173 
00174       void save() { /* nothing to do, fully persisted storage */ }
00175 
00176       void close()
00177       {
00178          int rval unused__ = -1;
00179 
00180          if(pitr_)
00181          {
00182             // Close open cursor
00183             KVDSDBDX__(rval, pitr_->close()); // Failure is not an option!
00184             pitr_ = 0;
00185          }
00186 
00187          if(pdb_)
00188          {
00189             // Close DB
00190             KVDSDBDX__(rval, pdb_->close(0)); // Failure is not an option!
00191             pdb_.reset();
00192          }
00193       }
00194 
00198       store_type & get_store() { return pdb_; }
00199 
00200    private:
00201       void assert_data_store_open() const
00202       {
00203          if(!pdb_) { throw std::runtime_error("Datastore is not open"); }
00204       }
00205 
00206       void set_next_itr()
00207       {
00208             MallocDbt kt;
00209             MallocDbt vt;
00210 
00211             int rval = 0;
00212             KVDSDBDX__(rval, pitr_->get(&kt, &vt, DB_NEXT));
00213 
00214             switch(rval)
00215             {
00216             case 0:
00218                break;
00219             case DB_NOTFOUND:
00220                {
00222                   int rrval unused__ = -1;
00223                   KVDSDBDX__(rrval, pitr_->close());
00224                   pitr_ = 0;
00225                }
00226                break;
00227             default:
00228                throw std::runtime_error("Unable to increment DB cursor");
00229             }
00230       }
00231 
00232    public: // IKvds interface implementation
00233 
00234 
00235       bool put(
00236          void const * pkey, size_t const ksize,
00237          void const * pval, size_t const vsize
00238          )
00239       {
00240          assert_data_store_open();
00241 
00242          ConstDbt kt(pkey, ksize);
00243          ConstDbt vt(pval, vsize);
00244 
00245          int rval = -1;
00246 
00247          KVDSDBDX__(rval, pdb_->put(0, &kt, &vt, 0));
00248 
00249          return 0 == rval;
00250       }
00251 
00252       bool get(
00253          void const * pkey, size_t const ksize,
00254          void * pval, size_t & vsize
00255          )
00256       {
00257          assert_data_store_open();
00258 
00259          ConstDbt kt(pkey, ksize);
00260          kt.set_ulen(ksize);
00261 
00262          // since we can't do a partial read from Bdb and getting size requires a full read
00263          // we might as well just read everything and return up to what was requested.
00264 
00265          MallocDbt vt;
00266          int rval = get(kt, vt);
00267 
00268          if(rval == 0)
00269          {
00270             vsize = std::min((size_t)vt.get_size(), vsize);
00271             memcpy(pval, vt.get_data(), vsize);
00272          }
00273          else { vsize = 0; }
00274 
00275          return 0 == rval;
00276       }
00277 
00278       bool add(
00279          void const * pkey, size_t const ksize,
00280          void const * pval, size_t const vsize
00281          )
00282       {
00283          assert_data_store_open();
00284 
00285          int rval = -1;
00286 
00287          if(vsize > 0)
00288          {
00289             // Unfortunatly, BDB provides no way to just append data values to an existing key
00290             // So we must get the existing value, append the new values and write it back.
00291 
00292             ConstDbt kt(pkey, ksize);
00293             UsermemDbt evt;
00294 
00295             KVDSDBDX__(rval, pdb_->get(0, &kt, &evt, 0));
00296 
00297             switch(rval)
00298             {
00299             case DB_BUFFER_SMALL:
00300                // Item exists so get it, append new data and write back
00301                {
00302                   size_t const esize = evt.get_size();
00303 
00305                   byte_array_t buf(esize + vsize);
00306                   evt.set_data(&buf[0]);
00307                   evt.set_ulen(esize);
00308 
00309                   // Get existing value
00310                   KVDSDBDX__(rval, pdb_->get(0, &kt, &evt, 0));
00311 
00312                   if(0 == rval)
00313                   {
00314                      // Append new value
00315                      memcpy(&buf[esize], pval, vsize);
00316                      evt.set_size(buf.size());
00317                      evt.set_ulen(buf.size());
00318                      KVDSDBDX__(rval, pdb_->put(0, &kt, &evt, 0));
00319                   }
00320                }
00321                break;
00322             case DB_NOTFOUND:
00323                // New item, just add it
00324                ConstDbt vt(pval, vsize);
00325                KVDSDBDX__(rval, pdb_->put(0, &kt, &vt, 0));
00326                break;
00327             }
00328          }
00329          else
00330          {
00331             rval = 0;
00332          }
00333 
00334          return 0 == rval;
00335       }
00336 
00337       bool all(
00338          void const * pkey, size_t const ksize,
00339          void * pval, size_t & vsize
00340          )
00341       {
00342          assert_data_store_open();
00343 
00344          bool found = false;
00345          size_t esize = 0;
00346 
00347          if(siz(pkey, ksize, esize))
00348          {
00349             if(esize <= vsize)
00350             {
00351                found = get(
00352                   pkey, ksize,
00353                   pval, esize
00354                   );
00355             }
00356 
00357             vsize = esize;
00358          }
00359          else { vsize = 0; }
00360 
00361 
00362          return found;
00363       }
00364 
00365       bool xst(
00366          void const * pkey, size_t const ksize
00367          )
00368       {
00369          assert_data_store_open();
00370 
00371          ConstDbt kt(pkey, ksize);
00372 
00373          int rval = -1;
00374 
00375          KVDSDBDX__(rval, pdb_->exists(0, &kt, 0));
00376 
00377          return 0 == rval;
00378       }
00379 
00380       bool del(
00381          void const * pkey, size_t const ksize
00382          )
00383       {
00384          assert_data_store_open();
00385 
00386          ConstDbt kt(pkey, ksize);
00387 
00388          int rval = -1;
00389 
00390          KVDSDBDX__(rval, pdb_->del(0, &kt, 0));
00391 
00392          return 0 == rval;
00393       }
00394 
00395       bool clr()
00396       {
00397          assert_data_store_open();
00398 
00399          int rval = -1;
00400 
00401          unsigned int count = 0;
00402          KVDSDBDX__(rval, pdb_->truncate(0, &count, 0));
00403 
00404          return 0 == rval;
00405       }
00406 
00407 
00408 
00409       bool beg()
00410       {
00411          assert_data_store_open();
00412          if (pitr_ != 0) { pitr_->close(); }
00413          pdb_->cursor(0, &pitr_, 0);
00414 
00415          if(pitr_)
00416          {
00417             set_next_itr();
00418          }
00419 
00420          return pitr_ != 0;
00421       }
00422 
00423       bool nxt(
00424          void * pkey, size_t & ksize
00425          )
00426       {
00427          assert_data_store_open();
00428 
00429          int rval = -1;
00430 
00431          if(pitr_)
00432          {
00433             UsermemDbt kt(pkey, ksize);
00434             kt.set_ulen(ksize);
00435             MallocDbt vt;
00436 
00437             KVDSDBDX__(rval, pitr_->get(&kt, &vt, DB_CURRENT));
00438 
00439             switch(rval)
00440             {
00441             case 0:
00442                set_next_itr();
00443                break;
00444             case DB_BUFFER_SMALL:
00445                ksize = kt.get_size();
00446                break;
00447             }
00448          }
00449          else
00450          {
00451             ksize = 0;
00452          }
00453 
00454          return 0 == rval;
00455       }
00456 
00457       bool end()
00458       {
00459          return 0 == pitr_;
00460       }
00461 
00462       bool siz(
00463          void const * pkey, size_t const ksize,
00464          size_t & vsize
00465          )
00466       {
00467          assert_data_store_open();
00468 
00469          ConstDbt kt(pkey, ksize);
00470          UsermemDbt vt;
00471 
00472          int rval = -1;
00473          KVDSDBDX__(rval, pdb_->get(0, &kt, &vt, 0));
00474 
00475          switch(rval)
00476          {
00477          case DB_BUFFER_SMALL:
00478             vsize = vt.get_size();
00479             rval = 0;
00480             break;
00481          case 0:
00482             vsize = 0;
00483             break;
00484          }
00485 
00486          return 0 == rval;
00487       }
00488 
00489       bool cnt(boost::uint64_t & cnt);
00490 
00491       bool nil(bool & isnil)
00492       {
00493          boost::uint64_t icnt = 0;
00494          bool ok = cnt(icnt);
00495          if(ok) { isnil = (0 == icnt); }
00496          return ok;
00497       }
00498 
00499 private:
00500 
00501    int get(Dbt & kt, Dbt & vt)
00502    {
00503       int rval = -1;
00504       KVDSDBDX__(rval, pdb_->get(0, &kt, &vt, 0));
00505       return rval;
00506    }
00507 
00508    store_type pdb_;
00509       Dbc * pitr_;
00510    };
00511 
00512    // Stats for HASH and BTREE are obtained differently, hence the specialisation
00513    template<>
00514    inline bool KvdsBdb<DB_HASH>::cnt(boost::uint64_t & cnt)
00515    {
00516       assert_data_store_open();
00517 
00518       int rval = -1;
00519 
00520       ScopedFree<DB_HASH_STAT> sf;
00521       KVDSDBDX__(rval, pdb_->stat(0, (void *)&sf.p, 0));
00522 
00523       if(0 == rval && sf.p)
00524       {
00525          cnt = sf.p->hash_nkeys;
00526       }
00527 
00528       return 0 == rval;
00529    }
00530 
00531    template<>
00532    inline bool KvdsBdb<DB_BTREE>::cnt(boost::uint64_t & cnt)
00533    {
00534       assert_data_store_open();
00535 
00536       int rval = -1;
00537 
00538       ScopedFree<DB_BTREE_STAT> sf;
00539       KVDSDBDX__(rval, pdb_->stat(0, (void *)&sf.p, 0));
00540 
00541       if(0 == rval && sf.p)
00542       {
00543          cnt = sf.p->bt_nkeys;
00544       }
00545 
00546       return 0 == rval;
00547    }
00548 
00549    typedef KvdsBdb<DB_HASH>  KvdsBht; 
00550    typedef KvdsBdb<DB_BTREE> KvdsBbt; 
00551 
00552 }}
00553 
00554 #endif // MOOST_KVDS_KVDSBDB_LINUX_HPP__