lmdbal/src/storage.cpp

443 lines
14 KiB
C++

/*
* LMDB Abstraction Layer.
* Copyright (C) 2023 Yury Gubich <blue@macaw.me>
*
* This program 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 3 of the License, or
* (at your option) any later version.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "storage.h"
#define UNUSED(x) (void)(x)
/**
* \class LMDBAL::iStorage
*
* \brief Storage interface
*
* This is a interface-like class, it's designed to be an inner database interface to
* be used as a polymorphic entity, and provide protected interaction with the database
* from the heirs code
*/
/**
* \brief Constructs a storage interface
*
* \param[in] parent - LMDBAL::Base pointer for the owning database (borrowed)
* \param[in] name - the name of the storage
* \param[in] duplicates - true if key duplicates are allowed (false by default)
*/
LMDBAL::iStorage::iStorage(Base* parent, const std::string& name, bool duplicates):
dbi(),
db(parent),
name(name),
duplicates(duplicates)
{}
/**
* \brief Destroys a storage interface
*/
LMDBAL::iStorage::~iStorage() {}
/**
* \brief A private virtual function I need to close each storage in the database
*/
void LMDBAL::iStorage::close() {
mdb_dbi_close(db->environment, dbi);
}
/**
* \brief Checks if the transaction is still active, returns inner TransactionID
*
* This method is for internal usage only
*
* \param[in] txn - a transaction, you want to extract ID from
* \param[in] action - a description of what you're going to do, in case of exception the error description will be verbose
* \returns - Transaction inner TransactionID
*
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
LMDBAL::TransactionID LMDBAL::iStorage::extractTransactionId(const Transaction& txn, const std::string& action) const {
if (!txn.isActive())
throw TransactionTerminated(db->name, name, action);
return txn.txn;
}
/**
* \brief Drops content of a storage interface
*
* Designed to drop storage content
*
* \exception LMDBAL::Closed thrown if the database was closed
* \exception LMDBAL::Unknown thrown if something unexpected happened
*/
void LMDBAL::iStorage::drop() {
ensureOpened(dropMethodName);
TransactionID txn = beginTransaction();
int rc = iStorage::drop(txn);
if (rc != MDB_SUCCESS) {
abortTransaction(txn);
throw Unknown(db->name, mdb_strerror(rc), name);
}
db->commitTransaction(txn);
handleDrop();
}
/**
* \brief Drops content of a storage interface (transaction variant)
*
* Just performs content drop
*
* \param[in] transaction - transaction ID, must be writable transaction!
* \returns MDB_SUCCESS if everything went fine, MDB_<error> code otherwise
*/
int LMDBAL::iStorage::drop(TransactionID transaction) {
return mdb_drop(transaction, dbi, 0);
}
/**
* \brief Drops content of a storage interface (public transaction variant)
*
* Just performs content drop
*
* \param[in] txn - transaction ID, must be writable transaction!
* \returns MDB_SUCCESS if everything went fine, MDB_<error> code otherwise
*
* \exception LMDBAL::TransactionTerminated thrown if the transaction was not active
*/
int LMDBAL::iStorage::drop(const WriteTransaction& txn) {
ensureOpened(dropMethodName);
return drop(extractTransactionId(txn, dropMethodName));
}
/**
* \brief Helper function, thows exception if the database is not opened
*
* \param[in] methodName - name of the method this function is called from, just for display in std::exception::what() message
*
* \exception LMDBAL::Closed thrown if the database was closed
*/
void LMDBAL::iStorage::ensureOpened(const std::string& methodName) const {
if (!isDBOpened())
throw Closed(methodName, db->name, name);
}
/**
* \brief Storage size
*
* \returns amount of records in the storage
*
* \exception LMDBAL::Closed thrown if the database was closed
* \exception LMDBAL::Unknown thrown if something unexpected happened
*/
LMDBAL::SizeType LMDBAL::iStorage::count() const {
ensureOpened(countMethodName);
TransactionID txn = beginReadOnlyTransaction();
SizeType amount;
try {
amount = count(txn);
} catch (...) {
abortTransaction(txn);
throw;
}
abortTransaction(txn);
return amount;
}
/**
* \brief Storage size (private transaction variant)
*
* \param[in] txn - transaction ID, can be read-only transaction
* \returns amount of records in the storage
*
* \exception LMDBAL::Unknown thrown if something unexpected happened
*/
LMDBAL::SizeType LMDBAL::iStorage::count(TransactionID txn) const {
MDB_stat stat;
int rc = mdb_stat(txn, dbi, &stat);
if (rc != MDB_SUCCESS)
throw Unknown(db->name, mdb_strerror(rc), name);
return stat.ms_entries;
}
/**
* \brief Storage size (public transaction variant)
*
* \param[in] txn - transaction, can be read-only transaction
* \returns amount of records in the storage
*
* \exception LMDBAL::Closed thrown if the database was closed
* \exception LMDBAL::Unknown thrown if something unexpected happened
* \exception LMDBAL::TransactionTerminated thrown if the passed transaction not active, any action with it's inner ID is an error
*/
LMDBAL::SizeType LMDBAL::iStorage::count(const Transaction& txn) const {
ensureOpened(countMethodName);
return count(extractTransactionId(txn, countMethodName));
}
/**
* \brief Throws LMDBAL::Exist or LMDBAL::Unknown (transaction vairiant)
*
* Helper function ment to be used in heirs and reduce the code a bit
*
* \param[in] rc - result of lmdb low level operation
* \param[in] txn - transaction ID to be aborted, any transaction
* \param[in] key - requested key string representation, just to show in std::exception::what() message
*
* \exception LMDBAL::Exist thrown if rc == MDB_KEYEXIST
* \exception LMDBAL::Unknown thrown if rc != MDB_KEYEXIST
*/
void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, TransactionID txn, const std::string& key) const {
abortTransaction(txn);
throwDuplicateOrUnknown(rc, key);
}
/**
* \brief Throws LMDBAL::NotFound or LMDBAL::Unknown (transaction vairiant)
*
* Helper function ment to be used in heirs and reduce the code a bit
*
* \param[in] rc - result of lmdb low level operation
* \param[in] txn - transaction ID to be aborted, any transaction
* \param[in] key - requested key string representation, just to show in std::exception::what() message
*
* \exception LMDBAL::NotFound thrown if rc == MDB_NOTFOUND
* \exception LMDBAL::Unknown thrown if rc != MDB_NOTFOUND
*/
void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, LMDBAL::TransactionID txn, const std::string& key) const {
abortTransaction(txn);
throwNotFoundOrUnknown(rc, key);
}
/**
* \brief Throws LMDBAL::Exist or LMDBAL::Unknown
*
* Helper function ment to be used in heirs and reduce the code a bit
*
* \param[in] rc - result of lmdb low level operation
* \param[in] key - requested key string representation, just to show in std::exception::what() message
*
* \exception LMDBAL::Exist thrown if rc == MDB_KEYEXIST
* \exception LMDBAL::Unknown thrown if rc != MDB_KEYEXIST
*/
void LMDBAL::iStorage::throwDuplicateOrUnknown(int rc, const std::string& key) const {
if (rc == MDB_KEYEXIST)
throwDuplicate(key);
else
throwUnknown(rc);
}
/**
* \brief Throws LMDBAL::NotFound or LMDBAL::Unknown (transaction variant)
*
* Helper function ment to be used in heirs and reduce the code a bit
*
* \param[in] rc - result of lmdb low level operation
* \param[in] key - requested key string representation, just to show in std::exception::what() message
*
* \exception LMDBAL::NotFound thrown if rc == MDB_NOTFOUND
* \exception LMDBAL::Unknown thrown if rc != MDB_NOTFOUND
*/
void LMDBAL::iStorage::throwNotFoundOrUnknown(int rc, const std::string& key) const {
if (rc == MDB_NOTFOUND)
throwNotFound(key);
else
throwUnknown(rc);
}
/**
* \brief Throws LMDBAL::Unknown (transaction vairiant)
*
* Helper function ment to be used in heirs and reduce the code a bit
*
* \param[in] rc - result of lmdb low level operation
* \param[in] txn - transaction ID to be aborted, any transaction
*
* \exception LMDBAL::Unknown thrown everytime
*/
void LMDBAL::iStorage::throwUnknown(int rc, LMDBAL::TransactionID txn) const {
abortTransaction(txn);
throwUnknown(rc);
}
/**
* \brief Database name
*
* Ment to be used in heirs, to provide some sort of interface to acces to some of the database information
*
* \returns database name
*/
const std::string & LMDBAL::iStorage::dbName() const {
return db->name;}
/**
* \brief Is database opened
*
* Ment to be used in heirs, to provide some sort of interface to acces to some of the database information
*
* \returns true if database is ipened, false otherwise
*/
bool LMDBAL::iStorage::isDBOpened() const {
return db->opened;}
/**
* \brief Throws LMDBAL::Unknown
*
* Helper function ment to be used in heirs and reduce the code a bit
*
* \param[in] rc - result of lmdb low level operation
*
* \exception LMDBAL::Unknown thrown everytime
*/
void LMDBAL::iStorage::throwUnknown(int rc) const {
throw Unknown(db->name, mdb_strerror(rc), name);}
/**
* \brief Throws LMDBAL::Unknown
*
* Helper function ment to be used in heirs and reduce the code a bit
*
* \param[in] message - a message you wish to appear in the exception reason
*
* \exception LMDBAL::Unknown thrown everytime
*/
void LMDBAL::iStorage::throwUnknown(const std::string& message) const {
throw Unknown(db->name, message, name);}
/**
* \brief Throws LMDBAL::Exist
*
* Helper function ment to be used in heirs and reduce the code a bit
*
* \param[in] key - requested key string representation, just to show in std::exception::what() message
*
* \exception LMDBAL::Exist thrown everytime
*/
void LMDBAL::iStorage::throwDuplicate(const std::string& key) const {
throw Exist(key, db->name, name);}
/**
* \brief Throws LMDBAL::NotFound
*
* Helper function ment to be used in heirs and reduce the code a bit
*
* \param[in] key - requested key string representation, just to show in std::exception::what() message
*
* \exception LMDBAL::NotFound thrown everytime
*/
void LMDBAL::iStorage::throwNotFound(const std::string& key) const {
throw NotFound(key, db->name, name);}
/**
* \brief Throws LMDBAL::CursorNotReady
*
* Helper function ment to be used in heirs and reduce the code a bit
*
* \param[in] method - called cursor method name, just to show in std::exception::what() message
*
* \exception LMDBAL::CursorNotReady thrown everytime
*/
void LMDBAL::iStorage::throwCursorNotReady(const std::string& method) const {
throw CursorNotReady(method, db->name, name);}
/**
* \brief Begins read-only transaction
*
* Ment to be called from heirs, name is reported to the database just to be displayed in std::exception::what() message
*
* \returns read only transaction
*/
LMDBAL::TransactionID LMDBAL::iStorage::beginReadOnlyTransaction() const {
return db->beginPrivateReadOnlyTransaction(name);}
/**
* \brief Begins writable transaction
*
* Ment to be called from heirs, name is reported to the database just to be displayed in std::exception::what() message
*
* \returns read only transaction
*/
LMDBAL::TransactionID LMDBAL::iStorage::beginTransaction() const {
return db->beginPrivateTransaction(name);}
/**
* \brief Aborts transaction
*
* Ment to be called from heirs, name is reported to the database just to be displayed in std::exception::what() message
*/
void LMDBAL::iStorage::abortTransaction(LMDBAL::TransactionID id) const {
db->abortPrivateTransaction(id, name);}
/**
* \brief Commits transaction
*
* Ment to be called from heirs, name is reported to the database just to be displayed in std::exception::what() message
*
* \exception LMDBAL::Unknown thrown if something unexpected happened
*/
void LMDBAL::iStorage::commitTransaction(LMDBAL::TransactionID id) {
db->commitPrivateTransaction(id, name);}
/**
* \brief called on beginning of public transaction
*
* This function is called on every storage of the database
* when user calls LMDBAL::Base::beginTransaction() or LMDBAL::Base::beginReadOnlyTransaction()
*
* This function is met to be reimplemented in heirs
* if the heir code requires some transaction custom handling
*
* \param[in] txn - ID of started transaction
* \param[in] readOnly - true if transaction is read-only, false otherwise
*/
void LMDBAL::iStorage::transactionStarted(LMDBAL::TransactionID txn, bool readOnly) const {
UNUSED(txn);
UNUSED(readOnly);
}
/**
* \brief called on commitment of public transaction
*
* This function is called on every storage of the database
* when user calls LMDBAL::Base::commitTransaction(LMDBAL::TransactionID)
*
* This function is met to be reimplemented in heirs
* if the heir code requires some transaction custom handling
*
* \param[in] txn - ID of started transaction
*/
void LMDBAL::iStorage::transactionCommited(LMDBAL::TransactionID txn) {
UNUSED(txn);}
/**
* \brief called on abortion of public transaction
*
* This function is called on every storage of the database
* when user calls LMDBAL::Base::abortTransaction(LMDBAL::TransactionID)
*
* This function is met to be reimplemented in heirs
* if the heir code requires some transaction custom handling
*
* \param[in] txn - ID of started transaction
*/
void LMDBAL::iStorage::transactionAborted(LMDBAL::TransactionID txn) const {
UNUSED(txn);}
void LMDBAL::iStorage::handleDrop() {}