summaryrefslogtreecommitdiff
path: root/cpp/src/IceDB/IceDB.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/IceDB/IceDB.cpp')
-rw-r--r--cpp/src/IceDB/IceDB.cpp508
1 files changed, 508 insertions, 0 deletions
diff --git a/cpp/src/IceDB/IceDB.cpp b/cpp/src/IceDB/IceDB.cpp
new file mode 100644
index 00000000000..8f4d5316909
--- /dev/null
+++ b/cpp/src/IceDB/IceDB.cpp
@@ -0,0 +1,508 @@
+// **********************************************************************
+//
+// Copyright (c) 2003-2016 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 <IceDB/IceDB.h>
+#include <Ice/Initialize.h>
+
+#include <lmdb.h>
+
+using namespace IceDB;
+using namespace std;
+
+LMDBException::LMDBException(const char* file, int line, int err) :
+ IceUtil::Exception(file, line),
+ _error(err)
+{
+}
+
+#ifndef ICE_CPP11_COMPILER
+LMDBException::~LMDBException() throw()
+{
+}
+#endif
+
+string
+LMDBException::ice_id() const
+{
+ return "::IceDB::LMDBException";
+}
+
+void
+LMDBException::ice_print(ostream& out) const
+{
+ IceUtil::Exception::ice_print(out);
+ out << ": " << mdb_strerror(_error);
+}
+
+#ifndef ICE_CPP11_MAPPING
+LMDBException*
+LMDBException::ice_clone() const
+{
+ return new LMDBException(*this);
+}
+#endif
+
+void
+LMDBException::ice_throw() const
+{
+ throw *this;
+}
+
+int
+LMDBException::error() const
+{
+ return _error;
+}
+
+KeyTooLongException::KeyTooLongException(const char* file, int line, size_t size) :
+ IceUtil::Exception(file, line),
+ _size(size)
+{
+}
+
+#ifndef ICE_CPP11_COMPILER
+KeyTooLongException::~KeyTooLongException() throw()
+{
+}
+#endif
+
+string
+KeyTooLongException::ice_id() const
+{
+ return "::IceDB::KeyTooLongException";
+}
+
+void
+KeyTooLongException::ice_print(ostream& out) const
+{
+ IceUtil::Exception::ice_print(out);
+ out << ": ";
+ if(_size > 0)
+ {
+ out << "Key size = " << _size << ", ";
+ }
+ out << "Max size = " << maxKeySize;
+}
+
+#ifndef ICE_CPP11_MAPPING
+KeyTooLongException*
+KeyTooLongException::ice_clone() const
+{
+ return new KeyTooLongException(*this);
+}
+#endif
+
+void
+KeyTooLongException::ice_throw() const
+{
+ throw *this;
+}
+
+BadEnvException::BadEnvException(const char* file, int line, size_t size) :
+ IceUtil::Exception(file, line),
+ _size(size)
+{
+}
+
+#ifndef ICE_CPP11_COMPILER
+BadEnvException::~BadEnvException() throw()
+{
+}
+#endif
+
+string
+BadEnvException::ice_id() const
+{
+ return "::IceDB::BadEnvException";
+}
+
+void
+BadEnvException::ice_print(ostream& out) const
+{
+ IceUtil::Exception::ice_print(out);
+ out << ": LMDB env max key size = " << _size;
+ out << ", IceDB max key size = " << maxKeySize;
+}
+
+#ifndef ICE_CPP11_MAPPING
+BadEnvException*
+BadEnvException::ice_clone() const
+{
+ return new BadEnvException(*this);
+}
+#endif
+
+void
+BadEnvException::ice_throw() const
+{
+ throw *this;
+}
+
+
+Env::Env(const string& path, MDB_dbi maxDbs, size_t mapSize, unsigned int maxReaders)
+{
+ int rc = mdb_env_create(&_menv);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+
+ if(maxDbs != 0)
+ {
+ rc = mdb_env_set_maxdbs(_menv, maxDbs);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+ }
+
+ if(mapSize != 0)
+ {
+ // Make sure the map size is a multiple of the page size
+ size_t pageSize;
+#ifdef _WIN32
+ SYSTEM_INFO si;
+ GetSystemInfo(&si);
+ pageSize = si.dwPageSize;
+#else
+ pageSize = sysconf(_SC_PAGESIZE);
+#endif
+ size_t remainder = mapSize % pageSize;
+ if(remainder != 0)
+ {
+ mapSize = mapSize + pageSize - remainder;
+ }
+ rc = mdb_env_set_mapsize(_menv, mapSize);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+ }
+
+ if(maxReaders != 0)
+ {
+ rc = mdb_env_set_maxreaders(_menv, maxReaders);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+ }
+
+ rc = mdb_env_open(_menv, path.c_str(), 0, 0644);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+
+ size_t envMaxKeySize = mdb_env_get_maxkeysize(_menv);
+ if(maxKeySize > envMaxKeySize)
+ {
+ throw BadEnvException(__FILE__, __LINE__, envMaxKeySize);
+ }
+}
+
+Env::~Env()
+{
+ close();
+}
+
+void
+Env::close()
+{
+ if(_menv != 0)
+ {
+ mdb_env_close(_menv);
+ _menv = 0;
+ }
+}
+
+MDB_env*
+Env::menv() const
+{
+ return _menv;
+}
+
+Txn::Txn(const Env& env, unsigned int flags)
+{
+ const int rc = mdb_txn_begin(env.menv(), 0, flags, &_mtxn);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+}
+
+Txn::~Txn()
+{
+ rollback();
+}
+
+void
+Txn::commit()
+{
+ const int rc = mdb_txn_commit(_mtxn);
+ _mtxn = 0;
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+}
+
+void
+Txn::rollback()
+{
+ if(_mtxn != 0)
+ {
+ mdb_txn_abort(_mtxn);
+ _mtxn = 0;
+ }
+}
+
+MDB_txn*
+Txn::mtxn() const
+{
+ return _mtxn;
+}
+
+ReadOnlyTxn::~ReadOnlyTxn()
+{
+ // Out of line to avoid weak vtable
+}
+
+ReadOnlyTxn::ReadOnlyTxn(const Env& env) :
+ Txn(env, MDB_RDONLY)
+{
+}
+
+void
+ReadOnlyTxn::reset()
+{
+ mdb_txn_reset(_mtxn);
+}
+
+void
+ReadOnlyTxn::renew()
+{
+ const int rc = mdb_txn_renew(_mtxn);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+}
+
+ReadWriteTxn::~ReadWriteTxn()
+{
+ // Out of line to avoid weak vtable
+}
+
+ReadWriteTxn::ReadWriteTxn(const Env& env) :
+ Txn(env, 0)
+{
+}
+
+DbiBase::DbiBase(const Txn& txn, const std::string& name, unsigned int flags, MDB_cmp_func* cmp)
+{
+ int rc = mdb_dbi_open(txn.mtxn(), name.c_str(), flags, &_mdbi);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+ if(cmp != 0)
+ {
+ rc = mdb_set_compare(txn.mtxn(), _mdbi, cmp);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+ }
+}
+
+DbiBase::DbiBase() :
+ _mdbi(0)
+{
+}
+
+DbiBase::~DbiBase()
+{
+}
+
+void
+DbiBase::clear(const ReadWriteTxn& txn)
+{
+ const int rc = mdb_drop(txn.mtxn(), _mdbi, 0);
+ if (rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+}
+
+MDB_dbi
+DbiBase::mdbi() const
+{
+ return _mdbi;
+}
+
+bool
+DbiBase::get(const Txn& txn, MDB_val* key, MDB_val* data) const
+{
+ assert(key->mv_size <= maxKeySize);
+
+ const int rc = mdb_get(txn.mtxn(), _mdbi, key, data);
+ if(rc != MDB_SUCCESS && rc != MDB_NOTFOUND)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+ return rc == MDB_SUCCESS;
+}
+
+void
+DbiBase::put(const ReadWriteTxn& txn, MDB_val* key, MDB_val* data, unsigned int flags)
+{
+ assert(key->mv_size <= maxKeySize);
+
+ const int rc = mdb_put(txn.mtxn(), _mdbi, key, data, flags);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+}
+
+bool
+DbiBase::find(const Txn& txn, MDB_val* key) const
+{
+ MDB_val data;
+ return get(txn, key, &data);
+}
+
+bool
+DbiBase::del(const ReadWriteTxn& txn, MDB_val* key, MDB_val* data)
+{
+ assert(key->mv_size <= maxKeySize);
+
+ const int rc = mdb_del(txn.mtxn(), _mdbi, key, data);
+ if (rc != MDB_SUCCESS && rc != MDB_NOTFOUND)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+ return rc == MDB_SUCCESS;
+}
+
+CursorBase::CursorBase(MDB_dbi dbi, const Txn& txn, bool readOnly) :
+ _readOnly(readOnly)
+{
+ const int rc = mdb_cursor_open(txn.mtxn(), dbi, &_mcursor);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+}
+
+CursorBase::~CursorBase()
+{
+ if(_readOnly)
+ {
+ close();
+ }
+}
+
+void
+CursorBase::close()
+{
+ if(_mcursor != 0)
+ {
+ mdb_cursor_close(_mcursor);
+ _mcursor = 0;
+ }
+}
+
+MDB_cursor*
+CursorBase::mcursor() const
+{
+ return _mcursor;
+}
+
+bool
+CursorBase::get(MDB_val* key, MDB_val* data, MDB_cursor_op op)
+{
+ const int rc = mdb_cursor_get(_mcursor, key, data, op);
+ if(rc != MDB_SUCCESS && rc != MDB_NOTFOUND)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+ return rc == MDB_SUCCESS;
+}
+
+void
+CursorBase::put(MDB_val* key, MDB_val* data, unsigned int flags)
+{
+ assert(key->mv_size <= maxKeySize);
+ assert(!_readOnly);
+
+ const int rc = mdb_cursor_put(_mcursor, key, data, flags);
+ if (rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+}
+
+bool
+CursorBase::find(MDB_val* key)
+{
+ MDB_val data;
+ return get(key, &data, MDB_SET);
+}
+
+bool
+CursorBase::find(MDB_val* key, MDB_val* data)
+{
+ return get(key, data, MDB_SET);
+}
+
+void
+CursorBase::del(unsigned int flags)
+{
+ assert(!_readOnly);
+
+ const int rc = mdb_cursor_del(_mcursor, flags);
+ if (rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+}
+
+void
+CursorBase::renew(const ReadOnlyTxn& txn)
+{
+ assert(_readOnly);
+ const int rc = mdb_cursor_renew(txn.mtxn(), CursorBase::_mcursor);
+ if(rc != MDB_SUCCESS)
+ {
+ throw LMDBException(__FILE__, __LINE__, rc);
+ }
+}
+
+
+//
+// On Windows, we use a default LMDB map size of 10MB, whereas on other platforms
+// (Linux, OS X), we use a default of 100MB.
+//
+// On Windows, LMDB does not use sparse files and allocates immediately the file
+// with the given (max) size. This is why we need a fairly small default map size
+// on Windows, and a larger value on other platforms.
+
+size_t
+IceDB::getMapSize(int configValue)
+{
+#ifdef _WIN32
+ const size_t defaultMapSize = 10;
+#else
+ const size_t defaultMapSize = 100;
+#endif
+
+ return ((configValue <= 0) ? defaultMapSize : configValue) * 1024 * 1024;
+}