#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)"));
}
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";
}
TEST_F(SQLTransactionTest, NestedRollback) {
EXPECT_FALSE(db_.HasActiveTransactions());
EXPECT_EQ(0, db_.transaction_nesting());
{
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());
{
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";
{
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";
{
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());
}
}
}