Skip to content

Commit

Permalink
Merge branch 'master' into feat-dm
Browse files Browse the repository at this point in the history
  • Loading branch information
xzhseh authored Jul 1, 2023
2 parents 6db727b + 9553f1c commit 75dca21
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 0 deletions.
11 changes: 11 additions & 0 deletions src/include/storage/index/b_plus_tree.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ class BPlusTree {
// read data from file and remove one by one
void RemoveFromFile(const std::string &file_name, Transaction *txn = nullptr);

/**
* @brief Read batch operations from input file, below is a sample file format
* insert some keys and delete 8, 9 from the tree with one step.
* { i1 i2 i3 i4 i5 i6 i7 i8 i9 i10 i30 d8 d9 } // batch.txt
* B+ Tree(4 max leaf, 4 max internal) after processing:
* (5)
* (3) (7)
* (1,2) (3,4) (5,6) (7,10,30) // The output tree example
*/
void BatchOpsFromFile(const std::string &file_name, Transaction *txn = nullptr);

private:
/* Debug Routines for FREE!! */
void ToGraph(page_id_t page_id, const BPlusTreePage *page, std::ofstream &out);
Expand Down
27 changes: 27 additions & 0 deletions src/storage/index/b_plus_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,33 @@ void BPLUSTREE_TYPE::RemoveFromFile(const std::string &file_name, Transaction *t
}
}

/*
* This method is used for test only
* Read data from file and insert/remove one by one
*/
INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::BatchOpsFromFile(const std::string &file_name, Transaction *txn) {
int64_t key;
char instruction;
std::ifstream input(file_name);
while (input) {
input >> instruction >> key;
RID rid(key);
KeyType index_key;
index_key.SetFromInteger(key);
switch (instruction) {
case 'i':
Insert(index_key, rid, txn);
break;
case 'd':
Remove(index_key, txn);
break;
default:
break;
}
}
}

INDEX_TEMPLATE_ARGUMENTS
void BPLUSTREE_TYPE::Print(BufferPoolManager *bpm) {
auto root_page_id = GetRootPageId();
Expand Down
95 changes: 95 additions & 0 deletions test/concurrency/common_checker.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<int> &v1_vec, const std::vector<int> &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, ",");
Expand All @@ -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<int> &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');
Expand Down
5 changes: 5 additions & 0 deletions tools/b_plus_tree_printer/b_plus_tree_printer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ auto UsageMessage() -> std::string {
"\ti <k> -- Insert <k> (int64_t) as both key and value).\n"
"\tf <filename> -- insert multiple keys from reading file.\n"
"\tc <filename> -- delete multiple keys from reading file.\n"
"\tx <filename> -- insert or delete multiple keys from reading file.\n"
"\td <k> -- Delete key <k> and its associated value.\n"
"\tg <filename>.dot -- Output the tree in graph format to a dot file\n"
"\tp -- Print the B+ tree.\n"
Expand Down Expand Up @@ -92,6 +93,10 @@ auto main(int argc, char **argv) -> int {
std::cin >> filename;
tree.RemoveFromFile(filename, transaction);
break;
case 'x':
std::cin >> filename;
tree.BatchOpsFromFile(filename, transaction);
break;
case 'd':
std::cin >> key;
index_key.SetFromInteger(key);
Expand Down

0 comments on commit 75dca21

Please sign in to comment.