// 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 "sourcelocation.h" #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(const source_location &sourceLocation) = 0; virtual void immediateBegin(const source_location &sourceLocation) = 0; virtual void exclusiveBegin(const source_location &sourceLocation) = 0; virtual void commit(const source_location &sourceLocation) = 0; virtual void rollback(const source_location &sourceLocation) = 0; virtual void lock() = 0; virtual void unlock() = 0; virtual void immediateSessionBegin(const source_location &sourceLocation) = 0; virtual void sessionCommit(const source_location &sourceLocation) = 0; virtual void sessionRollback(const source_location &sourceLocation) = 0; protected: ~TransactionInterface() = default; }; template class AbstractTransaction { public: using Transaction = TransactionInterface; AbstractTransaction(const AbstractTransaction &) = delete; AbstractTransaction &operator=(const AbstractTransaction &) = delete; void commit(const source_location &sourceLocation = source_location::current()) { m_interface.commit(sourceLocation); 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 ImplicitTransaction { public: using Transaction = TransactionInterface; ~ImplicitTransaction() = default; ImplicitTransaction(TransactionInterface &transactionInterface) : m_locker(transactionInterface) {} ImplicitTransaction(const ImplicitTransaction &) = delete; ImplicitTransaction &operator=(const ImplicitTransaction &) = delete; protected: std::unique_lock m_locker; }; template class AbstractThrowingSessionTransaction { public: using Transaction = TransactionInterface; AbstractThrowingSessionTransaction(const AbstractThrowingSessionTransaction &) = delete; AbstractThrowingSessionTransaction &operator=(const AbstractThrowingSessionTransaction &) = delete; void commit(const source_location &sourceLocation = source_location::current()) { m_interface.sessionCommit(sourceLocation); m_isAlreadyCommited = true; m_locker.unlock(); } ~AbstractThrowingSessionTransaction() noexcept(false) { try { if (m_rollback) m_interface.sessionRollback(m_sourceLocation); } catch (...) { if (!std::uncaught_exceptions()) throw; } } protected: AbstractThrowingSessionTransaction(TransactionInterface &transactionInterface, const source_location &sourceLocation) : m_sourceLocation(sourceLocation) , m_interface(transactionInterface) {} protected: source_location m_sourceLocation; 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(m_sourceLocation); } catch (...) { if (!std::uncaught_exceptions()) throw; } } protected: AbstractThrowingTransaction(TransactionInterface &transactionInterface, const source_location &sourceLocation) : AbstractTransaction(transactionInterface) , m_sourceLocation(sourceLocation) { } private: source_location m_sourceLocation; }; 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(m_sourceLocation); } catch (...) { } } protected: AbstractNonThrowingDestructorTransaction(TransactionInterface &transactionInterface, const source_location &sourceLocation) : AbstractTransaction(transactionInterface) , m_sourceLocation(sourceLocation) { } private: source_location m_sourceLocation; }; template class BasicDeferredTransaction : public BaseTransaction { public: BasicDeferredTransaction(typename BaseTransaction::Transaction &transactionInterface, const source_location &sourceLocation = source_location::current()) : BaseTransaction(transactionInterface, sourceLocation) { transactionInterface.deferredBegin(sourceLocation); } ~BasicDeferredTransaction() { BaseTransaction::m_rollback = !BaseTransaction::m_isAlreadyCommited; } }; template class DeferredTransaction final : public BasicDeferredTransaction> { using Base = BasicDeferredTransaction>; public: using Base::Base; }; template auto withTransaction(TransactionInterface &transactionInterface, Callable &&callable, const source_location &sourceLocation) -> std::invoke_result_t { Transaction transaction{transactionInterface, sourceLocation}; if constexpr (std::is_void_v>) { callable(); transaction.commit(sourceLocation); } else { auto results = callable(); transaction.commit(sourceLocation); return results; } } template auto withImplicitTransaction(TransactionInterface &transactionInterface, Callable &&callable) { ImplicitTransaction transaction{transactionInterface}; if constexpr (std::is_void_v>) { callable(); } else { return callable(); } } template auto withDeferredTransaction(TransactionInterface &transactionInterface, Callable &&callable, const source_location &sourceLocation = source_location::current()) { if constexpr (std::is_void_v>) { withTransaction>(transactionInterface, std::forward(callable), sourceLocation); } else { return withTransaction>(transactionInterface, std::forward( callable), sourceLocation); } } template DeferredTransaction(TransactionInterface &, const source_location &) -> DeferredTransaction; template DeferredTransaction(TransactionInterface &) -> DeferredTransaction; template class DeferredNonThrowingDestructorTransaction final : public BasicDeferredTransaction> { using Base = BasicDeferredTransaction>; public: using Base::Base; }; template DeferredNonThrowingDestructorTransaction(TransactionInterface &, const source_location &) -> DeferredNonThrowingDestructorTransaction; template DeferredNonThrowingDestructorTransaction(TransactionInterface &) -> DeferredNonThrowingDestructorTransaction; template class BasicImmediateTransaction : public BaseTransaction { public: BasicImmediateTransaction(typename BaseTransaction::Transaction &transactionInterface, const source_location &sourceLocation = source_location::current()) : BaseTransaction(transactionInterface, sourceLocation) { transactionInterface.immediateBegin(sourceLocation); } ~BasicImmediateTransaction() { BaseTransaction::m_rollback = !BaseTransaction::m_isAlreadyCommited; } }; template class ImmediateTransaction final : public BasicImmediateTransaction> { using Base = BasicImmediateTransaction>; public: using Base::Base; }; template auto withImmediateTransaction(TransactionInterface &transactionInterface, Callable &&callable, const source_location &sourceLocation = source_location::current()) { if constexpr (std::is_void_v>) { withTransaction>(transactionInterface, std::forward(callable), sourceLocation); } else { return withTransaction>(transactionInterface, std::forward( callable), sourceLocation); } } template ImmediateTransaction(TransactionInterface &, const source_location &) -> ImmediateTransaction; template ImmediateTransaction(TransactionInterface &) -> ImmediateTransaction; template class ImmediateNonThrowingDestructorTransaction final : public BasicImmediateTransaction> { using Base = BasicImmediateTransaction>; public: using Base::Base; }; template ImmediateNonThrowingDestructorTransaction(TransactionInterface &, const source_location &) -> ImmediateNonThrowingDestructorTransaction; template ImmediateNonThrowingDestructorTransaction(TransactionInterface &) -> ImmediateNonThrowingDestructorTransaction; template class BasicExclusiveTransaction : public BaseTransaction { public: BasicExclusiveTransaction(typename BaseTransaction::Transaction &transactionInterface, const source_location &sourceLocation = source_location::current()) : BaseTransaction(transactionInterface, sourceLocation) { transactionInterface.exclusiveBegin(sourceLocation); } ~BasicExclusiveTransaction() { BaseTransaction::m_rollback = !BaseTransaction::m_isAlreadyCommited; } }; template class ExclusiveTransaction final : public BasicExclusiveTransaction> { using Base = BasicExclusiveTransaction>; public: using Base::Base; }; template ExclusiveTransaction(TransactionInterface &, const source_location &) -> ExclusiveTransaction; template ExclusiveTransaction(TransactionInterface &) -> ExclusiveTransaction; template class ExclusiveNonThrowingDestructorTransaction final : public BasicExclusiveTransaction> { using Base = BasicExclusiveTransaction>; public: using Base::Base; }; template ExclusiveNonThrowingDestructorTransaction(TransactionInterface &, const source_location &) -> ExclusiveNonThrowingDestructorTransaction; template ExclusiveNonThrowingDestructorTransaction(TransactionInterface &) -> ExclusiveNonThrowingDestructorTransaction; template class ImmediateSessionTransaction final : public AbstractThrowingSessionTransaction { using Base = AbstractThrowingSessionTransaction; public: ImmediateSessionTransaction(typename Base::Transaction &transactionInterface, const source_location &sourceLocation = source_location::current()) : AbstractThrowingSessionTransaction(transactionInterface, sourceLocation) { transactionInterface.immediateSessionBegin(sourceLocation); } ~ImmediateSessionTransaction() { Base::m_rollback = !Base::m_isAlreadyCommited; } }; template ImmediateSessionTransaction(TransactionInterface &, const source_location &) -> ImmediateSessionTransaction; template ImmediateSessionTransaction(TransactionInterface &) -> ImmediateSessionTransaction; } // namespace Sqlite