diff options
author | Bernard Normier <bernard@zeroc.com> | 2007-12-12 12:03:04 -0500 |
---|---|---|
committer | Bernard Normier <bernard@zeroc.com> | 2007-12-12 12:03:04 -0500 |
commit | 28c22de9b812daeffa630656818da6ec5411a7da (patch) | |
tree | a3b5142c2144ead6aafb62dd71baf0657296e014 /cpp/src/Freeze/MapDb.cpp | |
parent | Fixed bug #2546 (diff) | |
download | ice-28c22de9b812daeffa630656818da6ec5411a7da.tar.bz2 ice-28c22de9b812daeffa630656818da6ec5411a7da.tar.xz ice-28c22de9b812daeffa630656818da6ec5411a7da.zip |
Fixed bug #2557 (closing database within transaction)
Diffstat (limited to 'cpp/src/Freeze/MapDb.cpp')
-rw-r--r-- | cpp/src/Freeze/MapDb.cpp | 478 |
1 files changed, 478 insertions, 0 deletions
diff --git a/cpp/src/Freeze/MapDb.cpp b/cpp/src/Freeze/MapDb.cpp new file mode 100644 index 00000000000..6b86f95b393 --- /dev/null +++ b/cpp/src/Freeze/MapDb.cpp @@ -0,0 +1,478 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2007 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <Freeze/MapDb.h> +#include <Freeze/Exception.h> +#include <Freeze/Util.h> +#include <Freeze/Catalog.h> +#include <Freeze/CatalogIndexList.h> +#include <algorithm> + +using namespace std; +using namespace Ice; +using namespace Freeze; + +namespace +{ + +const string _catalogName = "__catalog"; +const string _catalogIndexListName = "__catalogIndexList"; + +} + +extern "C" +{ +static int customCompare(DB* db, const DBT* dbt1, const DBT* dbt2) +{ + MapDb* me = static_cast<MapDb*>(db->app_private); + Ice::Byte* first = static_cast<Ice::Byte*>(dbt1->data); + Key k1(first, first + dbt1->size); + first = static_cast<Ice::Byte*>(dbt2->data); + Key k2(first, first + dbt2->size); + + return me->getKeyCompare()->compare(k1, k2); +} +} + +const string& +Freeze::catalogName() +{ + return _catalogName; +} + +const string& +Freeze::catalogIndexListName() +{ + return _catalogIndexListName; +} + +Freeze::MapDb::~MapDb() +{ + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "closing Db \"" << _dbName << "\""; + } + + clearIndices(); + + if(get_DB() != 0) + { + try + { + close(0); + } + catch(const ::DbException& dx) + { + throw DatabaseException(__FILE__, __LINE__, dx.what()); + } + } +} + +Freeze::MapDb::MapDb(const ConnectionIPtr& connection, + const string& dbName, + const string& key, + const string& value, + const KeyCompareBasePtr& keyCompare, + const vector<MapIndexBasePtr>& indices, + bool createDb) : + Db(connection->dbEnv()->getEnv(), 0), + _communicator(connection->communicator()), + _dbName(dbName), + _trace(connection->trace()), + _keyCompare(keyCompare) +{ + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "opening Db \"" << _dbName << "\""; + } + + Catalog catalog(connection, _catalogName); + + TransactionPtr tx = connection->currentTransaction(); + bool ownTx = (tx == 0); + + for(;;) + { + try + { + if(ownTx) + { + tx = 0; + tx = connection->beginTransaction(); + } + + Catalog::iterator ci = catalog.find(_dbName); + + if(ci != catalog.end()) + { + if(ci->second.evictor) + { + throw DatabaseException(__FILE__, __LINE__, _dbName + " is an evictor database"); + } + + _key = ci->second.key; + _value = ci->second.value; + checkTypes(key, value); + } + else + { + _key = key; + _value = value; + } + + set_app_private(this); + if(_keyCompare->compareEnabled()) + { + set_bt_compare(&customCompare); + } + + PropertiesPtr properties = _communicator->getProperties(); + string propPrefix = "Freeze.Map." + _dbName + "."; + + int btreeMinKey = properties->getPropertyAsInt(propPrefix + "BtreeMinKey"); + if(btreeMinKey > 2) + { + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "Setting \"" << _dbName << "\"'s btree minkey to " << btreeMinKey; + } + set_bt_minkey(btreeMinKey); + } + + bool checksum = properties->getPropertyAsInt(propPrefix + "Checksum") > 0; + if(checksum) + { + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "Turning checksum on for \"" << _dbName << "\""; + } + + set_flags(DB_CHKSUM); + } + + int pageSize = properties->getPropertyAsInt(propPrefix + "PageSize"); + if(pageSize > 0) + { + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "Setting \"" << _dbName << "\"'s pagesize to " << pageSize; + } + set_pagesize(pageSize); + } + + + DbTxn* txn = getTxn(tx); + + u_int32_t flags = DB_THREAD; + if(createDb) + { + flags |= DB_CREATE; + } + open(txn, _dbName.c_str(), 0, DB_BTREE, flags, FREEZE_DB_MODE); + + + StringSeq oldIndices; + StringSeq newIndices; + size_t oldSize = 0; + CatalogIndexList catalogIndexList(connection, _catalogIndexListName); + + if(createDb) + { + CatalogIndexList::iterator cil = catalogIndexList.find(_dbName); + if(cil != catalogIndexList.end()) + { + oldIndices = cil->second; + oldSize = oldIndices.size(); + } + } + + for(vector<MapIndexBasePtr>::const_iterator p = indices.begin(); + p != indices.end(); ++p) + { + const MapIndexBasePtr& indexBase = *p; + assert(indexBase->_impl == 0); + assert(indexBase->_communicator == 0); + indexBase->_communicator = connection->communicator(); + + auto_ptr<MapIndexI> indexI; + + try + { + indexI.reset(new MapIndexI(connection, *this, txn, createDb, indexBase)); + } + catch(const DbDeadlockException&) + { + throw; + } + catch(const DbException& dx) + { + string message = "Error while opening index \"" + _dbName + + "." + indexBase->name() + "\": " + dx.what(); + + throw DatabaseException(__FILE__, __LINE__, message); + } + +#ifndef NDEBUG + bool inserted = +#endif + _indices.insert(IndexMap::value_type(indexBase->name(), indexI.get())).second; + assert(inserted); + + indexBase->_impl = indexI.release(); + + if(createDb) + { + newIndices.push_back(indexBase->name()); + oldIndices.erase(std::remove(oldIndices.begin(), oldIndices.end(), indexBase->name()), oldIndices.end()); + } + } + + if(ci == catalog.end()) + { + CatalogData catalogData; + catalogData.evictor = false; + catalogData.key = key; + catalogData.value = value; + catalog.put(Catalog::value_type(_dbName, catalogData)); + } + + if(createDb) + { + // + // Remove old indices and write the new ones + // + bool indexRemoved = false; + + for(StringSeq::const_iterator q = oldIndices.begin(); q != oldIndices.end(); ++q) + { + const string& index = *q; + + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "removing old index \"" << index << "\" on Db \"" << _dbName << "\""; + } + + try + { + connection->removeMapIndex(_dbName, *q); + indexRemoved = true; + } + catch(const IndexNotFoundException&) + { + // Ignored + + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "index \"" << index << "\" on Db \"" << _dbName << "\" does not exist"; + } + } + } + + if(indexRemoved || oldSize != newIndices.size()) + { + if(newIndices.size() == 0) + { + catalogIndexList.erase(_dbName); + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "Removed catalogIndexList entry for Db \"" << _dbName << "\""; + } + + } + else + { + catalogIndexList.put(CatalogIndexList::value_type(_dbName, newIndices)); + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "Updated catalogIndexList entry for Db \"" << _dbName << "\""; + } + } + } + } + + if(ownTx) + { + tx->commit(); + } + break; // for(;;) + } + catch(const DbDeadlockException& dx) + { + if(ownTx) + { + if(connection->deadlockWarning()) + { + Warning out(connection->communicator()->getLogger()); + out << "Deadlock in Freeze::MapDb::MapDb on Map \"" + << _dbName << "\"; retrying ..."; + } + + // + // Ignored, try again + // + } + else + { + throw DeadlockException(__FILE__, __LINE__, dx.what(), tx); + } + } + catch(const DbException& dx) + { + if(ownTx) + { + try + { + tx->rollback(); + } + catch(...) + { + } + } + + string message = "Error while opening Db \"" + _dbName + + "\": " + dx.what(); + + throw DatabaseException(__FILE__, __LINE__, message); + } + catch(...) + { + if(ownTx && tx != 0) + { + try + { + tx->rollback(); + } + catch(...) + { + } + } + throw; + } + } +} + +Freeze::MapDb::MapDb(const Ice::CommunicatorPtr& communicator, const string& dbName, const string& keyTypeId, const string& valueTypeId, DbEnv* env) : + Db(env, 0), + _communicator(communicator), + _dbName(dbName), + _key(keyTypeId), + _value(valueTypeId), + _trace(communicator->getProperties()->getPropertyAsInt("Freeze.Trace.Map")) +{ + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "opening Db \"" << _dbName << "\""; + } + + try + { + PropertiesPtr properties = _communicator->getProperties(); + string propPrefix = "Freeze.Map." + _dbName + "."; + + int btreeMinKey = properties->getPropertyAsInt(propPrefix + "BtreeMinKey"); + if(btreeMinKey > 2) + { + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "Setting \"" << _dbName << "\"'s btree minkey to " << btreeMinKey; + } + set_bt_minkey(btreeMinKey); + } + + bool checksum = properties->getPropertyAsInt(propPrefix + "Checksum") > 0; + if(checksum) + { + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "Turning checksum on for \"" << _dbName << "\""; + } + + set_flags(DB_CHKSUM); + } + + int pageSize = properties->getPropertyAsInt(propPrefix + "PageSize"); + if(pageSize > 0) + { + if(_trace >= 1) + { + Trace out(_communicator->getLogger(), "Freeze.Map"); + out << "Setting \"" << _dbName << "\"'s pagesize to " << pageSize; + } + set_pagesize(pageSize); + } + + u_int32_t flags = DB_THREAD | DB_CREATE | DB_AUTO_COMMIT; + open(0, _dbName.c_str(), 0, DB_BTREE, flags, FREEZE_DB_MODE); + } + catch(const ::DbException& dx) + { + throw DatabaseException(__FILE__, __LINE__, dx.what()); + } +} + +void +Freeze::MapDb::connectIndices(const vector<MapIndexBasePtr>& indices) const +{ + for(vector<MapIndexBasePtr>::const_iterator p = indices.begin(); + p != indices.end(); ++p) + { + const MapIndexBasePtr& indexBase = *p; + assert(indexBase->_impl == 0); + + IndexMap::const_iterator q = _indices.find(indexBase->name()); + + assert(q != _indices.end()); + indexBase->_impl = q->second; + indexBase->_communicator = _communicator; + } +} + +void +Freeze::MapDb::clearIndices() +{ + try + { + for(IndexMap::iterator p = _indices.begin(); p != _indices.end(); ++p) + { + delete p->second; + } + } + catch(const ::DbException& dx) + { + throw DatabaseException(__FILE__, __LINE__, dx.what()); + } + _indices.clear(); +} + +void +Freeze::MapDb::checkTypes(const string& key, const string& value) const +{ + if(key != _key) + { + throw DatabaseException(__FILE__, __LINE__, + _dbName + "'s key type is " + _key + ", not " + key); + } + if(value != _value) + { + throw DatabaseException(__FILE__, __LINE__, + _dbName + "'s value type is " + _value + ", not " + value); + } +} |