1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2009 Oracle.  All rights reserved.
5 *
6 * $Id$
7 */
8
9#include <string>
10
11#include "dbstl_container.h"
12#include "dbstl_resource_manager.h"
13#include "dbstl_exception.h"
14#include "dbstl_utility.h"
15#include "dbstl_inner_utility.h"
16
17typedef struct {
18	time_t	tv_sec;				/* seconds */
19	long	tv_nsec;			/* nanoseconds */
20} db_timespec;
21
22extern "C"{
23void __os_id (DB_ENV *, pid_t *, db_threadid_t*);
24void __os_gettime(ENV *env, db_timespec *tp, int monotonic);
25}
26
27START_NS(dbstl)
28
29using std::string;
30u_int32_t db_container::g_db_file_suffix_ = 0;
31
32void set_global_dbfile_suffix_number(u_int32_t num)
33{
34	db_container::g_db_file_suffix_ = num;
35}
36
37// Internally used memory allocation functions, they will throw an exception
38// of NotEnoughMemoryException if can't allocate memory.
39void *DbstlReAlloc(void *ptr, size_t size)
40{
41	void *p;
42
43	assert(size != 0);
44	if ((p = realloc(ptr, size)) == NULL)
45		THROW(NotEnoughMemoryException,
46		    ("DbstlReAlloc failed to allocate memory", size));
47
48	return p;
49}
50
51void *DbstlMalloc(size_t size)
52{
53	void *p;
54
55	assert(size != 0);
56	if ((p = malloc(size)) == NULL)
57		THROW(NotEnoughMemoryException,
58		    ("DbstlMalloc failed to allocate memory", size));
59	return p;
60}
61
62void db_container::init_members()
63{
64	txn_begin_flags_ = 0;
65	commit_flags_ = 0;
66	cursor_oflags_ = 0;
67	pdb_ = NULL;
68	is_set_ = false;
69	auto_commit_ = false;
70	dbenv_ = NULL;
71}
72
73void db_container::init_members(Db *dbp, DbEnv *envp)
74{
75	txn_begin_flags_ = 0;
76	commit_flags_ = 0;
77	is_set_ = false;
78	cursor_oflags_ = 0;
79	pdb_ = dbp;
80	dbenv_ = envp;
81	set_auto_commit(pdb_);
82}
83
84db_container::db_container()
85{
86	init_members();
87}
88
89db_container::db_container(const db_container& dbctnr)
90{
91	init_members(dbctnr);
92}
93
94db_container::db_container(Db *dbp, DbEnv *envp)
95{
96	init_members(dbp, envp);
97}
98
99void db_container::init_members(const db_container&dbctnr)
100{
101	txn_begin_flags_ = dbctnr.txn_begin_flags_;
102	commit_flags_ = dbctnr.commit_flags_;
103	cursor_oflags_ = dbctnr.cursor_oflags_;
104	// We don't copy database handles because we will clone another
105	// database from dbctnr's db, and get its handle. We will
106	// copy the following database properties because they will be
107	// definitely identical.
108	//
109	pdb_ = NULL;
110	is_set_ = dbctnr.is_set_;
111	auto_commit_ = dbctnr.auto_commit_;
112	dbenv_ = dbctnr.dbenv_;
113}
114void db_container::open_db_handles(Db *&pdb, DbEnv *&penv, DBTYPE dbtype,
115    u_int32_t oflags, u_int32_t sflags)
116{
117	if (pdb == NULL) {
118		pdb = open_db(penv, NULL, dbtype, oflags, sflags);
119		this->pdb_ = pdb;
120	}
121
122	if (penv == NULL) {
123		penv = pdb->get_env();
124		this->dbenv_ = penv;
125		set_auto_commit(pdb_);
126	}
127}
128
129Db* db_container::clone_db_config(Db *dbp)
130{
131	string str;
132
133	return clone_db_config(dbp, str);
134}
135
136// Open a new db with identical configuration to dbp. The dbfname brings
137// back the generated db file name.
138Db* db_container::clone_db_config(Db *dbp, string &dbfname)
139{
140	Db *tdb = NULL;
141	int ret;
142	DBTYPE dbtype;
143	u_int32_t oflags, sflags;
144	const char *dbfilename, *dbname, *tdbname;
145
146	BDBOP2(dbp->get_type(&dbtype), ret, dbp->close(0));
147	BDBOP2(dbp->get_open_flags(&oflags), ret, dbp->close(0));
148	BDBOP2(dbp->get_flags(&sflags), ret, dbp->close(0));
149
150	BDBOP (dbp->get_dbname(&dbfilename, &dbname), ret);
151	if (dbfilename == NULL) {
152		tdb = open_db(dbp->get_env(),
153		    dbfilename, dbtype, oflags, sflags, 0420, NULL, 0, dbname);
154		dbfname.assign("");
155
156	} else {
157		construct_db_file_name(dbfname);
158		tdbname = dbfname.c_str();
159		tdb = open_db(dbp->get_env(), tdbname, dbtype, oflags, sflags);
160	}
161
162	return tdb;
163}
164
165int db_container::construct_db_file_name(string &filename) const
166{
167	db_threadid_t tid;
168	db_timespec ts;
169	int len;
170	char name[64];
171
172	__os_gettime(NULL, &ts, 1);
173	__os_id(NULL, NULL, &tid);
174
175	// avoid name clash
176	len = _snprintf(name, 64, "tmpdb_db_map_%lu_%d_%u.db",
177	    (u_long)((uintptr_t)tid + ts.tv_nsec), rand(), g_db_file_suffix_++);
178	filename = name;
179
180	return 0;
181}
182
183void db_container::set_auto_commit(Db *db)
184{
185	u_int32_t envof, envf, dbf;
186
187	if (db == NULL || dbenv_ == NULL) {
188		auto_commit_ = false;
189		return;
190	}
191
192	dbenv_->get_open_flags(&envof);
193	if ((envof & DB_INIT_TXN) == 0) {
194		this->auto_commit_ = false;
195	} else {
196		dbenv_->get_flags(&envf);
197		db->get_flags(&dbf);
198		if (((envf & DB_AUTO_COMMIT) != 0) ||
199		    ((dbf & DB_AUTO_COMMIT) != 0))
200			this->auto_commit_ = true;
201		else
202			this->auto_commit_ = false;
203	}
204}
205
206void db_container::set_db_handle(Db *dbp, DbEnv *newenv)
207{
208	const char *errmsg;
209
210	if ((errmsg = verify_config(dbp, newenv)) != NULL) {
211		THROW(InvalidArgumentException, ("Db*", errmsg));
212
213	}
214
215	pdb_ = dbp;
216	if (newenv)
217		dbenv_ = newenv;
218
219}
220
221void db_container::verify_db_handles(const db_container &cntnr) const
222{
223	Db *pdb2 = cntnr.get_db_handle();
224	const char *home = NULL, *home2 = NULL, *dbf = NULL, *dbn = NULL,
225	    *dbf2 = NULL, *dbn2 = NULL;
226	int ret = 0;
227	u_int32_t flags = 0, flags2 = 0;
228	bool same_dbfile, same_dbname, anonymous_inmemdbs;
229	// Check the two database handles do not refer to the same database.
230	// If they don't point to two anonymous databases at the same time,
231	// then two identical file names and two identical database names
232	// mean the two databases are the same.
233	assert(this->pdb_ != pdb2);
234	if (pdb_ == NULL)
235		return;
236
237	BDBOP(pdb_->get_dbname(&dbf, &dbn), ret);
238	BDBOP(pdb2->get_dbname(&dbf2, &dbn2), ret);
239
240	anonymous_inmemdbs = (dbf == NULL && dbf2 == NULL &&
241	    dbn == NULL && dbn2 == NULL);
242
243	same_dbfile = (dbf != NULL && dbf2 != NULL && (strcmp(dbf, dbf2) == 0))
244	    || (dbf == NULL && dbf2 == NULL);
245
246	same_dbname = (dbn == NULL && dbn2 == NULL) ||
247	    (dbn != NULL && dbn2 != NULL && strcmp(dbn, dbn2) == 0);
248
249	assert((!(anonymous_inmemdbs) && same_dbfile && same_dbname) == false);
250
251	// If any one of the two environments are transactional, both of them
252	// should be opened in the same transactional environment.
253	DbEnv *penv2 = cntnr.get_db_env_handle();
254	if (dbenv_ != penv2 ){
255		BDBOP(this->dbenv_->get_open_flags(&flags), ret);
256		BDBOP(penv2->get_open_flags(&flags2), ret);
257
258		if ((flags & DB_INIT_TXN) || (flags2 & DB_INIT_TXN)) {
259			BDBOP(dbenv_->get_home(&home), ret);
260			BDBOP(penv2->get_home(&home), ret);
261			assert(home != NULL && home2 != NULL &&
262			    strcmp(home, home2) == 0);
263		}
264	}
265}
266
267bool operator==(const Dbt&d1, const Dbt&d2)
268{
269	if (d1.get_size() != d2.get_size())
270		return false;
271
272	return memcmp(d1.get_data(), d2.get_data(),
273	    d2.get_size()) == 0;
274}
275
276bool operator==(const DBT&d1, const DBT&d2)
277{
278	if (d1.size != d2.size)
279		return false;
280	return memcmp(d1.data, d2.data, d2.size) == 0;
281}
282
283void close_all_dbs()
284{
285	ResourceManager::instance()->close_all_dbs();
286}
287
288void close_db(Db *pdb)
289{
290	ResourceManager::instance()->close_db(pdb);
291}
292
293DbTxn* begin_txn(u_int32_t flags, DbEnv*env)
294{
295	return ResourceManager::instance()->begin_txn(flags, env, 1);
296}
297
298void commit_txn(DbEnv *env, u_int32_t flags)
299{
300	ResourceManager::instance()->commit_txn(env, flags);
301}
302
303void commit_txn(DbEnv *env, DbTxn *txn, u_int32_t flags)
304{
305	ResourceManager::instance()->commit_txn(env, txn, flags);
306}
307
308void abort_txn(DbEnv *env)
309{
310	ResourceManager::instance()->abort_txn(env);
311}
312
313void abort_txn(DbEnv *env, DbTxn *txn)
314{
315	ResourceManager::instance()->abort_txn(env, txn);
316}
317
318DbTxn* current_txn(DbEnv *env)
319{
320	return ResourceManager::instance()->current_txn(env);
321}
322
323DbTxn* set_current_txn_handle(DbEnv *env, DbTxn *newtxn)
324{
325	return ResourceManager::instance()->
326	    set_current_txn_handle(env, newtxn);
327}
328
329void register_db(Db *pdb1)
330{
331	ResourceManager::instance()->register_db(pdb1);
332}
333
334void register_db_env(DbEnv *env1)
335{
336	ResourceManager::instance()->register_db_env(env1);
337}
338
339Db* open_db (DbEnv *penv, const char *filename, DBTYPE dbtype,
340    u_int32_t oflags, u_int32_t set_flags, int mode,
341    DbTxn *txn, u_int32_t cflags, const char *dbname)
342{
343	return ResourceManager::instance()->open_db(
344	    penv, filename, dbtype, oflags, set_flags, mode, txn,
345	    cflags, dbname);
346}
347
348DbEnv* open_env(const char *env_home, u_int32_t set_flags,
349    u_int32_t oflags, u_int32_t cachesize, int mode, u_int32_t cflags)
350{
351	return ResourceManager::instance()->open_env(
352	    env_home, set_flags, oflags, cachesize, mode, cflags);
353}
354
355void close_db_env(DbEnv *pdbenv)
356{
357
358	ResourceManager::instance()->close_db_env(pdbenv);
359}
360
361void close_all_db_envs()
362{
363	ResourceManager::instance()->close_all_db_envs();
364}
365
366size_t close_db_cursors(Db *dbp1)
367{
368	return ResourceManager::instance()->close_db_cursors(dbp1);
369}
370
371db_mutex_t alloc_mutex()
372{
373	int ret;
374	db_mutex_t mtx;
375
376	BDBOP2(ResourceManager::instance()->get_mutex_env()->mutex_alloc(
377	    DB_MUTEX_PROCESS_ONLY, &mtx), ret, ResourceManager::
378	    instance()->get_mutex_env()->mutex_free(mtx));
379	return mtx;
380}
381
382int lock_mutex(db_mutex_t mtx)
383{
384	int ret;
385
386	BDBOP2(ResourceManager::instance()->global_lock(mtx), ret,
387	    ResourceManager::
388	    instance()->get_mutex_env()->mutex_free(mtx));
389	return 0;
390}
391
392int unlock_mutex(db_mutex_t mtx)
393{
394	int ret;
395
396	BDBOP2(ResourceManager::instance()->global_unlock(mtx), ret,
397	    ResourceManager::
398	    instance()->get_mutex_env()->mutex_free(mtx));
399	return 0;
400}
401
402void free_mutex(db_mutex_t mtx)
403{
404	ResourceManager::instance()->get_mutex_env()->mutex_free(mtx);
405}
406
407void dbstl_startup()
408{
409	ResourceManager::instance()->global_startup();
410}
411
412void dbstl_exit()
413{
414	ResourceManager::instance()->global_exit();
415}
416
417// Internally used only.
418void throw_bdb_exception(const char *caller, int error)
419{
420	 switch (error) {
421	 case DB_LOCK_DEADLOCK:
422		 {
423			 DbDeadlockException dl_except(caller);
424			 throw dl_except;
425		 }
426	 case DB_LOCK_NOTGRANTED:
427		 {
428			 DbLockNotGrantedException lng_except(caller);
429			 throw lng_except;
430		 }
431	 case DB_REP_HANDLE_DEAD:
432		 {
433			 DbRepHandleDeadException hd_except(caller);
434			 throw hd_except;
435		 }
436	 case DB_RUNRECOVERY:
437		 {
438			 DbRunRecoveryException rr_except(caller);
439			 throw rr_except;
440		 }
441	 default:
442		 {
443			 DbException except(caller, error);
444			 throw except;
445		 }
446	 }
447}
448
449void register_global_object(DbstlGlobalInnerObject *gio)
450{
451	ResourceManager::instance()->register_global_object(gio);
452}
453
454u_int32_t hash_default(Db * /* dbp */, const void *key, u_int32_t len)
455{
456	const u_int8_t *k, *e;
457	u_int32_t h;
458
459	k = (const u_int8_t *)key;
460	e = k + len;
461	for (h = 0; k < e; ++k) {
462		h *= 16777619;
463		h ^= *k;
464	}
465	return (h);
466}
467
468bool DbstlMultipleDataIterator::next(Dbt &data)
469{
470	if (*p_ == (u_int32_t)-1) {
471		data.set_data(0);
472		data.set_size(0);
473		p_ = 0;
474	} else {
475		data.set_data(data_ + *p_--);
476		data.set_size(*p_--);
477		if (data.get_size() == 0 && data.get_data() == data_)
478			data.set_data(0);
479	}
480	return (p_ != 0);
481}
482
483bool DbstlMultipleKeyDataIterator::next(Dbt &key, Dbt &data)
484{
485	if (*p_ == (u_int32_t)-1) {
486		key.set_data(0);
487		key.set_size(0);
488		data.set_data(0);
489		data.set_size(0);
490		p_ = 0;
491	} else {
492		key.set_data(data_ + *p_);
493		p_--;
494		key.set_size(*p_);
495		p_--;
496		data.set_data(data_ + *p_);
497		p_--;
498		data.set_size(*p_);
499		p_--;
500	}
501	return (p_ != 0);
502}
503
504bool DbstlMultipleRecnoDataIterator::next(db_recno_t &recno, Dbt &data)
505{
506	if (*p_ == (u_int32_t)0) {
507		recno = 0;
508		data.set_data(0);
509		data.set_size(0);
510		p_ = 0;
511	} else {
512		recno = *p_--;
513		data.set_data(data_ + *p_--);
514		data.set_size(*p_--);
515	}
516	return (p_ != 0);
517}
518
519END_NS
520
521