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