summaryrefslogtreecommitdiff
path: root/cpp/src/Freeze/MapDb.cpp
diff options
context:
space:
mode:
authorBernard Normier <bernard@zeroc.com>2007-12-12 12:03:04 -0500
committerBernard Normier <bernard@zeroc.com>2007-12-12 12:03:04 -0500
commit28c22de9b812daeffa630656818da6ec5411a7da (patch)
treea3b5142c2144ead6aafb62dd71baf0657296e014 /cpp/src/Freeze/MapDb.cpp
parentFixed bug #2546 (diff)
downloadice-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.cpp478
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);
+ }
+}