libmoost
|
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__