| // Copyright 2011 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "sql/transaction.h" |
| |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "sql/database.h" |
| #include "sql/statement.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/sqlite/sqlite3.h" |
| |
| namespace sql { |
| |
| namespace { |
| |
| class SQLTransactionTest : public testing::Test { |
| public: |
| ~SQLTransactionTest() override = default; |
| |
| void SetUp() override { |
| ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
| ASSERT_TRUE( |
| db_.Open(temp_dir_.GetPath().AppendASCII("transaction_test.sqlite"))); |
| |
| ASSERT_TRUE(db_.Execute("CREATE TABLE foo (a, b)")); |
| } |
| |
| // Returns the number of rows in table "foo". |
| int CountFoo() { |
| Statement count(db_.GetUniqueStatement("SELECT count(*) FROM foo")); |
| count.Step(); |
| return count.ColumnInt(0); |
| } |
| |
| protected: |
| base::ScopedTempDir temp_dir_; |
| Database db_; |
| }; |
| |
| TEST_F(SQLTransactionTest, Commit) { |
| { |
| Transaction transaction(&db_); |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_FALSE(transaction.IsActiveForTesting()); |
| |
| ASSERT_TRUE(transaction.Begin()); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_TRUE(transaction.IsActiveForTesting()); |
| |
| ASSERT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); |
| ASSERT_EQ(1, CountFoo()) << "INSERT did not work as intended"; |
| |
| transaction.Commit(); |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_FALSE(transaction.IsActiveForTesting()); |
| } |
| |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_EQ(1, CountFoo()) << "Transaction changes not committed"; |
| } |
| |
| TEST_F(SQLTransactionTest, RollbackOnDestruction) { |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| |
| { |
| Transaction transaction(&db_); |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_FALSE(transaction.IsActiveForTesting()); |
| |
| ASSERT_TRUE(transaction.Begin()); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_TRUE(transaction.IsActiveForTesting()); |
| |
| ASSERT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); |
| ASSERT_EQ(1, CountFoo()) << "INSERT did not work as intended"; |
| } |
| |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_EQ(0, CountFoo()) << "Transaction changes not rolled back"; |
| } |
| |
| TEST_F(SQLTransactionTest, ExplicitRollback) { |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| |
| { |
| Transaction transaction(&db_); |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_FALSE(transaction.IsActiveForTesting()); |
| |
| ASSERT_TRUE(transaction.Begin()); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_TRUE(transaction.IsActiveForTesting()); |
| |
| ASSERT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); |
| ASSERT_EQ(1, CountFoo()) << "INSERT did not work as intended"; |
| |
| transaction.Rollback(); |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_FALSE(transaction.IsActiveForTesting()); |
| EXPECT_EQ(0, CountFoo()) << "Transaction changes not rolled back"; |
| } |
| |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_EQ(0, CountFoo()) << "Transaction changes not rolled back"; |
| } |
| |
| // Rolling back any part of a transaction should roll back all of them. |
| TEST_F(SQLTransactionTest, NestedRollback) { |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_EQ(0, db_.transaction_nesting()); |
| |
| // Outermost transaction. |
| { |
| Transaction outer_txn(&db_); |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_EQ(0, db_.transaction_nesting()); |
| |
| ASSERT_TRUE(outer_txn.Begin()); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(1, db_.transaction_nesting()); |
| |
| // First inner transaction is committed. |
| { |
| Transaction committed_inner_txn(&db_); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(1, db_.transaction_nesting()); |
| |
| ASSERT_TRUE(committed_inner_txn.Begin()); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(2, db_.transaction_nesting()); |
| |
| ASSERT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (1, 2)")); |
| ASSERT_EQ(1, CountFoo()) << "INSERT did not work as intended"; |
| |
| committed_inner_txn.Commit(); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(1, db_.transaction_nesting()); |
| } |
| |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(1, db_.transaction_nesting()); |
| EXPECT_EQ(1, CountFoo()) << "First inner transaction did not commit"; |
| |
| // Second inner transaction is rolled back. |
| { |
| Transaction rolled_back_inner_txn(&db_); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(1, db_.transaction_nesting()); |
| |
| ASSERT_TRUE(rolled_back_inner_txn.Begin()); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(2, db_.transaction_nesting()); |
| |
| ASSERT_TRUE(db_.Execute("INSERT INTO foo (a, b) VALUES (2, 3)")); |
| ASSERT_EQ(2, CountFoo()) << "INSERT did not work as intended"; |
| |
| rolled_back_inner_txn.Rollback(); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(1, db_.transaction_nesting()); |
| EXPECT_EQ(2, CountFoo()) |
| << "Nested transaction rollback deferred to top-level transaction"; |
| } |
| |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(1, db_.transaction_nesting()); |
| EXPECT_EQ(2, CountFoo()) |
| << "Nested transaction rollback deferred to top-level transaction"; |
| |
| // Third inner transaction fails in Begin(), because a nested transaction |
| // has already been rolled back. |
| { |
| Transaction failed_inner_txn(&db_); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(1, db_.transaction_nesting()); |
| |
| EXPECT_FALSE(failed_inner_txn.Begin()); |
| EXPECT_TRUE(db_.HasActiveTransactions()); |
| EXPECT_EQ(1, db_.transaction_nesting()); |
| } |
| } |
| |
| EXPECT_FALSE(db_.HasActiveTransactions()); |
| EXPECT_EQ(0, db_.transaction_nesting()); |
| EXPECT_EQ(0, CountFoo()); |
| } |
| |
| } // namespace |
| |
| } // namespace sql |