diff --git a/test/concurrency/common_checker.h b/test/concurrency/common_checker.h index 9c258c58d..0398dcbcc 100644 --- a/test/concurrency/common_checker.h +++ b/test/concurrency/common_checker.h @@ -30,6 +30,71 @@ auto Insert(Transaction *txn, BustubInstance &instance, int v1) -> void { ASSERT_EQ(ss.str(), "3,\n"); } +/// @brief Use this to write your own test cases, if needed +/// @param v1_vec Elements to be insert into `t1:v1`, the size of which can either be 1, or same as @v2_vec +/// @param v2_vec Elements to be insert into `t1:v2`, must not be empty +/// @param flag When set to `true`, the size of @v1_vec should be exactly one, the default value is `false` +/// Usage: Insert(txn, instance, {1, 2, 3}, {4, 5, 6}, flag = false); +/// This overloaded function will then bound each pair from @v1_vec & @v2_vec as a insert value +/// If flag is set to `true`, v1_vec should contain exactly one element, +/// The insert pair will be {v1_vec[0], v2_vec[0]}, {v1_vec[0], v2_vec[1]} ... +auto Insert(Transaction *txn, BustubInstance &instance, const std::vector &v1_vec, const std::vector &v2_vec, + bool flag = false) -> void { + // Check if the input vectors have the same size. + if (!flag) { + if (v1_vec.size() != v2_vec.size()) { + fmt::print(stderr, "Vectors @v1_vec and @v2_vec must have the same size\n"); + return; + } + if (v1_vec.empty()) { + fmt::print(stderr, "Input vectors must not be empty\n"); + return; + } + } else { + if (v1_vec.size() != 1) { + fmt::print(stderr, "Size of @v1_vec should be exactly 1 if flag is set to `true`\n"); + return; + } + if (v2_vec.empty()) { + fmt::print(stderr, "Input vectors must not be empty\n"); + return; + } + } + + std::stringstream ss; + auto writer = bustub::SimpleStreamWriter(ss, true, ","); + + assert(v1_vec.size() == 1); + std::string val_str{}; + if (!flag) { + for (size_t i = 0; i < v1_vec.size(); ++i) { + val_str += fmt::format("({}, {}), ", v1_vec[i], v2_vec[i]); + } + } else { + for (const auto &v2 : v2_vec) { + val_str += fmt::format("({}, {}), ", v1_vec[0], v2); + } + } + + // Remove the last comma and space + assert(!val_str.empty() && val_str.size() >= 2); + val_str.pop_back(); + val_str.pop_back(); + + fmt::print(stderr, "insert data with @v1_vec = {} and @v2_vec = {} in txn {} {}\n", v1_vec, v2_vec, + txn->GetTransactionId(), txn->GetIsolationLevel()); + + std::string sql = "INSERT INTO t1 VALUES " + val_str; + + bool res = instance.ExecuteSqlTxn(sql, writer, txn); + if (!res) { + fmt::print(stderr, "Failed to insert data with @v1_vec = {} and @v2_vec = {} in txn {} {}\n", v1_vec, v2_vec); + } + + // The final number of inserted elements should be the size of @v2_vec + ASSERT_EQ(ss.str(), fmt::format("{},\n", v2_vec.size())); +} + auto Delete(Transaction *txn, BustubInstance &instance, int v1) -> void { std::stringstream ss; auto writer = bustub::SimpleStreamWriter(ss, true, ","); @@ -39,6 +104,36 @@ auto Delete(Transaction *txn, BustubInstance &instance, int v1) -> void { ASSERT_EQ(ss.str(), "3,\n"); } +/// @brief Use this to write your own test cases, if needed +/// @param d_vec Elements to be deleted from t1, represents the value of `t1:v1` +/// @param d_size The size you expect to be deleted from t1, will be used as sanity check, default value is 3 +/// This overloaded function performs a vectorized self-constructed deletion to better test the implementation +/// You should calculate the number of elements that logically should be deleted, and pass it in as @d_size. +auto Delete(Transaction *txn, BustubInstance &instance, const std::vector &d_vec, int d_size = 3) -> void { + if (d_vec.empty()) { + fmt::print(stderr, "Input vec must not be empty\n"); + return; + } + + assert(!d_vec.empty()); + + std::stringstream ss; + auto writer = bustub::SimpleStreamWriter(ss, true, ","); + + for (const auto &v1 : d_vec) { + fmt::print(stderr, "delete data with v1 = {} in txn {} {}\n", v1, txn->GetTransactionId(), + txn->GetIsolationLevel()); + std::string sql = fmt::format("DELETE FROM t1 WHERE v1 = {}", v1); + bool res = instance.ExecuteSqlTxn(sql, writer, txn); + if (!res) { + fmt::print(stderr, "Failed to delete data with v1 = {} in txn {} {}\n", v1, txn->GetTransactionId(), + txn->GetIsolationLevel()); + } + } + + ASSERT_EQ(ss.str(), fmt::format("{},\n", d_size)); +} + auto ExpectResult(const std::string &actual_result, const std::string &expected_result) -> bool { auto actual_result_rows = bustub::StringUtil::Split(actual_result, '\n'); auto expected_result_rows = bustub::StringUtil::Split(expected_result, '\n');