// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once #include "sqliteglobal.h" #include #include #include namespace Sqlite { class DatabaseBackend; class Database; class TransactionInterface { public: TransactionInterface() = default; TransactionInterface(const TransactionInterface &) = delete; TransactionInterface &operator=(const TransactionInterface &) = delete; virtual void deferredBegin() = 0; virtual void immediateBegin() = 0; virtual void exclusiveBegin() = 0; virtual void commit() = 0; virtual void rollback() = 0; virtual void lock() = 0; virtual void unlock() = 0; virtual void immediateSessionBegin() = 0; virtual void sessionCommit() = 0; virtual void sessionRollback() = 0; protected: ~TransactionInterface() = default; }; template class AbstractTransaction { public: using Transaction = TransactionInterface; AbstractTransaction(const AbstractTransaction &) = delete; AbstractTransaction &operator=(const AbstractTransaction &) = delete; void commit() { m_interface.commit(); m_isAlreadyCommited = true; m_locker.unlock(); } protected: ~AbstractTransaction() = default; AbstractTransaction(TransactionInterface &transactionInterface) : m_interface(transactionInterface) { } protected: TransactionInterface &m_interface; std::unique_lock m_locker{m_interface}; bool m_isAlreadyCommited = false; bool m_rollback = false; }; template class AbstractThrowingSessionTransaction { public: using Transaction = TransactionInterface; AbstractThrowingSessionTransaction(const AbstractThrowingSessionTransaction &) = delete; AbstractThrowingSessionTransaction &operator=(const AbstractThrowingSessionTransaction &) = delete; void commit() { m_interface.sessionCommit(); m_isAlreadyCommited = true; m_locker.unlock(); } ~AbstractThrowingSessionTransaction() noexcept(false) { try { if (m_rollback) m_interface.sessionRollback(); } catch (...) { if (!std::uncaught_exceptions()) throw; } } protected: AbstractThrowingSessionTransaction(TransactionInterface &transactionInterface) : m_interface(transactionInterface) {} protected: TransactionInterface &m_interface; std::unique_lock m_locker{m_interface}; bool m_isAlreadyCommited = false; bool m_rollback = false; }; template class AbstractThrowingTransaction : public AbstractTransaction { using Base = AbstractTransaction; public: AbstractThrowingTransaction(const AbstractThrowingTransaction &) = delete; AbstractThrowingTransaction &operator=(const AbstractThrowingTransaction &) = delete; ~AbstractThrowingTransaction() noexcept(false) { try { if (Base::m_rollback) Base::m_interface.rollback(); } catch (...) { if (!std::uncaught_exceptions()) throw; } } protected: AbstractThrowingTransaction(TransactionInterface &transactionInterface) : AbstractTransaction(transactionInterface) { } }; template class AbstractNonThrowingDestructorTransaction : public AbstractTransaction { using Base = AbstractTransaction; public: AbstractNonThrowingDestructorTransaction(const AbstractNonThrowingDestructorTransaction &) = delete; AbstractNonThrowingDestructorTransaction &operator=(const AbstractNonThrowingDestructorTransaction &) = delete; ~AbstractNonThrowingDestructorTransaction() { try { if (Base::m_rollback) Base::m_interface.rollback(); } catch (...) { } } protected: AbstractNonThrowingDestructorTransaction(TransactionInterface &transactionInterface) : AbstractTransaction(transactionInterface) { } }; template class BasicDeferredTransaction : public BaseTransaction { public: BasicDeferredTransaction(typename BaseTransaction::Transaction &transactionInterface) : BaseTransaction(transactionInterface) { transactionInterface.deferredBegin(); } ~BasicDeferredTransaction() { BaseTransaction::m_rollback = !BaseTransaction::m_isAlreadyCommited; } }; template class DeferredTransaction final : public BasicDeferredTransaction> { using Base = BasicDeferredTransaction>; public: using Base::Base; }; template DeferredTransaction(TransactionInterface &) -> DeferredTransaction; template class DeferredNonThrowingDestructorTransaction final : public BasicDeferredTransaction> { using Base = BasicDeferredTransaction>; public: using Base::Base; }; template DeferredNonThrowingDestructorTransaction(TransactionInterface &) -> DeferredNonThrowingDestructorTransaction; template class BasicImmediateTransaction : public BaseTransaction { public: BasicImmediateTransaction(typename BaseTransaction::Transaction &transactionInterface) : BaseTransaction(transactionInterface) { transactionInterface.immediateBegin(); } ~BasicImmediateTransaction() { BaseTransaction::m_rollback = !BaseTransaction::m_isAlreadyCommited; } }; template class ImmediateTransaction final : public BasicImmediateTransaction> { using Base = BasicImmediateTransaction>; public: using Base::Base; }; template ImmediateTransaction(TransactionInterface &) -> ImmediateTransaction; template class ImmediateNonThrowingDestructorTransaction final : public BasicImmediateTransaction> { using Base = BasicImmediateTransaction>; public: using Base::Base; }; template ImmediateNonThrowingDestructorTransaction(TransactionInterface &) -> ImmediateNonThrowingDestructorTransaction; template class BasicExclusiveTransaction : public BaseTransaction { public: BasicExclusiveTransaction(typename BaseTransaction::Transaction &transactionInterface) : BaseTransaction(transactionInterface) { transactionInterface.exclusiveBegin(); } ~BasicExclusiveTransaction() { BaseTransaction::m_rollback = !BaseTransaction::m_isAlreadyCommited; } }; template class ExclusiveTransaction final : public BasicExclusiveTransaction> { using Base = BasicExclusiveTransaction>; public: using Base::Base; }; template ExclusiveTransaction(TransactionInterface &) -> ExclusiveTransaction; template class ExclusiveNonThrowingDestructorTransaction final : public BasicExclusiveTransaction> { using Base = BasicExclusiveTransaction>; public: using Base::Base; }; template ExclusiveNonThrowingDestructorTransaction(TransactionInterface &) -> ExclusiveNonThrowingDestructorTransaction; template class ImmediateSessionTransaction final : public AbstractThrowingSessionTransaction { using Base = AbstractThrowingSessionTransaction; public: ImmediateSessionTransaction(typename Base::Transaction &transactionInterface) : AbstractThrowingSessionTransaction(transactionInterface) { transactionInterface.immediateSessionBegin(); } ~ImmediateSessionTransaction() { Base::m_rollback = !Base::m_isAlreadyCommited; } }; template ImmediateSessionTransaction(TransactionInterface &) -> ImmediateSessionTransaction; } // namespace Sqlite