summaryrefslogtreecommitdiff
path: root/stl/dbstl_container.h
blob: 279fe408fbce1a57478f0e92dbcdf2c423b725bf (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
/*-
 * See the file LICENSE for redistribution information.
 *
 * Copyright (c) 2009 Oracle.  All rights reserved.
 *
 * $Id$
 */

#ifndef _DB_STL_CONTAINER_H__
#define _DB_STL_CONTAINER_H__

#include "dbstl_common.h"
#include "dbstl_resource_manager.h"
#include <assert.h>

START_NS(dbstl)

class ResourceManager;

//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
//
// db_container class definition
//
// This class's begin_txn, commit/abort_txn is used to wrap each DB related
// operation inside dbstl. When auto commit is enabled, each operation will
// be auto committed before returning, and aborted when an exception is thrown.
//
// It also contains members to hold all needed parameters and flags for
// transaction and cursor related calls.
//
// Container classes will inherit from this class. Each container will enclose
// every db related operation with db_container::begin_txn and
// db_container::commit_txn, and if exception is not caught, abort_txn()
// should be called.

/** \defgroup dbstl_containers dbstl container classes
A dbstl container is very much like a C++ STL container. It stores a 
collection of data items, or key/data pairs. 
Each container is backed by a Berkeley DB database created in an explicit
database environment or an internal private environment; And the database
itself can be created explicitly with all kinds of configurations, or
by dbstl internally. For each type of container, some specific type of 
database and/or configurations must be used or specified to the database
and its environment. dbstl will check the database and environment conform 
to the requirement. When users don't have a chance to specify a container's
backing database and environment, like in copy constructors, dbstl will
create proper databases and/or environment for it. There are two helper 
functions to make it easier to create/open an environment or database, they
are dbstl::open_db() and dbstl::open_env();
\sa dbstl::open_db() dbstl::open_env() db_vector db_map db_multimap db_set 
db_multiset
*/

/** \ingroup dbstl_containers
@{
This class is the base class for all db container classes, you don't directly
use this class, but all container classes inherit from this class, so you need
to know the methods that can be accessed via concrete container classes.
This class is also used to support auto commit transactions. Autocommit is
enabled when DB_AUTO_COMMIT is set to the database or database environment
handle and the environment is transactional.

Inside dbstl, there are transactions begun and committed/aborted if the backing
database and/or environment requires auto commit, and there are cursors
opened internally, and you can set the flags used by the transaction and cursor
functions via set functions of this class.

All dbstl containers are fully multi-threaded, you should not need any
synchronization to use them in the correct way, but this class is not thread
safe, access to its members are not proctected by any mutex because the data
members of this class are supposed to be set before they are used, 
and remain read only afterwards. If this is not the case, you must synchronize
the access.
*/
class _exported db_container
{
private:
	// By default these flags are 0, users should configure these values
	// on container initialization.
	//
	u_int32_t txn_begin_flags_, commit_flags_;
	mutable u_int32_t cursor_oflags_;

	// Berkeley DB database handle for each container. Subclasses do not
	// need to define it.
	//
	Db *pdb_;

	// Berkeley DB environment handle in which the db handle is opened.
	DbEnv *dbenv_;

	// db_map_iterator<> needs to know whether the container is a
	// db_(multi)set or not
	//
	bool is_set_;

	// Determined automatically, by inspecting users the Berkeley DB
	// database and environment configuration options.
	//
	bool auto_commit_;

	// If exisiting random temporary database name generation mechanism is
	// still causing name clashes, users can set this global suffix number
	// which will be append to each temporary database file name and by 
	// default it is 0. there is a dbstl::set_global_dbfile_suffix_number
	// to do so.
	static u_int32_t g_db_file_suffix_;
	friend void set_global_dbfile_suffix_number(u_int32_t);
protected:

	// Does not clone or copy data from database parameter.
	// Return the db file name back in the dbfname parameter.
	// We construct a default database name the user can rename it using
	// either DbEnv::dbrename or Db::rename.
	//
	Db* clone_db_config(Db *dbp, std::string &dbfname);
	Db* clone_db_config(Db *dbp);

	// Store the name into name parameter whose length is n. Return -1 if
	// not enough space.
	int construct_db_file_name(std::string &filename) const;

	// Check that this container and cntnr are backed by different databases
	// and if any one of them is using transactions, both should be in the
	// same transactional environment.
	// Called by all deriving classes' methods
	// which have a container parameter.
	void verify_db_handles(const db_container &cntnr) const;

	void open_db_handles(Db *&pdb, DbEnv *&penv, DBTYPE dbtype,
	    u_int32_t oflags, u_int32_t sflags);

	inline void set_is_set(bool b)
	{
		is_set_ = b;
	}

	inline bool is_set() const
	{
		return is_set_;
	}

	// Database and environment handles and autocommit and is_set_ are 
	// not assigned, because they are the nature of my own db, not 
	// that of dbctnr.
	inline const db_container &operator=(const db_container & dbctnr)
	{
		ASSIGNMENT_PREDCOND(dbctnr)
		txn_begin_flags_ = dbctnr.txn_begin_flags_;
		commit_flags_ = dbctnr.commit_flags_;
		cursor_oflags_ = dbctnr.cursor_oflags_;
		return dbctnr;
	}

public:
	/// Default constructor.
	db_container();

	/// Copy constructor.
	/// The new container will be backed by another database within the
	/// same environment unless dbctnr's backing database is in its own
	/// internal private environment. The name of the database is coined
	/// based on current time and thread id and some random number. If
	/// this is still causing naming clashes, you can set a suffix number
	/// via "set_global_dbfile_suffix_number" function; And following db
	/// file will suffix this number in the file name for additional 
	/// randomness. And the suffix will be incremented after each such use.
	/// You can change the file name via DbEnv::rename.
	/// If dbctnr is using an anonymous database, the newly constructed
	/// container will also use an anonymous one.
	/// \param dbctnr The container to initialize this container.
	db_container(const db_container &dbctnr);

	/**
	This constructor is not directly called by the user, but invoked by
	constructors of concrete container classes. The statement about the
	parameters applies to constructors of all container classes.
	\param dbp Database handle. dbp is supposed to be opened inside envp.
	Each dbstl container is backed by a Berkeley DB database, so dbstl 
	will create an internal anonymous database if dbp is NULL. 
	\param envp Environment handle. And envp can also be NULL, meaning the
	dbp handle may be created in its internal private environment.
	*/
	db_container(Db *dbp, DbEnv *envp);

	/// The backing database is not closed in this function. It is closed
	/// when current thread exits and the database is no longer referenced
	/// by any other container instances in this process. 
	/// In order to make the reference counting work alright, you must call
	/// register_db(Db*) and register_db_env(DbEnv*) correctly.
	/// \sa register_db(Db*) register_db_env(DbEnv*) 
	virtual ~db_container(){}

	/// \name Get and set functions for data members.
	/// Note that these functions are not thread safe, because all data 
	/// members of db_container are supposed to be set on container 
	/// construction and initialization, and remain read only afterwards.
	//@{
	/// Get the backing database's open flags.
	/// \return The backing database's open flags.
	inline u_int32_t get_db_open_flags()   const
	{
		u_int32_t oflags;
		pdb_->get_open_flags(&oflags);
		return oflags;
	}

	/// Get the backing database's flags that are set via Db::set_flags()
	/// function.
	/// \return Flags set to this container's database handle.
	inline u_int32_t get_db_set_flags()   const
	{
		u_int32_t oflags;
		pdb_->get_flags(&oflags);
		return oflags;
	}

	/// Get the backing database's handle. 
	/// \return The backing database handle of this container.
	inline Db* get_db_handle() const
	{
		return pdb_;
	}

	/// Get the backing database environment's handle. 
	/// \return The backing database environment handle of this container.
	inline DbEnv* get_db_env_handle()  const
	{
		return dbenv_;
	}

	/** 
	Set the underlying database's handle, and optionally environment 
	handle if the environment has also changed. That is, users can change
	the container object's underlying database while the object is alive.
	dbstl will verify that the handles set conforms to the concrete 
	container's requirement to Berkeley DB database/environment handles.
	\param dbp The database handle to set.
	\param newenv The database environment handle to set.
	*/
	void set_db_handle(Db *dbp, DbEnv *newenv = NULL);

	/** Set the flags required by the Berkeley DB functions 
	DbEnv::txn_begin(), DbTxn::commit() and DbEnv::cursor(). These flags
	will be set to this container's auto commit member functions when 
	auto commit transaction is used, except that cursor_oflags is set to 
	the Dbc::cursor when creating an iterator for this container.
	By default the three flags are all zero.
	You can also set the values of the flags individually by using the 
	appropriate set functions in this class. The corresponding get 
	functions return the flags actually used.
	\param txn_begin_flags Flags to be set to DbEnv::txn_begin().
	\param commit_flags Flags to be set to DbTxn::commit().
	\param cursor_open_flags Flags to be set to Db::cursor().
	*/
	inline void set_all_flags(u_int32_t txn_begin_flags,
	    u_int32_t commit_flags, u_int32_t cursor_open_flags)
	{
		this->txn_begin_flags_ = txn_begin_flags;
		this->commit_flags_ = commit_flags;
		this->cursor_oflags_ = cursor_open_flags;
	}

	/// Set flag of DbEnv::txn_begin() call.
	/// \param flag Flags to be set to DbEnv::txn_begin().
	inline void set_txn_begin_flags(u_int32_t flag )
	{
		txn_begin_flags_ = flag;
	}

	/// Get flag of DbEnv::txn_begin() call.
	/// \return Flags to be set to DbEnv::txn_begin().
	inline u_int32_t get_txn_begin_flags()  const
	{
		return txn_begin_flags_;
	}

	/// Set flag of DbTxn::commit() call.
	/// \param flag Flags to be set to DbTxn::commit().
	inline void set_commit_flags(u_int32_t flag)
	{
		commit_flags_ = flag;
	}

	/// Get flag of DbTxn::commit() call.
	/// \return Flags to be set to DbTxn::commit().
	inline u_int32_t get_commit_flags()  const
	{
		return commit_flags_;
	}

	/// Get flag of Db::cursor() call.
	/// \return Flags to be set to Db::cursor().
	inline u_int32_t get_cursor_open_flags()  const
	{
		return cursor_oflags_;
	}

	/// Set flag of Db::cursor() call.
	/// \param flag Flags to be set to Db::cursor().
	inline void set_cursor_open_flags(u_int32_t flag)
	{
		cursor_oflags_ = flag;
	}
	//@} // getter_setters

protected:
	void init_members();
	void init_members(Db *pdb, DbEnv *env);
	void init_members(const db_container&cnt);
	// Called internally by concrete container constructors. Verification
	// is done by the specialized constructors
	void set_db_handle_int(Db *dbp, DbEnv *envp)
	{
		this->pdb_ = dbp;
		this->dbenv_ = envp;
	}

	// Child classes override this function to check the db and environment
	// handles are correctly configured.
	virtual const char* verify_config(Db* pdb, DbEnv* penv) const
	{
		if (pdb != NULL && ((pdb->get_create_flags() & 
		    DB_CXX_NO_EXCEPTIONS) == 0))
			return 
"Db and DbEnv object must be constructed with DB_CXX_NO_EXCEPTIONS flag set.";

		if (penv != NULL && ((penv->get_create_flags() & 
		    DB_CXX_NO_EXCEPTIONS) == 0))
			return 
"Db and DbEnv object must be constructed with DB_CXX_NO_EXCEPTIONS flag set.";
		return NULL;
	}

	// Use db and dbenv_ to determine whether to enable autocommit. If
	// DB_AUTOCOMMIT is set on dbenv_ or db and dbenv_ is transactional,
	// that db should be autocommit.
	//
	void set_auto_commit(Db* db);

	// Begin a transaction. Used to make a container's db related
	// operations auto commit when the operation completes and abort
	// when the operation fails. If there is already a transaction for this
	// container's environment, then that transaction is used. If the
	// transaction was created by DB STL, its reference count is
	// incremented (user created and external transactions are not
	// reference counted because they can be nested.).
	inline DbTxn* begin_txn() const
	{
		DbTxn *txn = NULL;

		if (this->auto_commit_) {
			txn = ResourceManager::instance()->begin_txn(
			    this->txn_begin_flags_, dbenv_, 0);
		}
		return txn;
	}

	inline void commit_txn()  const
	{
		if (this->auto_commit_) {
			ResourceManager::instance()->
			    commit_txn(pdb_->get_env(), this->commit_flags_);
		}
	}

	inline void abort_txn() const
	{
		if (this->auto_commit_)
			ResourceManager::instance()->
			    abort_txn(pdb_->get_env());
	}

	inline DbTxn *current_txn() const
	{
		return ResourceManager::instance()->
		    current_txn(pdb_->get_env());
	}
}; // db_container
/** @} */ // dbstl_containers

/// \addtogroup dbstl_helper_classes
//@{
/// Bulk retrieval configuration helper class. Used by the begin() function of
/// a container.
class _exported BulkRetrievalOption
{
public:
	enum Option {BulkRetrieval, NoBulkRetrieval};

protected:
	Option bulk_retrieve;
	u_int32_t bulk_buf_sz_;

	inline BulkRetrievalOption()
	{
		bulk_retrieve = NoBulkRetrieval;
		bulk_buf_sz_ = DBSTL_BULK_BUF_SIZE;
	}

public:
	inline BulkRetrievalOption(Option bulk_retrieve1, 
	    u_int32_t bulk_buf_sz = DBSTL_BULK_BUF_SIZE)
	{
		this->bulk_retrieve = bulk_retrieve1;
		this->bulk_buf_sz_ = bulk_buf_sz;
	}

	// The following two static members should best be const, but this is
	// impossible because in C++ this definition is a function declaration:
	// const static BulkRetrievalOption BulkRetrieval(BulkRetrieval);
	// i.e. const static members can only be inited with default copy
	// constructor.
	//
  	// Static data members can't be compiled into a .lib for a dll on
  	// Windows, code using the static members will have link error---
  	// unresolved symbols, so we have to use a static function here.
 	/// This function indicates that you need a bulk retrieval iterator, 
 	/// and it can be also used to optionally set the bulk read buffer size.
  	inline static BulkRetrievalOption bulk_retrieval(
  	    u_int32_t bulk_buf_sz = DBSTL_BULK_BUF_SIZE)
  	{
  		return BulkRetrievalOption(BulkRetrieval, bulk_buf_sz);
  	}
  
 	/// This function indicates that you do not need a bulk retrieval 
 	/// iterator.
  	inline static BulkRetrievalOption no_bulk_retrieval()
  	{
  		return BulkRetrievalOption(NoBulkRetrieval, 0);
  	}
  
 	/// Equality comparison.
  	inline bool operator==(const BulkRetrievalOption& bro) const
  	{
  		return bulk_retrieve == bro.bulk_retrieve;
  	}
  
 	/// Assignment operator.
  	inline void operator=(BulkRetrievalOption::Option opt)
  	{
  		bulk_retrieve = opt;
  	}
  
 	/// Return the buffer size set to this object.
 	inline u_int32_t bulk_buf_size()
  	{
  		return bulk_buf_sz_;
  	}
};
//@}

/// \addtogroup dbstl_helper_classes
//@{
/// Read-modify-write cursor configuration helper class. Used by each begin()
/// function of all containers.
class _exported ReadModifyWriteOption
{
protected:
	enum Option{ReadModifyWrite, NoReadModifyWrite};
	Option rmw;

	inline ReadModifyWriteOption(Option rmw1)
	{
		this->rmw = rmw1;
	}

	inline ReadModifyWriteOption()
	{
		rmw = NoReadModifyWrite;
	}

public:
	/// Assignment operator.
	inline void operator=(ReadModifyWriteOption::Option rmw1)
	{
		this->rmw = rmw1;
	}

	/// Equality comparison.
	inline bool operator==(const ReadModifyWriteOption &rmw1) const
	{
		return this->rmw == rmw1.rmw;
	}

	/// Call this function to tell the container's begin() function that
	/// you need a read-modify-write iterator.
	inline static ReadModifyWriteOption read_modify_write()
	{
		return ReadModifyWriteOption(ReadModifyWrite);
	}

	/// Call this function to tell the container's begin() function that
	/// you do not need a read-modify-write iterator. This is the default
	/// value for the parameter of any container's begin() function.
	inline static ReadModifyWriteOption no_read_modify_write()
	{
		return ReadModifyWriteOption(NoReadModifyWrite);
	}

};
//@} // dbstl_helper_classes

// The classes in the Berkeley DB C++ API do not expose data and p_ member.
// Extend the class to provide this functionality, rather than altering the
// internal implementations.
//
class _exported DbstlMultipleIterator : protected DbMultipleIterator
{
protected:
	DbstlMultipleIterator(const Dbt &dbt) : DbMultipleIterator(dbt) {}
public:
	u_int32_t get_pointer()
	{
		u_int32_t off;
		off = (u_int32_t)((u_int8_t*)p_ - data_);
		return off;
	}

	inline void set_pointer(u_int32_t offset)
	{
		p_ = (u_int32_t*)(data_ + offset);
	}

};

class _exported DbstlMultipleKeyDataIterator : public DbstlMultipleIterator
{
public:
	DbstlMultipleKeyDataIterator(const Dbt &dbt)
	    : DbstlMultipleIterator(dbt) {}
	bool next(Dbt &key, Dbt &data);
};

class _exported DbstlMultipleRecnoDataIterator : public DbstlMultipleIterator
{
public:
	DbstlMultipleRecnoDataIterator(const Dbt &dbt)
	   : DbstlMultipleIterator(dbt) {}
	bool next(db_recno_t &recno, Dbt &data);
};

class _exported DbstlMultipleDataIterator : public DbstlMultipleIterator
{
public:
	DbstlMultipleDataIterator(const Dbt &dbt)
	    : DbstlMultipleIterator(dbt) {}
	bool next(Dbt &data);
};

// These classes are used to give data values meaningful default
// initializations. They are only necessary for types that do not have a
// reasonable default constructor - explicitly char *, wchar_t * and T*.
// The string types don't have a reasonable default initializer since we store
// the underlying content, not a pointer.
// So we fully instantiate types for T* and const T* and set the pointers to
// NULL and leave other types intact.
template <Typename T>
class DbstlInitializeDefault
{
public:
	DbstlInitializeDefault(T&){}
};

template <Typename T>
class DbstlInitializeDefault<T *>
{
public:
	DbstlInitializeDefault(T *& t){t = NULL;}
};

template <Typename T>
class DbstlInitializeDefault<const T *>
{
public:
	DbstlInitializeDefault(const T *& t){t = NULL;}
};

END_NS

#endif //_DB_STL_CONTAINER_H__