Files
secondo/StorageManager/bdbEnvironment.cpp
2026-01-23 17:03:45 +08:00

2182 lines
55 KiB
C++

/*
----
This file is part of SECONDO.
Copyright (C) 2004-2008, University in Hagen, Faculty of Mathematics and
Computer Science, Database Systems for New Applications.
SECONDO is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
SECONDO is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with SECONDO; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
----
1 Implementation of SmiEnvironment using the Berkeley-DB
May 2002 Ulrich Telle
September 2002 Ulrich Telle, abort transaction after deadlock
February 2003 Ulrich Telle, adjusted for Berkeley DB version 4.1.25
April 2003 Ulrich Telle, implemented temporary SmiFiles
October 2003 M. Spiekermann Startup modified. SecondoHome directory will be
created if the configuration file contains no or a non-existent directory.
April 2004 Hoffmann Changed some implementation details, so that the list
databases command is available under Windows XP.
August 2004 M. Spiekermann, A new parameter ~dontSyncDiskCache~ has been
introduced to speed up closing files at the end of a query. The problem arised
in the ~Array~ algebra. Since big arrays open many files a remarkable delay
between the end of a query and the representation of the result was detected.
Since a query changes no data on disk syncronisation is not neccessary.
August 26, 2004. M. Spiekermann removed the DB\_PRIVATE flag in the ~Startup~
of the Berkeley-DB Environment. This has two reasons. First the Berkeley-DB
tools like db\_stat, db\_checkpoint etc. can not operate on such environments,
and second environments produced by SecondoTTY and the Secondo-Server should
now be more compatible.
*/
#include <string>
#include <string.h>
#include <algorithm>
#include <cctype>
#include <vector>
#include <queue>
#include <map>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <cassert>
#include <cstring>
#include <db_cxx.h>
#undef TRACE_ON
#include "Trace.h"
#include "SecondoSMI.h"
#include "SmiBDB.h"
#include "SmiCodes.h"
#include "../Tools/Flob/Flob.h"
#include "Profiles.h"
#include "FileSystem.h"
#include "LogMsg.h"
#include "StopWatch.h"
#include "CacheInfo.h"
#include "WinUnix.h"
#include "DbVersion.h"
#include "StringUtils.h"
#ifndef SECONDO_WIN32
#include <libgen.h>
#include <unistd.h>
#endif
/* For sending messages */
#include "NestedList.h"
#include "Messages.h"
#ifdef SM_FILE_ID
// realize file id handling by shared memory instead
// of synchronization via berkeley db
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
boost::interprocess::named_mutex SmiEnvironment::file_id_mutex(
boost::interprocess::open_or_create,
(stringutils::replaceAll(SmiEnvironment::GetSecondoHome(),"/","_")
+ "secondo_file_id_mutex").c_str());
#endif
using namespace std;
//static MessageCenter* smi_msg = MessageCenter::GetInstance();
/* --- Prototypes of internal functions --- */
static int getfilename( Db* dbp, const Dbt* pkey, const Dbt* pdata, Dbt* skey );
/* --- Implementation of class SmiEnvironment --- */
SmiEnvironment SmiEnvironment::instance;
SmiError SmiEnvironment::lastError = E_SMI_OK;
string SmiEnvironment::lastMessage;
int SmiEnvironment::numOfErrors = 0;
bool SmiEnvironment::smiStarted = false;
bool SmiEnvironment::singleUserMode = false;
bool SmiEnvironment::useTransactions = false;
bool SmiEnvironment::dontSyncDiskCache = false;
string SmiEnvironment::configFile;
string SmiEnvironment::uid;
bool SmiEnvironment::dbOpened = false;
string SmiEnvironment::database;
string SmiEnvironment::registrar;
#ifdef THREAD_SAFE
boost::recursive_mutex SmiEnvironment::Implementation::env_impl_mtx;
boost::recursive_mutex SmiEnvironment::env_mtx;
#endif
#ifdef SM_FILE_ID
boost::interprocess::shared_memory_object* SmiEnvironment::file_id_shm=0;
#endif
bool traceDBHandles = false;
SmiEnvironment::SmiType SmiEnvironment::smiType = SmiEnvironment::SmiBerkeleyDB;
u_int32_t
SmiEnvironment::Implementation::AutoCommitFlag = DB_AUTO_COMMIT;
SmiEnvironment::Implementation::Implementation(const bool log_auto_remove)
: bdbHome( "" ), tmpHome( "" ), tmpId( 0 ), envClosed( false ),
usrTxn( 0 ), txnStarted( false ), txnMustAbort( false ),
bdbDatabases( 0 ), bdbSeq( 0 ), bdbCatalog( 0 ), bdbCatalogIndex( 0 )
{
bdbEnv = new DbEnv( DB_CXX_NO_EXCEPTIONS );
#if DB_VERSION_REQUIRED(4,8)
if(log_auto_remove){
bdbEnv->log_set_config(DB_LOG_AUTO_REMOVE, 1);
}
#endif
tmpEnv = 0;
dbHandles.reserve( DEFAULT_DBHANDLE_ALLOCATION_COUNT );
SmiDbHandleEntry dummy(0);
dbHandles.push_back( dummy );
firstFreeDbHandle = 0;
}
SmiEnvironment::Implementation::~Implementation()
{
if ( !envClosed )
{
//cerr << "SmiEnvironment::Implementation::~Implementation()"
// << " -> Closing Environments" << endl;
CloseDbHandles();
int rc = bdbEnv->close( 0 );
SetBDBError(rc);
}
if (bdbEnv) {
delete bdbEnv;
bdbEnv = 0;
}
}
DbHandleIndex
SmiEnvironment::Implementation::AllocateDbHandle()
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
// Read trace flag here, because class instance is initialized
// before RTFlags are parsed
traceDBHandles = RTFlag::isActive("SMI:traceHandles");
DbHandleIndex idx = instance.impl->firstFreeDbHandle;
if (traceDBHandles)
cerr << "Allocate: firstFree idx = " << idx << endl;
if ( idx == 0 )
{
// --- Allocate new handle entry
if ( instance.impl->dbHandles.size() >=
instance.impl->dbHandles.capacity() )
{
instance.impl->dbHandles.reserve( instance.impl->dbHandles.size()
+ DEFAULT_DBHANDLE_ALLOCATION_COUNT );
}
SmiDbHandleEntry dummy(0);
instance.impl->dbHandles.push_back( dummy );
idx = instance.impl->dbHandles.size() - 1;
}
else
{
// --- Reuse free handle entry
instance.impl->firstFreeDbHandle =
instance.impl->dbHandles[idx].getNextFree();
}
instance.impl->dbHandles[idx].setHandle(
new Db( instance.impl->bdbEnv, DB_CXX_NO_EXCEPTIONS ));
instance.impl->dbHandles[idx].setInUse(true);
instance.impl->dbHandles[idx].setNextFree(0);
if (traceDBHandles)
cerr << "Allocate: returned idx = " << idx << endl;
return (idx);
}
void SmiEnvironment::Implementation::SetAutoRemoveLogs(bool enable){
#if DB_VERSION_REQUIRED(4,8)
if(bdbEnv){
bdbEnv->log_set_config(DB_LOG_AUTO_REMOVE, enable?1:0);
}
#endif
}
int SmiEnvironment::Implementation::FindOpen(const string& fileName,
const u_int32_t flags){
for(unsigned int i=0;i<instance.impl->dbHandles.size();i++){
if(instance.impl->dbHandles[i].canBeUsedFor(fileName,flags)){
return i;
}
}
return -1;
}
void SmiEnvironment::Implementation::SetUnUsed(DbHandleIndex idx )
{
instance.impl->dbHandles[idx].setInUse(false);
}
void SmiEnvironment::Implementation::SetUsed(DbHandleIndex idx )
{
instance.impl->dbHandles[idx].setInUse(true);
}
Db*
SmiEnvironment::Implementation::GetDbHandle( DbHandleIndex idx )
{
return (instance.impl->dbHandles[idx].getHandle());
}
void
SmiEnvironment::Implementation::FreeDbHandle( DbHandleIndex idx )
{
if (traceDBHandles)
{
string f = instance.impl->dbHandles[idx].getFileName();
cerr << "free handle for idx = "
<< idx << " (" << f << ")" << endl;
}
instance.impl->dbHandles[idx].setInUse(false);
}
void
SmiEnvironment::Implementation::DeleteDbHandle( DbHandleIndex idx )
{
int flag = 0;
if ( dontSyncDiskCache ) {
flag = DB_NOSYNC;
}
if (traceDBHandles)
{
string f = instance.impl->dbHandles[idx].getFileName();
cerr << "delete handle for idx = "
<< idx << " (" << f << ")" << endl;
}
int rc = instance.impl->dbHandles[idx].closeAndDeleteHandle( flag );
SetBDBError(rc);
instance.impl->dbHandles[idx].setNextFree(
instance.impl->firstFreeDbHandle);
instance.impl->firstFreeDbHandle = idx;
}
void
SmiEnvironment::Implementation::CloseDbHandles()
{
SmiEnvironment::Implementation& env = (*(instance.impl));
unsigned int size = env.dbHandles.size();
int closed = 0;
if (traceDBHandles)
cerr << "CloseDbHandles (size = " << size << ")" << endl;
for ( DbHandleIndex idx = 1; idx < size; idx++ )
{
if ( !instance.impl->dbHandles[idx].isInUse() &&
instance.impl->dbHandles[idx].hasHandle())
{
DeleteDbHandle(idx);
closed++;
}
}
if ( traceDBHandles ) {
cerr << "CloseDbHandles() - Report: size = " << size
<< ", closed = " << closed
<< ", nosync = " << dontSyncDiskCache << endl;
}
}
/*
After a crash of Secondo with Transactions disabled, sometimes, the internal
counter producing new filenames is not written back to the catalog even if
files exists using newer numbers. This leads to unusability of the complete
database because existing files are used again (but assumed to be empty).
Here, the complete database directory is scanned and the counter is set
to the highest used number plus 1.
*/
bool SmiEnvironment::Implementation::CorrectFileId(const bool lockrequired){
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
if ( !dbOpened ) {
SetError( E_SMI_DB_NOTOPEN );
return false;
}
bool res = false;
#ifdef SM_FILE_ID
try{
if(lockrequired){
file_id_mutex.lock();
}
#endif
string folder = instance.impl->bdbHome + PATH_SLASH + database;
FilenameList files;
FileSystem::FileSearch(folder, files);
SmiFileId id = 0;
string digits = "0123456789";
for(size_t i=0;i<files.size();i++){
string f = files[i];
// extract basename
f = f.substr(f.find_last_of(PATH_SLASH)+1);
size_t p1 = f.find_first_of(digits);
size_t p2 = f.find_last_of(digits);
if(p1!=string::npos){
SmiFileId num = atoi(f.substr(p1,(p2+1)-p1).c_str());
if(num > id){
id = num;
}
}
}
id++;
res = SetFileId(id, false);
#ifdef SM_FILE_ID
if(lockrequired){
file_id_mutex.unlock();
}
} catch(boost::interprocess::interprocess_exception ex){
cerr << "exception occurred " << ex.what() << endl;
}
#endif
return res;
}
bool SmiEnvironment::Implementation::SetFileId(SmiFileId id,
bool lockrequired){
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
if ( !dbOpened ) {
SetError( E_SMI_DB_NOTOPEN );
return false;
}
#ifdef SM_FILE_ID
try{
if(lockrequired){
file_id_mutex.lock();
}
if(file_id_shm==0){
string dbname = database;
string shmName = stringutils::replaceAll(GetSecondoHome(),"/","_")
+ "_" + dbname + "_file_id";
file_id_shm =
new boost::interprocess::shared_memory_object(
boost::interprocess::open_or_create,
shmName.c_str(),
boost::interprocess::read_write);
file_id_shm->truncate(sizeof(SmiFileId));
}
boost::interprocess::mapped_region region(*file_id_shm,
boost::interprocess::read_write);
SmiFileId* fid = (SmiFileId*) region.get_address();
*fid = id;
if(lockrequired){
file_id_mutex.unlock();
}
return true;
} catch(boost::interprocess::interprocess_exception e){
cerr << "Problem in setting file id" << endl;
cerr << e.what();
return false;
}
#else
int rc = 0;
DbEnv* dbenv = instance.impl->bdbEnv;
Db* dbseq = instance.impl->bdbSeq;
if(!dbseq){
return false;
}
DbTxn* tid = 0;
db_recno_t seqno = SMI_SEQUENCE_FILEID;
if ( useTransactions ) {
rc = dbenv->txn_begin( 0, &tid, 0 );
SetBDBError(rc);
}
bool operationSuccess = true;
if ( rc == 0 ) {
Dbt key( &seqno, sizeof(seqno) );
Dbt data;
data.set_flags( DB_DBT_USERMEM );
data.set_data( &id );
data.set_ulen( sizeof( SmiFileId ) );
SmiFileId sid =0;
data.set_data(&sid);
// Aquire write lock to prevent deadlock in
// read-modify-write cycle
u_int32_t rwFlag = useTransactions ? DB_RMW : 0;
rc = dbseq->get(tid,&key,&data,rwFlag);
if(rc != 0) {
SetBDBError(rc);
operationSuccess = false;
}
if(operationSuccess) {
//cout << "stored value is " << sid << endl;
//cout << "change to " << id << endl;
// Prevent the decrease of the stored file id.
//
// This can happen when another SECONDO instance
// requests a new file id, and we perform a scan before
// the file is created in the file system. In this
// case, we would overwrite the value with an outdated
// file id.
if(id > sid) {
sid = id;
rc = dbseq->put( tid, &key, &data, 0 );
SetBDBError(rc);
operationSuccess = (rc == 0);
}
}
}
if(tid != 0) {
if(operationSuccess) {
rc = tid->commit( 0 );
} else {
rc = tid->abort();
}
SetBDBError(rc);
}
return operationSuccess;
#endif
}
SmiFileId
SmiEnvironment::Implementation::GetFileId( const bool isTemporary )
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
SmiFileId newFileId = 0;
if ( isTemporary )
{
newFileId = ++instance.impl->tmpId;
//SPM: This implementation will be sufficient for
//a multi user mode since the file name will also
//contain the process id
if ( RTFlag::isActive("SMI:LogFileCreation") )
{
// // build a two elem list (simple count)
// stringstream s;
// s << newFileId;
// NList msgList( NList("simple"), NList("GetFileId: " + s.str()));
// // send the message, the message center will call
// // the registered handlers. Normally the client applications
// // will register them.
// smi_msg->Send(msgList);
cout << "SMI:LogFileCreation: GetFileId: " << newFileId << endl;
}
return newFileId;
}
if ( !dbOpened )
{
SetError( E_SMI_DB_NOTOPEN );
return (newFileId);
}
#ifdef SM_FILE_ID
SmiFileId fidr = 0;
try{
file_id_mutex.lock();
if(!file_id_shm){
string dbname = database;
string shmName = stringutils::replaceAll(GetSecondoHome(),"/","_")
+ "_" + dbname + "_file_id";
try {
file_id_shm =
new boost::interprocess::shared_memory_object(
boost::interprocess::open_only,
shmName.c_str(),
boost::interprocess::read_write);
} catch(...){
CorrectFileId(false); // force to create a file id
}
}
boost::interprocess::mapped_region
region (*file_id_shm,
boost::interprocess::read_write);
SmiFileId* fid = (SmiFileId*) region.get_address();
(*fid)++;
fidr = *fid;
file_id_mutex.unlock();
} catch( boost::interprocess::interprocess_exception ex){
cerr << "Exception occurred " << ex.what() << endl;
}
return fidr;
#else
int rc = 0;
DbEnv* dbenv = instance.impl->bdbEnv;
Db* dbseq = instance.impl->bdbSeq;
if ( dbseq )
{
DbTxn* tid = 0;
db_recno_t seqno = SMI_SEQUENCE_FILEID;
Dbt key( &seqno, sizeof(seqno) );
Dbt data;
data.set_flags( DB_DBT_USERMEM );
data.set_data( &newFileId );
data.set_ulen( sizeof( newFileId ) );
if ( useTransactions )
{
rc = dbenv->txn_begin( 0, &tid, 0 );
SetBDBError(rc);
}
if ( rc == 0 )
{
u_int32_t rwFlag = useTransactions ? DB_RMW : 0;
rc = dbseq->get( tid, &key, &data, rwFlag );
if ( rc == DB_NOTFOUND || rc == DB_KEYEMPTY )
{
newFileId = 1;
data.set_size( sizeof( newFileId ) );
rc = 0;
}
else if ( rc == 0 )
{
newFileId++;
}
if ( rc == 0 )
{
if ( (rc = dbseq->put( tid, &key, &data, 0 )) == 0 )
{
if ( tid != 0 )
{
rc = tid->commit( 0 );
SetBDBError(rc);
}
}
else
{
SetBDBError(rc);
if ( tid != 0 )
{
rc = tid->abort();
SetBDBError(rc);
}
}
if ( rc != 0 )
{
newFileId = 0;
}
}
}
else if ( tid != 0 )
{
rc = tid->abort();
SetBDBError(rc);
}
}
if ( RTFlag::isActive("SMI:LogFileCreation") )
{
// // build a two elem list (simple count)
// stringstream s;
// s << newFileId;
// NList msgList( NList("simple"), NList("GetFileId: " + s.str()));
// // send the message, the message center will call
// // the registered handlers. Normally the client applications
// // will register them.
// smi_msg->Send(msgList);
cout << "SMI:LogFileCreation: GetFileId: " << newFileId << endl;;
}
return (newFileId);
#endif
}
bool
SmiEnvironment::Implementation::LookUpCatalog( Dbt& key,
SmiCatalogEntry& entry )
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
TRACE_ENTER
if ( !dbOpened )
{
SetError( E_SMI_DB_NOTOPEN );
return (false);
}
int rc = 0;
DbEnv* dbenv = instance.impl->bdbEnv;
Db* dbidx = instance.impl->bdbCatalogIndex;
if ( dbidx )
{
DbTxn* tid = 0;
Dbt data;
data.set_flags( DB_DBT_USERMEM );
data.set_data( &entry );
data.set_ulen( sizeof( entry ) );
if ( useTransactions )
{
rc = dbenv->txn_begin( 0, &tid, 0 );
SetBDBError( rc );
}
// transaction initialized, lookup file
if ( rc == 0 )
{
rc = dbidx->get( tid, &key, &data, 0 );
// valid return codes
if ( rc == 0 || rc == DB_NOTFOUND || rc == DB_KEYEMPTY )
{
if (useTransactions) {
int rc_tid = tid->commit( 0 );
SetBDBError( rc_tid );
}
}
else
{
if ( useTransactions ) {
int rc_tid = tid->abort();
SetBDBError( rc_tid );
}
SetBDBError( rc );
}
}
else // initialization of transaction failed
{
if ( tid != 0 ) { // abort if necessary
rc = tid->abort();
}
SetBDBError( rc );
}
}
else
{
SetError2( E_SMI_CATALOG_LOOKUP, "Catalog Index not present!" );
rc = E_SMI_CATALOG_LOOKUP;
}
TRACE_LEAVE
return (rc == 0);
}
bool
SmiEnvironment::Implementation::LookUpCatalog( const string& fileName,
SmiCatalogEntry& entry )
{
// cout << "Lookup: key = <" << fileName << ">" << endl;
Dbt key( (void*) fileName.c_str(), fileName.length() );
bool rc = LookUpCatalog(key, entry);
// cout << "rc = " << rc << endl;
// cout << "fileId = " << entry.fileId << endl;
return rc;
}
// SPM: The function below will always fail, since the fileId cannot be used
// to lookup the index of file names. Here some discussion is needed whether
// anonymous files need to be inserted into the file catalog, since its
// file name is determined by the file id. The other way round named files
// are unique by their names, thus its not necessary to map them to an id
// as long as they are only used for system purposes. Thus the overall question
// is: Do we really need a file catalog?
bool
SmiEnvironment::Implementation::LookUpCatalog( const SmiFileId fileId,
SmiCatalogEntry& entry )
{
Dbt key( (void*) &fileId, sizeof(SmiFileId) );
return LookUpCatalog(key, entry);
}
bool
SmiEnvironment::Implementation::InsertIntoCatalog(
const SmiCatalogEntry& entry, DbTxn* tid )
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
if ( !dbOpened )
{
SetError( E_SMI_DB_NOTOPEN );
return (false);
}
// cout << "Insert: fileId = " << entry.fileId << endl;
// cout << "Insert: fileName = <" << entry.fileName << ">" << endl;
int rc = 0;
Db* dbctl = instance.impl->bdbCatalog;
if ( dbctl )
{
Dbt key( (void*) &entry.fileId, sizeof( entry.fileId ) );
Dbt data( (void*) &entry, sizeof( entry ) );
rc = dbctl->put( tid, &key, &data, DB_NOOVERWRITE );
if ( rc == 0 || rc == DB_KEYEXIST )
{
if ( rc == 0 )
{
SetError( E_SMI_OK );
}
else
{
SetError( E_SMI_CATALOG_KEYEXIST );
}
}
else
{
SetBDBError( rc );
}
}
else
{
SetError( E_SMI_CATALOG_INSERT );
rc = E_SMI_CATALOG_INSERT;
}
return (rc == 0);
}
bool
SmiEnvironment::
Implementation::DeleteFromCatalog( const SmiCatalogEntry& entry, DbTxn* tid )
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
if ( !dbOpened )
{
SetError( E_SMI_DB_NOTOPEN );
return (false);
}
int rc = 0;
Db* dbidx = instance.impl->bdbCatalogIndex;
if ( dbidx )
{
Dbt key( (void*) &entry.fileId, sizeof( entry.fileId ) );
rc = dbidx->del( tid, &key, 0 );
if ( rc != 0 && rc != DB_NOTFOUND )
{
SetBDBError( rc );
}
}
else
{
SetError( E_SMI_CATALOG_DELETE );
rc = E_SMI_CATALOG_DELETE;
}
return (rc == 0);
}
bool
SmiEnvironment::Implementation::UpdateCatalog( bool onCommit )
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
if ( !dbOpened )
{
SetError( E_SMI_DB_NOTOPEN );
return (false);
}
bool ok = true;
int rc = 0;
DbEnv* dbenv = instance.impl->bdbEnv;
DbTxn* ptid = instance.impl->usrTxn;
DbTxn* tid = 0;
if ( useTransactions )
{
rc = dbenv->txn_begin( ptid, &tid, 0 );
SetBDBError( rc );
}
if ( rc == 0 )
{
map<string,SmiCatalogFilesEntry>::iterator it =
instance.impl->bdbFilesToCatalog.begin();
for ( ; ok && it != instance.impl->bdbFilesToCatalog.end(); ++it )
{
if ( it->second.updateOnCommit )
{
ok = InsertIntoCatalog( it->second.entry, tid );
}
else
{
cerr << "SMI: Deleting "
<< it->first.c_str() << " from file Catalog" << endl;
ok = DeleteFromCatalog( it->second.entry, tid );
}
}
//instance.impl->bdbFilesToCatalog.clear();
if ( tid != 0 )
{
if ( ok )
{
rc = tid->commit( 0 );
SetBDBError( rc );
}
else
{
rc = tid->abort();
SetBDBError( rc );
}
}
/*
it = instance.impl->bdbFilesToCatalog.begin();
ok = true;
for ( ; ok && it != instance.impl->bdbFilesToCatalog.end(); ++it )
{
if ( it->second.updateOnCommit )
{
SmiCatalogEntry x;
const string n(it->second.entry.fileName);
LookUpCatalog(n, x);
cout << "Lookup string " << endl;
cout << "x.fileId = " << x.fileId << endl;
cout << "x.fileName = <" << x.fileName << ">" << endl;
SmiCatalogEntry y;
LookUpCatalog(it->second.entry.fileId, y);
cout << "Lookup fileId " << endl;
cout << "x.fileId = " << x.fileId << endl;
cout << "y.fileId = " << y.fileId << endl;
cout << "y.fileName = <" << y.fileName << ">" << endl;
}
} */
instance.impl->bdbFilesToCatalog.clear();
}
else
{
if ( tid != 0 )
{
rc = tid->abort();
SetBDBError( rc );
}
}
return (ok);
}
bool
SmiEnvironment::Implementation::EraseFiles( bool onCommit,
const bool dontReportError )
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
if ( !dbOpened )
{
SetError( E_SMI_DB_NOTOPEN );
return (false);
}
int rc = 0;
DbEnv* dbenv = instance.impl->bdbEnv;
// SPM: For being able to finish a query without
// SMI-errors we will keep track of the already removed files.
// The real source of trouble is why some files are added more than once.
// However, in the future one should extend bdbFileToDrop to a class which
// alerts when multiple inserts of the same file are done!
map<SmiFileId, bool> removed;
while ( !instance.impl->bdbFilesToDrop.empty() )
{
SmiDropFilesEntry entry = instance.impl->bdbFilesToDrop.front();
if ( removed.find(entry.fileId) == removed.end() ) {
if ( onCommit && entry.dropOnCommit )
{
Db* dbp = new Db( dbenv, DB_CXX_NO_EXCEPTIONS );
string file = ConstructFileName( entry.fileId );
//cerr << "Erasing file " << file << endl;
//cerr << "onCommit:" << onCommit << endl;
//cerr << "entry:" << entry.dropOnCommit << endl;
rc = dbp->remove( file.c_str(), 0, 0 );
if(rc!=0 && !dontReportError){
SetBDBError(rc);
}
removed[entry.fileId] = true;
delete dbp;
}
} else {
//cerr << "Warning: File id = "
// << entry.fileId << " already removed!" << endl;
}
instance.impl->bdbFilesToDrop.pop();
}
return (rc == 0);
}
bool
SmiEnvironment::ListDatabases( string& dbname )
{
bool ok = true;
Db* dbctl = instance.impl->bdbDatabases;
dbname = "";
if ( dbctl )
{
try {
// Acquire a cursor for the table.
Dbc *dbcp;
int rc = dbctl->cursor(NULL, &dbcp, 0);
SetBDBError(rc);
// Walk through the table, getting the key/data pairs.
Dbt key;
Dbt data;
while (dbcp->get(&key, &data, DB_NEXT) == 0) {
dbname += (char *)key.get_data();
dbname += "#";
}
rc = dbcp->close();
SetBDBError(rc);
}
catch (DbException &dbe) {
cerr << "Error: " << dbe.what() << "\n";
ok = false;
}
}
else
{
SetError( E_SMI_DB_NOTFOUND );
}
return (ok);
}
string
SmiEnvironment::Implementation::ConstructFileName( SmiFileId fileId,
const bool isTemporary )
{
assert( fileId != 0);
ostringstream os;
if ( !isTemporary )
{
os << database << PATH_SLASH << "d" << fileId << ".sdb";
}
else
{
os << "t." << WinUnix::getpid() << "."<< fileId << ".sdb";
}
if ( RTFlag::isActive("SMI:LogFileCreation") )
{
// // build a two elem list (simple count)
// NList msgList( NList("simple"), NList("ConstructFileName: " + os.str()));
// // send the message, the message center will call
// // the registered handlers. Normally the client applications
// // will register them.
// smi_msg->Send(msgList);
cout << "SMI:LogFileCreation: ConstructFileName: " << os.str() << endl;
}
return (os.str());
}
bool
SmiEnvironment::Implementation::LookUpDatabase( const string& dbname )
{
const int MAX = 1024;
bool ok = false;
int rc = 0, len = 0;
char buf[MAX+1];
Db* dbctl = instance.impl->bdbDatabases;
if ( dbctl )
{
// truncate string if necessary
len = dbname.length();
if (len > MAX)
len = MAX;
for (int i = 0; i < len; i++) buf[i] = dbname[i];
buf[len] = '\0';
Dbt key(buf, len + 1);
Dbt data(buf, len + 1);
rc = dbctl->get( 0, &key, &data, 0 );
if ( !(rc == 0 || rc == DB_NOTFOUND || rc == DB_KEYEMPTY) )
SetBDBError( rc );
// the operation is only successful if the key was found
ok = (rc == 0);
}
return (ok);
}
bool
SmiEnvironment::Implementation::InsertDatabase( const string& dbname )
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
const int MAX = 1024;
bool ok = false;
int rc = 0;
Db* dbctl = instance.impl->bdbDatabases;
int len;
char buf[MAX+1];
len = dbname.length();
if (len > MAX)
len = MAX;
for (int i = 0; i < len; i++) buf[i] = dbname[i];
buf[len] = '\0';
Dbt key(buf, len + 1);
Dbt data(buf, len + 1);
rc = dbctl->put(0, &key, &data, DB_NOOVERWRITE | AutoCommitFlag);
SetBDBError( rc );
ok = (rc == 0);
return (ok);
}
bool
SmiEnvironment::Implementation::DeleteDatabase( const string& dbname )
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_impl_mtx);
#endif
const int MAX = 1024;
bool ok = false;
int rc = 0, len;
char buf[MAX+1];
Db* dbctl = instance.impl->bdbDatabases;
if ( dbctl )
{
len = dbname.length();
if (len > MAX)
len = MAX;
for (int i = 0; i < len; i++) buf[i] = dbname[i];
buf[len] = '\0';
Dbt key(buf, len + 1);
rc = dbctl->del( 0, &key, AutoCommitFlag );
SetBDBError( rc );
ok = (rc == 0);
}
return (ok);
}
SmiEnvironment::SmiEnvironment()
{
//cerr << "*** Constructor SmiEnvironment ***" << endl;
impl = new Implementation(true);
}
SmiEnvironment::~SmiEnvironment()
{
//cerr << "*** Destructor SmiEnvironment ***" << endl;
delete impl;
impl = 0;
}
void SmiEnvironment::SetAutoRemoveLogs(const bool enable){
if(instance.impl){
instance.impl->SetAutoRemoveLogs(enable);
}
}
bool SmiEnvironment::correctFileId(){
if(instance.impl){
return instance.impl->CorrectFileId();
} else {
return false;
}
}
void
SmiEnvironment::SetSmiError( const SmiError smiErr,
const int sysErr, const string& file, int pos,
const string& desc )
{
if ( sysErr != 0 )
{
if ( sysErr == DB_LOCK_DEADLOCK && instance.impl->txnStarted )
{
instance.impl->txnMustAbort = true;
}
string msg = Err2Msg(E_SMI_BDB) + " sysErrCode="
+ to_string(sysErr) + " " + DbEnv::strerror( sysErr );
SetSmiError(smiErr, msg, file, pos, desc);
// DbEnv: BDB2055 Lock table is out of available lock entries
// E_SMI_BDB sysErrCode=12 Cannot allocate memory
if(sysErr == 12 && useTransactions)
{
msg = "This error may indicate that the number of MaxLocks / "
"MaxLockObjects is too low.\n"
"Please check your SecondoConfig.ini or disable "
"transactions (RTFlags += SMI:NoTransactions)\n";
SetSmiError(smiErr, msg, file, pos, "");
}
}
}
bool
SmiEnvironment::SetHomeDir(const string& dbDir)
{
string home = dbDir;
if(home.empty()){
cerr << "missing specification of SecondoHome" << endl;
return false;
}
if (!FileSystem::FileOrFolderExists(home)) {
cout << "Creating directory " << home << endl;
if (!FileSystem::CreateFolderEx(home)) {
cerr << "Error: Could not create folder '" << home
<< "'." << endl;
return false;
}
}
cout << "Database directory: SecondoHome='" << home << "'." << endl;
instance.impl->bdbHome = home;
return true;
}
string SmiEnvironment::GetSecondoHome(){
return instance.impl? instance.impl->bdbHome : "";
}
int
SmiEnvironment::CreateTmpEnvironment(ostream& errStream)
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_mtx);
#endif
int rc = 0;
assert(instance.impl);
if(instance.impl->tmpEnv){
// temporary environment already exists
return 0;
}
instance.impl->tmpEnv = new DbEnv( DB_CXX_NO_EXCEPTIONS );
DbEnv* dbtmp = instance.impl->tmpEnv;
// define a prefix for additional error messages
dbtmp->set_error_stream( &errStream );
dbtmp->set_errpfx( "DbEnv-Tmp" );
// define a temporary directory
string oldHome = FileSystem::GetCurrentFolder();
FileSystem::SetCurrentFolder( instance.impl->bdbHome );
ostringstream tmpHome;
tmpHome << "0tmp" << WinUnix::getpid();
FileSystem::EraseFolder(tmpHome.str());
FileSystem::CreateFolder( tmpHome.str() );
instance.impl->tmpHome = tmpHome.str();
string bdbTmpHome = instance.impl->bdbHome + PATH_SLASH + tmpHome.str();
rc = dbtmp->set_cachesize( 0, 32*1024*1024, 0);
SetBDBError(rc);
rc = dbtmp->open( bdbTmpHome.c_str(),
DB_PRIVATE | DB_INIT_MPOOL | DB_CREATE, 0 );
SetBDBError(rc);
FileSystem::SetCurrentFolder( oldHome );
return rc;
}
int
SmiEnvironment::DeleteTmpEnvironment()
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_mtx);
#endif
// --- Remove the temporary environment
DbEnv* dbtmp = instance.impl->tmpEnv;
int rc = dbtmp->close( 0 );
SetBDBError( rc );
const string& tmpHome = instance.impl->tmpHome;
string bdbHome = instance.impl->bdbHome;
if (traceDBHandles) {
cerr << "Removing temporary Berkeley-DB environment "
<< "tmpHome = " << tmpHome << endl
<< "bdbHome = " << bdbHome << endl;
}
string tmpDir("");
FileSystem::AppendItem(bdbHome, tmpHome);
FileSystem::EraseFolder( bdbHome );
delete dbtmp;
instance.impl->tmpEnv = 0;
return rc;
}
bool SmiEnvironment::StartUp(const RunMode mode, const string& parmFile,
const string& dbDir,
ostream& errStream,
const string& port) {
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_mtx);
#endif
if (smiStarted) {
return true;
}
cout << "Startup of the Storage Management Interface (SMI) ..." << endl;
if (RTFlag::isActive("SMI:NoTransactions")) {
SmiEnvironment::Implementation::AutoCommitFlag = 0;
cout << endl << "Transactions: unused" << endl;
}
int rc = 0;
int errors = GetNumOfErrors();
DbEnv* dbenv = instance.impl->bdbEnv;
assert(dbenv);
char* absoluteparm = WinUnix::getAbsolutePath(parmFile.c_str());
if(absoluteparm){
configFile = string(absoluteparm);
free(absoluteparm);
}else {
configFile = parmFile;
}
// --- Set the name of the registrar for registering and locking databases
registrar = SmiProfile::GetUniqueSocketName(configFile, port);
// --- Set output stream for error messages from Berkeley DB
// and the prefix string for these messages
dbenv->set_error_stream(&errStream);
dbenv->set_errpfx("DbEnv");
// --- Set time between checkpoints
instance.impl->minutes =
SmiProfile::GetParameter("BerkeleyDB", "CheckpointTime", 5, configFile);
// --- Set cache size
u_int32_t cachesize = SmiProfile::GetParameter("BerkeleyDB", "CacheSize",
CACHE_SIZE_STD, configFile);
if (cachesize < CACHE_SIZE_STD) {
cachesize = CACHE_SIZE_STD;
}
else if (cachesize > CACHE_SIZE_MAX) {
cachesize = CACHE_SIZE_MAX;
}
cout << "Cachesize: " << cachesize << " kb." << endl;
rc = dbenv->set_cachesize( 0, cachesize * 1024, 0 );
SetBDBError(rc);
// --- Set locking configuration
u_int32_t timeout_value;
timeout_value = SmiProfile::GetParameter("BerkeleyDB", "Timeout_LCK", 0,
configFile.c_str());
db_timeout_t microSeconds = timeout_value;
rc = dbenv->set_timeout(microSeconds, DB_SET_LOCK_TIMEOUT);
SetBDBError(rc);
cout << "Lock timeout: " << microSeconds << " microseconds" << endl;
timeout_value = SmiProfile::GetParameter("BerkeleyDB", "Timeout_TXN", 0,
configFile.c_str() );
microSeconds = timeout_value;
rc = dbenv->set_timeout(microSeconds, DB_SET_TXN_TIMEOUT);
SetBDBError(rc);
cout << "TXN timeout: " << microSeconds << " microseconds" << endl;
u_int32_t lockValue;
lockValue = SmiProfile::GetParameter("BerkeleyDB", "MaxLockers", 0,
configFile.c_str() );
if (lockValue > 0) {
rc = dbenv->set_lk_max_lockers(lockValue);
SetBDBError(rc);
}
lockValue = SmiProfile::GetParameter("BerkeleyDB", "MaxLocks", 0,
configFile.c_str() );
if (lockValue > 0) {
rc = dbenv->set_lk_max_locks(lockValue);
SetBDBError(rc);
}
lockValue = SmiProfile::GetParameter("BerkeleyDB", "MaxLockObjects", 0,
configFile.c_str() );
if (lockValue > 0) {
rc = dbenv->set_lk_max_objects(lockValue);
SetBDBError(rc);
}
rc = dbenv->set_lk_detect(DB_LOCK_DEFAULT);
SetBDBError(rc);
// --- Set log directory, if requested
string logDir = SmiProfile::GetParameter("BerkeleyDB", "LogDir", "",
configFile.c_str() );
if (logDir.length() > 0) {
rc = dbenv->set_lg_dir(logDir.c_str());
SetBDBError(rc);
}
// --- Set log buffer size
u_int32_t lBufSize = 0;
lBufSize = SmiProfile::GetParameter("BerkeleyDB", "LogBufSize", 0,
configFile.c_str() );
if (lBufSize > 0) {
cout << "Log buffer size: " << lBufSize << " kb." << endl;
rc = dbenv->set_lg_max(4 * lBufSize * 1024);
SetBDBError(rc);
rc = dbenv->set_lg_bsize(lBufSize * 1024);
SetBDBError(rc);
}
string home = dbDir;
if(home.empty()){
home = SmiProfile::GetParameter("Environment", "SecondoHome", "",
configFile.c_str());
}
// --- Open Berkeley DB environment
if (rc == 0) {
if (!SetHomeDir(home)) {
return false;
}
u_int32_t flags = 0;
switch (mode) {
case SmiEnvironment::SingleUserSimple:
cout << "SMI-Mode: SingleUserSimple" << endl;
singleUserMode = true;
useTransactions = false;
flags = DB_CREATE | DB_INIT_MPOOL | DB_PRIVATE;
/*
creates a private environment for the calling process and enables
automatic recovery during startup. If the environment does not exist,
it is created. Transactions, logging and locking are disabled.
Using this mode is not recommended except for read-only databases.
*/
break;
case SmiEnvironment::SingleUser:
cout << "SMI-Mode: SingleUser" << endl;
singleUserMode = true;
useTransactions = true;
flags = DB_CREATE | DB_RECOVER |
DB_INIT_LOCK | DB_INIT_LOG | DB_INIT_MPOOL |
DB_INIT_TXN;
/*
creates a private environment for the calling process and enables
automatic recovery during startup. If the environment does not exist,
it is created. Transactions and logging are enabled, locking is disabled.
*/
break;
case SmiEnvironment::MultiUserMaster:
cout << "SMI-Mode: MultiUserMaster" << endl;
singleUserMode = false;
useTransactions = true;
flags = DB_CREATE | DB_RECOVER | DB_INIT_LOCK |
DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_TXN;
/*
enables automatic recovery during startup. If the environment does not exist,
it is created. Transactions, logging and locking are enabled.
*/
break;
case SmiEnvironment::MultiUser:
default:
cout << "SMI-Mode: MultiUser" << endl;
singleUserMode = false;
useTransactions = true;
flags = DB_CREATE | DB_INIT_LOCK | DB_INIT_LOG |
DB_INIT_MPOOL | DB_INIT_TXN;
/*
If the environment does not exist, it is created.
Transactions, logging and locking are enabled.
*/
break;
}
rc = dbenv->open(instance.impl->bdbHome.c_str(), flags, 0);
SetBDBError(rc);
if (rc == 0) {
// --- Open Database Catalog
Db* dbctlg = new Db(dbenv, DB_CXX_NO_EXCEPTIONS);
rc = dbctlg->open(0, "databases", 0, DB_BTREE,
DB_CREATE | Implementation::AutoCommitFlag, 0);
SetBDBError(rc);
if (rc == 0) {
smiStarted = true;
instance.impl->bdbDatabases = dbctlg;
}
else {
rc = dbctlg->close( 0 );
SetBDBError(rc);
delete dbctlg;
instance.impl->bdbDatabases = 0;
}
}
}
if (useTransactions) {
db_timeout_t microSeconds = 0;
rc = dbenv->get_timeout(&microSeconds, DB_SET_LOCK_TIMEOUT);
SetBDBError(rc);
cout << "Lock timeout: " << microSeconds << " microseconds" << endl;
rc = dbenv->get_timeout(&microSeconds, DB_SET_TXN_TIMEOUT);
SetBDBError(rc);
cout << "TXN timeout: " << microSeconds << " microseconds" << endl;
}
// --- Create temporary Berkeley DB environment
rc = CreateTmpEnvironment(errStream);
smiStarted = smiStarted && (rc == 0);
if (GetNumOfErrors() > errors) {
cout << "Some errors happend during startup!" << endl;
string errorStack="";
GetLastErrorCode(errorStack);
cout << errorStack << endl;
}
return smiStarted;
}
bool
SmiEnvironment::ShutDown()
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_mtx);
#endif
if ( !smiStarted )
{
return (true);
}
int rc = 0;
int errs = GetNumOfErrors();
DbEnv* dbenv = instance.impl->bdbEnv;
Db* dbctlg = instance.impl->bdbDatabases;
// --- Current database should be already closed
assert(!dbOpened);
// destroy files which have been allocated by the FlobManager
Flob::destroyManager();
DeleteTmpEnvironment();
// --- Close Berkeley DB environment
if ( dbctlg )
{
rc = dbctlg->close( 0 );
SetBDBError( rc );
delete dbctlg;
instance.impl->bdbDatabases = 0;
}
instance.impl->CloseDbHandles();
if (traceDBHandles)
cerr << "Closing Berkeley-DB environment "
<< instance.impl->bdbHome << endl;
rc = dbenv->close( 0 );
SetBDBError( rc );
instance.impl->envClosed = true;
smiStarted = false;
// --- Check if new errors arised
bool ok = ( errs == GetNumOfErrors() );
if (!ok) throw
SecondoException( "SMI Problems while closing "
"the Berkeley-DB environment! "
"Check the involved code parts of this session. "
"All SMI files must be closed properly." );
return ok;
}
bool
SmiEnvironment::CreateDatabase( const string& dbname )
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_mtx);
#endif
bool ok = false;
if ( dbOpened )
{
SetError( E_SMI_DB_NOTCLOSED );
return (false);
}
if ( SetDatabaseName( dbname ) )
{
if ( !SmiEnvironment::Implementation::LookUpDatabase( database ) )
{
// --- Create directory for the new database
string oldHome = FileSystem::GetCurrentFolder();
FileSystem::SetCurrentFolder( instance.impl->bdbHome );
FileSystem::CreateFolder( database );
// --- Initialize Database
if ( InitializeDatabase() )
{
// --- Insert into Catalog of Databases
if ( SmiEnvironment::Implementation::InsertDatabase( database ) )
{
RegisterDatabase( database );
dbOpened = ok = true;
SetError( E_SMI_OK );
}
else
{
SetError( E_SMI_DB_CREATE );
}
}
else
{
SetError( E_SMI_DB_CREATE );
FileSystem::DeleteFileOrFolder( database );
}
FileSystem::SetCurrentFolder( oldHome );
}
else
{
SetError( E_SMI_DB_EXISTING );
}
}
else
{
SetError( E_SMI_DB_INVALIDNAME );
}
return (ok);
}
SI_Error
SmiEnvironment::OpenDatabase( const string& dbname )
{
TRACE_ENTER
SI_Error ok = ERR_NO_ERROR;
if ( dbOpened )
return ERR_DATABASE_OPEN;
if(SetDatabaseName( dbname )){
if ( SmiEnvironment::Implementation::LookUpDatabase( database ) )
{
if ( InitializeDatabase() )
{
RegisterDatabase( database );
dbOpened = true;
}
else
{
ok = ERR_SYSTEM_ERROR;
SetError2( E_SMI_DB_OPEN, "Initializing the database failed." );
}
}
else
{
ok = ERR_IDENT_UNKNOWN_DB_NAME;
}
}
else
{
SetError( E_SMI_DB_INVALIDNAME );
ok = E_SMI_DB_INVALIDNAME;
}
TRACE_LEAVE
return (ok);
}
bool
SmiEnvironment::CloseDatabase()
{
//cerr << "Function CloseDatabase" << endl;
if ( !dbOpened )
{
SetError( E_SMI_DB_NOTOPEN );
return (false);
}
string dbname = database;
int rc = 0;
Db* dbseq = instance.impl->bdbSeq;
Db* dbctl = instance.impl->bdbCatalog;
Db* dbidx = instance.impl->bdbCatalogIndex;
// --- Implicitly commit/abort transaction
if (instance.impl->txnStarted)
{
CommitTransaction();
}
else
{
SmiEnvironment::Implementation::UpdateCatalog(true);
SmiEnvironment::Implementation::CloseDbHandles();
//SmiEnvironment::Implementation::EraseFiles( rc == 0 );
}
// --- Close Berkeley DB sequences
if ( dbseq )
{
rc = dbseq->close( 0 );
SetBDBError( rc );
delete dbseq;
dbseq = 0;
instance.impl->bdbSeq = 0;
}
// --- Close Berkely DB filecatalog and associated index
if ( dbctl )
{
rc = dbctl->close( 0 );
SetBDBError( rc );
delete dbctl;
dbctl = 0;
instance.impl->bdbCatalog = 0;
}
if ( dbidx )
{
rc = dbidx->close( 0 );
SetBDBError( rc );
delete dbidx;
dbidx = 0;
instance.impl->bdbCatalogIndex = 0;
}
UnregisterDatabase( database );
dbOpened = false;
#ifdef SM_FILE_ID
if(file_id_shm) {
try{
file_id_mutex.lock();
string shmName = stringutils::replaceAll(GetSecondoHome(),"/","_")
+ "_" +dbname + "_file_id";
if(!boost::interprocess::shared_memory_object::remove(shmName.c_str())){
cerr << "removing shared memory " << shmName << " failed " << endl;
} else {
cout << "removing shared memory" << shmName << " successful" << endl;
}
delete file_id_shm;
file_id_shm = 0;
file_id_mutex.unlock();
}catch(boost::interprocess::interprocess_exception ex){
cerr << "exception occured " << ex.what() << endl;
}
}
#endif
return (rc == 0);
}
bool
SmiEnvironment::EraseDatabase( const string& dbname )
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_mtx);
#endif
bool ok = true;
if ( !dbOpened )
{
if(SetDatabaseName( dbname )){
if ( SmiEnvironment::Implementation::LookUpDatabase( database ) )
{
if ( LockDatabase( database ) )
{
DbEnv* dbenv = instance.impl->bdbEnv;
SmiEnvironment::Implementation::DeleteDatabase( database );
string oldHome = FileSystem::GetCurrentFolder();
FileSystem::SetCurrentFolder( instance.impl->bdbHome );
FilenameList fnl;
if ( FileSystem::FileSearch( database, fnl, 0, 3 ) )
{
vector<string>::const_iterator iter = fnl.begin();
while ( iter != fnl.end() )
{
Db* dbp = new Db( dbenv, DB_CXX_NO_EXCEPTIONS );
string fileName(*iter);
cout << "Removing " << fileName << endl;
int rc = dbp->remove( fileName.c_str(), 0, 0 );
ok = ok && (rc == 0);
SetBDBError( rc );
delete dbp;
iter++;
}
//ok = ok && FileSystem::EraseFolder( database );
// In client server mode there are problems
// when a database are created and deleted within the
// same session. Keeping the folder fixes this problem.
}
else
cerr<<"\nWarning: database " << database
<< ": database folder not found.\n";
FileSystem::SetCurrentFolder( oldHome );
ok = ok && UnlockDatabase( database );
if ( !ok ) {
SetError( E_SMI_DB_ERASE );
}
}
else
{
SetError( E_SMI_DB_NOTLOCKED );
ok = false;
}
}
else {
// database does not exist. Nothing to erase!
}
} else {
SetError(E_SMI_DB_INVALIDNAME);
ok = false;
}
}
else
{
SetError( E_SMI_DB_NOTCLOSED );
ok = false;
}
return (ok);
}
bool
SmiEnvironment::BeginTransaction()
{
int rc = 0;
if ( !instance.impl->txnStarted )
{
DbEnv* dbenv = instance.impl->bdbEnv;
if ( useTransactions )
{
rc = dbenv->txn_begin( 0, &instance.impl->usrTxn, 0 );
}
if ( rc == 0 )
{
instance.impl->txnStarted = true;
}
else
{
SetBDBError( rc );
}
}
else
{
rc = E_SMI_TXN_RUNNING;
SetError( E_SMI_TXN_RUNNING );
}
return (rc == 0);
}
void
SmiEnvironment::UpdateCatalog() {
SmiEnvironment::Implementation::UpdateCatalog( true );
SmiEnvironment::Implementation::CloseDbHandles();
}
bool
SmiEnvironment::CommitTransaction(bool closeDBhandles)
{
int rc = 0;
if ( instance.impl->txnStarted )
{
// --- Close _all_ open cursors first!!!
SmiEnvironment::Implementation::UpdateCatalog( true );
if ( useTransactions )
{
if ( instance.impl->txnMustAbort )
{
rc = instance.impl->usrTxn->abort();
// possibly DB_LOCK_DEADLOCK;
}
else
{
rc = instance.impl->usrTxn->commit( 0 );
}
SetBDBError( rc );
}
StopWatch closeTime; // measure time for closing DbHandles
LOGMSG( "SMI:DbHandles",
cerr << "Calling CloseDbHandles() ..." << endl;
)
if(closeDBhandles){
SmiEnvironment::Implementation::CloseDbHandles();
SmiEnvironment::Implementation::EraseFiles( rc == 0, rc != 0 );
}
LOGMSG( "SMI:DbHandles",
cerr << "Time for CloseDbHandles(): " << closeTime.diffTimes() << endl;
)
instance.impl->txnStarted = false;
instance.impl->txnMustAbort = false;
instance.impl->usrTxn = 0;
if ( singleUserMode && useTransactions )
{
rc = instance.impl->bdbEnv->txn_checkpoint( 0, 0, 0 );
SetBDBError( rc );
}
}
else
{
rc = E_SMI_TXN_NOTRUNNING;
SetError( E_SMI_TXN_NOTRUNNING );
}
return (rc == 0);
}
bool
SmiEnvironment::AbortTransaction()
{
int rc = 0;
if ( instance.impl->txnStarted )
{
// --- Close _all_ open cursors first!!!
SmiEnvironment::Implementation::UpdateCatalog( false );
if ( useTransactions )
{
rc = instance.impl->usrTxn->abort();
SetBDBError( rc );
}
SmiEnvironment::Implementation::CloseDbHandles();
SmiEnvironment::Implementation::EraseFiles( false, true );
instance.impl->txnStarted = false;
instance.impl->txnMustAbort = false;
instance.impl->usrTxn = 0;
if ( singleUserMode && useTransactions )
{
rc = instance.impl->bdbEnv->txn_checkpoint( 0, 0, 0 );
SetBDBError( rc );
}
}
else
{
rc = E_SMI_TXN_NOTRUNNING;
SetError( E_SMI_TXN_NOTRUNNING );
}
return (rc == 0);
}
bool
SmiEnvironment::InitializeDatabase()
{
#ifdef THREAD_SAFE
boost::lock_guard<boost::recursive_mutex> guard(env_mtx);
#endif
TRACE_ENTER
int rc = 0;
string prefix = database+PATH_SLASH;
string dbseqFileName = prefix+"sequences";
string dbctlFileName = prefix+"filecatalog";
string dbidxFileName = prefix+"fileindex";
DbEnv* dbenv = instance.impl->bdbEnv;
Db* dbctl = 0;
Db* dbidx = 0;
// --- Create Sequence, if it does not exist
Db* dbseq = new Db( dbenv, DB_CXX_NO_EXCEPTIONS );
dbseq->set_re_len( sizeof( SmiFileId ) );
rc = dbseq->open( 0, dbseqFileName.c_str(), 0, DB_QUEUE,
DB_CREATE | Implementation::AutoCommitFlag, 0 );
if ( rc == 0 )
{
instance.impl->bdbSeq = dbseq;
}
else
{
instance.impl->bdbSeq = 0;
if (rc != ENOENT)
SetBDBError(rc);
}
// --- Create system catalog, if it does not exist
if ( rc == 0 )
{
dbctl = new Db( dbenv, DB_CXX_NO_EXCEPTIONS );
dbidx = new Db( dbenv, DB_CXX_NO_EXCEPTIONS );
rc = dbctl->open( 0, dbctlFileName.c_str(), 0, DB_BTREE,
DB_CREATE | Implementation::AutoCommitFlag, 0 );
if ( rc == 0 )
{
instance.impl->bdbCatalog = dbctl;
}
else
{
instance.impl->bdbCatalog = 0;
if (rc != ENOENT)
SetBDBError(rc);
}
rc = dbidx->open( 0, dbidxFileName.c_str(), 0, DB_BTREE,
DB_CREATE | Implementation::AutoCommitFlag, 0 );
if ( rc == 0 )
{
instance.impl->bdbCatalogIndex = dbidx;
}
else
{
instance.impl->bdbCatalogIndex = 0;
if (rc != ENOENT)
SetBDBError(rc);
}
// --- Associate the secondary key with the primary key
if ( rc == 0 )
{
rc = dbctl->associate( 0, dbidx, getfilename,
Implementation::AutoCommitFlag );
SetBDBError(rc);
}
}
// --- Start user transaction
if ( rc != 0 )
{
// --- In case of an error delete the created files
if ( dbseq )
{
rc = dbseq->close( 0 );
SetBDBError(rc);
delete dbseq;
instance.impl->bdbSeq = 0;
// spm: Removing database files which could not be opened
// or created makes no sense. This will fail or may corrupt
// the database if open returns some error
//Db* dbp = new Db( dbenv, DB_CXX_NO_EXCEPTIONS );
//rc = dbp->remove( dbseqFileName.c_str(), 0, 0 );
//delete dbp;
}
if ( dbctl )
{
rc = dbctl->close( 0 );
SetBDBError(rc);
delete dbctl;
instance.impl->bdbCatalog = 0;
//Db* dbp = new Db( dbenv, DB_CXX_NO_EXCEPTIONS );
//rc = dbp->remove( dbctlFileName.c_str(), 0, 0 );
//delete dbp;
}
if ( dbidx )
{
rc = dbidx->close( 0 );
SetBDBError(rc);
delete dbidx;
instance.impl->bdbCatalogIndex = 0;
//Db* dbp = new Db( dbenv, DB_CXX_NO_EXCEPTIONS );
//rc = dbp->remove( dbidxFileName.c_str(), 0, 0 );
//delete dbp;
}
}
TRACE_LEAVE
return (rc == 0);
}
bool
SmiEnvironment::GetCacheStatistics(CacheInfo& ci, vector<FileInfo*>& fi)
{
DbEnv* dbenv = instance.impl->bdbEnv;
DB_MPOOL_STAT* gsp;
DB_MPOOL_FSTAT** fsp;
int rc = dbenv->memp_stat( &gsp, &fsp, DB_STAT_CLEAR);
if(rc!=0){
cerr << "error during memstat operation" << endl;
return false;
}
ci.bytes = gsp->st_bytes;
ci.regsize = gsp->st_regsize;
ci.cache_hit = gsp->st_cache_hit;
ci.cache_miss = gsp->st_cache_miss;
ci.page_create = gsp->st_page_create;
ci.page_in = gsp->st_page_in;
ci.page_out = gsp->st_page_out;
ci.pages = gsp->st_pages;
free(gsp);
// copy the number of file statistics
DB_MPOOL_FSTAT** fsp2(fsp);;
if (fsp != 0)
{
while (*fsp != 0)
{
DB_MPOOL_FSTAT& fs = **fsp;
string ml(fs.file_name);
FileInfo* fstat = new FileInfo(0,
ml,
fs.st_pagesize,
fs.st_cache_hit,
fs.st_cache_miss,
fs.st_page_create,
fs.st_page_in,
fs.st_page_out);
fi.push_back( fstat );
fsp++;
}
free(fsp2);
}
return (rc == 0);
}
/* --- Definition of internal procedures --- */
static int
getfilename( Db* dbp, const Dbt* pkey, const Dbt* pdata, Dbt* skey )
{
SmiCatalogEntry* entry = (SmiCatalogEntry*) pdata->get_data();
skey->set_data( entry->fileName );
skey->set_size( strlen( entry->fileName ) );
skey->set_ulen( 0 );
skey->set_dlen( 0 );
skey->set_doff( 0 );
skey->set_flags( 0 );
return (0);
}
/* --- bdbEnvironment.cpp --- */