00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include <ncbi_pch.hpp>
00033 #include <db/bdb/bdb_file.hpp>
00034 #include <db/bdb/bdb_env.hpp>
00035 #include <db/bdb/bdb_trans.hpp>
00036
00037 #include <db/error_codes.hpp>
00038
00039 #include <db.h>
00040
00041 #include <vector>
00042
00043 #ifdef verify
00044 #undef verify
00045 #endif
00046
00047 #if DB_VERSION_MAJOR >= 4
00048 #if DB_VERSION_MINOR >= 4 || DB_VERSION_MAJOR > 4
00049 #define HAVE_GET_MPF
00050 #endif
00051 #endif
00052
00053
00054 #define NCBI_USE_ERRCODE_X Db_Bdb_File
00055
00056 BEGIN_NCBI_SCOPE
00057
00058 const char CBDB_RawFile::kDefaultDatabase[] = "_table";
00059 const int CBDB_RawFile::kOpenFileMask = 0664;
00060
00061
00062
00063
00064
00065
00066
00067 class CDB_guard
00068 {
00069 public:
00070 CDB_guard(DB** db) : m_DB(db) {}
00071 ~CDB_guard()
00072 {
00073 if (m_DB && *m_DB) {
00074 (*m_DB)->close(*m_DB, 0);
00075 *m_DB = 0;
00076 }
00077 }
00078 void release() { m_DB = 0; }
00079 private:
00080 DB** m_DB;
00081 };
00082
00083
00084
00085
00086
00087
00088 CBDB_MultiRowBuffer::CBDB_MultiRowBuffer(size_t buf_size)
00089 : m_Data_DBT(new DBT)
00090 , m_Buf(new unsigned char[buf_size])
00091 , m_BufSize(buf_size)
00092 , m_BufPtr(0)
00093 , m_LastKey(0)
00094 , m_LastData(0)
00095 , m_LastKeyLen(0)
00096 , m_LastDataLen(0)
00097 {
00098 }
00099
00100 CBDB_MultiRowBuffer::~CBDB_MultiRowBuffer()
00101 {
00102 delete [] (unsigned char*)(m_Buf);
00103 delete m_Data_DBT;
00104 }
00105
00106 void CBDB_MultiRowBuffer::InitDBT()
00107 {
00108 memset(m_Data_DBT, 0, sizeof(DBT));
00109 m_Data_DBT->data = m_Buf;
00110 m_Data_DBT->ulen = m_Data_DBT->size = m_BufSize;
00111 m_Data_DBT->flags = DB_DBT_USERMEM;
00112 }
00113
00114 void CBDB_MultiRowBuffer::MultipleInit()
00115 {
00116 DB_MULTIPLE_INIT(m_BufPtr, m_Data_DBT);
00117 }
00118
00119
00120
00121
00122
00123
00124
00125
00126 CBDB_RawFile::CBDB_RawFile(EDuplicateKeys dup_keys, EDBType db_type)
00127 : m_DB_Type(db_type),
00128 m_DB(0),
00129 m_DBT_Key(0),
00130 m_DBT_Data(0),
00131 m_Env(0),
00132 m_Trans(0),
00133 m_TransAssociation(CBDB_Transaction::eFullAssociation),
00134 m_RecLen(0),
00135 m_H_ffactor(0),
00136 m_H_nelem(0),
00137 m_BT_minkey(0),
00138 m_Compressor(0),
00139 m_DB_Attached(false),
00140 m_ByteSwapped(false),
00141 m_RevSplitOff(false),
00142 m_CmpOverride(true),
00143 m_PageSize(0),
00144 m_CacheSize(256 * 1024),
00145 m_DuplicateKeys(dup_keys),
00146 m_OpenMode(eReadOnly)
00147 {
00148 if (m_DB_Type == eQueue)
00149 {
00150 dup_keys = eDuplicatesDisable;
00151 }
00152
00153 try
00154 {
00155 m_DBT_Key = new DBT;
00156 m_DBT_Data = new DBT;
00157 }
00158 catch (...)
00159 {
00160 delete m_DBT_Key;
00161 delete m_DBT_Data;
00162 throw;
00163 }
00164
00165 ::memset(m_DBT_Key, 0, sizeof(DBT));
00166 ::memset(m_DBT_Data, 0, sizeof(DBT));
00167 }
00168
00169
00170 CBDB_RawFile::~CBDB_RawFile()
00171 {
00172 x_Close(eIgnoreError);
00173 delete m_DBT_Key;
00174 delete m_DBT_Data;
00175
00176
00177
00178 if ( m_Trans != 0 &&
00179 (m_TransAssociation == (int) CBDB_Transaction::eFullAssociation) &&
00180 (m_Trans->IsInProgress())) {
00181 _ASSERT(0);
00182
00183
00184
00185 BDB_THROW(eTransInProgress,
00186 "Cannot close the file while transaction is in progress.");
00187 }
00188 }
00189
00190
00191 void CBDB_RawFile::Close()
00192 {
00193 x_Close(eThrowOnError);
00194 }
00195
00196 void CBDB_RawFile::Attach(CBDB_RawFile& bdb_file)
00197 {
00198 Close();
00199 m_DB = bdb_file.m_DB;
00200 m_DB_Attached = true;
00201 }
00202
00203 void CBDB_RawFile::SetEnv(CBDB_Env& env)
00204 {
00205 m_Env = &env;
00206 }
00207
00208 void CBDB_RawFile::SetCachePriority(ECachePriority priority)
00209 {
00210 #ifdef HAVE_GETMPF
00211 if (m_DB) {
00212 DB_MPOOLFILE* mpf = m_DB->get_mpf(m_DB);
00213 if (mpf) {
00214 DB_CACHE_PRIORITY p = DB_PRIORITY_DEFAULT;
00215 switch (priority) {
00216 case eCache_Lowest:
00217 p = DB_PRIORITY_VERY_LOW;
00218 break;
00219 case eCache_Low:
00220 p = DB_PRIORITY_LOW;
00221 break;
00222
00223 default:
00224 case eCache_Default:
00225 p = DB_PRIORITY_DEFAULT;
00226 break;
00227 case eCache_High:
00228 p = DB_PRIORITY_HIGH;
00229 break;
00230 case eCache_Highest:
00231 p = DB_PRIORITY_VERY_HIGH;
00232 break;
00233 }
00234
00235 mpf->set_priority(mpf, p);
00236 }
00237 }
00238 #endif
00239 }
00240
00241 DB_TXN* CBDB_RawFile::GetTxn()
00242 {
00243 if (m_Trans)
00244 return m_Trans->GetTxn();
00245 return 0;
00246 }
00247
00248 void CBDB_RawFile::SetCompressor(ICompression* compressor, EOwnership own)
00249 {
00250 m_Compressor.reset(compressor, own);
00251 }
00252
00253 void CBDB_RawFile::x_Close(EIgnoreError close_mode)
00254 {
00255 if (m_FileName.empty())
00256 return;
00257 LOG_POST_X(1, Info << "Closing: " << m_FileName);
00258
00259 if (m_DB_Attached) {
00260 m_DB = 0;
00261 m_DB_Attached = false;
00262 }
00263 else
00264 if (m_DB) {
00265 int ret = m_DB->close(m_DB, 0);
00266 m_DB = 0;
00267 if (close_mode == eThrowOnError) {
00268 BDB_CHECK(ret, m_FileName.c_str());
00269 if (m_Env)
00270 m_Env->LsnResetForMemLog(m_FileName.c_str());
00271 } else {
00272 if (ret != 0) {
00273 ERR_POST_X(2, "Error when closing " << m_FileName);
00274 } else {
00275 try {
00276 if (m_Env)
00277 m_Env->LsnResetForMemLog(m_FileName.c_str());
00278 } catch (CBDB_Exception& ex) {
00279 ERR_POST_X(3, "Error " << ex.what() << " resetting LSN for "
00280 << m_FileName);
00281 }
00282 }
00283 }
00284 }
00285
00286 m_FileName.erase();
00287 m_Database.erase();
00288 }
00289
00290
00291 void CBDB_RawFile::Open(const string& filename,
00292 const string& database,
00293 EOpenMode open_mode,
00294 bool support_dirty_read,
00295 unsigned rec_len)
00296 {
00297 if ( !m_FileName.empty() )
00298 Close();
00299
00300 const char * db;
00301 if (database.empty()) {
00302 db = 0;
00303 } else {
00304 db = database.c_str();
00305 }
00306
00307
00308 x_Open(filename.c_str(), db, open_mode, support_dirty_read, rec_len);
00309
00310 m_FileName = filename;
00311 if (db)
00312 m_Database = database;
00313 else
00314 m_Database = "";
00315 }
00316
00317
00318 void CBDB_RawFile::Reopen(EOpenMode open_mode,
00319 bool support_dirty_read,
00320 unsigned rec_len)
00321 {
00322 _ASSERT(!m_FileName.empty());
00323
00324 if (m_DB_Attached) {
00325 BDB_THROW(eInvalidOperation, "Cannot reopen attached object");
00326 }
00327
00328 int ret = m_DB->close(m_DB, 0);
00329 m_DB = 0;
00330
00331 BDB_CHECK(ret, m_FileName.c_str());
00332 x_Open(m_FileName.c_str(),
00333 !m_Database.empty() ? m_Database.c_str() : 0,
00334 open_mode, support_dirty_read, rec_len);
00335 }
00336
00337
00338 void CBDB_RawFile::Rename(const string& file,
00339 const string& old_name,
00340 const string& new_name)
00341 {
00342 _ASSERT(m_DB);
00343 if (IsOpen()) {
00344 NCBI_THROW(CBDB_Exception, eUnknown,
00345 "Cannot call rename on an opened database");
00346 }
00347 int ret = m_DB->rename(m_DB,
00348 file.c_str(), old_name.c_str(), new_name.c_str(),
00349 0);
00350 BDB_CHECK(ret, file.c_str());
00351 }
00352
00353
00354 void CBDB_RawFile::Remove(const string& filename, const string& database)
00355 {
00356 const char* db_name;
00357 if (database.empty()) {
00358 db_name = 0;
00359 } else {
00360 db_name = database.c_str();
00361 }
00362
00363 if (m_DB_Attached) {
00364 BDB_THROW(eInvalidOperation, "Cannot remove attached object");
00365 }
00366
00367
00368
00369 DB* db = 0;
00370 CDB_guard guard(&db);
00371 int ret = db_create(&db, m_Env ? m_Env->GetEnv() : 0, 0);
00372 BDB_CHECK(ret, 0);
00373
00374 ret = db->remove(db, filename.c_str(), db_name, 0);
00375 guard.release();
00376 if (ret == ENOENT || ret == EINVAL)
00377 return;
00378
00379 BDB_CHECK(ret, filename.c_str());
00380 }
00381
00382
00383 unsigned int CBDB_RawFile::Truncate()
00384 {
00385 _ASSERT(m_DB != 0);
00386 u_int32_t count;
00387 DB_TXN* txn = GetTxn();
00388 int ret = m_DB->truncate(m_DB,
00389 txn,
00390 &count,
00391 0);
00392
00393 BDB_CHECK(ret, FileName().c_str());
00394 return count;
00395 }
00396
00397 class DBT_ptr {
00398 public:
00399 DBT_ptr() {
00400 memset(&m_DBT, 0, sizeof(m_DBT));
00401 }
00402 ~DBT_ptr() {
00403 if (m_DBT.data) free(m_DBT.data);
00404 m_DBT.size = 0;
00405 m_DBT.data = 0;
00406 }
00407 DBT_ptr& operator=(const DBT& dbt) {
00408 if (m_DBT.data) free(m_DBT.data);
00409 m_DBT.size = dbt.size;
00410 m_DBT.data = malloc(dbt.size);
00411 memcpy(m_DBT.data, dbt.data, dbt.size);
00412 return *this;
00413 }
00414 DBT* operator*() {
00415 return &m_DBT;
00416 }
00417 private:
00418 DBT m_DBT;
00419 };
00420
00421
00422 unsigned int CBDB_RawFile::SafeTruncate()
00423 {
00424 _ASSERT(m_DB != 0);
00425 u_int32_t count = 0;
00426
00427 SetTransaction(NULL);
00428 bool done = false;
00429 const int k_bulk_init = 1000;
00430 int bulk = k_bulk_init;
00431 int bulk_age = 0;
00432
00433 vector<DBT_ptr> keys;
00434 keys.resize(k_bulk_init);
00435 while (!done) {
00436 int nrec, ret;
00437 DBC* dbcp = 0;
00438 DBT key, data;
00439 ret = m_DB->cursor(m_DB, NULL, &dbcp, 0);
00440 BDB_CHECK(ret, FileName().c_str());
00441 memset(&key, 0, sizeof(key));
00442 memset(&data, 0, sizeof(data));
00443 for (nrec = 0; nrec < bulk && (ret = dbcp->c_get(dbcp, &key, &data, DB_NEXT)) == 0;
00444 ++nrec) {
00445 keys[nrec] = key;
00446 }
00447 if (dbcp) dbcp->c_close(dbcp);
00448 dbcp = 0;
00449 if (ret != DB_NOTFOUND) {
00450 BDB_CHECK(ret, FileName().c_str());
00451 }
00452 if (!nrec) break;
00453 DB_TXN* txn = m_Env->CreateTxn();
00454 try {
00455 for (int n = 0; n < nrec; ++n) {
00456 ret = m_DB->del(m_DB, txn, *keys[n], 0);
00457 if (ret) break;
00458 }
00459 if (ret == 0) txn->commit(txn, 0);
00460 else {
00461 txn->abort(txn);
00462 BDB_CHECK(ret, FileName().c_str());
00463 }
00464 done = nrec < bulk;
00465 count += nrec;
00466 if (! done && bulk < k_bulk_init && ++bulk_age > 3) {
00467 bulk *=2;
00468 if (bulk > k_bulk_init) bulk = k_bulk_init;
00469 bulk_age = 0;
00470 }
00471 } catch (CBDB_ErrnoException& ex) {
00472 if (! ex.IsNoMem()) throw;
00473 bulk /= 2;
00474 if (bulk == 0) throw;
00475 bulk_age = 0;
00476 done = false;
00477 }
00478 }
00479 return count;
00480 }
00481
00482
00483
00484
00485 static bool s_DefaultCompactCallback()
00486 {
00487 return true;
00488 }
00489
00490 void CBDB_RawFile::Compact(ECompact compact_type,
00491 int target_fill_pct)
00492 {
00493 CompactEx(s_DefaultCompactCallback, compact_type, target_fill_pct);
00494 }
00495
00496
00497 void CBDB_RawFile::CompactEx(FContinueCompact compact_callback,
00498 ECompact compact_type,
00499 int target_fill_pct)
00500 {
00501 #ifdef DB_COMPACT_FLAGS
00502 _ASSERT(m_DB != 0);
00503
00504 u_int32_t flags = 0;
00505 if (compact_type == eCompactNoFree) {
00506
00507 } else if (compact_type == eCompactFreeExisting) {
00508 flags = DB_FREELIST_ONLY;
00509 } else if (compact_type == eCompactFreeAll) {
00510 flags = DB_FREE_SPACE;
00511 }
00512
00513 target_fill_pct = max(target_fill_pct, 0);
00514 DB_TXN* txn = GetTxn();
00515
00516 unsigned int pages_examined = 0;
00517 unsigned int pages_freed = 0;
00518 unsigned int levels_removed = 0;
00519 unsigned int pages_truncated = 0;
00520 for (int i = 0; i < 2; ++i) {
00521 DB_COMPACT compact;
00522 memset(&compact, 0, sizeof(compact));
00523 compact.compact_fillpercent = target_fill_pct;
00524 compact.compact_timeout = 0;
00525
00526 int ret = m_DB->compact(m_DB, txn, NULL, NULL, &compact,
00527 flags, NULL);
00528 BDB_CHECK(ret, FileName().c_str());
00529
00530 pages_examined += compact.compact_pages_examine;
00531 pages_freed += compact.compact_pages_free;
00532 levels_removed += compact.compact_levels;
00533 pages_truncated += compact.compact_pages_truncated;
00534
00535 LOG_POST_X(4, Info << "CBDB_RawFile::Compact(): "
00536 << "round " << i + 1 << ": "
00537 << compact.compact_pages_examine << " pages examined / "
00538 << compact.compact_pages_free << " pages freed / "
00539 << compact.compact_levels << " levels removed / "
00540 << compact.compact_pages_truncated << " pages truncated");
00541
00542 if ( !compact_callback() ) {
00543 break;
00544 }
00545 }
00546
00547 LOG_POST_X(5, Info << "CBDB_RawFile::Compact(): "
00548 << pages_examined << " pages examined / "
00549 << pages_freed << " pages freed / "
00550 << levels_removed << " levels removed / "
00551 << pages_truncated << " pages truncated");
00552 #endif
00553 }
00554
00555
00556 void CBDB_RawFile::SetCacheSize(unsigned int cache_size)
00557 {
00558 m_CacheSize = cache_size;
00559 if (m_DB) {
00560 int ret = m_DB->set_cachesize(m_DB, 0, m_CacheSize, 1);
00561 BDB_CHECK(ret, 0);
00562 }
00563 }
00564
00565
00566 void CBDB_RawFile::RevSplitOff()
00567 {
00568 m_RevSplitOff = true;
00569 }
00570
00571 void CBDB_RawFile::x_SetTransaction(CBDB_Transaction* trans)
00572 {
00573 if (m_Trans) {
00574 if (m_TransAssociation == (int) CBDB_Transaction::eFullAssociation) {
00575 m_Trans->Remove(this);
00576 }
00577 }
00578
00579 m_Trans = trans;
00580 if (m_Trans) {
00581 m_TransAssociation = m_Trans->GetAssociationMode();
00582 if (m_TransAssociation == (int) CBDB_Transaction::eFullAssociation) {
00583 m_Trans->Add(this);
00584 }
00585 }
00586 }
00587
00588 void CBDB_RawFile::x_RemoveTransaction(CBDB_Transaction* trans)
00589 {
00590 if (trans == m_Trans) {
00591 m_Trans = 0;
00592 }
00593 }
00594
00595 void CBDB_RawFile::SetTransaction(ITransaction* trans)
00596 {
00597 CBDB_Transaction* db_trans = CBDB_Transaction::CastTransaction(trans);
00598 x_SetTransaction(db_trans);
00599 }
00600
00601 void CBDB_RawFile::RemoveTransaction(ITransaction* trans)
00602 {
00603 CBDB_Transaction* db_trans = CBDB_Transaction::CastTransaction(trans);
00604 x_RemoveTransaction(db_trans);
00605 }
00606
00607 ITransaction* CBDB_RawFile::GetTransaction()
00608 {
00609 return m_Trans;
00610 }
00611
00612
00613 void CBDB_RawFile::x_CreateDB(unsigned rec_len)
00614 {
00615 _ASSERT(m_DB == 0);
00616 _ASSERT(!m_DB_Attached);
00617
00618 CDB_guard guard(&m_DB);
00619
00620 int ret = db_create(&m_DB, m_Env ? m_Env->GetEnv() : 0, 0);
00621 BDB_CHECK(ret, 0);
00622
00623 if (m_DB_Type == eBtree && m_CmpOverride) {
00624 SetCmp(m_DB);
00625 }
00626 if (m_DB_Type == eHash && m_CmpOverride) {
00627 SetHash(m_DB);
00628 }
00629
00630 if ( m_PageSize ) {
00631 ret = m_DB->set_pagesize(m_DB, m_PageSize);
00632 BDB_CHECK(ret, 0);
00633 }
00634
00635 if (!m_Env) {
00636 ret = m_DB->set_cachesize(m_DB, 0, m_CacheSize, 1);
00637 BDB_CHECK(ret, 0);
00638 }
00639
00640 if (DuplicatesAllowed()) {
00641 ret = m_DB->set_flags(m_DB, DB_DUP);
00642
00643 BDB_CHECK(ret, 0);
00644 }
00645
00646 if (m_RevSplitOff) {
00647 ret = m_DB->set_flags(m_DB, DB_REVSPLITOFF);
00648 BDB_CHECK(ret, 0);
00649 }
00650
00651 switch (m_DB_Type)
00652 {
00653 case eQueue:
00654 _ASSERT(rec_len);
00655 m_RecLen = rec_len;
00656 ret = m_DB->set_re_len(m_DB, rec_len);
00657 BDB_CHECK(ret, 0);
00658 break;
00659
00660 case eHash:
00661 if (m_H_ffactor) {
00662 int ret = m_DB->set_h_ffactor(m_DB, m_H_ffactor);
00663 BDB_CHECK(ret, FileName().c_str());
00664 }
00665 if (m_H_nelem) {
00666 int ret = m_DB->set_h_nelem(m_DB, m_H_nelem);
00667 BDB_CHECK(ret, FileName().c_str());
00668 }
00669 break;
00670
00671 case eBtree:
00672 if (m_BT_minkey) {
00673 int ret = m_DB->set_bt_minkey(m_DB, m_BT_minkey);
00674 BDB_CHECK(ret, FileName().c_str());
00675 }
00676 break;
00677
00678 default:
00679 break;
00680 }
00681
00682 guard.release();
00683 }
00684
00685
00686 void CBDB_RawFile::x_Open(const char* filename,
00687 const char* database,
00688 EOpenMode open_mode,
00689 bool support_dirty_read,
00690 unsigned rec_len)
00691 {
00692 int ret;
00693 if (m_DB == 0) {
00694 x_CreateDB(rec_len);
00695 }
00696
00697 if (open_mode == eCreate) {
00698 Remove(filename, database ? database : "");
00699 x_Create(filename, database);
00700 }
00701 else {
00702 u_int32_t open_flags;
00703
00704 switch (open_mode)
00705 {
00706 case eReadOnly:
00707 open_flags = DB_RDONLY;
00708 break;
00709 case eCreate:
00710 open_flags = DB_CREATE;
00711 break;
00712 default:
00713 open_flags = 0;
00714 break;
00715 }
00716
00717 DB_TXN* txn = 0;
00718
00719 if (m_Env) {
00720 if (m_Env->IsTransactional()) {
00721 open_flags |= DB_THREAD | DB_AUTO_COMMIT;
00722 }
00723 }
00724
00725 if (support_dirty_read) {
00726 open_flags |= DB_DIRTY_READ;
00727 }
00728
00729
00730 DBTYPE db_type = DB_BTREE;
00731 switch (m_DB_Type) {
00732 case eBtree:
00733 db_type = DB_BTREE;
00734 break;
00735 case eHash:
00736 db_type = DB_HASH;
00737 break;
00738 case eQueue:
00739 db_type = DB_QUEUE;
00740 m_RecLen = rec_len;
00741 break;
00742 default:
00743 _ASSERT(0);
00744 }
00745
00746 ret = m_DB->open(m_DB,
00747 txn,
00748 filename,
00749 database,
00750 db_type,
00751 open_flags,
00752 kOpenFileMask
00753 );
00754 if ( ret ) {
00755 if (open_mode == eCreate ||
00756 open_mode == eReadWriteCreate)
00757 {
00758 x_Create(filename, database);
00759 }
00760 else {
00761 m_DB->close(m_DB, 0);
00762 m_DB = 0;
00763 BDB_CHECK(ret, filename);
00764 }
00765 } else {
00766
00767
00768
00769 int isswapped;
00770 ret = m_DB->get_byteswapped(m_DB, &isswapped);
00771 BDB_CHECK(ret, filename);
00772
00773 m_ByteSwapped = (isswapped!=0);
00774 if (m_ByteSwapped) {
00775
00776 m_DB->close(m_DB, 0);
00777 m_DB = 0;
00778
00779 x_SetByteSwapped(m_ByteSwapped);
00780 x_CreateDB(rec_len);
00781
00782 ret = m_DB->open(m_DB,
00783 txn,
00784 filename,
00785 database,
00786 db_type,
00787 open_flags,
00788 kOpenFileMask);
00789 BDB_CHECK(ret, filename);
00790
00791 }
00792
00793 }
00794 }
00795
00796 m_OpenMode = open_mode;
00797 }
00798
00799
00800 void CBDB_RawFile::SetPageSize(unsigned int page_size)
00801 {
00802 _ASSERT(m_DB == 0);
00803 if (((page_size - 1) & page_size) != 0) {
00804 BDB_THROW(eInvalidValue, "Page size must be power of 2");
00805 }
00806 m_PageSize = page_size;
00807 }
00808
00809 unsigned int CBDB_RawFile::GetPageSize()
00810 {
00811 if ( !m_PageSize && m_DB) {
00812 int ret = m_DB->get_pagesize(m_DB, &m_PageSize);
00813 BDB_CHECK(ret, 0);
00814 }
00815 return m_PageSize;
00816 }
00817
00818 void CBDB_RawFile::Sync()
00819 {
00820 int ret = m_DB->sync(m_DB, 0);
00821 BDB_CHECK(ret, FileName().c_str());
00822 }
00823
00824
00825
00826
00827 #ifndef BDB_USE_NEW_STAT
00828 #if DB_VERSION_MAJOR >= 4
00829 #if DB_VERSION_MINOR >= 3
00830 #define BDB_USE_NEW_STAT
00831 #endif
00832 #endif
00833 #endif
00834
00835
00836 unsigned CBDB_RawFile::CountRecs(bool bFast)
00837 {
00838 Uint4 flags = 0;
00839 if (bFast) {
00840 flags = DB_FAST_STAT;
00841 }
00842 DB_BTREE_STAT* stp;
00843 #ifdef BDB_USE_NEW_STAT
00844 CBDB_Transaction* trans = GetBDBTransaction();
00845 DB_TXN* txn = trans ? trans->GetTxn() : 0;
00846 int ret = m_DB->stat(m_DB, txn, &stp, flags);
00847 #else
00848 int ret = m_DB->stat(m_DB, &stp, flags);
00849 #endif
00850
00851 BDB_CHECK(ret, FileName().c_str());
00852 u_int32_t rc = stp->bt_ndata;
00853
00854 ::free(stp);
00855
00856 return rc;
00857 }
00858
00859 void CBDB_RawFile::PrintStat(CNcbiOstream & out)
00860 {
00861 DB_BTREE_STAT* stp = 0;
00862 #ifdef BDB_USE_NEW_STAT
00863 CBDB_Transaction* trans = GetBDBTransaction();
00864 DB_TXN* txn = trans ? trans->GetTxn() : 0;
00865 int ret = m_DB->stat(m_DB, txn, &stp, 0);
00866 #else
00867 int ret = m_DB->stat(m_DB, &stp, 0);
00868 #endif
00869
00870 BDB_CHECK(ret, FileName().c_str());
00871
00872 out << FileName().c_str() << NcbiEndl;
00873 out << "bt_version : " << stp->bt_version << NcbiEndl
00874 << "bt_nkeys : " << stp->bt_nkeys << NcbiEndl
00875 << "bt_ndata : " << stp->bt_ndata << NcbiEndl
00876 << "bt_pagesize : " << stp->bt_pagesize << NcbiEndl
00877 << "bt_levels : " << stp->bt_levels << NcbiEndl
00878 << "bt_int_pg : " << stp->bt_int_pg << NcbiEndl
00879 << "bt_leaf_pg : " << stp->bt_leaf_pg << NcbiEndl
00880 << "bt_dup_pg : " << stp->bt_dup_pg << NcbiEndl
00881 << "bt_over_pg : " << stp->bt_over_pg << NcbiEndl
00882 #ifdef BDB_USE_NEW_STAT
00883 << "bt_empty_pg : " << stp->bt_empty_pg << NcbiEndl
00884 #endif
00885 << "bt_free : " << stp->bt_free << NcbiEndl
00886 << "bt_int_pgfree : " << stp->bt_int_pgfree << NcbiEndl
00887 << "bt_leaf_pgfree: " << stp->bt_leaf_pgfree<< NcbiEndl
00888 << "bt_dup_pgfree : " << stp->bt_dup_pgfree << NcbiEndl
00889 << "bt_over_pgfree: " << stp->bt_over_pgfree<< NcbiEndl
00890 ;
00891
00892 if (stp)
00893 ::free(stp);
00894 }
00895
00896
00897 void CBDB_RawFile::x_Create(const char* filename, const char* database)
00898 {
00899 _ASSERT(!m_DB_Attached);
00900 u_int32_t open_flags = DB_CREATE;
00901
00902 DB_TXN* txn = 0;
00903
00904 if (m_Env) {
00905 if (m_Env->IsTransactional()) {
00906 open_flags |= DB_THREAD | DB_AUTO_COMMIT;
00907 }
00908 }
00909
00910 DBTYPE db_type = DB_BTREE;
00911 switch (m_DB_Type) {
00912 case eBtree:
00913 break;
00914 case eQueue:
00915 db_type = DB_QUEUE;
00916 break;
00917 case eHash:
00918 db_type = DB_HASH;
00919 break;
00920 default:
00921 _ASSERT(0);
00922 }
00923
00924 int ret = m_DB->open(m_DB,
00925 txn,
00926 filename,
00927 database,
00928 db_type,
00929 open_flags,
00930 kOpenFileMask);
00931 if ( ret ) {
00932 m_DB->close(m_DB, 0);
00933 m_DB = 0;
00934 BDB_CHECK(ret, filename);
00935 }
00936 }
00937
00938
00939 DBC* CBDB_RawFile::CreateCursor(CBDB_Transaction* trans,
00940 unsigned int flags) const
00941 {
00942 DBC* cursor;
00943
00944 if (!m_DB) {
00945 BDB_THROW(eInvalidValue, "Cannot create cursor for unopen file.");
00946 }
00947
00948 DB_TXN* txn = 0;
00949 if (trans) {
00950 txn = trans->GetTxn();
00951 }
00952
00953 int ret = m_DB->cursor(m_DB,
00954 txn,
00955 &cursor,
00956 flags);
00957 BDB_CHECK(ret, FileName().c_str());
00958 return cursor;
00959 }
00960
00961 void CBDB_RawFile::x_SetByteSwapped(bool bswp)
00962 {
00963 m_ByteSwapped = bswp;
00964 }
00965
00966
00967 void CBDB_RawFile::SetHashFillFactor(unsigned h_ffactor)
00968 {
00969 _ASSERT(m_DB_Type == eHash);
00970 m_H_ffactor = h_ffactor;
00971 }
00972
00973 void CBDB_RawFile::SetHashNelem(unsigned h_nelem)
00974 {
00975 _ASSERT(m_DB_Type == eHash);
00976 m_H_nelem = h_nelem;
00977 }
00978
00979 void CBDB_RawFile::SetHash(DB* db)
00980 {
00981 _ASSERT(m_DB_Type == eHash);
00982 int ret = db->set_h_hash(db, BDB_Hash);
00983 BDB_CHECK(ret, 0);
00984 }
00985
00986 void CBDB_RawFile::SetBtreeMinKeysPerPage(unsigned int keys_per_page)
00987 {
00988 _ASSERT(m_DB_Type == eBtree);
00989 m_BT_minkey = max((unsigned int)2, keys_per_page);
00990 }
00991
00992 unsigned int CBDB_RawFile::GetBtreeMinKeysPerPage()
00993 {
00994 _ASSERT(m_DB_Type == eBtree);
00995 if ( !m_BT_minkey && m_DB) {
00996 int ret = m_DB->get_bt_minkey(m_DB, &m_BT_minkey);
00997 BDB_CHECK(ret, 0);
00998 }
00999 return m_BT_minkey;
01000 }
01001
01002 int CBDB_RawFile::x_FetchBufferDecompress(DBT *data, void* usr_data)
01003 {
01004 data->data = usr_data;
01005
01006 unsigned char* compressed = m_CompressBuffer.data();
01007 unsigned bytes_compressed;
01008 #ifdef HAVE_UNALIGNED_READS
01009 bytes_compressed = *((unsigned*)compressed);
01010 #else
01011 ::memcpy(&bytes_compressed, compressed, 4);
01012 #endif
01013
01014 if (bytes_compressed == 0) {
01015 data->size-=4;
01016 if (data->data) {
01017 ::memcpy(data->data, compressed + 4, data->size);
01018 }
01019 } else {
01020 if (data->ulen < bytes_compressed) {
01021 data->size = bytes_compressed;
01022 return DB_BUFFER_SMALL;
01023 }
01024 data->size-=4;
01025 if (data->data) {
01026 size_t dst_len;
01027 bool decomp_ok =
01028 m_Compressor->DecompressBuffer(compressed + 4,
01029 data->size,
01030 data->data,
01031 data->ulen,
01032 &dst_len);
01033 data->size = bytes_compressed;
01034 if (!decomp_ok) {
01035 BDB_THROW(eCompressorError,
01036 m_Compressor->GetErrorDescription());
01037 }
01038 _ASSERT(dst_len == bytes_compressed);
01039 }
01040 }
01041 return 0;
01042 }
01043
01044 int CBDB_RawFile::x_DB_Fetch(DBT *key,
01045 DBT *data,
01046 unsigned flags)
01047 {
01048 _ASSERT(key);
01049 _ASSERT(data);
01050
01051 int ret;
01052 DB_TXN* txn = GetTxn();
01053
01054 if (m_Compressor.get()) {
01055 _ASSERT(flags == 0 || flags & DB_DBT_USERMEM);
01056 _ASSERT(data->doff == 0);
01057
01058 m_CompressBuffer.resize_mem(data->ulen + 4);
01059 void* usr_data = data->data;
01060 data->data = m_CompressBuffer.data();
01061
01062 ret = m_DB->get(m_DB, txn, key, data, flags);
01063 if (ret == 0) {
01064 ret = x_FetchBufferDecompress(data, usr_data);
01065 }
01066 } else {
01067 ret = m_DB->get(m_DB, txn, key, data, flags);
01068 }
01069 return ret;
01070 }
01071
01072
01073 int CBDB_RawFile::x_DBC_Fetch(DBC* dbc,
01074 DBT *key,
01075 DBT *data,
01076 unsigned flags)
01077 {
01078 int ret;
01079 if (m_Compressor.get()) {
01080 m_CompressBuffer.resize_mem(data->ulen + 4);
01081 void* usr_data = data->data;
01082 data->data = m_CompressBuffer.data();
01083
01084 ret = dbc->c_get(dbc, key, data, flags);
01085 if (ret == 0) {
01086 ret = x_FetchBufferDecompress(data, usr_data);
01087 }
01088 } else {
01089 ret = dbc->c_get(dbc, key, data, flags);
01090 }
01091 return ret;
01092 }
01093
01094
01095
01096 const unsigned k_BDB_CompressionCutOff = 128;
01097
01098 int CBDB_RawFile::x_DB_Put(DBT *key,
01099 DBT *data,
01100 unsigned flags)
01101 {
01102 int ret;
01103
01104 DB_TXN* txn = GetTxn();
01105 if (m_Compressor.get()) {
01106
01107 void* usr_data = data->data;
01108 unsigned usr_size = data->size;
01109
01110 m_CompressBuffer.resize_mem(data->size + 4);
01111
01112 bool compressed = false;
01113
01114 if (data->size > k_BDB_CompressionCutOff) {
01115 m_CompressBuffer.resize_mem(data->size + 4);
01116
01117 unsigned *buf = (unsigned*)m_CompressBuffer.data();
01118 #ifdef HAVE_UNALIGNED_READS
01119 *buf = 0;
01120 #else
01121 ::memset(buf, 0, 4);
01122 #endif
01123 buf += 4;
01124
01125 size_t dst_len;
01126
01127 bool compressed =
01128 m_Compressor->CompressBuffer(data->data, data->size,
01129 buf, data->size,
01130 &dst_len);
01131 if (compressed) {
01132 _ASSERT(dst_len <= data->size);
01133 buf = (unsigned*)m_CompressBuffer.data();
01134 #ifdef HAVE_UNALIGNED_READS
01135 *buf = dst_len;
01136 #else
01137 ::memcpy(buf, &dst_len, 4);
01138 #endif
01139 m_CompressBuffer.resize_mem(dst_len);
01140 }
01141 }
01142
01143 if (!compressed) {
01144 unsigned *buf = (unsigned*)m_CompressBuffer.data();
01145 #ifdef HAVE_UNALIGNED_READS
01146 *buf = 0;
01147 #else
01148 ::memset(buf, 0, 4);
01149 #endif
01150 buf += 4;
01151 ::memcpy(buf, data->data, data->size);
01152 }
01153
01154
01155 data->data = m_CompressBuffer.data();
01156 data->size = m_CompressBuffer.size();
01157
01158 ret = m_DB->put(m_DB, txn, key, data, flags);
01159
01160
01161 data->data = usr_data;
01162 data->size = usr_size;
01163
01164 } else {
01165 ret = m_DB->put(m_DB, txn, key, data, flags);
01166 }
01167 return ret;
01168 }
01169
01170
01171 int CBDB_RawFile::x_DB_CPut(DBC *dbc,
01172 DBT *key,
01173 DBT *data,
01174 unsigned flags)
01175 {
01176 int ret;
01177
01178 if (m_Compressor.get()) {
01179
01180 void* usr_data = data->data;
01181 unsigned usr_size = data->size;
01182
01183 m_CompressBuffer.resize_mem(data->size + 4);
01184
01185 bool compressed = false;
01186
01187 if (data->size > k_BDB_CompressionCutOff) {
01188 m_CompressBuffer.resize_mem(data->size + 4);
01189
01190 unsigned *buf = (unsigned*)m_CompressBuffer.data();
01191 #ifdef HAVE_UNALIGNED_READS
01192 *buf = 0;
01193 #else
01194 ::memset(buf, 0, 4);
01195 #endif
01196 buf += 4;
01197
01198 size_t dst_len;
01199
01200 bool compressed =
01201 m_Compressor->CompressBuffer(data->data, data->size,
01202 buf, data->size,
01203 &dst_len);
01204 if (compressed) {
01205 _ASSERT(dst_len <= data->size);
01206 buf = (unsigned*)m_CompressBuffer.data();
01207 #ifdef HAVE_UNALIGNED_READS
01208 *buf = dst_len;
01209 #else
01210 ::memcpy(buf, &dst_len, 4);
01211 #endif
01212 m_CompressBuffer.resize_mem(dst_len);
01213 }
01214 }
01215
01216 if (!compressed) {
01217 unsigned *buf = (unsigned*)m_CompressBuffer.data();
01218 #ifdef HAVE_UNALIGNED_READS
01219 *buf = 0;
01220 #else
01221 ::memset(buf, 0, 4);
01222 #endif
01223 buf += 4;
01224 ::memcpy(buf, data->data, data->size);
01225 }
01226
01227
01228 data->data = m_CompressBuffer.data();
01229 data->size = m_CompressBuffer.size();
01230
01231 ret = dbc->c_put(dbc, key, data, flags);
01232
01233
01234 data->data = usr_data;
01235 data->size = usr_size;
01236
01237 } else {
01238 ret = dbc->c_put(dbc, key, data, flags);
01239 }
01240
01241 return ret;
01242
01243 }
01244
01245
01246
01247
01248
01249
01250
01251
01252 CBDB_File::CBDB_File(EDuplicateKeys dup_keys, EDBType db_type)
01253 : CBDB_RawFile(dup_keys, db_type),
01254 m_KeyBuf(new CBDB_BufferManager),
01255 m_BufsAttached(false),
01256 m_BufsCreated(false),
01257 m_DataBufDisabled(false),
01258 m_LegacyString(false),
01259 m_OwnFields(false),
01260 m_DisabledNull(false),
01261 m_PrefixCompress(false)
01262 {
01263 }
01264
01265 void CBDB_File::SetFieldOwnership(bool own_fields)
01266 {
01267 m_OwnFields = own_fields;
01268
01269 m_KeyBuf->SetFieldOwnership(own_fields);
01270 if (m_DataBuf.get() != 0) {
01271 m_DataBuf->SetFieldOwnership(own_fields);
01272 }
01273 }
01274
01275 void CBDB_File::x_ConstructKeyBuf()
01276 {
01277 m_KeyBuf.reset(new CBDB_BufferManager);
01278 m_KeyBuf->SetLegacyStringsCheck(m_LegacyString);
01279 m_KeyBuf->SetFieldOwnership(m_OwnFields);
01280 }
01281
01282 void CBDB_File::x_ConstructDataBuf()
01283 {
01284 m_DataBuf.reset(new CBDB_BufferManager);
01285 if (!m_DisabledNull) {
01286 m_DataBuf->SetNullable();
01287 }
01288 m_DataBuf->SetLegacyStringsCheck(m_LegacyString);
01289 m_DataBuf->SetFieldOwnership(m_OwnFields);
01290 }
01291
01292 void CBDB_File::BindKey(const char* field_name,
01293 CBDB_Field* key_field,
01294 size_t buf_size)
01295 {
01296 _ASSERT(!IsOpen());
01297 _ASSERT(m_KeyBuf.get());
01298 _ASSERT(key_field);
01299
01300 key_field->SetName(field_name);
01301 m_KeyBuf->Bind(key_field);
01302 if ( buf_size )
01303 key_field->SetDataSize(buf_size);
01304 }
01305
01306
01307 void CBDB_File::BindData(const char* field_name,
01308 CBDB_Field* data_field,
01309 size_t buf_size,
01310 ENullable is_nullable)
01311 {
01312 _ASSERT(!IsOpen());
01313 _ASSERT(data_field);
01314
01315 data_field->SetName(field_name);
01316
01317 if (m_DataBuf.get() == 0) {
01318 x_ConstructDataBuf();
01319 }
01320
01321 m_DataBuf->Bind(data_field);
01322 if ( buf_size > 0) {
01323 data_field->SetDataSize(buf_size);
01324 }
01325 if (is_nullable == eNullable && !m_DisabledNull) {
01326 data_field->SetNullable();
01327 }
01328 }
01329
01330
01331 void CBDB_File::Open(const string& filename,
01332 const string& database,
01333 EOpenMode open_mode,
01334 bool support_dirty_read,
01335 unsigned rec_len)
01336 {
01337 if ( IsOpen() )
01338 Close();
01339 x_CheckConstructBuffers();
01340
01341 if (m_DB_Type == eQueue) {
01342 DisableDataPacking();
01343 if (m_DataBuf.get()) {
01344 rec_len = m_DataBuf->ComputeBufferSize();
01345 }
01346 }
01347
01348 CBDB_RawFile::Open(filename, database,
01349 open_mode, support_dirty_read, rec_len);
01350
01351 m_DB->app_private = (void*) m_KeyBuf.get();
01352
01353 }
01354
01355
01356 void CBDB_File::Reopen(EOpenMode open_mode, bool support_dirty_read)
01357 {
01358 unsigned rec_len = 0;
01359 if (m_DB_Type == eQueue) {
01360 if (m_DataBuf.get()) {
01361 rec_len = m_DataBuf->ComputeBufferSize();
01362 }
01363 }
01364 CBDB_RawFile::Reopen(open_mode, support_dirty_read, rec_len);
01365 m_DB->app_private = (void*) m_KeyBuf.get();
01366 if ( m_DataBuf.get() ) {
01367 m_DataBuf->SetAllNull();
01368 }
01369 bool byte_swapped = IsByteSwapped();
01370 m_KeyBuf->SetByteSwapped(byte_swapped);
01371 if (m_DataBuf.get()) {
01372 m_DataBuf->SetByteSwapped(byte_swapped);
01373 }
01374 }
01375
01376
01377 void CBDB_File::Attach(CBDB_File& db_file)
01378 {
01379 CBDB_RawFile::Attach(db_file);
01380 x_CheckConstructBuffers();
01381 SetLegacyStringsCheck(db_file.m_LegacyString);
01382 }
01383
01384 void CBDB_File::SetLegacyStringsCheck(bool value)
01385 {
01386 m_LegacyString = value;
01387 if (m_KeyBuf.get()) {
01388 m_KeyBuf->SetLegacyStringsCheck(value);
01389 }
01390 if (m_DataBuf.get()) {
01391 m_DataBuf->SetLegacyStringsCheck(value);
01392 }
01393 }
01394
01395 void CBDB_File::x_SetByteSwapped(bool bswp)
01396 {
01397 CBDB_RawFile::x_SetByteSwapped(bswp);
01398 m_KeyBuf->SetByteSwapped(bswp);
01399 if (m_DataBuf.get()) {
01400 m_DataBuf->SetByteSwapped(bswp);
01401 }
01402 }
01403
01404 void CBDB_File::Verify(const char* filename,
01405 const char* database,
01406 FILE* backup)
01407 {
01408 if (m_DB == 0) {
01409 x_CreateDB(0);
01410 }
01411 x_CheckConstructBuffers();
01412 m_DB->app_private = (void*) m_KeyBuf.get();
01413
01414
01415 m_DB->verify(m_DB, filename, database, backup, backup ? DB_SALVAGE: 0);
01416 }
01417
01418
01419 #if DB_VERSION_MAJOR >= 4
01420 #if DB_VERSION_MINOR >= 3
01421 #define BDB_CHECK_BUFFER_SMALL
01422 #endif
01423 #endif
01424
01425
01426 EBDB_ErrCode CBDB_File::x_Fetch(unsigned int flags)
01427 {
01428 x_StartRead();
01429
01430 int ret = x_DB_Fetch(m_DBT_Key,
01431 m_DBT_Data,
01432 flags);
01433
01434 if (ret == DB_NOTFOUND) {
01435 return eBDB_NotFound;
01436 }
01437
01438
01439
01440 # ifdef BDB_CHECK_BUFFER_SMALL
01441 if ((ret == ENOMEM || ret == DB_BUFFER_SMALL)
01442 && m_DataBufDisabled && m_DBT_Data->data == 0) {
01443 ret = 0;
01444 }
01445 # else
01446 if (ret == ENOMEM && m_DataBufDisabled && m_DBT_Data->data == 0) {
01447 ret = 0;
01448 }
01449 # endif
01450 BDB_CHECK(ret, FileName().c_str());
01451
01452 x_EndRead();
01453 return eBDB_Ok;
01454 }
01455
01456 EBDB_ErrCode CBDB_File::FetchForUpdate()
01457 {
01458 return x_Fetch(DB_RMW);
01459 }
01460
01461
01462 DBT* CBDB_File::CloneDBT_Key()
01463 {
01464 x_StartRead();
01465 x_EndRead();
01466
01467 DBT* dbt = new DBT;
01468 ::memset(dbt, 0, sizeof(DBT));
01469
01470
01471 if (m_DBT_Key->ulen) {
01472 dbt->size = m_DBT_Key->size;
01473 dbt->ulen = m_DBT_Key->ulen;
01474 unsigned char* p = (unsigned char*)malloc(dbt->ulen);
01475 ::memcpy(p, m_DBT_Key->data, m_DBT_Key->size);
01476 dbt->data = p;
01477 dbt->flags = DB_DBT_USERMEM;
01478 }
01479 return dbt;
01480 }
01481
01482 void CBDB_File::DestroyDBT_Clone(DBT* dbt)
01483 {
01484 unsigned char* p = (unsigned char*)dbt->data;
01485 free(p); dbt->data = NULL;
01486 delete dbt;
01487 }
01488
01489
01490 EBDB_ErrCode CBDB_File::Insert(EAfterWrite write_flag)
01491 {
01492 CheckNullDataConstraint();
01493
01494 unsigned int flags;
01495 if (DuplicatesAllowed()) {
01496 flags = 0;
01497 } else {
01498 flags = DB_NOOVERWRITE;
01499 }
01500 return x_Write(flags, write_flag);
01501 }
01502
01503 unsigned CBDB_File::Append(EAfterWrite write_flag)
01504 {
01505 unsigned int flags = DB_APPEND;
01506 x_Write(flags, write_flag);
01507 unsigned rec_id;
01508 memcpy(&rec_id, m_DBT_Key->data, sizeof(rec_id));
01509 return rec_id;
01510 }
01511
01512
01513 EBDB_ErrCode CBDB_File::UpdateInsert(EAfterWrite write_flag)
01514 {
01515 CheckNullDataConstraint();
01516 return x_Write(0, write_flag);
01517 }
01518
01519
01520 EBDB_ErrCode CBDB_File::Delete(EIgnoreError on_error)
01521 {
01522 EBDB_ErrCode rcode = eBDB_Ok;
01523 m_KeyBuf->PrepareDBT_ForWrite(m_DBT_Key);
01524 DB_TXN* txn = GetTxn();
01525
01526 int ret = m_DB->del(m_DB,
01527 txn,
01528 m_DBT_Key,
01529 0);
01530 if (ret == DB_NOTFOUND) {
01531 ret = 0;
01532 rcode = eBDB_NotFound;
01533 }
01534 if (on_error != eIgnoreError) {
01535 BDB_CHECK(ret, FileName().c_str());
01536 }
01537 Discard();
01538 return rcode;
01539 }
01540
01541
01542 void CBDB_File::Discard()
01543 {
01544 m_KeyBuf->ArrangePtrsUnpacked();
01545 if ( m_DataBuf.get() ) {
01546 m_DataBuf->ArrangePtrsUnpacked();
01547 m_DataBuf->SetAllNull();
01548 }
01549 }
01550
01551
01552
01553 size_t
01554 BDB_compare_prefix(DB* dbp, const DBT* a, const DBT* b)
01555 {
01556 size_t cnt, len;
01557 char* p1, *p2;
01558
01559 cnt = 1; len = a->size > b->size ? b->size : a->size;
01560 p1 = (char*)a->data, p2 = (char*)b->data;
01561 for (;len--; ++p1, ++p2, ++cnt) {
01562 if (*p1 != *p2) {
01563 return (cnt);
01564 }
01565 }
01566 if (a->size < b->size) return (a->size + 1);
01567 if (b->size < a->size) return (b->size + 1);
01568 return (b->size);
01569 }
01570
01571
01572 void CBDB_File::SetCmp(DB* db)
01573 {
01574 _ASSERT(m_DB_Type == eBtree);
01575 BDB_CompareFunction func = m_KeyBuf->GetCompareFunction();
01576 _ASSERT(func);
01577 int ret = db->set_bt_compare(db, func);
01578 BDB_CHECK(ret, 0);
01579
01580 if (m_PrefixCompress) {
01581 ret = m_DB->set_bt_prefix(m_DB, BDB_compare_prefix);
01582 BDB_CHECK(ret, 0);
01583 }
01584 }
01585
01586
01587
01588 CBDB_File::TUnifiedFieldIndex
01589 CBDB_File::GetFieldIdx(const string& name) const
01590 {
01591 int fidx = 0;
01592 if (m_KeyBuf.get()) {
01593 fidx = m_KeyBuf->GetFieldIndex(name);
01594 if (fidx >= 0) {
01595 return BDB_GetUFieldIdx(fidx, true );
01596 }
01597 }
01598
01599 if (m_DataBuf.get()) {
01600 fidx = m_DataBuf->GetFieldIndex(name);
01601 if (fidx >= 0) {
01602 return BDB_GetUFieldIdx(fidx, false );
01603 }
01604 }
01605 return 0;
01606 }
01607
01608 const CBDB_Field& CBDB_File::GetField(TUnifiedFieldIndex idx) const
01609 {
01610 _ASSERT(idx != 0);
01611
01612 const CBDB_BufferManager* buffer;
01613
01614 if (idx < 0) {
01615 idx = -idx;
01616 --idx;
01617 buffer = m_KeyBuf.get();
01618 } else {
01619 --idx;
01620 buffer = m_DataBuf.get();
01621 }
01622
01623 _ASSERT(buffer);
01624
01625 const CBDB_Field& fld = buffer->GetField(idx);
01626 return fld;
01627 }
01628
01629
01630 CBDB_Field& CBDB_File::GetField(TUnifiedFieldIndex idx)
01631 {
01632 _ASSERT(idx != 0);
01633
01634 CBDB_BufferManager* buffer;
01635
01636 if (idx < 0) {
01637 idx = -idx;
01638 --idx;
01639 buffer = m_KeyBuf.get();
01640 } else {
01641 --idx;
01642 buffer = m_DataBuf.get();
01643 }
01644 _ASSERT(buffer);
01645
01646 CBDB_Field& fld = buffer->GetField(idx);
01647 return fld;
01648 }
01649
01650 void CBDB_File::DisableDataPacking()
01651 {
01652 if (m_DataBuf.get()) {
01653 m_DataBuf->SetPackable(false);
01654 }
01655 }
01656
01657 void CBDB_File::CopyFrom(const CBDB_File& dbf)
01658 {
01659 const CBDB_BufferManager* src_key = dbf.GetKeyBuffer();
01660 const CBDB_BufferManager* src_data = dbf.GetDataBuffer();
01661
01662 CBDB_BufferManager* key = GetKeyBuffer();
01663 CBDB_BufferManager* data = GetDataBuffer();
01664
01665 key->CopyFrom(*src_key);
01666 if (data) {
01667 data->CopyFrom(*src_data);
01668 }
01669 }
01670
01671 void CBDB_File::DuplicateStructure(const CBDB_File& dbf)
01672 {
01673 const CBDB_BufferManager* src_key = dbf.GetKeyBuffer();
01674 const CBDB_BufferManager* src_data = dbf.GetDataBuffer();
01675
01676 _ASSERT(src_key);
01677
01678 x_ConstructKeyBuf();
01679 m_KeyBuf->DuplicateStructureFrom(*src_key);
01680
01681 if (src_data) {
01682 x_ConstructDataBuf();
01683 m_DataBuf->DuplicateStructureFrom(*src_data);
01684 } else {
01685 m_DataBuf.reset(0);
01686 }
01687 }
01688
01689 EBDB_ErrCode CBDB_File::ReadCursor(DBC* dbc, unsigned int bdb_flag)
01690 {
01691 x_StartRead();
01692
01693 if (m_DataBufDisabled) {
01694 m_DBT_Data->size = 0;
01695 m_DBT_Data->flags = 0;
01696 m_DBT_Data->data = 0;
01697 }
01698
01699 int ret = x_DBC_Fetch(dbc, m_DBT_Key, m_DBT_Data, bdb_flag);
01700
01701 switch (ret) {
01702 case DB_NOTFOUND:
01703 return eBDB_NotFound;
01704 case DB_KEYEMPTY:
01705
01706 return eBDB_KeyEmpty;
01707 }
01708
01709 BDB_CHECK(ret, FileName().c_str());
01710 x_EndRead();
01711 return eBDB_Ok;
01712 }
01713
01714 EBDB_ErrCode CBDB_File::ReadCursor(DBC* dbc,
01715 unsigned int bdb_flag,
01716 TBuffer* buf)
01717 {
01718 _ASSERT(buf);
01719
01720 if (buf->size() == 0) {
01721 buf->resize(1024);
01722 }
01723 if (buf->size() < buf->capacity()) {
01724 buf->resize(buf->capacity());
01725 }
01726
01727 x_StartRead();
01728 m_DBT_Data->data = &((*buf)[0]);
01729 m_DBT_Data->ulen = buf->size();
01730 m_DBT_Data->size = 0;
01731 m_DBT_Data->flags = DB_DBT_USERMEM;
01732
01733 int ret = x_DBC_Fetch(dbc, m_DBT_Key, m_DBT_Data, bdb_flag);
01734
01735 switch (ret) {
01736 case DB_NOTFOUND:
01737 buf->resize_mem(0);
01738 return eBDB_NotFound;
01739 case DB_KEYEMPTY:
01740
01741 buf->resize_mem(0);
01742 return eBDB_KeyEmpty;
01743 case DB_BUFFER_SMALL:
01744 buf->resize_mem(m_DBT_Data->size);
01745 return this->ReadCursor(dbc, bdb_flag, buf);
01746 break;
01747 }
01748
01749 BDB_CHECK(ret, FileName().c_str());
01750 buf->resize(m_DBT_Data->size);
01751 x_EndRead();
01752
01753 return eBDB_Ok;
01754 }
01755
01756 EBDB_ErrCode CBDB_File::ReadCursor(DBC* dbc,
01757 unsigned int bdb_flag,
01758 void** buf,
01759 size_t buf_size,
01760 EReallocMode allow_realloc)
01761 {
01762 x_StartRead();
01763 m_DBT_Data->data = buf ? *buf : 0;
01764 m_DBT_Data->ulen = (unsigned)buf_size;
01765 m_DBT_Data->size = 0;
01766
01767 if (allow_realloc == eReallocForbidden) {
01768 m_DBT_Data->flags = DB_DBT_USERMEM;
01769 } else {
01770 if (m_DBT_Data->data == 0) {
01771 m_DBT_Data->flags = DB_DBT_MALLOC;
01772 } else {
01773
01774 _ASSERT(m_Compressor.get() == 0);
01775
01776 if (m_Compressor.get()) {
01777 BDB_THROW(eCompressorError,
01778 "Use of dynamic reallocation on compressed file - not implemented");
01779 }
01780
01781 m_DBT_Data->flags = DB_DBT_REALLOC;
01782 }
01783 }
01784
01785 int ret = x_DBC_Fetch(dbc, m_DBT_Key, m_DBT_Data, bdb_flag);
01786
01787 if ( buf )
01788 *buf = m_DBT_Data->data;
01789
01790 switch (ret) {
01791 case DB_NOTFOUND:
01792 return eBDB_NotFound;
01793 case DB_KEYEMPTY:
01794
01795 return eBDB_KeyEmpty;
01796 }
01797
01798 BDB_CHECK(ret, FileName().c_str());
01799 x_EndRead();
01800 return eBDB_Ok;
01801 }
01802
01803 EBDB_ErrCode CBDB_File::ReadCursor(DBC* dbc,
01804 unsigned int bdb_flag,
01805 CBDB_MultiRowBuffer* multirow_buf,
01806 bool multirow_only)
01807 {
01808 int ret;
01809 db_recno_t recno = 0;
01810 if (multirow_buf == 0) {
01811 return ReadCursor(dbc, bdb_flag);
01812 }
01813
01814
01815
01816 if (multirow_buf->m_BufPtr != 0) {
01817 switch (m_DB_Type) {
01818 case eBtree:
01819 case eHash:
01820 {{
01821 DB_MULTIPLE_KEY_NEXT(multirow_buf->m_BufPtr,
01822 multirow_buf->m_Data_DBT,
01823 multirow_buf->m_LastKey,
01824 multirow_buf->m_LastKeyLen,
01825 multirow_buf->m_LastData,
01826 multirow_buf->m_LastDataLen);
01827 }}
01828 break;
01829
01830 case eQueue:
01831 {{
01832 DB_MULTIPLE_RECNO_NEXT(multirow_buf->m_BufPtr,
01833 multirow_buf->m_Data_DBT,
01834 recno,
01835 multirow_buf->m_LastData,
01836 multirow_buf->m_LastDataLen);
01837 multirow_buf->m_LastKey = &recno;
01838 multirow_buf->m_LastKeyLen = sizeof(recno);
01839 }}
01840 break;
01841
01842 default:
01843 _ASSERT(0);
01844 NCBI_THROW(CException, eUnknown, "invalid multifetch cursor type");
01845 }
01846
01847 if (multirow_buf->m_BufPtr != 0) {
01848 goto read_epilog;
01849 }
01850 if (multirow_only) {
01851 return eBDB_MultiRowEnd;
01852 }
01853 } else {
01854 if (multirow_only) {
01855 return eBDB_MultiRowEnd;
01856 }
01857 }
01858
01859
01860
01861 m_KeyBuf->Pack();
01862 m_KeyBuf->PrepareDBT_ForRead(m_DBT_Key);
01863
01864 multirow_buf->InitDBT();
01865
01866
01867 ret = dbc->c_get(dbc,
01868 m_DBT_Key,
01869 multirow_buf->m_Data_DBT,
01870 bdb_flag | DB_MULTIPLE_KEY);
01871 switch (ret) {
01872 case DB_NOTFOUND:
01873 return eBDB_NotFound;
01874 case DB_KEYEMPTY:
01875
01876 return eBDB_KeyEmpty;
01877 }
01878
01879 BDB_CHECK(ret, FileName().c_str());
01880
01881
01882
01883
01884 multirow_buf->MultipleInit();
01885
01886 switch (m_DB_Type) {
01887 case eBtree:
01888 case eHash:
01889 {{
01890 DB_MULTIPLE_KEY_NEXT(multirow_buf->m_BufPtr,
01891 multirow_buf->m_Data_DBT,
01892 multirow_buf->m_LastKey,
01893 multirow_buf->m_LastKeyLen,
01894 multirow_buf->m_LastData,
01895 multirow_buf->m_LastDataLen);
01896 }}
01897 break;
01898
01899 case eQueue:
01900 {{
01901 DB_MULTIPLE_RECNO_NEXT(multirow_buf->m_BufPtr,
01902 multirow_buf->m_Data_DBT,
01903 recno,
01904 multirow_buf->m_LastData,
01905 multirow_buf->m_LastDataLen);
01906 multirow_buf->m_LastKey = &recno;
01907 multirow_buf->m_LastKeyLen = sizeof(recno);
01908 }}
01909 break;
01910
01911 default:
01912 _ASSERT(0);
01913 NCBI_THROW(CException, eUnknown, "invalid multifetch cursor type");
01914 }
01915
01916 if (multirow_buf->m_BufPtr == 0) {
01917 return eBDB_NotFound;
01918 }
01919
01920
01921
01922 read_epilog:
01923 m_KeyBuf->CopyPackedFrom(multirow_buf->m_LastKey,
01924 multirow_buf->m_LastKeyLen);
01925 if ( m_DataBuf.get() ) {
01926 if (m_Compressor.get()) {
01927
01928 unsigned char* uncompressed_data =
01929 (unsigned char*) multirow_buf->m_LastData;
01930 unsigned bytes_compressed;
01931 #ifdef HAVE_UNALIGNED_READS
01932 bytes_compressed = *((unsigned*)multirow_buf->m_LastData);
01933 #else
01934 ::memcpy(&bytes_compressed, multirow_buf->m_LastData, 4);
01935 #endif
01936 uncompressed_data += 4;
01937
01938 if (bytes_compressed == 0) {
01939 m_DataBuf->CopyPackedFrom(uncompressed_data,
01940 multirow_buf->m_LastDataLen-4);
01941 } else {
01942 m_CompressBuffer.resize_mem(bytes_compressed);
01943 size_t dst_len;
01944 bool decomp_ok =
01945 m_Compressor->DecompressBuffer(uncompressed_data,
01946 multirow_buf->m_LastDataLen-4,
01947 m_CompressBuffer.data(),
01948 m_CompressBuffer.size(),
01949 &dst_len);
01950 if (!decomp_ok) {
01951 BDB_THROW(eCompressorError,
01952 m_Compressor->GetErrorDescription());
01953 }
01954 _ASSERT(dst_len == m_CompressBuffer.size());
01955 m_DataBuf->CopyPackedFrom(m_CompressBuffer.data(),
01956 dst_len);
01957
01958 }
01959
01960 } else {
01961 m_DataBuf->CopyPackedFrom(multirow_buf->m_LastData,
01962 multirow_buf->m_LastDataLen);
01963 }
01964 }
01965 return eBDB_Ok;
01966 }
01967
01968
01969
01970 EBDB_ErrCode CBDB_File::WriteCursor(DBC* dbc, unsigned int bdb_flag,
01971 EAfterWrite write_flag)
01972 {
01973 CheckNullDataConstraint();
01974 return x_Write(bdb_flag, write_flag, dbc);
01975 }
01976
01977 EBDB_ErrCode CBDB_File::WriteCursor(const void* data,
01978 size_t size,
01979 DBC* dbc,
01980 unsigned int bdb_flag,
01981 EAfterWrite write_flag)
01982 {
01983 if (!m_DataBufDisabled) {
01984 BDB_THROW(eInvalidOperation, "BLOB operation on non BLOB table");
01985 }
01986
01987 m_DBT_Data->data = const_cast<void*> (data);
01988 m_DBT_Data->size = m_DBT_Data->ulen = (unsigned)size;
01989
01990 return x_Write(bdb_flag, write_flag, dbc);
01991 }
01992
01993
01994 EBDB_ErrCode CBDB_File::DeleteCursor(DBC* dbc, EIgnoreError on_error)
01995 {
01996 int ret = dbc->c_del(dbc, 0);
01997
01998 if (on_error != CBDB_File::eIgnoreError) {
01999 BDB_CHECK(ret, FileName().c_str());
02000 }
02001
02002 return eBDB_Ok;
02003 }
02004
02005 void CBDB_File::x_CheckConstructBuffers()
02006 {
02007 if (!m_BufsAttached && !m_BufsCreated) {
02008 if (m_KeyBuf->FieldCount() == 0) {
02009 BDB_THROW(eInvalidValue, "Empty BDB key (no fields defined).");
02010 }
02011
02012 m_KeyBuf->Construct();
02013 if ( m_DataBuf.get() ) {
02014 m_DataBuf->Construct();
02015 m_DataBuf->SetAllNull();
02016 }
02017 m_BufsCreated = 1;
02018 }
02019 }
02020
02021 void CBDB_File::x_StartRead()
02022 {
02023 m_KeyBuf->Pack();
02024 m_KeyBuf->PrepareDBT_ForRead(m_DBT_Key);
02025
02026 if (!m_DataBufDisabled) {
02027 if ( m_DataBuf.get() ) {
02028 m_DataBuf->PrepareDBT_ForRead(m_DBT_Data);
02029 }
02030 else {
02031 m_DBT_Data->size = 0;
02032 m_DBT_Data->flags = 0;
02033 m_DBT_Data->data = 0;
02034 }
02035 }
02036 }
02037
02038
02039 void CBDB_File::x_EndRead()
02040 {
02041 m_KeyBuf->SetDBT_Size(m_DBT_Key->size);
02042 m_KeyBuf->ArrangePtrsPacked();
02043 if ( m_DataBuf.get() ) {
02044 m_DataBuf->SetDBT_Size(m_DBT_Data->size);
02045 m_DataBuf->ArrangePtrsPacked();
02046 }
02047 }
02048
02049
02050 EBDB_ErrCode CBDB_File::x_Write(unsigned int flags,
02051 EAfterWrite write_flag,
02052 DBC * dbc)
02053 {
02054 m_KeyBuf->PrepareDBT_ForWrite(m_DBT_Key);
02055
02056 if (!m_DataBufDisabled) {
02057 if ( m_DataBuf.get() ) {
02058 m_DataBuf->PrepareDBT_ForWrite(m_DBT_Data);
02059 }
02060 }
02061
02062 int ret=0;
02063 if (dbc) {
02064 ret = x_DB_CPut(dbc, m_DBT_Key, m_DBT_Data, flags);
02065 } else {
02066 ret = x_DB_Put(m_DBT_Key, m_DBT_Data, flags);
02067 }
02068 if (ret == DB_KEYEXIST)
02069 return eBDB_KeyDup;
02070
02071 BDB_CHECK(ret, FileName().c_str());
02072
02073 if (write_flag == eDiscardData) {
02074 Discard();
02075 }
02076
02077 return eBDB_Ok;
02078 }
02079
02080
02081
02082
02083
02084
02085
02086
02087 CBDB_IdFile::CBDB_IdFile()
02088 : CBDB_File()
02089 {
02090 BindKey("id", &IdKey);
02091 }
02092
02093 void CBDB_IdFile::SetCmp(DB* )
02094 {
02095 BDB_CompareFunction func = BDB_Int4Compare;
02096 if (IsByteSwapped()) {
02097 func = BDB_ByteSwap_Int4Compare;
02098 }
02099
02100 _ASSERT(func);
02101 int ret = m_DB->set_bt_compare(m_DB, func);
02102 BDB_CHECK(ret, 0);
02103 }
02104
02105
02106 END_NCBI_SCOPE
02107
02108