/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef DOM_QUOTA_CACHINGDATABASECONNECTION_H_ #define DOM_QUOTA_CACHINGDATABASECONNECTION_H_ #include "mozilla/dom/quota/Config.h" #include "mozStorageHelper.h" #include "nsCOMPtr.h" #include "nscore.h" #include "nsHashKeys.h" #include "nsInterfaceHashtable.h" #include "nsString.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/InitializedOnce.h" #include "mozilla/NotNull.h" #include "mozilla/dom/quota/QuotaCommon.h" #include "mozilla/dom/quota/ResultExtensions.h" #include "mozilla/dom/quota/ScopedLogExtraInfo.h" namespace mozilla::dom::quota { class CachingDatabaseConnection { public: class CachedStatement; // A stack-only RAII wrapper that resets its borrowed statement when the // wrapper goes out of scope. Note it's intentionally not declared MOZ_RAII, // because it actually is used as a temporary in simple cases like // `stmt.Borrow()->Execute()`. It also automatically exposes the current query // to ScopedLogExtraInfo as "query" in builds where this mechanism is active. class MOZ_STACK_CLASS BorrowedStatement : mozStorageStatementScoper { public: mozIStorageStatement& operator*() const; MOZ_NONNULL_RETURN mozIStorageStatement* operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN; BorrowedStatement(BorrowedStatement&& aOther) = default; // No funny business allowed. BorrowedStatement& operator=(BorrowedStatement&&) = delete; BorrowedStatement(const BorrowedStatement&) = delete; BorrowedStatement& operator=(const BorrowedStatement&) = delete; private: friend class CachedStatement; #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED BorrowedStatement(NotNull aStatement, const nsACString& aQuery) : mozStorageStatementScoper(aStatement), mExtraInfo{ScopedLogExtraInfo::kTagQuery, aQuery} {} ScopedLogExtraInfo mExtraInfo; #else MOZ_IMPLICIT BorrowedStatement(NotNull aStatement) : mozStorageStatementScoper(aStatement) {} #endif }; class LazyStatement; void AssertIsOnConnectionThread() const { #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED mOwningThread->AssertOwnership("CachingDatabaseConnection not thread-safe"); #endif } bool HasStorageConnection() const { return static_cast(mStorageConnection); } mozIStorageConnection& MutableStorageConnection() const { AssertIsOnConnectionThread(); MOZ_ASSERT(mStorageConnection); return **mStorageConnection; } Result GetCachedStatement( const nsACString& aQuery); Result BorrowCachedStatement( const nsACString& aQuery); template nsresult ExecuteCachedStatement(const nsACString& aQuery, BindFunctor&& aBindFunctor) { QM_TRY_INSPECT(const auto& stmt, BorrowCachedStatement(aQuery)); QM_TRY(std::forward(aBindFunctor)(*stmt)); QM_TRY(MOZ_TO_RESULT(stmt->Execute())); return NS_OK; } nsresult ExecuteCachedStatement(const nsACString& aQuery); template Result, nsresult> BorrowAndExecuteSingleStepStatement(const nsACString& aQuery, BindFunctor&& aBindFunctor); #ifdef DEBUG ~CachingDatabaseConnection() { MOZ_ASSERT(!mStorageConnection); MOZ_ASSERT(!mCachedStatements.Count()); } #endif protected: explicit CachingDatabaseConnection( MovingNotNull> aStorageConnection); CachingDatabaseConnection() = default; void LazyInit( MovingNotNull> aStorageConnection); void Close(); private: #ifdef MOZ_THREAD_SAFETY_OWNERSHIP_CHECKS_SUPPORTED LazyInitializedOnce mOwningThread; #endif LazyInitializedOnceEarlyDestructible< const NotNull>> mStorageConnection; nsInterfaceHashtable mCachedStatements; }; class CachingDatabaseConnection::CachedStatement final { friend class CachingDatabaseConnection; nsCOMPtr mStatement; #ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED nsCString mQuery; #endif #ifdef DEBUG CachingDatabaseConnection* mDEBUGConnection; #endif public: #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING) CachedStatement(); ~CachedStatement(); #else CachedStatement() = default; #endif void AssertIsOnConnectionThread() const; explicit operator bool() const; BorrowedStatement Borrow() const; private: // Only called by CachingDatabaseConnection. CachedStatement(CachingDatabaseConnection* aConnection, nsCOMPtr aStatement, const nsACString& aQuery); public: #if defined(NS_BUILD_REFCNT_LOGGING) CachedStatement(CachedStatement&& aOther) : mStatement(std::move(aOther.mStatement)) # ifdef QM_SCOPED_LOG_EXTRA_INFO_ENABLED , mQuery(std::move(aOther.mQuery)) # endif # ifdef DEBUG , mDEBUGConnection(aOther.mDEBUGConnection) # endif { MOZ_COUNT_CTOR(CachingDatabaseConnection::CachedStatement); } #else CachedStatement(CachedStatement&&) = default; #endif CachedStatement& operator=(CachedStatement&&) = default; // No funny business allowed. CachedStatement(const CachedStatement&) = delete; CachedStatement& operator=(const CachedStatement&) = delete; }; class CachingDatabaseConnection::LazyStatement final { public: LazyStatement(CachingDatabaseConnection& aConnection, const nsACString& aQueryString) : mConnection{aConnection}, mQueryString{aQueryString} {} Result Borrow(); template Result, nsresult> BorrowAndExecuteSingleStep(BindFunctor&& aBindFunctor) { QM_TRY_UNWRAP(auto borrowedStatement, Borrow()); QM_TRY(std::forward(aBindFunctor)(*borrowedStatement)); QM_TRY_INSPECT( const bool& hasResult, MOZ_TO_RESULT_INVOKE_MEMBER(&*borrowedStatement, ExecuteStep)); return hasResult ? Some(std::move(borrowedStatement)) : Nothing{}; } private: Result Initialize(); CachingDatabaseConnection& mConnection; const nsCString mQueryString; CachingDatabaseConnection::CachedStatement mCachedStatement; }; template Result, nsresult> CachingDatabaseConnection::BorrowAndExecuteSingleStepStatement( const nsACString& aQuery, BindFunctor&& aBindFunctor) { return LazyStatement{*this, aQuery}.BorrowAndExecuteSingleStep( std::forward(aBindFunctor)); } } // namespace mozilla::dom::quota #endif // DOM_QUOTA_CACHINGDATABASECONNECTION_H_