src/db/bdb/bdb_file.cpp

Go to the documentation of this file.
00001 /*  $Id: bdb_file.cpp 170651 2009-09-15 18:41:12Z joukovv $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Author: Anatoliy Kuznetsov
00027  *
00028  * File Description:  BDB libarary file implementations.
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 /// Auto-pointer style guard class for DB structure
00064 ///
00065 /// @internal
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 //  CBDB_MultiRowBuffer::
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 //  CBDB_RawFile::
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     // It's illegal to close a file involved in active transactions
00177 
00178     if ( m_Trans != 0 &&
00179         (m_TransAssociation == (int) CBDB_Transaction::eFullAssociation) &&
00180         (m_Trans->IsInProgress())) {
00181         _ASSERT(0);
00182 
00183         // If we are here we can try to communicate by throwing
00184         // an exception. It's illegal, but situation is bad enough already
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     // temporary DB is used here, because BDB remove call invalidates the
00368     // DB argument redardless of the result.
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;  // Non existent table cannot be removed
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 /// Compact the database.  The target fill percent per page can be
00484 /// supplied, to allow for known expansion
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         /// default
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; // GetTxn();
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,             // database name
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             // file opened succesfully, check if it needs
00767             // a byte swapping (different byteorder)
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                 // re-open the file
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, // database name
00786                                  db_type,
00787                                  open_flags,
00788                                  kOpenFileMask);
00789                 BDB_CHECK(ret, filename);
00790 
00791             }
00792 
00793         }
00794     } // else open_mode == Create
00795 
00796     m_OpenMode = open_mode;
00797 }
00798 
00799 
00800 void CBDB_RawFile::SetPageSize(unsigned int page_size)
00801 {
00802     _ASSERT(m_DB == 0); // we can set page size only before opening the file
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 // check BDB version 4.3 changed DB->stat signature
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; //GetTxn();
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,        // database name
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; // GetTxn();
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 /// Record size cut off for compression
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        // save original data fields
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)  { // store uncompressed data
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         // store the compress buffer
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         // restore buffers
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        // save original data fields
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)  { // store uncompressed data
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         // store the compress buffer
01228         data->data = m_CompressBuffer.data();
01229         data->size = m_CompressBuffer.size();
01230 
01231         ret = dbc->c_put(dbc, key, data, flags);
01232 
01233         // restore buffers
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 //  CBDB_File::
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) {  // data buffer is not yet created
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     /*int ret = */
01415     m_DB->verify(m_DB, filename, database, backup, backup ? DB_SALVAGE: 0);
01416 }
01417 
01418 // v 4.3.xx introduced new error code DB_BUFFER_SMALL
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     // Disable error reporting for custom m_DBT_data management
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     // Clone the "data" area (needs to be properly deleted!)
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_NODUPDATA |*/ 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 /// @internal
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) {    //  field name found
01595             return BDB_GetUFieldIdx(fidx, true /*key*/);
01596         }
01597     }
01598 
01599     if (m_DataBuf.get()) {
01600         fidx = m_DataBuf->GetFieldIndex(name);
01601         if (fidx >= 0) {    //  field name found
01602             return BDB_GetUFieldIdx(fidx, false /*non-key*/);
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) { // key buffer
01615         idx = -idx;
01616         --idx;
01617         buffer = m_KeyBuf.get();
01618     } else {  // data buffer
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) {     // key buffer
01637         idx = -idx;
01638         --idx;
01639         buffer = m_KeyBuf.get();
01640     } else {          // data buffer
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); // disable packing
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         // record has been deleted
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         // record has been deleted
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     } // switch
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             // compressor does not support re-alloc mode
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         // record has been deleted
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     // Something sits in the memory buffer already, get the next record
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     // read prolog actions
01861     m_KeyBuf->Pack();
01862     m_KeyBuf->PrepareDBT_ForRead(m_DBT_Key);
01863 
01864     multirow_buf->InitDBT();
01865 
01866     // Cursor read
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         // record has been deleted
01876         return eBDB_KeyEmpty;
01877     }
01878 
01879     BDB_CHECK(ret, FileName().c_str());
01880 
01881 
01882     // Get the first record out of the fetching buffer
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     // Read epilog (copy things into field buffer)
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             // first 4 bytes in the buffer encode length
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 //  CBDB_IdFile::
02085 //
02086 
02087 CBDB_IdFile::CBDB_IdFile()
02088 : CBDB_File()
02089 {
02090     BindKey("id", &IdKey);
02091 }
02092 
02093 void CBDB_IdFile::SetCmp(DB* /* 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 

Generated on Sun Dec 6 22:22:46 2009 for NCBI C++ ToolKit by  doxygen 1.4.6
Modified on Mon Dec 07 16:20:58 2009 by modify_doxy.py rev. 173732