blob: 8e20d7fe737ac82f542df540c6153843d9ad620f [file] [log] [blame]
// 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/memory/weak_ptr.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_);
base::WeakPtr<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_