diff options
author | Kim Kibum <kb0929.kim@samsung.com> | 2012-05-21 17:40:46 +0900 |
---|---|---|
committer | Kim Kibum <kb0929.kim@samsung.com> | 2012-05-21 17:40:46 +0900 |
commit | 2e082c838d2ca750f5daac6dcdabecc22dfd4e46 (patch) | |
tree | 01c1dd87d4cc0b62a655c0d768ff695d2d244728 /stl/dbstl_dbc.h | |
parent | a86e3ca152fb414b376e64c449c201d762e414dd (diff) | |
download | db4-2e082c838d2ca750f5daac6dcdabecc22dfd4e46.tar.gz db4-2e082c838d2ca750f5daac6dcdabecc22dfd4e46.tar.bz2 db4-2e082c838d2ca750f5daac6dcdabecc22dfd4e46.zip |
Upload Tizen:Base source
Diffstat (limited to 'stl/dbstl_dbc.h')
-rw-r--r-- | stl/dbstl_dbc.h | 1327 |
1 files changed, 1327 insertions, 0 deletions
diff --git a/stl/dbstl_dbc.h b/stl/dbstl_dbc.h new file mode 100644 index 0000000..bcf6695 --- /dev/null +++ b/stl/dbstl_dbc.h @@ -0,0 +1,1327 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2009 Oracle. All rights reserved. + * + * $Id$ + */ + +#ifndef _DB_STL_DBC_H +#define _DB_STL_DBC_H + +#include <set> +#include <errno.h> + +#include "dbstl_common.h" +#include "dbstl_dbt.h" +#include "dbstl_exception.h" +#include "dbstl_container.h" +#include "dbstl_resource_manager.h" + +START_NS(dbstl) + +// Forward declarations. +class db_container; +class DbCursorBase; +template<Typename data_dt> +class RandDbCursor; +class DbstlMultipleKeyDataIterator; +class DbstlMultipleRecnoDataIterator; +using std::set; + +///////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////// +// +// LazyDupCursor class template definition. +// +// This class allows us to make a shallow copy on construction. When the +// cursor pointer is first dereferenced a deep copy is made. +// +// The allowed type for BaseType is DbCursor<> and RandDbCursor<> +// The expected usage of this class is: +// 1. Create an iterator in container::begin(), the iterator::pcsr.csr_ptr_ +// points to an object, thus no need to duplicate. +// 2. The iterator is created with default argument, thus the +// iterator::pcsr.csr_ptr_ and dup_src_ is NULL, and this iterator is +// copied using copy constructor for may be many times, but until the +// cursor is really used, no cursor is duplicated. +// +// There is an informing mechanism between an instance of this class and +// its dup_src_ cursor: when that cursor is about to change state, it will +// inform all registered LazyDupCursor "listeners" of the change, so that +// they will duplicate from the cursor before the change, because that +// is the expected cursor state for the listeners. + +template <Typename BaseType> +class LazyDupCursor +{ + // dup_src_ is used by this class internally to duplicate another + // cursor and set to csr_ptr_, and it is assigned in the copy + // constructor from another LazyDupCursor object's csr_ptr_; csr_ptr_ + // is the acutual pointer that is used to perform cursor operations. + // + BaseType *csr_ptr_, *dup_src_; + typedef LazyDupCursor<BaseType> self; + +public: + //////////////////////////////////////////////////////////////////// + // + // Begin public constructors and destructor. + // + inline LazyDupCursor() + { + csr_ptr_ = NULL; + dup_src_ = NULL; + } + + // Used in all iterator types' constructors, dbcptr is created + // solely for this object, and the cursor is not yet opened, so we + // simply assign it to csr_ptr_. + explicit inline LazyDupCursor(BaseType *dbcptr) + { + csr_ptr_ = dbcptr; + // Already have pointer, do not need to duplicate. + dup_src_ = NULL; + } + + // Do not copy to csr_ptr_, shallow copy from dp2.csr_ptr_. + LazyDupCursor(const self& dp2) + { + csr_ptr_ = NULL; + if (dp2.csr_ptr_) + dup_src_ = dp2.csr_ptr_; + else + dup_src_ = dp2.dup_src_; + if (dup_src_) + dup_src_->add_dupper(this); + } + + ~LazyDupCursor() + { + // Not duplicated yet, remove from dup_src_. + if (csr_ptr_ == NULL && dup_src_ != NULL) + dup_src_->erase_dupper(this); + if (csr_ptr_) + delete csr_ptr_;// Delete the cursor. + } + + //////////////////////////////////////////////////////////////////// + + // Deep copy. + inline const self& operator=(const self &dp2) + { + BaseType *dcb; + + dcb = dp2.csr_ptr_ ? dp2.csr_ptr_ : dp2.dup_src_; + this->operator=(dcb); + + return dp2; + } + + // Deep copy. + inline BaseType *operator=(BaseType *dcb) + { + + if (csr_ptr_) { + // Only dup_src_ will inform this, not csr_ptr_. + delete csr_ptr_; + csr_ptr_ = NULL; + } + + if (dcb) + csr_ptr_ = new BaseType(*dcb); + if (dup_src_ != NULL) { + dup_src_->erase_dupper(this); + dup_src_ = NULL; + } + + return dcb; + } + + void set_cursor(BaseType *dbc) + { + assert(dbc != NULL); + if (csr_ptr_) { + // Only dup_src_ will inform this, not csr_ptr_. + delete csr_ptr_; + csr_ptr_ = NULL; + } + + csr_ptr_ = dbc; + if (dup_src_ != NULL) { + dup_src_->erase_dupper(this); + dup_src_ = NULL; + } + } + + // If dup_src_ is informing this object, pass false parameter. + inline BaseType* duplicate(bool erase_dupper = true) + { + assert(dup_src_ != NULL); + if (csr_ptr_) { + // Only dup_src_ will inform this, not csr_ptr_. + delete csr_ptr_; + csr_ptr_ = NULL; + } + csr_ptr_ = new BaseType(*dup_src_); + if (erase_dupper) + dup_src_->erase_dupper(this); + dup_src_ = NULL; + return csr_ptr_; + } + + inline BaseType* operator->() + { + if (csr_ptr_) + return csr_ptr_; + + return duplicate(); + } + + inline operator bool() + { + return csr_ptr_ != NULL; + } + + inline bool operator!() + { + return !csr_ptr_; + } + + inline bool operator==(void *p) + { + return csr_ptr_ == p; + } + + inline BaseType* base_ptr(){ + if (csr_ptr_) + return csr_ptr_; + return duplicate(); + } +}; + + +///////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////// +// +// DbCursorBase class definition. +// +// DbCursorBase is the base class for DbCursor<> class template, this class +// wraps the Berkeley DB cursor, in order for the ResourceManager to close +// the Berkeley DB cursor and set the pointer to null. +// If we don't set the cursor to NULL, the handle could become valid again, +// since Berkeley DB recycles handles. DB STL would then try to use the same +// handle across different instances, which is not supported. +// +// In ResourceManager, whenver a cursor is opened, it stores the +// DbCursorBase* pointer, so that when need to close the cursor, it calls +// DbCursorBase::close() function. +// +class DbCursorBase +{ +protected: + Dbc *csr_; + DbTxn *owner_txn_; + Db *owner_db_; + int csr_status_; + +public: + enum DbcGetSkipOptions{SKIP_KEY, SKIP_DATA, SKIP_NONE}; + inline DbTxn *get_owner_txn() const { return owner_txn_;} + inline void set_owner_txn(DbTxn *otxn) { owner_txn_ = otxn;} + + inline Db *get_owner_db() const { return owner_db_;} + inline void set_owner_db(Db *odb) { owner_db_ = odb;} + + inline Dbc *get_cursor() const { return csr_;} + inline Dbc *&get_cursor_reference() { return csr_;} + inline void set_cursor(Dbc*csr1) + { + if (csr_) + ResourceManager::instance()->remove_cursor(this); + csr_ = csr1; + } + + inline int close() + { + int ret = 0; + + if (csr_ != NULL && (((DBC *)csr_)->flags & DBC_ACTIVE) != 0) { + ret = csr_->close(); + csr_ = NULL; + } + return ret; + } + + DbCursorBase(){ + owner_txn_ = NULL; + owner_db_ = NULL; + csr_ = NULL; + csr_status_ = 0; + } + + DbCursorBase(const DbCursorBase &csrbase) + { + this->operator=(csrbase); + } + + const DbCursorBase &operator=(const DbCursorBase &csrbase) + { + owner_txn_ = csrbase.owner_txn_; + owner_db_ = csrbase.owner_db_; + csr_ = NULL; // Need to call DbCursor<>::dup to duplicate. + csr_status_ = 0; + return csrbase; + } + + virtual ~DbCursorBase() + { + close(); + } +}; // DbCursorBase + +//////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////// +// +// DbCursor class template definition +// +// DbCursor is the connection between Berkeley DB and dbstl container classes +// it is the wrapper class for Dbc* cursor of Berkeley Db, to be used for +// iterator classes of Berkeley DB backed STL container classes. +// Requirement: +// 1. Deep copy using Dbc->dup. +// 2. Dbc*cursor management via ResourceManager class. +// 3. Provide methods to do increment, decrement and advance operations, +// advance is only available for random access iterator from DB_RECNO +// containers. +// + +template<typename key_dt, typename data_dt> +class DbCursor : public DbCursorBase{ +protected: + // Lazy duplication support: store the LazyDupCursor objects which + // will duplicate from this cursor. + typedef LazyDupCursor<DbCursor<key_dt, data_dt> > dupper_t; + typedef LazyDupCursor<RandDbCursor<data_dt> > dupperr_t; + typedef set<LazyDupCursor<DbCursor<key_dt, data_dt> >* > dupset_t; + typedef set<LazyDupCursor<RandDbCursor<data_dt> >* > dupsetr_t; + + set<LazyDupCursor<DbCursor<key_dt, data_dt> >* > sduppers1_; + set<LazyDupCursor<RandDbCursor<data_dt> >* > sduppers2_; + + // We must use DB_DBT_USERMEM for Dbc::get and Db::get if they are + // used in multi-threaded application, so we use key_buf_ and + // data_buf_ data members for get operations, and initialize them + // to use user memory. + Dbt key_buf_, data_buf_; + + // Similar to Berkeley DB C++ API's classes, used to iterate through + // bulk retrieved key/data pairs. + DbstlMultipleKeyDataIterator *multi_itr_; + DbstlMultipleRecnoDataIterator *recno_itr_; + + // Whether to use bulk retrieval. If non-zero, do bulk retrieval, + // bulk buffer size is this member, otherwise not bulk read. + // By default this member is 0. + u_int32_t bulk_retrieval_; + // Whether to use DB_RMW flag in Dbc::get, by default false. + bool rmw_get_; + + // Whether to poll data from cursor's current position on every + // get_current_key/data call. + // Note that curr_key_/curr_data_ members are always maintained + // to contain current k/d value of the pair pointed to by csr_. + // If doing bulk retrieval, this flag is ignored, we will always + // read data from bulk buffer. + bool directdb_get_; + + // Inform LazyDupCursor objects registered in this object to do + // duplication because this cursor is to be changed. + // This function should be called in any function of + // DbCursor and RandDbCursor whenever the cursor is about to change + // state(move/close, etc). + inline void inform_duppers() + { + typename dupset_t::iterator i1; + typename dupsetr_t::iterator i2; + for (i1 = sduppers1_.begin(); i1 != sduppers1_.end(); i1++) + (*i1)->duplicate(false); + for (i2 = sduppers2_.begin(); i2 != sduppers2_.end(); i2++) + (*i2)->duplicate(false); + sduppers1_.clear(); + sduppers2_.clear(); + } + +public: + friend class DataItem; + + // Current key/data pair pointed by "csr_" Dbc*cursor. They are both + // maintained on cursor movement. If directdb_get_ is true, + // they are both refreshed on every get_current{[_key][_data]} call and + // the retrieved key/data pair is returned to user. + DataItem curr_key_; + DataItem curr_data_; + + typedef DbCursor<key_dt, data_dt> self; + + // This function is used by all iterators to do equals comparison. + // Random iterators will also use it to do less than/greater than + // comparisons. + // Internally, the page number difference or index difference is + // returned, so for btree and hash databases, if two cursors point to + // the same key/data pair, we will get 0 returned, meaning they are + // equal; if return value is not 0, it means no more than that they + // they are not equal. We can't assume any order information between + // the two cursors. For recno databases, we use the recno to do less + // than and greater than comparison. So we can get a reliable knowledge + // of the relative position of two iterators from the return value. + int compare(const self *csr2) const{ + int res, ret; + + BDBOP(((DBC *)csr_)->cmp((DBC *)csr_, (DBC *)csr2->csr_, + &res, 0), ret); + return res; + } + + //////////////////////////////////////////////////////////////////// + // + // Add and remove cursor change event listeners. + // + inline void add_dupper(dupper_t *dupper) + { + sduppers1_.insert(dupper); + } + + inline void add_dupper(dupperr_t *dupper) + { + sduppers2_.insert(dupper); + } + + inline void erase_dupper(dupper_t *dup1) + { + sduppers1_.erase(dup1); + } + + inline void erase_dupper(dupperr_t *dup1) + { + sduppers2_.erase(dup1); + } + + //////////////////////////////////////////////////////////////////// + +public: + + inline bool get_rmw() + { + return rmw_get_; + } + + bool set_rmw(bool rmw, DB_ENV *env = NULL ) + { + u_int32_t flag = 0; + DB_ENV *dbenv = NULL; + int ret; + + if (env) + dbenv = env; + else + dbenv = ((DBC*)csr_)->dbenv; + BDBOP(dbenv->get_open_flags(dbenv, &flag), ret); + + // DB_RMW flag requires locking subsystem started. + if (rmw && ((flag & DB_INIT_LOCK) || (flag & DB_INIT_CDB) || + (flag & DB_INIT_TXN))) + rmw_get_ = true; + else + rmw_get_ = false; + return rmw_get_; + } + + // Modify bulk buffer size. Bulk read is enabled when creating an + // iterator, so users later can only modify the bulk buffer size + // to another value, but can't enable/disable bulk read while an + // iterator is already alive. + // Returns true if succeeded, false otherwise. + inline bool set_bulk_buffer(u_int32_t sz) + { + if (bulk_retrieval_ && sz) { + normalize_bulk_bufsize(sz); + bulk_retrieval_ = sz; + return true; + } + + return false; + + } + + inline u_int32_t get_bulk_bufsize() + { + return bulk_retrieval_; + } + + inline void enlarge_dbt(Dbt &d, u_int32_t sz) + { + void *p; + + p = DbstlReAlloc(d.get_data(), sz); + dbstl_assert(p != NULL); + d.set_ulen(sz); + d.set_data(p); + d.set_size(sz); + } + // Move forward or backward, often by 1 key/data pair, we can use + // different flags for Dbc::get function. Then update the key/data + // pair and csr_status_ members. + // + int increment(int flag) + { + int ret = 0; + Dbt &k = key_buf_, &d = data_buf_; + u_int32_t sz, getflags = 0, bulk_bufsz; + + if (csr_ == NULL) + return INVALID_ITERATOR_CURSOR; + curr_key_.reset(); + curr_data_.reset(); + inform_duppers(); + + // Berkeley DB cursor flags are not bitwise set, so we can't + // use bit operations here. + // + if (this->bulk_retrieval_ != 0) + switch (flag) { + case DB_PREV: + case DB_PREV_DUP: + case DB_PREV_NODUP: + case DB_LAST: + case DB_JOIN_ITEM: + case DB_GET_RECNO: + case DB_SET_RECNO: + break; + default: + getflags |= DB_MULTIPLE_KEY; + if (data_buf_.get_ulen() != bulk_retrieval_) + enlarge_dbt(data_buf_, bulk_retrieval_); + break; + } + + if (this->rmw_get_) + getflags |= DB_RMW; + + // Do not use BDBOP or BDBOP2 here because it is likely + // that an iteration will step onto end() position. +retry: ret = csr_->get(&k, &d, flag | getflags); + if (ret == 0) { + if (bulk_retrieval_ && (getflags & DB_MULTIPLE_KEY)) { + // A new retrieval, so both multi_itr_ and + // recno_itr_ must be NULL. + if (((DBC*)csr_)->dbtype == DB_RECNO) { + if (recno_itr_) { + delete recno_itr_; + recno_itr_ = NULL; + } + recno_itr_ = + new DbstlMultipleRecnoDataIterator(d); + } else { + if (multi_itr_) { + delete multi_itr_; + multi_itr_ = NULL; + } + multi_itr_ = new + DbstlMultipleKeyDataIterator(d); + } + } else { + // Non bulk retrieval succeeded. + curr_key_.set_dbt(k, false); + curr_data_.set_dbt(d, false); + limit_buf_size_after_use(); + } + } else if (ret == DB_BUFFER_SMALL) { + // Either the key or data DBTs might trigger a + // DB_KEYSMALL return. Only enlarge the DBT if it + // is actually too small. + if (((sz = d.get_size()) > 0) && (sz > d.get_ulen())) + enlarge_dbt(d, sz); + + if (((sz = k.get_size()) > 0) && (sz > k.get_ulen())) + enlarge_dbt(k, sz); + + goto retry; + } else { + if (ret == DB_NOTFOUND) { + ret = INVALID_ITERATOR_POSITION; + this->curr_key_.reset(); + this->curr_data_.reset(); + } else if (bulk_retrieval_ && + (getflags & DB_MULTIPLE_KEY)){ + BDBOP(((DBC*)csr_)->dbp-> + get_pagesize(((DBC*)csr_)-> + dbp, &bulk_bufsz), ret); + if (bulk_bufsz > d.get_ulen()) {// buf size error + normalize_bulk_bufsize(bulk_bufsz); + bulk_retrieval_ = bulk_bufsz; + enlarge_dbt(d, bulk_bufsz); + goto retry; + } else + throw_bdb_exception( + "DbCursor<>::increment", ret); + } else + throw_bdb_exception( + "DbCursor<>::increment", ret); + } + + csr_status_ = ret; + return ret; + } + + // After each use of key_buf_ and data_buf_, limit their buffer size to + // a reasonable size so that they don't waste a big memory space. + inline void limit_buf_size_after_use() + { + if (bulk_retrieval_) + // Bulk buffer has to be huge, so don't check it. + return; + + if (key_buf_.get_ulen() > DBSTL_MAX_KEY_BUF_LEN) { + key_buf_.set_data(DbstlReAlloc(key_buf_.get_data(), + DBSTL_MAX_KEY_BUF_LEN)); + key_buf_.set_ulen(DBSTL_MAX_KEY_BUF_LEN); + } + if (data_buf_.get_ulen() > DBSTL_MAX_DATA_BUF_LEN) { + data_buf_.set_data(DbstlReAlloc(data_buf_.get_data(), + DBSTL_MAX_DATA_BUF_LEN)); + data_buf_.set_ulen(DBSTL_MAX_DATA_BUF_LEN); + } + } + + // Duplicate this object's cursor and set it to dbc1. + // + inline int dup(DbCursor<key_dt, data_dt>& dbc1) const + { + Dbc* pcsr = 0; + int ret; + + if (csr_ != 0 && csr_->dup(&pcsr, DB_POSITION) == 0) { + dbc1.set_cursor(pcsr); + dbc1.set_owner_db(this->get_owner_db()); + dbc1.set_owner_txn(this->get_owner_txn()); + ResourceManager::instance()->add_cursor( + this->get_owner_db(), &dbc1); + ret = 0; + } else + ret = ITERATOR_DUP_ERROR; + + return ret; + } + +public: + // Open a cursor, do not move it, it is at an invalid position. + // All cursors should be opened using this method. + // + inline int open(db_container *pdbc, int flags) + { + int ret; + + Db *pdb = pdbc->get_db_handle(); + if (pdb == NULL) + return 0; + if (csr_) // Close before open. + return 0; + ret = ResourceManager::instance()-> + open_cursor(this, pdb, flags); + set_rmw(rmw_get_); + this->csr_status_ = ret; + return ret; + } + + // Move Berkeley DB cursor to specified key k, by default use DB_SET, + // but DB_SET_RANGE can and may also be used. + // + int move_to(const key_dt&k, u_int32_t flag = DB_SET) + { + Dbt &d1 = data_buf_; + int ret; + u_int32_t sz; + DataItem k1(k, true); + + if (csr_ == NULL) + return INVALID_ITERATOR_CURSOR; + + curr_key_.reset(); + curr_data_.reset(); + inform_duppers(); + + // It is likely that k is not in db, causing get(DB_SET) to + // fail, we should not throw an exception because of this. + // + if (rmw_get_) + flag |= DB_RMW; +retry: ret = csr_->get(&k1.get_dbt(), &d1, flag); + if (ret == 0) { + curr_key_ = k1; + curr_data_.set_dbt(d1, false); + limit_buf_size_after_use(); + } else if (ret == DB_BUFFER_SMALL) { + sz = d1.get_size(); + assert(sz > 0); + enlarge_dbt(d1, sz); + goto retry; + } else { + if (ret == DB_NOTFOUND) { + ret = INVALID_ITERATOR_POSITION; + // Invalidate current values because it is + // at an invalid position. + this->curr_key_.reset(); + this->curr_data_.reset(); + } else + throw_bdb_exception("DbCursor<>::move_to", ret); + } + + csr_status_ = ret; + return ret; + } + + // Returns the number of keys equal to the current one. + inline size_t count() + { + int ret; + db_recno_t cnt; + + BDBOP2(csr_->count(&cnt, 0), ret, close()); + return (size_t)cnt; + } + + int insert(const key_dt&k, const data_dt& d, int pos = DB_BEFORE) + { + // !!!XXX: + // We do a deep copy of the input data into a local + // variable. Apparently not doing so causes issues + // when using gcc. Even though the put completes prior + // to returning from this function call. + // It would be best to avoid this additional copy. + int ret; + // (k, d) pair may be a temporary pair, so we must copy them. + DataItem k1(k, false), d1(d, false); + + inform_duppers(); + if (pos == DB_AFTER) { + ret = this->csr_->put(&k1.get_dbt(), &d1.get_dbt(), + pos); + // May be using this flag for an empty database, + // because begin() an iterator of an empty db_vector + // equals its end() iterator, so use DB_KEYLAST to + // retry. + // + if (ret == EINVAL || ret == 0) + return ret; + else if (ret) + throw_bdb_exception("DbCursor<>::insert", ret); + } + if (pos == DB_NODUPDATA) + BDBOP3(this->csr_->put(&k1.get_dbt(), &d1.get_dbt(), + pos), ret, DB_KEYEXIST, close()); + else + BDBOP2(this->csr_->put(&k1.get_dbt(), &d1.get_dbt(), + pos), ret, close()); + this->csr_status_ = ret; + if (ret == 0) { + curr_key_ = k1; + curr_data_ = d1; + } + // This cursor points to the new key/data pair now. + return ret; + } + + // Replace current cursor-pointed data item with d. + inline int replace(const data_dt& d) + { + Dbt k1; + int ret; + // !!!XXX: + // We do a deep copy of the input data into a local + // variable. Apparently not doing so causes issues + // when using gcc. Even though the put completes prior + // to returning from this function call. + // It would be best to avoid this additional copy. + // d may be a temporary object, so we must copy it. + DataItem d1(d, false); + + + BDBOP2(this->csr_->put(&k1, &d1.get_dbt(), DB_CURRENT), + ret, close()); + curr_data_ = d1; // Update current data. + + this->csr_status_ = ret; + return ret; + } + + // Remove old key and insert new key-psuodo_data. First insert then + // move to old key and remove it so that the cursor remains at the + // old key's position, according to DB documentation. + // But from practice I can see + // the cursor after delete seems not at old position because a for + // loop iteration exits prematurelly, not all elements are passed. + // + inline int replace_key(const key_dt&k) + { + data_dt d; + key_dt k0; + int ret; + + this->get_current_key_data(k0, d); + if (k0 == k) + return 0; + + DbCursor<key_dt, data_dt> csr2; + this->dup(csr2); + // Delete current, then insert new key/data pair. + ret = csr2.del(); + ret = csr2.insert(k, d, DB_KEYLAST); + this->csr_status_ = ret; + + // Now this->csr_ is sitting on an invalid position, its + // iterator is invalidated. Must first move it to the next + // position before using it. + return ret; + } + + inline int del() + { + int ret; + + inform_duppers(); + BDBOP2(csr_->del(0), ret, close()); + + // By default pos.csr_ will stay at where it was after delete, + // which now is an invalid position. So we need to move to + // next to conform to stl specifications, but we don't move it + // here, iterator::erase should move the iterator itself + // forward. + // + this->csr_status_ = ret; + return ret; + } + + // Make sure the bulk buffer is large enough, and a multiple of 1KB. + // This function may be called prior to cursor initialization, it is + // not possible to verify that the buffer size is a multiple of the + // page size here. + u_int32_t normalize_bulk_bufsize(u_int32_t &bulksz) + { + if (bulksz == 0) + return 0; + + while (bulksz < 16 * sizeof(data_dt)) + bulksz *= 2; + + bulksz = bulksz + 1024 - bulksz % 1024; + + return bulksz; + } + + //////////////////////////////////////////////////////////////////// + // + // Begin public constructors and destructor. + // + explicit DbCursor(u_int32_t b_bulk_retrieval = 0, bool brmw1 = false, + bool directdbget = true) : DbCursorBase(), + curr_key_(sizeof(key_dt)), curr_data_(sizeof(data_dt)) + { + u_int32_t bulksz = sizeof(data_dt); // non-bulk + rmw_get_ = brmw1; + this->bulk_retrieval_ = + normalize_bulk_bufsize(b_bulk_retrieval); + recno_itr_ = NULL; + multi_itr_ = NULL; + + if (bulk_retrieval_) { + if (bulksz <= bulk_retrieval_) + bulksz = bulk_retrieval_; + else { + normalize_bulk_bufsize(bulksz); + bulk_retrieval_ = bulksz; + } + } + key_buf_.set_data(DbstlMalloc(sizeof(key_dt))); + key_buf_.set_ulen(sizeof(key_dt)); + key_buf_.set_flags(DB_DBT_USERMEM); + data_buf_.set_data(DbstlMalloc(bulksz)); + data_buf_.set_ulen(bulksz); + data_buf_.set_flags(DB_DBT_USERMEM); + directdb_get_ = directdbget; + } + + // Copy constructor, duplicate cursor here. + DbCursor(const DbCursor<key_dt, data_dt>& dbc) : + DbCursorBase(dbc), + curr_key_(dbc.curr_key_), curr_data_(dbc.curr_data_) + { + void *pk, *pd; + + dbc.dup(*this); + csr_status_ = dbc.csr_status_; + if (csr_ || dbc.csr_) + this->rmw_get_ = set_rmw(dbc.rmw_get_, + ((DBC*)dbc.csr_)->dbenv); + else + rmw_get_ = dbc.rmw_get_; + + bulk_retrieval_ = dbc.bulk_retrieval_; + + // Now we have to copy key_buf_ and data_buf_ to support + // multiple retrieval. + key_buf_.set_data(pk = DbstlMalloc(dbc.key_buf_.get_ulen())); + key_buf_.set_ulen(dbc.key_buf_.get_ulen()); + key_buf_.set_size(dbc.key_buf_.get_size()); + key_buf_.set_flags(DB_DBT_USERMEM); + memcpy(pk, dbc.key_buf_.get_data(), key_buf_.get_ulen()); + + data_buf_.set_data(pd = DbstlMalloc(dbc.data_buf_.get_ulen())); + data_buf_.set_ulen(dbc.data_buf_.get_ulen()); + data_buf_.set_size(dbc.data_buf_.get_size()); + data_buf_.set_flags(DB_DBT_USERMEM); + memcpy(pd, dbc.data_buf_.get_data(), data_buf_.get_ulen()); + if (dbc.recno_itr_) { + recno_itr_ = new DbstlMultipleRecnoDataIterator( + data_buf_); + recno_itr_->set_pointer(dbc.recno_itr_->get_pointer()); + } else + recno_itr_ = NULL; + if (dbc.multi_itr_) { + multi_itr_ = new DbstlMultipleKeyDataIterator( + data_buf_); + multi_itr_->set_pointer(dbc.multi_itr_->get_pointer()); + + } else + multi_itr_ = NULL; + + directdb_get_ = dbc.directdb_get_; + + // Do not copy sduppers, they are private to each DbCursor<> + // object. + } + + virtual ~DbCursor() + { + close(); // Call close() ahead of freeing following buffers. + free(key_buf_.get_data()); + free(data_buf_.get_data()); + if (multi_itr_) + delete multi_itr_; + if (recno_itr_) + delete recno_itr_; + } + + //////////////////////////////////////////////////////////////////// + + const DbCursor<key_dt, data_dt>& operator= + (const DbCursor<key_dt, data_dt>& dbc) + { + void *pk; + u_int32_t ulen; + + DbCursorBase::operator =(dbc); + dbc.dup(*this); + curr_key_ = dbc.curr_key_; + curr_data_ = dbc.curr_data_; + rmw_get_ = dbc.rmw_get_; + this->bulk_retrieval_ = dbc.bulk_retrieval_; + this->directdb_get_ = dbc.directdb_get_; + // Now we have to copy key_buf_ and data_buf_ to support + // bulk retrieval. + key_buf_.set_data(pk = DbstlReAlloc(key_buf_.get_data(), + ulen = dbc.key_buf_.get_ulen())); + key_buf_.set_ulen(ulen); + key_buf_.set_size(dbc.key_buf_.get_size()); + key_buf_.set_flags(DB_DBT_USERMEM); + memcpy(pk, dbc.key_buf_.get_data(), ulen); + + data_buf_.set_data(pk = DbstlReAlloc(key_buf_.get_data(), + ulen = dbc.key_buf_.get_ulen())); + data_buf_.set_ulen(ulen); + data_buf_.set_size(dbc.data_buf_.get_size()); + data_buf_.set_flags(DB_DBT_USERMEM); + memcpy(pk, dbc.key_buf_.get_data(), ulen); + + if (dbc.recno_itr_) { + if (recno_itr_) { + delete recno_itr_; + recno_itr_ = NULL; + } + recno_itr_ = new DbstlMultipleRecnoDataIterator( + data_buf_); + recno_itr_->set_pointer(dbc.recno_itr_->get_pointer()); + } else if (recno_itr_) { + delete recno_itr_; + recno_itr_ = NULL; + } + + if (dbc.multi_itr_) { + if (multi_itr_) { + delete multi_itr_; + multi_itr_ = NULL; + } + multi_itr_ = new DbstlMultipleKeyDataIterator( + data_buf_); + multi_itr_->set_pointer(dbc.multi_itr_->get_pointer()); + + } else if (multi_itr_) { + delete multi_itr_; + multi_itr_ = NULL; + } + + return dbc; + // Do not copy sduppers, they are private to each DbCursor<> + // object. + + } + + // Move Dbc*cursor to next position. If doing bulk read, read from + // the bulk buffer. If bulk buffer exhausted, do another bulk read + // from database, and then read from the bulk buffer. Quit if no + // more data in database. + // + int next(int flag = DB_NEXT) + { + Dbt k, d; + db_recno_t recno; + int ret; + +retry: if (bulk_retrieval_) { + if (multi_itr_) { + if (multi_itr_->next(k, d)) { + curr_key_.set_dbt(k, false); + curr_data_.set_dbt(d, false); + return 0; + } else { + delete multi_itr_; + multi_itr_ = NULL; + } + } + if (recno_itr_) { + if (recno_itr_->next(recno, d)) { + curr_key_.set_dbt(k, false); + curr_data_.set_dbt(d, false); + return 0; + } else { + delete recno_itr_; + recno_itr_ = NULL; + } + } + } + ret = increment(flag); + if (bulk_retrieval_ && ret == 0) + goto retry; + return ret; + } + + inline int prev(int flag = DB_PREV) + { + return increment(flag); + } + + // Move Dbc*cursor to first element. If doing bulk read, read data + // from bulk buffer. + int first() + { + Dbt k, d; + db_recno_t recno; + int ret; + + ret = increment(DB_FIRST); + if (bulk_retrieval_) { + if (multi_itr_) { + if (multi_itr_->next(k, d)) { + curr_key_.set_dbt(k, false); + curr_data_.set_dbt(d, false); + return 0; + } else { + delete multi_itr_; + multi_itr_ = NULL; + } + } + if (recno_itr_) { + if (recno_itr_->next(recno, d)) { + curr_key_.set_dbt(k, false); + curr_data_.set_dbt(d, false); + return 0; + } else { + delete recno_itr_; + recno_itr_ = NULL; + } + } + } + + return ret; + } + + inline int last() + { + return increment(DB_LAST); + } + + // Get current key/data pair, shallow copy. Return 0 on success, + // -1 if no data. + inline int get_current_key_data(key_dt&k, data_dt&d) + { + if (directdb_get_) + update_current_key_data_from_db( + DbCursorBase::SKIP_NONE); + if (curr_key_.get_data(k) == 0 && curr_data_.get_data(d) == 0) + return 0; + else + return INVALID_KEY_DATA; + } + + // Get current data, shallow copy. Return 0 on success, -1 if no data. + inline int get_current_data(data_dt&d) + { + if (directdb_get_) + update_current_key_data_from_db(DbCursorBase::SKIP_KEY); + if (curr_data_.get_data(d) == 0) + return 0; + else + return INVALID_KEY_DATA; + } + + // Get current key, shallow copy. Return 0 on success, -1 if no data. + inline int get_current_key(key_dt&k) + { + if (directdb_get_) + update_current_key_data_from_db( + DbCursorBase::SKIP_DATA); + if (curr_key_.get_data(k) == 0) + return 0; + else + return INVALID_KEY_DATA; + } + + inline void close() + { + if (csr_) { + inform_duppers(); + ResourceManager::instance()->remove_cursor(this); + } + csr_ = NULL; + } + + // Parameter skipkd specifies skip retrieving key or data: + // If 0, don't skip, retrieve both; + // If 1, skip retrieving key; + // If 2, skip retrieving data. + // Do not poll from db again if doing bulk retrieval. + void update_current_key_data_from_db(DbcGetSkipOptions skipkd) { + int ret; + u_int32_t sz, sz1, kflags = DB_DBT_USERMEM, + dflags = DB_DBT_USERMEM; + // Do not poll from db again if doing bulk retrieval. + if (this->bulk_retrieval_) + return; + if (this->csr_status_ != 0) { + curr_key_.reset(); + curr_data_.reset(); + return; + } + + // We will modify flags if skip key or data, so cache old + // value and set it after get calls. + if (skipkd != DbCursorBase::SKIP_NONE) { + kflags = key_buf_.get_flags(); + dflags = data_buf_.get_flags(); + } + if (skipkd == DbCursorBase::SKIP_KEY) { + key_buf_.set_dlen(0); + key_buf_.set_flags(DB_DBT_PARTIAL | DB_DBT_USERMEM); + } + + if (skipkd == DbCursorBase::SKIP_DATA) { + data_buf_.set_dlen(0); + data_buf_.set_flags(DB_DBT_PARTIAL | DB_DBT_USERMEM); + } +retry: ret = csr_->get(&key_buf_, &data_buf_, DB_CURRENT); + if (ret == 0) { + if (skipkd != DbCursorBase::SKIP_KEY) + curr_key_ = key_buf_; + if (skipkd != DbCursorBase::SKIP_DATA) + curr_data_ = data_buf_; + limit_buf_size_after_use(); + } else if (ret == DB_BUFFER_SMALL) { + if ((sz = key_buf_.get_size()) > 0) + enlarge_dbt(key_buf_, sz); + if ((sz1 = data_buf_.get_size()) > 0) + enlarge_dbt(data_buf_, sz1); + if (sz == 0 && sz1 == 0) + THROW0(InvalidDbtException); + goto retry; + } else { + if (skipkd != DbCursorBase::SKIP_NONE) { + key_buf_.set_flags(kflags); + data_buf_.set_flags(dflags); + } + throw_bdb_exception( + "DbCursor<>::update_current_key_data_from_db", ret); + } + + if (skipkd != DbCursorBase::SKIP_NONE) { + key_buf_.set_flags(kflags); + data_buf_.set_flags(dflags); + } + } +}; // DbCursor<> + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// +// RandDbCursor class template definition +// +// RandDbCursor is a random accessible cursor wrapper for use by +// db_vector_iterator, it derives from DbCursor<> class. It has a fixed key +// data type, which is index_type. +// +typedef db_recno_t index_type; +template<Typename data_dt> +class RandDbCursor : public DbCursor<index_type, data_dt> +{ +protected: + friend class DataItem; + typedef ssize_t difference_type; +public: + typedef RandDbCursor<data_dt> self; + typedef DbCursor<index_type, data_dt> base; + + // Return current csr_ pointed element's index in recno database + // (i.e. the index starting from 1). csr_ must be open and + // point to an existing key/data pair. + // + inline index_type get_current_index() const + { + index_type ndx; + + if (this->directdb_get_) + ((self *)this)->update_current_key_data_from_db( + DbCursorBase::SKIP_DATA); + this->curr_key_.get_data(ndx); + return ndx; + } + + inline int compare(const self *csr2) const{ + index_type i1, i2; + + i1 = this->get_current_index(); + i2 = csr2->get_current_index(); + return i1 - i2; + } + + // Insert data d before/after current position. + int insert(const data_dt& d, int pos = DB_BEFORE){ + int k = 1, ret; + //data_dt dta; + + // Inserting into empty db, must set key to 1. + if (pos == DB_KEYLAST) + k = 1; + + ret = base::insert(k, d, pos); + + // Inserting into a empty db using begin() itr, so flag is + // DB_AFTER and surely failed, so change to use DB_KEYLAST + // and try again. + if (ret == EINVAL) { + k = 1; + pos = DB_KEYLAST; + ret = base::insert(k, d, pos); + } + this->csr_status_ = ret; + return ret; + } + + /* + * Move the cursor n positions, if reaches the beginning or end, + * returns DB_NOTFOUND. + */ + int advance(difference_type n) + { + int ret = 0; + index_type indx; + u_int32_t sz, flags = 0; + + indx = this->get_current_index(); + if (n == 0) + return 0; + + index_type i = (index_type)n; + indx += i; + + if (n < 0 && indx < 1) { // Index in recno db starts from 1. + + ret = INVALID_ITERATOR_POSITION; + return ret; + } + this->inform_duppers(); + + // Do a search to determine whether new position is valid. + Dbt k, &d = this->data_buf_; + + + k.set_data(&indx); + k.set_size(sizeof(indx)); + if (this->rmw_get_) + flags |= DB_RMW; + +retry: if (this->csr_ && + ((ret = this->csr_->get(&k, &d, DB_SET)) == DB_NOTFOUND)) { + this->csr_status_ = ret = INVALID_ITERATOR_POSITION; + this->curr_key_.reset(); + this->curr_data_.reset(); + } else if (ret == DB_BUFFER_SMALL) { + sz = d.get_size(); + assert(sz > 0); + this->enlarge_dbt(d, sz); + goto retry; + } else if (ret == 0) { + this->curr_key_.set_dbt(k, false); + this->curr_data_.set_dbt(d, false); + this->limit_buf_size_after_use(); + } else + throw_bdb_exception("RandDbCursor<>::advance", ret); + this->csr_status_ = ret; + return ret; + } + + // Return the last index of recno db (index starting from 1), + // it will also move the underlying cursor to last key/data pair. + // + inline index_type last_index() + { + int ret; + + ret = this->last(); + if (ret) + return 0;// Invalid position. + else + return get_current_index(); + } + + explicit RandDbCursor(u_int32_t b_bulk_retrieval = 0, + bool b_rmw1 = false, bool directdbget = true) + : base(b_bulk_retrieval, b_rmw1, directdbget) + { + } + + RandDbCursor(const RandDbCursor<data_dt>& rdbc) : base(rdbc) + { + } + + explicit RandDbCursor(Dbc* csr1, int posidx = 0) : base(csr1) + { + } + + virtual ~RandDbCursor() + { + } + +}; // RandDbCursor<> + +END_NS //ns dbstl + +#endif // !_DB_STL_DBC_H |