1 /*-
2  * Copyright (c) 2009, 2020 Oracle and/or its affiliates.  All rights reserved.
3  *
4  * See the file LICENSE for license information.
5  *
6  * $Id$
7  */
8 
9 #ifndef _DB_STL_DBC_H
10 #define _DB_STL_DBC_H
11 
12 #include <errno.h>
13 
14 #include <set>
15 
16 #include "dbstl_common.h"
17 #include "dbstl_dbt.h"
18 #include "dbstl_exception.h"
19 #include "dbstl_container.h"
20 #include "dbstl_resource_manager.h"
21 
22 START_NS(dbstl)
23 
24 // Forward declarations.
25 class db_container;
26 class DbCursorBase;
27 template<Typename data_dt>
28 class RandDbCursor;
29 class DbstlMultipleKeyDataIterator;
30 class DbstlMultipleRecnoDataIterator;
31 using std::set;
32 
33 /////////////////////////////////////////////////////////////////////
34 /////////////////////////////////////////////////////////////////////
35 //
36 // LazyDupCursor class template definition.
37 //
38 // This class allows us to make a shallow copy on construction. When the
39 // cursor pointer is first dereferenced a deep copy is made.
40 //
41 // The allowed type for BaseType is DbCursor<> and RandDbCursor<>
42 // The expected usage of this class is:
43 // 1. Create an iterator in container::begin(), the iterator::pcsr.csr_ptr_
44 // points to an object, thus no need to duplicate.
45 // 2. The iterator is created with default argument, thus the
46 // iterator::pcsr.csr_ptr_ and dup_src_ is NULL, and this iterator is
47 // copied using copy constructor for may be many times, but until the
48 // cursor is really used, no cursor is duplicated.
49 //
50 // There is an informing mechanism between an instance of this class and
51 // its dup_src_ cursor: when that cursor is about to change state, it will
52 // inform all registered LazyDupCursor "listeners" of the change, so that
53 // they will duplicate from the cursor before the change, because that
54 // is the expected cursor state for the listeners.
55 
56 template <Typename BaseType>
57 class LazyDupCursor
58 {
59 	// dup_src_ is used by this class internally to duplicate another
60 	// cursor and set to csr_ptr_, and it is assigned in the copy
61 	// constructor from another LazyDupCursor object's csr_ptr_; csr_ptr_
62 	// is the acutual pointer that is used to perform cursor operations.
63 	//
64 	BaseType *csr_ptr_, *dup_src_;
65 	typedef LazyDupCursor<BaseType> self;
66 
67 public:
68 	////////////////////////////////////////////////////////////////////
69 	//
70 	// Begin public constructors and destructor.
71 	//
LazyDupCursor()72 	inline LazyDupCursor()
73 	{
74 		csr_ptr_ = NULL;
75 		dup_src_ = NULL;
76 	}
77 
78 	// Used in all iterator types' constructors, dbcptr is created
79 	// solely for this object, and the cursor is not yet opened, so we
80 	// simply assign it to csr_ptr_.
LazyDupCursor(BaseType * dbcptr)81 	explicit inline LazyDupCursor(BaseType *dbcptr)
82 	{
83 		csr_ptr_ = dbcptr;
84 		// Already have pointer, do not need to duplicate.
85 		dup_src_ = NULL;
86 	}
87 
88 	// Do not copy to csr_ptr_, shallow copy from dp2.csr_ptr_.
LazyDupCursor(const self & dp2)89 	LazyDupCursor(const self& dp2)
90 	{
91 		csr_ptr_ = NULL;
92 		if (dp2.csr_ptr_)
93 			dup_src_ = dp2.csr_ptr_;
94 		else
95 			dup_src_ = dp2.dup_src_;
96 		if (dup_src_)
97 			dup_src_->add_dupper(this);
98 	}
99 
~LazyDupCursor()100 	~LazyDupCursor()
101 	{
102 		// Not duplicated yet, remove from dup_src_.
103 		if (csr_ptr_ == NULL && dup_src_ != NULL)
104 			dup_src_->erase_dupper(this);
105 		if (csr_ptr_)
106 			delete csr_ptr_;// Delete the cursor.
107 	}
108 
109 	////////////////////////////////////////////////////////////////////
110 
111 	// Deep copy.
112 	inline const self& operator=(const self &dp2)
113 	{
114 		BaseType *dcb;
115 
116 		dcb = dp2.csr_ptr_ ? dp2.csr_ptr_ : dp2.dup_src_;
117 		this->operator=(dcb);
118 
119 		return dp2;
120 	}
121 
122 	// Deep copy.
123 	inline BaseType *operator=(BaseType *dcb)
124 	{
125 
126 		if (csr_ptr_) {
127 			// Only dup_src_ will inform this, not csr_ptr_.
128 			delete csr_ptr_;
129 			csr_ptr_ = NULL;
130 		}
131 
132 		if (dcb)
133 			csr_ptr_ = new BaseType(*dcb);
134 		if (dup_src_ != NULL) {
135 			dup_src_->erase_dupper(this);
136 			dup_src_ = NULL;
137 		}
138 
139 		return dcb;
140 	}
141 
set_cursor(BaseType * dbc)142 	void set_cursor(BaseType *dbc)
143 	{
144 		assert(dbc != NULL);
145 		if (csr_ptr_) {
146 			// Only dup_src_ will inform this, not csr_ptr_.
147 			delete csr_ptr_;
148 			csr_ptr_ = NULL;
149 		}
150 
151 		csr_ptr_ = dbc;
152 		if (dup_src_ != NULL) {
153 			dup_src_->erase_dupper(this);
154 			dup_src_ = NULL;
155 		}
156 	}
157 
158 	// If dup_src_ is informing this object, pass false parameter.
159 	inline BaseType* duplicate(bool erase_dupper = true)
160 	{
161 		assert(dup_src_ != NULL);
162 		if (csr_ptr_) {
163 			// Only dup_src_ will inform this, not csr_ptr_.
164 			delete csr_ptr_;
165 			csr_ptr_ = NULL;
166 		}
167 		csr_ptr_ = new BaseType(*dup_src_);
168 		if (erase_dupper)
169 			dup_src_->erase_dupper(this);
170 		dup_src_ = NULL;
171 		return csr_ptr_;
172 	}
173 
174 	inline BaseType* operator->()
175 	{
176 		if (csr_ptr_)
177 			return csr_ptr_;
178 
179 		return duplicate();
180 	}
181 
182 	inline operator bool()
183 	{
184 		return csr_ptr_ != NULL;
185 	}
186 
187 	inline bool operator!()
188 	{
189 		return !csr_ptr_;
190 	}
191 
192 	inline bool operator==(void *p)
193 	{
194 		return csr_ptr_ == p;
195 	}
196 
base_ptr()197 	inline BaseType* base_ptr(){
198 		if (csr_ptr_)
199 			return csr_ptr_;
200 		return duplicate();
201 	}
202 };
203 
204 
205 /////////////////////////////////////////////////////////////////////////
206 /////////////////////////////////////////////////////////////////////////
207 //
208 // DbCursorBase class definition.
209 //
210 // DbCursorBase is the base class for DbCursor<> class template, this class
211 // wraps the Berkeley DB cursor, in order for the ResourceManager to close
212 // the Berkeley DB cursor and set the pointer to null.
213 // If we don't set the cursor to NULL, the handle could become valid again,
214 // since Berkeley DB recycles handles. DB STL would then try to use the same
215 // handle across different instances, which is not supported.
216 //
217 // In ResourceManager, whenver a cursor is opened, it stores the
218 // DbCursorBase* pointer, so that when need to close the cursor, it calls
219 // DbCursorBase::close() function.
220 //
221 class DbCursorBase
222 {
223 protected:
224 	Dbc *csr_;
225 	DbTxn *owner_txn_;
226 	Db *owner_db_;
227 	int csr_status_;
228 
229 public:
230 	enum DbcGetSkipOptions{SKIP_KEY, SKIP_DATA, SKIP_NONE};
get_owner_txn()231 	inline DbTxn *get_owner_txn() const { return owner_txn_;}
set_owner_txn(DbTxn * otxn)232 	inline void set_owner_txn(DbTxn *otxn) { owner_txn_ = otxn;}
233 
get_owner_db()234 	inline Db *get_owner_db() const { return owner_db_;}
set_owner_db(Db * odb)235 	inline void set_owner_db(Db *odb) { owner_db_ = odb;}
236 
get_cursor()237 	inline Dbc *get_cursor()  const { return csr_;}
get_cursor_reference()238 	inline Dbc *&get_cursor_reference() { return csr_;}
set_cursor(Dbc * csr1)239 	inline void set_cursor(Dbc*csr1)
240 	{
241 		if (csr_)
242 			ResourceManager::instance()->remove_cursor(this);
243 		csr_ = csr1;
244 	}
245 
close()246 	inline int close()
247 	{
248 		int ret = 0;
249 
250 		if (csr_ != NULL && (((DBC *)csr_)->flags & DBC_ACTIVE) != 0) {
251 			ret = csr_->close();
252 			csr_ = NULL;
253 		}
254 		return ret;
255 	}
256 
DbCursorBase()257 	DbCursorBase(){
258 		owner_txn_ = NULL;
259 		owner_db_ = NULL;
260 		csr_ = NULL;
261 		csr_status_ = 0;
262 	}
263 
DbCursorBase(const DbCursorBase & csrbase)264 	DbCursorBase(const DbCursorBase &csrbase)
265 	{
266 		this->operator=(csrbase);
267 	}
268 
269 	const DbCursorBase &operator=(const DbCursorBase &csrbase)
270 	{
271 		owner_txn_ = csrbase.owner_txn_;
272 		owner_db_ = csrbase.owner_db_;
273 		csr_ = NULL; // Need to call DbCursor<>::dup to duplicate.
274 		csr_status_ = 0;
275 		return csrbase;
276 	}
277 
~DbCursorBase()278 	virtual ~DbCursorBase()
279 	{
280 		close();
281 	}
282 }; // DbCursorBase
283 
284 ////////////////////////////////////////////////////////////////////////
285 ///////////////////////////////////////////////////////////////////////
286 //
287 // DbCursor class template definition
288 //
289 // DbCursor is the connection between Berkeley DB and dbstl container classes
290 // it is the wrapper class for Dbc* cursor of Berkeley Db, to be used for
291 // iterator classes of Berkeley DB backed STL container classes.
292 // Requirement:
293 // 1. Deep copy using Dbc->dup.
294 // 2. Dbc*cursor management via ResourceManager class.
295 // 3. Provide methods to do increment, decrement and advance operations,
296 //    advance is only available for random access iterator from DB_RECNO
297 //    containers.
298 //
299 
300 template<typename key_dt, typename data_dt>
301 class DbCursor : public DbCursorBase{
302 protected:
303 	// Lazy duplication support: store the LazyDupCursor objects which
304 	// will duplicate from this cursor.
305 	typedef LazyDupCursor<DbCursor<key_dt, data_dt> > dupper_t;
306 	typedef LazyDupCursor<RandDbCursor<data_dt> > dupperr_t;
307 	typedef set<LazyDupCursor<DbCursor<key_dt, data_dt> >* > dupset_t;
308 	typedef set<LazyDupCursor<RandDbCursor<data_dt> >* > dupsetr_t;
309 
310 	set<LazyDupCursor<DbCursor<key_dt, data_dt> >* > sduppers1_;
311 	set<LazyDupCursor<RandDbCursor<data_dt> >* > sduppers2_;
312 
313 	// We must use DB_DBT_USERMEM for Dbc::get and Db::get if they are
314 	// used in multi-threaded application, so we use key_buf_ and
315 	// data_buf_ data members for get operations, and initialize them
316 	// to use user memory.
317 	Dbt key_buf_, data_buf_;
318 
319 	// Similar to Berkeley DB C++ API's classes, used to iterate through
320 	// bulk retrieved key/data pairs.
321 	DbstlMultipleKeyDataIterator *multi_itr_;
322 	DbstlMultipleRecnoDataIterator *recno_itr_;
323 
324 	// Whether to use bulk retrieval. If non-zero, do bulk retrieval,
325 	// bulk buffer size is this member, otherwise not bulk read.
326 	// By default this member is 0.
327 	u_int32_t bulk_retrieval_;
328 	// Whether to use DB_RMW flag in Dbc::get, by default false.
329 	bool rmw_get_;
330 
331 	// Whether to poll data from cursor's current position on every
332 	// get_current_key/data call.
333 	// Note that curr_key_/curr_data_ members are always maintained
334 	// to contain current k/d value of the pair pointed to by csr_.
335 	// If doing bulk retrieval, this flag is ignored, we will always
336 	// read data from bulk buffer.
337 	bool directdb_get_;
338 
339 	// Inform LazyDupCursor objects registered in this object to do
340 	// duplication because this cursor is to be changed.
341 	// This function should be called in any function of
342 	// DbCursor and RandDbCursor whenever the cursor is about to change
343 	// state(move/close, etc).
inform_duppers()344 	inline void inform_duppers()
345 	{
346 		typename dupset_t::iterator i1;
347 		typename dupsetr_t::iterator i2;
348 		for (i1 = sduppers1_.begin(); i1 != sduppers1_.end(); i1++)
349 			(*i1)->duplicate(false);
350 		for (i2 = sduppers2_.begin(); i2 != sduppers2_.end(); i2++)
351 			(*i2)->duplicate(false);
352 		sduppers1_.clear();
353 		sduppers2_.clear();
354 	}
355 
356 public:
357 	friend class DataItem;
358 
359 	// Current key/data pair pointed by "csr_" Dbc*cursor. They are both
360 	// maintained on cursor movement. If directdb_get_ is true,
361 	// they are both refreshed on every get_current{[_key][_data]} call and
362 	// the retrieved key/data pair is returned to user.
363 	DataItem curr_key_;
364 	DataItem curr_data_;
365 
366 	typedef DbCursor<key_dt, data_dt> self;
367 
368 	// This function is used by all iterators to do equals comparison.
369 	// Random iterators will also use it to do less than/greater than
370 	// comparisons.
371 	// Internally, the page number difference or index difference is
372 	// returned, so for btree and hash databases, if two cursors point to
373 	// the same key/data pair, we will get 0 returned, meaning they are
374 	// equal; if return value is not 0, it means no more than that they
375 	// they are not equal. We can't assume any order information between
376 	// the two cursors. For recno databases, we use the recno to do less
377 	// than and greater than comparison. So we can get a reliable knowledge
378 	// of the relative position of two iterators from the return value.
compare(const self * csr2)379 	int compare(const self *csr2) const{
380 		int res, ret;
381 
382 		BDBOP(((DBC *)csr_)->cmp((DBC *)csr_, (DBC *)csr2->csr_,
383 		    &res, 0), ret);
384 		return res;
385 	}
386 
387 	////////////////////////////////////////////////////////////////////
388 	//
389 	// Add and remove cursor change event listeners.
390 	//
add_dupper(dupper_t * dupper)391 	inline void add_dupper(dupper_t *dupper)
392 	{
393 		sduppers1_.insert(dupper);
394 	}
395 
add_dupper(dupperr_t * dupper)396 	inline void add_dupper(dupperr_t *dupper)
397 	{
398 		sduppers2_.insert(dupper);
399 	}
400 
erase_dupper(dupper_t * dup1)401 	inline void erase_dupper(dupper_t *dup1)
402 	{
403 		sduppers1_.erase(dup1);
404 	}
405 
erase_dupper(dupperr_t * dup1)406 	inline void erase_dupper(dupperr_t *dup1)
407 	{
408 		sduppers2_.erase(dup1);
409 	}
410 
411 	////////////////////////////////////////////////////////////////////
412 
413 public:
414 
get_rmw()415 	inline bool get_rmw()
416 	{
417 		return rmw_get_;
418 	}
419 
420 	bool set_rmw(bool rmw, DB_ENV *env = NULL )
421 	{
422 		u_int32_t flag = 0;
423 		DB_ENV *dbenv = NULL;
424 		int ret;
425 
426 		if (env)
427 			dbenv = env;
428 		else
429 			dbenv = ((DBC*)csr_)->dbenv;
430 		BDBOP(dbenv->get_open_flags(dbenv, &flag), ret);
431 
432 		// DB_RMW flag requires locking subsystem started.
433 		if (rmw && ((flag & DB_INIT_LOCK) || (flag & DB_INIT_CDB) ||
434 		    (flag & DB_INIT_TXN)))
435 			rmw_get_ = true;
436 		else
437 			rmw_get_ = false;
438 		return rmw_get_;
439 	}
440 
441 	// Modify bulk buffer size. Bulk read is enabled when creating an
442 	// iterator, so users later can only modify the bulk buffer size
443 	// to another value, but can't enable/disable bulk read while an
444 	// iterator is already alive.
445 	// Returns true if succeeded, false otherwise.
set_bulk_buffer(u_int32_t sz)446 	inline bool set_bulk_buffer(u_int32_t sz)
447 	{
448 		if (bulk_retrieval_ && sz) {
449 			normalize_bulk_bufsize(sz);
450 			bulk_retrieval_ = sz;
451 			return true;
452 		}
453 
454 		return false;
455 
456 	}
457 
get_bulk_bufsize()458 	inline u_int32_t get_bulk_bufsize()
459 	{
460 		return bulk_retrieval_;
461 	}
462 
enlarge_dbt(Dbt & d,u_int32_t sz)463 	inline void enlarge_dbt(Dbt &d, u_int32_t sz)
464 	{
465 		void *p;
466 
467 		p = DbstlReAlloc(d.get_data(), sz);
468 		dbstl_assert(p != NULL);
469 		d.set_ulen(sz);
470 		d.set_data(p);
471 		d.set_size(sz);
472 	}
473 	// Move forward or backward, often by 1 key/data pair, we can use
474 	// different flags for Dbc::get function. Then update the key/data
475 	// pair and csr_status_ members.
476 	//
increment(int flag)477 	int increment(int flag)
478 	{
479 		int ret = 0;
480 		Dbt &k = key_buf_, &d = data_buf_;
481 		u_int32_t sz, getflags = 0, bulk_bufsz;
482 
483 		if (csr_ == NULL)
484 			return INVALID_ITERATOR_CURSOR;
485 		curr_key_.reset();
486 		curr_data_.reset();
487 		inform_duppers();
488 
489 		// Berkeley DB cursor flags are not bitwise set, so we can't
490 		// use bit operations here.
491 		//
492 		if (this->bulk_retrieval_ != 0)
493 			switch (flag) {
494 			case DB_PREV:
495 			case DB_PREV_DUP:
496 			case DB_PREV_NODUP:
497 			case DB_LAST:
498 			case DB_JOIN_ITEM:
499 			case DB_GET_RECNO:
500 			case DB_SET_RECNO:
501 				break;
502 			default:
503 				getflags |= DB_MULTIPLE_KEY;
504 				if (data_buf_.get_ulen() != bulk_retrieval_)
505 					enlarge_dbt(data_buf_, bulk_retrieval_);
506 				break;
507 			}
508 
509 		if (this->rmw_get_)
510 			getflags |= DB_RMW;
511 
512 		// Do not use BDBOP or BDBOP2 here because it is likely
513 		// that an iteration will step onto end() position.
514 retry:		ret = csr_->get(&k, &d, flag | getflags);
515 		if (ret == 0) {
516 			if (bulk_retrieval_ && (getflags & DB_MULTIPLE_KEY)) {
517 				// A new retrieval, so both multi_itr_ and
518 				// recno_itr_ must be NULL.
519 				if (((DBC*)csr_)->dbtype == DB_RECNO) {
520 					if (recno_itr_) {
521 						delete recno_itr_;
522 						recno_itr_ = NULL;
523 					}
524 					recno_itr_ =
525 				    new DbstlMultipleRecnoDataIterator(d);
526 				} else {
527 					if (multi_itr_) {
528 						delete multi_itr_;
529 						multi_itr_ = NULL;
530 					}
531 					multi_itr_ = new
532 					    DbstlMultipleKeyDataIterator(d);
533 				}
534 			} else {
535 				// Non bulk retrieval succeeded.
536 				curr_key_.set_dbt(k, false);
537 				curr_data_.set_dbt(d, false);
538 				limit_buf_size_after_use();
539 			}
540 		} else if (ret == DB_BUFFER_SMALL) {
541 			// Either the key or data DBTs might trigger a
542 			// DB_KEYSMALL return. Only enlarge the DBT if it
543 			// is actually too small.
544 			if (((sz = d.get_size()) > 0) && (sz > d.get_ulen()))
545 				enlarge_dbt(d, sz);
546 
547 			if (((sz = k.get_size()) > 0) && (sz > k.get_ulen()))
548 				enlarge_dbt(k, sz);
549 
550 			goto retry;
551 		} else {
552 			if (ret == DB_NOTFOUND) {
553 				ret = INVALID_ITERATOR_POSITION;
554 				this->curr_key_.reset();
555 				this->curr_data_.reset();
556 			} else if (bulk_retrieval_ &&
557 			    (getflags & DB_MULTIPLE_KEY)){
558 				BDBOP(((DBC*)csr_)->dbp->
559 				    get_pagesize(((DBC*)csr_)->
560 				    dbp, &bulk_bufsz), ret);
561 				if (bulk_bufsz > d.get_ulen()) {// buf size error
562 					normalize_bulk_bufsize(bulk_bufsz);
563 					bulk_retrieval_ = bulk_bufsz;
564 					enlarge_dbt(d, bulk_bufsz);
565 					goto retry;
566 				} else
567 					throw_bdb_exception(
568 					    "DbCursor<>::increment", ret);
569 			} else
570 				throw_bdb_exception(
571 				    "DbCursor<>::increment", ret);
572 		}
573 
574 		csr_status_ = ret;
575 		return ret;
576 	}
577 
578 	// After each use of key_buf_ and data_buf_, limit their buffer size to
579 	// a reasonable size so that they don't waste a big memory space.
limit_buf_size_after_use()580 	inline void limit_buf_size_after_use()
581 	{
582 		if (bulk_retrieval_)
583 			// Bulk buffer has to be huge, so don't check it.
584 			return;
585 
586 		if (key_buf_.get_ulen() > DBSTL_MAX_KEY_BUF_LEN) {
587 			key_buf_.set_data(DbstlReAlloc(key_buf_.get_data(),
588 			    DBSTL_MAX_KEY_BUF_LEN));
589 			key_buf_.set_ulen(DBSTL_MAX_KEY_BUF_LEN);
590 		}
591 		if (data_buf_.get_ulen() > DBSTL_MAX_DATA_BUF_LEN) {
592 			data_buf_.set_data(DbstlReAlloc(data_buf_.get_data(),
593 			    DBSTL_MAX_DATA_BUF_LEN));
594 			data_buf_.set_ulen(DBSTL_MAX_DATA_BUF_LEN);
595 		}
596 	}
597 
598 	// Duplicate this object's cursor and set it to dbc1.
599 	//
dup(DbCursor<key_dt,data_dt> & dbc1)600 	inline int dup(DbCursor<key_dt, data_dt>& dbc1) const
601 	{
602 		Dbc* pcsr = 0;
603 		int ret;
604 
605 		if (csr_ != 0 && csr_->dup(&pcsr, DB_POSITION) == 0) {
606 			dbc1.set_cursor(pcsr);
607 			dbc1.set_owner_db(this->get_owner_db());
608 			dbc1.set_owner_txn(this->get_owner_txn());
609 			ResourceManager::instance()->add_cursor(
610 			    this->get_owner_db(), &dbc1);
611 			ret = 0;
612 		} else
613 			ret = ITERATOR_DUP_ERROR;
614 
615 		return ret;
616 	}
617 
618 public:
619 	// Open a cursor, do not move it, it is at an invalid position.
620 	// All cursors should be opened using this method.
621 	//
open(db_container * pdbc,int flags)622 	inline int open(db_container *pdbc, int flags)
623 	{
624 		int ret;
625 
626 		Db *pdb = pdbc->get_db_handle();
627 		if (pdb == NULL)
628 			return 0;
629 		if (csr_) // Close before open.
630 			return 0;
631 		ret = ResourceManager::instance()->
632 		    open_cursor(this, pdb, flags);
633 		set_rmw(rmw_get_);
634 		this->csr_status_ = ret;
635 		return ret;
636 	}
637 
638 	// Move Berkeley DB cursor to specified key k, by default use DB_SET,
639 	// but DB_SET_RANGE can and may also be used.
640 	//
641 	int move_to(const key_dt&k, u_int32_t flag = DB_SET)
642 	{
643 		Dbt &d1 = data_buf_;
644 		int ret;
645 		u_int32_t sz;
646 		DataItem k1(k, true);
647 
648 		if (csr_ == NULL)
649 			return INVALID_ITERATOR_CURSOR;
650 
651 		curr_key_.reset();
652 		curr_data_.reset();
653 		inform_duppers();
654 
655 		// It is likely that k is not in db, causing get(DB_SET) to
656 		// fail, we should not throw an exception because of this.
657 		//
658 		if (rmw_get_)
659 			flag |= DB_RMW;
660 retry:		ret = csr_->get(&k1.get_dbt(), &d1, flag);
661 		if (ret == 0) {
662 			curr_key_ = k1;
663 			curr_data_.set_dbt(d1, false);
664 			limit_buf_size_after_use();
665 		} else if (ret == DB_BUFFER_SMALL) {
666 			sz = d1.get_size();
667 			assert(sz > 0);
668 			enlarge_dbt(d1, sz);
669 			goto retry;
670 		} else {
671 			if (ret == DB_NOTFOUND) {
672 				ret = INVALID_ITERATOR_POSITION;
673 				// Invalidate current values because it is
674 				// at an invalid position.
675 				this->curr_key_.reset();
676 				this->curr_data_.reset();
677 			} else
678 				throw_bdb_exception("DbCursor<>::move_to", ret);
679 		}
680 
681 		csr_status_ = ret;
682 		return ret;
683 	}
684 
685 	// Returns the number of keys equal to the current one.
count()686 	inline size_t count()
687 	{
688 		int ret;
689 		db_recno_t cnt;
690 
691 		BDBOP2(csr_->count(&cnt, 0), ret, close());
692 		return (size_t)cnt;
693 	}
694 
695 	int insert(const key_dt&k, const data_dt& d, int pos = DB_BEFORE)
696 	{
697 		// !!!XXX:
698 		//         We do a deep copy of the input data into a local
699 		//         variable. Apparently not doing so causes issues
700 		//         when using gcc. Even though the put completes prior
701 		//         to returning from this function call.
702 		//         It would be best to avoid this additional copy.
703 		int ret;
704 		// (k, d) pair may be a temporary pair, so we must copy them.
705 		DataItem k1(k, false), d1(d, false);
706 
707 		inform_duppers();
708 		if (pos == DB_AFTER) {
709 			ret = this->csr_->put(&k1.get_dbt(), &d1.get_dbt(),
710 			    pos);
711 			// May be using this flag for an empty database,
712 			// because begin() an iterator of an empty db_vector
713 			// equals its end() iterator, so use DB_KEYLAST to
714 			// retry.
715 			//
716 			if (ret == EINVAL || ret == 0)
717 				return ret;
718 			else if (ret)
719 				throw_bdb_exception("DbCursor<>::insert", ret);
720 		}
721 		if (pos == DB_NODUPDATA)
722 			BDBOP3(this->csr_->put(&k1.get_dbt(), &d1.get_dbt(),
723 			    pos), ret, DB_KEYEXIST, close());
724 		else
725 			BDBOP2(this->csr_->put(&k1.get_dbt(), &d1.get_dbt(),
726 			    pos), ret, close());
727 		this->csr_status_ = ret;
728 		if (ret == 0) {
729 			curr_key_ = k1;
730 			curr_data_ = d1;
731 		}
732 		// This cursor points to the new key/data pair now.
733 		return ret;
734 	}
735 
736 	// Replace current cursor-pointed data item with d.
replace(const data_dt & d)737 	inline int replace(const data_dt& d)
738 	{
739 		Dbt k1;
740 		int ret;
741 		// !!!XXX:
742 		//         We do a deep copy of the input data into a local
743 		//         variable. Apparently not doing so causes issues
744 		//         when using gcc. Even though the put completes prior
745 		//         to returning from this function call.
746 		//         It would be best to avoid this additional copy.
747 		// d may be a temporary object, so we must copy it.
748 		DataItem d1(d, false);
749 
750 
751 		BDBOP2(this->csr_->put(&k1, &d1.get_dbt(), DB_CURRENT),
752 		    ret, close());
753 		curr_data_ = d1; // Update current data.
754 
755 		this->csr_status_ = ret;
756 		return ret;
757 	}
758 
759 	// Remove old key and insert new key-psuodo_data. First insert then
760 	// move to old key and remove it so that the cursor remains at the
761 	// old key's position, according to DB documentation.
762 	// But from practice I can see
763 	// the cursor after delete seems not at old position because a for
764 	// loop iteration exits prematurelly, not all elements are passed.
765 	//
replace_key(const key_dt & k)766 	inline int replace_key(const key_dt&k)
767 	{
768 		data_dt d;
769 		key_dt k0;
770 		int ret;
771 
772 		this->get_current_key_data(k0, d);
773 		if (k0 == k)
774 			return 0;
775 
776 		DbCursor<key_dt, data_dt> csr2;
777 		this->dup(csr2);
778 		// Delete current, then insert new key/data pair.
779 		ret = csr2.del();
780 		ret = csr2.insert(k, d, DB_KEYLAST);
781 		this->csr_status_ = ret;
782 
783 		// Now this->csr_ is sitting on an invalid position, its
784 		// iterator is invalidated. Must first move it to the next
785 		// position before using it.
786 		return ret;
787 	}
788 
del()789 	inline int del()
790 	{
791 		int ret;
792 
793 		inform_duppers();
794 		BDBOP2(csr_->del(0), ret, close());
795 
796 		// By default pos.csr_ will stay at where it was after delete,
797 		// which now is an invalid position. So we need to move to
798 		// next to conform to stl specifications, but we don't move it
799 		// here, iterator::erase should move the iterator itself
800 		// forward.
801 		//
802 		this->csr_status_ = ret;
803 		return ret;
804 	}
805 
806 	// Make sure the bulk buffer is large enough, and a multiple of 1KB.
807 	// This function may be called prior to cursor initialization, it is
808 	// not possible to verify that the buffer size is a multiple of the
809 	// page size here.
normalize_bulk_bufsize(u_int32_t & bulksz)810 	u_int32_t normalize_bulk_bufsize(u_int32_t &bulksz)
811 	{
812 		if (bulksz == 0)
813 			return 0;
814 
815 		while (bulksz < 16 * sizeof(data_dt))
816 			bulksz *= 2;
817 
818 		bulksz = bulksz + 1024 - bulksz % 1024;
819 
820 		return bulksz;
821 	}
822 
823 	////////////////////////////////////////////////////////////////////
824 	//
825 	// Begin public constructors and destructor.
826 	//
827 	explicit DbCursor(u_int32_t b_bulk_retrieval = 0, bool brmw1 = false,
DbCursorBase()828 	    bool directdbget = true) : DbCursorBase(),
829 	    curr_key_(sizeof(key_dt)), curr_data_(sizeof(data_dt))
830 	{
831 		u_int32_t bulksz = sizeof(data_dt); // non-bulk
832 		rmw_get_ = brmw1;
833 		this->bulk_retrieval_ =
834 		    normalize_bulk_bufsize(b_bulk_retrieval);
835 		recno_itr_ = NULL;
836 		multi_itr_ = NULL;
837 
838 		if (bulk_retrieval_) {
839 			if (bulksz <= bulk_retrieval_)
840 				bulksz = bulk_retrieval_;
841 			else {
842 				normalize_bulk_bufsize(bulksz);
843 				bulk_retrieval_ = bulksz;
844 			}
845 		}
846 		key_buf_.set_data(DbstlMalloc(sizeof(key_dt)));
847 		key_buf_.set_ulen(sizeof(key_dt));
848 		key_buf_.set_flags(DB_DBT_USERMEM);
849 		data_buf_.set_data(DbstlMalloc(bulksz));
850 		data_buf_.set_ulen(bulksz);
851 		data_buf_.set_flags(DB_DBT_USERMEM);
852 		directdb_get_ = directdbget;
853 	}
854 
855 	// Copy constructor, duplicate cursor here.
DbCursor(const DbCursor<key_dt,data_dt> & dbc)856 	DbCursor(const DbCursor<key_dt, data_dt>& dbc) :
857 	    DbCursorBase(dbc),
858 	    curr_key_(dbc.curr_key_), curr_data_(dbc.curr_data_)
859 	{
860 		void *pk, *pd;
861 
862 		dbc.dup(*this);
863 		csr_status_ = dbc.csr_status_;
864 		if (csr_ || dbc.csr_)
865 			this->rmw_get_ = set_rmw(dbc.rmw_get_,
866 			    ((DBC*)dbc.csr_)->dbenv);
867 		else
868 			rmw_get_ = dbc.rmw_get_;
869 
870 		bulk_retrieval_ = dbc.bulk_retrieval_;
871 
872 		// Now we have to copy key_buf_ and data_buf_ to support
873 		// multiple retrieval.
874 		key_buf_.set_data(pk = DbstlMalloc(dbc.key_buf_.get_ulen()));
875 		key_buf_.set_ulen(dbc.key_buf_.get_ulen());
876 		key_buf_.set_size(dbc.key_buf_.get_size());
877 		key_buf_.set_flags(DB_DBT_USERMEM);
878 		memcpy(pk, dbc.key_buf_.get_data(), key_buf_.get_ulen());
879 
880 		data_buf_.set_data(pd = DbstlMalloc(dbc.data_buf_.get_ulen()));
881 		data_buf_.set_ulen(dbc.data_buf_.get_ulen());
882 		data_buf_.set_size(dbc.data_buf_.get_size());
883 		data_buf_.set_flags(DB_DBT_USERMEM);
884 		memcpy(pd, dbc.data_buf_.get_data(), data_buf_.get_ulen());
885 		if (dbc.recno_itr_) {
886 			recno_itr_ = new DbstlMultipleRecnoDataIterator(
887 			    data_buf_);
888 			recno_itr_->set_pointer(dbc.recno_itr_->get_pointer());
889 		} else
890 			recno_itr_ = NULL;
891 		if (dbc.multi_itr_) {
892 			multi_itr_ = new DbstlMultipleKeyDataIterator(
893 			    data_buf_);
894 			multi_itr_->set_pointer(dbc.multi_itr_->get_pointer());
895 
896 		} else
897 			multi_itr_ = NULL;
898 
899 		directdb_get_ = dbc.directdb_get_;
900 
901 		// Do not copy sduppers, they are private to each DbCursor<>
902 		// object.
903 	}
904 
~DbCursor()905 	virtual ~DbCursor()
906 	{
907 		close(); // Call close() ahead of freeing following buffers.
908 		free(key_buf_.get_data());
909 		free(data_buf_.get_data());
910 		if (multi_itr_)
911 			delete multi_itr_;
912 		if (recno_itr_)
913 			delete recno_itr_;
914 	}
915 
916 	////////////////////////////////////////////////////////////////////
917 
918 	const DbCursor<key_dt, data_dt>& operator=
919 	    (const DbCursor<key_dt, data_dt>& dbc)
920 	{
921 		void *pk;
922 		u_int32_t ulen;
923 
924 		DbCursorBase::operator =(dbc);
925 		dbc.dup(*this);
926 		curr_key_ = dbc.curr_key_;
927 		curr_data_ = dbc.curr_data_;
928 		rmw_get_ = dbc.rmw_get_;
929 		this->bulk_retrieval_ = dbc.bulk_retrieval_;
930 		this->directdb_get_ = dbc.directdb_get_;
931 		// Now we have to copy key_buf_ and data_buf_ to support
932 		// bulk retrieval.
933 		key_buf_.set_data(pk = DbstlReAlloc(key_buf_.get_data(),
934 		    ulen = dbc.key_buf_.get_ulen()));
935 		key_buf_.set_ulen(ulen);
936 		key_buf_.set_size(dbc.key_buf_.get_size());
937 		key_buf_.set_flags(DB_DBT_USERMEM);
938 		memcpy(pk, dbc.key_buf_.get_data(), ulen);
939 
940 		data_buf_.set_data(pk = DbstlReAlloc(key_buf_.get_data(),
941 		    ulen = dbc.key_buf_.get_ulen()));
942 		data_buf_.set_ulen(ulen);
943 		data_buf_.set_size(dbc.data_buf_.get_size());
944 		data_buf_.set_flags(DB_DBT_USERMEM);
945 		memcpy(pk, dbc.key_buf_.get_data(), ulen);
946 
947 		if (dbc.recno_itr_) {
948 			if (recno_itr_) {
949 				delete recno_itr_;
950 				recno_itr_ = NULL;
951 			}
952 			recno_itr_ = new DbstlMultipleRecnoDataIterator(
953 			    data_buf_);
954 			recno_itr_->set_pointer(dbc.recno_itr_->get_pointer());
955 		} else if (recno_itr_) {
956 			delete recno_itr_;
957 			recno_itr_ = NULL;
958 		}
959 
960 		if (dbc.multi_itr_) {
961 			if (multi_itr_) {
962 				delete multi_itr_;
963 				multi_itr_ = NULL;
964 			}
965 			multi_itr_ = new DbstlMultipleKeyDataIterator(
966 			    data_buf_);
967 			multi_itr_->set_pointer(dbc.multi_itr_->get_pointer());
968 
969 		} else if (multi_itr_) {
970 			delete multi_itr_;
971 			multi_itr_ = NULL;
972 		}
973 
974 		return dbc;
975 		// Do not copy sduppers, they are private to each DbCursor<>
976 		// object.
977 
978 	}
979 
980 	// Move Dbc*cursor to next position. If doing bulk read, read from
981 	// the bulk buffer. If bulk buffer exhausted, do another bulk read
982 	// from database, and then read from the bulk buffer. Quit if no
983 	// more data in database.
984 	//
985 	int next(int flag = DB_NEXT)
986 	{
987 		Dbt k, d;
988 		db_recno_t recno;
989 		int ret;
990 
991 retry:		if (bulk_retrieval_) {
992 			if (multi_itr_) {
993 				if (multi_itr_->next(k, d)) {
994 					curr_key_.set_dbt(k, false);
995 					curr_data_.set_dbt(d, false);
996 					return 0;
997 				} else {
998 					delete multi_itr_;
999 					multi_itr_ = NULL;
1000 				}
1001 			}
1002 			if (recno_itr_) {
1003 				if (recno_itr_->next(recno, d)) {
1004 					curr_key_.set_dbt(k, false);
1005 					curr_data_.set_dbt(d, false);
1006 					return 0;
1007 				} else {
1008 					delete recno_itr_;
1009 					recno_itr_ = NULL;
1010 				}
1011 			}
1012 		}
1013 		ret = increment(flag);
1014 		if (bulk_retrieval_ && ret == 0)
1015 			goto retry;
1016 		return ret;
1017 	}
1018 
1019 	inline int prev(int flag = DB_PREV)
1020 	{
1021 		return increment(flag);
1022 	}
1023 
1024 	// Move Dbc*cursor to first element. If doing bulk read, read data
1025 	// from bulk buffer.
first()1026 	int first()
1027 	{
1028 		Dbt k, d;
1029 		db_recno_t recno;
1030 		int ret;
1031 
1032 		ret = increment(DB_FIRST);
1033 		if (bulk_retrieval_) {
1034 			if (multi_itr_) {
1035 				if (multi_itr_->next(k, d)) {
1036 					curr_key_.set_dbt(k, false);
1037 					curr_data_.set_dbt(d, false);
1038 					return 0;
1039 				} else {
1040 					delete multi_itr_;
1041 					multi_itr_ = NULL;
1042 				}
1043 			}
1044 			if (recno_itr_) {
1045 				if (recno_itr_->next(recno, d)) {
1046 					curr_key_.set_dbt(k, false);
1047 					curr_data_.set_dbt(d, false);
1048 					return 0;
1049 				} else {
1050 					delete recno_itr_;
1051 					recno_itr_ = NULL;
1052 				}
1053 			}
1054 		}
1055 
1056 		return ret;
1057 	}
1058 
last()1059 	inline int last()
1060 	{
1061 		return increment(DB_LAST);
1062 	}
1063 
1064 	// Get current key/data pair, shallow copy. Return 0 on success,
1065 	// -1 if no data.
get_current_key_data(key_dt & k,data_dt & d)1066 	inline int get_current_key_data(key_dt&k, data_dt&d)
1067 	{
1068 		if (directdb_get_)
1069 			update_current_key_data_from_db(
1070 			    DbCursorBase::SKIP_NONE);
1071 		if (curr_key_.get_data(k) == 0 && curr_data_.get_data(d) == 0)
1072 			return 0;
1073 		else
1074 			return INVALID_KEY_DATA;
1075 	}
1076 
1077 	// Get current data, shallow copy. Return 0 on success, -1 if no data.
get_current_data(data_dt & d)1078 	inline int get_current_data(data_dt&d)
1079 	{
1080 		if (directdb_get_)
1081 			update_current_key_data_from_db(DbCursorBase::SKIP_KEY);
1082 		if (curr_data_.get_data(d) == 0)
1083 			return 0;
1084 		else
1085 			return INVALID_KEY_DATA;
1086 	}
1087 
1088 	// Get current key, shallow copy. Return 0 on success, -1 if no data.
get_current_key(key_dt & k)1089 	inline int get_current_key(key_dt&k)
1090 	{
1091 		if (directdb_get_)
1092 			update_current_key_data_from_db(
1093 			    DbCursorBase::SKIP_DATA);
1094 		if (curr_key_.get_data(k) == 0)
1095 			return 0;
1096 		else
1097 			return INVALID_KEY_DATA;
1098 	}
1099 
close()1100 	inline void close()
1101 	{
1102 		if (csr_) {
1103 			inform_duppers();
1104 			ResourceManager::instance()->remove_cursor(this);
1105 		}
1106 		csr_ = NULL;
1107 	}
1108 
1109 	// Parameter skipkd specifies skip retrieving key or data:
1110 	// If 0, don't skip, retrieve both;
1111 	// If 1, skip retrieving key;
1112 	// If 2, skip retrieving data.
1113 	// Do not poll from db again if doing bulk retrieval.
update_current_key_data_from_db(DbcGetSkipOptions skipkd)1114 	void update_current_key_data_from_db(DbcGetSkipOptions skipkd) {
1115 		int ret;
1116 		u_int32_t sz, sz1, kflags = DB_DBT_USERMEM,
1117 		    dflags = DB_DBT_USERMEM;
1118 		// Do not poll from db again if doing bulk retrieval.
1119 		if (this->bulk_retrieval_)
1120 			return;
1121 		if (this->csr_status_ != 0) {
1122 			curr_key_.reset();
1123 			curr_data_.reset();
1124 			return;
1125 		}
1126 
1127 		// We will modify flags if skip key or data, so cache old
1128 		// value and set it after get calls.
1129 		if (skipkd != DbCursorBase::SKIP_NONE) {
1130 			kflags = key_buf_.get_flags();
1131 			dflags = data_buf_.get_flags();
1132 		}
1133 		if (skipkd == DbCursorBase::SKIP_KEY) {
1134 			key_buf_.set_dlen(0);
1135 			key_buf_.set_flags(DB_DBT_PARTIAL | DB_DBT_USERMEM);
1136 		}
1137 
1138 		if (skipkd == DbCursorBase::SKIP_DATA) {
1139 			data_buf_.set_dlen(0);
1140 			data_buf_.set_flags(DB_DBT_PARTIAL | DB_DBT_USERMEM);
1141 		}
1142 retry:		ret = csr_->get(&key_buf_, &data_buf_, DB_CURRENT);
1143 		if (ret == 0) {
1144 			if (skipkd != DbCursorBase::SKIP_KEY)
1145 				curr_key_ = key_buf_;
1146 			if (skipkd != DbCursorBase::SKIP_DATA)
1147 				curr_data_ = data_buf_;
1148 			limit_buf_size_after_use();
1149 		} else if (ret == DB_BUFFER_SMALL) {
1150 			if ((sz = key_buf_.get_size()) > 0)
1151 				enlarge_dbt(key_buf_, sz);
1152 			if ((sz1 = data_buf_.get_size()) > 0)
1153 				enlarge_dbt(data_buf_, sz1);
1154 			if (sz == 0 && sz1 == 0)
1155 				THROW0(InvalidDbtException);
1156 			goto retry;
1157 		} else {
1158 			if (skipkd != DbCursorBase::SKIP_NONE) {
1159 				key_buf_.set_flags(kflags);
1160 				data_buf_.set_flags(dflags);
1161 			}
1162 			throw_bdb_exception(
1163 			"DbCursor<>::update_current_key_data_from_db", ret);
1164 		}
1165 
1166 		if (skipkd != DbCursorBase::SKIP_NONE) {
1167 			key_buf_.set_flags(kflags);
1168 			data_buf_.set_flags(dflags);
1169 		}
1170 	}
1171 }; // DbCursor<>
1172 
1173 ////////////////////////////////////////////////////////////////////////
1174 ////////////////////////////////////////////////////////////////////////
1175 //
1176 // RandDbCursor class template definition
1177 //
1178 // RandDbCursor is a random accessible cursor wrapper for use by
1179 // db_vector_iterator, it derives from DbCursor<> class. It has a fixed key
1180 // data type, which is index_type.
1181 //
1182 typedef db_recno_t index_type;
1183 template<Typename data_dt>
1184 class RandDbCursor : public DbCursor<index_type, data_dt>
1185 {
1186 protected:
1187 	friend class DataItem;
1188 	typedef ssize_t difference_type;
1189 public:
1190 	typedef RandDbCursor<data_dt> self;
1191 	typedef DbCursor<index_type, data_dt> base;
1192 
1193 	// Return current csr_ pointed element's index in recno database
1194 	// (i.e. the index starting from 1). csr_ must be open and
1195 	// point to an existing key/data pair.
1196 	//
get_current_index()1197 	inline index_type get_current_index() const
1198 	{
1199 		index_type ndx;
1200 
1201 		if (this->directdb_get_)
1202 			((self *)this)->update_current_key_data_from_db(
1203 			    DbCursorBase::SKIP_DATA);
1204 		this->curr_key_.get_data(ndx);
1205 		return ndx;
1206 	}
1207 
compare(const self * csr2)1208 	inline int compare(const self *csr2) const{
1209 		index_type i1, i2;
1210 
1211 		i1 = this->get_current_index();
1212 		i2 = csr2->get_current_index();
1213 		return i1 - i2;
1214 	}
1215 
1216 	// Insert data d before/after current position.
1217 	int insert(const data_dt& d, int pos = DB_BEFORE){
1218 		int k = 1, ret;
1219 		//data_dt dta;
1220 
1221 		// Inserting into empty db, must set key to 1.
1222 		if (pos == DB_KEYLAST)
1223 			k = 1;
1224 
1225 		ret = base::insert(k, d, pos);
1226 
1227 		// Inserting into a empty db using begin() itr, so flag is
1228 		// DB_AFTER and surely failed, so change to use DB_KEYLAST
1229 		// and try again.
1230 		if (ret == EINVAL) {
1231 			k = 1;
1232 			pos = DB_KEYLAST;
1233 			ret = base::insert(k, d, pos);
1234 		}
1235 		this->csr_status_ = ret;
1236 		return ret;
1237 	}
1238 
1239 	/*
1240 	 * Move the cursor n positions, if reaches the beginning or end,
1241 	 * returns DB_NOTFOUND.
1242 	 */
advance(difference_type n)1243 	int advance(difference_type n)
1244 	{
1245 		int ret = 0;
1246 		index_type indx;
1247 		u_int32_t sz, flags = 0;
1248 
1249 		indx = this->get_current_index();
1250 		if (n == 0)
1251 			return 0;
1252 
1253 		index_type i = (index_type)n;
1254 		indx += i;
1255 
1256 		if (n < 0 && indx < 1) { // Index in recno db starts from 1.
1257 
1258 			ret = INVALID_ITERATOR_POSITION;
1259 			return ret;
1260 		}
1261 		this->inform_duppers();
1262 
1263 		// Do a search to determine whether new position is valid.
1264 		Dbt k, &d = this->data_buf_;
1265 
1266 
1267 		k.set_data(&indx);
1268 		k.set_size(sizeof(indx));
1269 		if (this->rmw_get_)
1270 			flags |= DB_RMW;
1271 
1272 retry:		if (this->csr_ &&
1273 		    ((ret = this->csr_->get(&k, &d, DB_SET)) == DB_NOTFOUND)) {
1274 			this->csr_status_ = ret = INVALID_ITERATOR_POSITION;
1275 			this->curr_key_.reset();
1276 			this->curr_data_.reset();
1277 		} else if (ret == DB_BUFFER_SMALL) {
1278 			sz = d.get_size();
1279 			assert(sz > 0);
1280 			this->enlarge_dbt(d, sz);
1281 			goto retry;
1282 		} else if (ret == 0) {
1283 			this->curr_key_.set_dbt(k, false);
1284 			this->curr_data_.set_dbt(d, false);
1285 			this->limit_buf_size_after_use();
1286 		} else
1287 			throw_bdb_exception("RandDbCursor<>::advance", ret);
1288 		this->csr_status_ = ret;
1289 		return ret;
1290 	}
1291 
1292 	// Return the last index of recno db (index starting from 1),
1293 	// it will also move the underlying cursor to last key/data pair.
1294 	//
last_index()1295 	inline index_type last_index()
1296 	{
1297 		int ret;
1298 
1299 		ret = this->last();
1300 		if (ret)
1301 			return 0;// Invalid position.
1302 		else
1303 			return get_current_index();
1304 	}
1305 
1306 	explicit RandDbCursor(u_int32_t b_bulk_retrieval = 0,
1307 	    bool b_rmw1 = false, bool directdbget = true)
base(b_bulk_retrieval,b_rmw1,directdbget)1308 	    : base(b_bulk_retrieval, b_rmw1, directdbget)
1309 	{
1310 	}
1311 
RandDbCursor(const RandDbCursor<data_dt> & rdbc)1312 	RandDbCursor(const RandDbCursor<data_dt>& rdbc) : base(rdbc)
1313 	{
1314 	}
1315 
base(csr1)1316 	explicit RandDbCursor(Dbc* csr1, int posidx = 0) : base(csr1)
1317 	{
1318 	}
1319 
~RandDbCursor()1320 	virtual ~RandDbCursor()
1321 	{
1322 	}
1323 
1324 }; // RandDbCursor<>
1325 
1326 END_NS //ns dbstl
1327 
1328 #endif // !_DB_STL_DBC_H
1329