// Copyright 2012 The Chromium Authors // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef SQL_TRANSACTION_H_ #define SQL_TRANSACTION_H_ #include "base/check.h" #include "base/component_export.h" #include "base/sequence_checker.h" #include "base/thread_annotations.h" namespace sql { class Database; // Automatically rolls back uncommitted transactions when going out of scope. // // This class is not thread-safe. Each instance must be used from a single // sequence. class COMPONENT_EXPORT(SQL) Transaction { public: // Creates an inactive instance. // // `database` must be non-null and must outlive the newly created instance. // // The instance must be activated by calling Begin(). // // sql::Database implements "virtual" nested transactions, as documented in // sql::Database::BeginTransaction(). This is a mis-feature, and should not be // used in new code. The sql::Database implementation does not match the // approach recommended at https://www.sqlite.org/lang_transaction.html. explicit Transaction(Database* database); Transaction(const Transaction&) = delete; Transaction& operator=(const Transaction&) = delete; Transaction(Transaction&&) = delete; Transaction& operator=(Transaction&&) = delete; ~Transaction(); // Activates an inactive transaction. Must be called after construction. // // Returns false in case of failure. If this method fails, the database // connection will still execute SQL statements, but they will not be enclosed // in a transaction scope. In most cases, Begin() callers should handle // failures by abandoning the high-level operation that was meant to be // carried out in the transaction. // // In most cases (no nested transactions), this method issues a BEGIN // statement, which invokes SQLite's deferred transaction startup documented // in https://www.sqlite.org/lang_transaction.html. This means the database // lock is not acquired by the time Begin() completes. Instead, the first // statement after Begin() will attempt to acquire a read or write lock. // // This method is not idempotent. Calling Begin() twice on a Transaction will // cause a DCHECK crash. [[nodiscard]] bool Begin(); // Explicitly rolls back the transaction. All changes will be forgotten. // // Most features can avoid calling this method, because Transactions that do // not get Commit()ed are automatically rolled back when they go out of scope. // // This method is not idempotent. Calling Rollback() twice on a Transaction // will cause a DCHECK crash. // // Must be called after a successful call to Begin(). Must not be called after // Commit(). void Rollback(); // Commits the transaction. All changes will be persisted in the database. // // Returns false in case of failure. The most common failure case is a SQLite // failure in committing the transaction. If sql::Database's support for // nested transactions is in use, this method will also fail if any nested // transaction has been rolled back. // // This method is not idempotent. Calling Commit() twice on a Transaction will // cause a DCHECK crash. // // Must be called after a successful call to Begin(). Must not be called after // Rollback(). bool Commit(); // True if Begin() succeeded, and neither Commit() nor Rollback() were called. bool IsActiveForTesting() const { DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); return is_active_; } private: SEQUENCE_CHECKER(sequence_checker_); Database& database_ GUARDED_BY_CONTEXT(sequence_checker_); #if DCHECK_IS_ON() bool begin_called_ GUARDED_BY_CONTEXT(sequence_checker_) = false; bool commit_called_ GUARDED_BY_CONTEXT(sequence_checker_) = false; bool rollback_called_ GUARDED_BY_CONTEXT(sequence_checker_) = false; #endif // DCHECK_IS_ON() // True between a successful Begin() and a Commit() / Rollback() call. bool is_active_ GUARDED_BY_CONTEXT(sequence_checker_) = false; }; } // namespace sql #endif // SQL_TRANSACTION_H_