diff --git a/src/storage/index/CMakeLists.txt b/src/storage/index/CMakeLists.txt index 6c2a456ab..15c08da82 100644 --- a/src/storage/index/CMakeLists.txt +++ b/src/storage/index/CMakeLists.txt @@ -3,6 +3,7 @@ add_library( OBJECT b_plus_tree_index.cpp b_plus_tree.cpp + b_plus_tree_helper.cpp extendible_hash_table_index.cpp index_iterator.cpp linear_probe_hash_table_index.cpp) diff --git a/src/storage/index/b_plus_tree.cpp b/src/storage/index/b_plus_tree.cpp index a906af0b9..7c916ae2c 100644 --- a/src/storage/index/b_plus_tree.cpp +++ b/src/storage/index/b_plus_tree.cpp @@ -111,229 +111,6 @@ auto BPLUSTREE_TYPE::End() -> INDEXITERATOR_TYPE { return INDEXITERATOR_TYPE(); INDEX_TEMPLATE_ARGUMENTS auto BPLUSTREE_TYPE::GetRootPageId() -> page_id_t { return 0; } -/***************************************************************************** - * UTILITIES AND DEBUG - *****************************************************************************/ - -/* - * This method is used for test only - * Read data from file and insert one by one - */ -INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::InsertFromFile(const std::string &file_name, Transaction *txn) { - int64_t key; - std::ifstream input(file_name); - while (input) { - input >> key; - - KeyType index_key; - index_key.SetFromInteger(key); - RID rid(key); - Insert(index_key, rid, txn); - } -} -/* - * This method is used for test only - * Read data from file and remove one by one - */ -INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::RemoveFromFile(const std::string &file_name, Transaction *txn) { - int64_t key; - std::ifstream input(file_name); - while (input) { - input >> key; - KeyType index_key; - index_key.SetFromInteger(key); - Remove(index_key, txn); - } -} - -INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::Print(BufferPoolManager *bpm) { - auto root_page_id = GetRootPageId(); - auto guard = bpm->FetchPageBasic(root_page_id); - PrintTree(guard.PageId(), guard.template As()); -} - -INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::PrintTree(page_id_t page_id, const BPlusTreePage *page) { - if (page->IsLeafPage()) { - auto *leaf = reinterpret_cast(page); - std::cout << "Leaf Page: " << page_id << "\tNext: " << leaf->GetNextPageId() << std::endl; - - // Print the contents of the leaf page. - std::cout << "Contents: "; - for (int i = 0; i < leaf->GetSize(); i++) { - std::cout << leaf->KeyAt(i); - if ((i + 1) < leaf->GetSize()) { - std::cout << ", "; - } - } - std::cout << std::endl; - std::cout << std::endl; - - } else { - auto *internal = reinterpret_cast(page); - std::cout << "Internal Page: " << page_id << std::endl; - - // Print the contents of the internal page. - std::cout << "Contents: "; - for (int i = 0; i < internal->GetSize(); i++) { - std::cout << internal->KeyAt(i) << ": " << internal->ValueAt(i); - if ((i + 1) < internal->GetSize()) { - std::cout << ", "; - } - } - std::cout << std::endl; - std::cout << std::endl; - for (int i = 0; i < internal->GetSize(); i++) { - auto guard = bpm_->FetchPageBasic(internal->ValueAt(i)); - PrintTree(guard.PageId(), guard.template As()); - } - } -} - -/** - * This method is used for debug only, You don't need to modify - */ -INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::Draw(BufferPoolManager *bpm, const std::string &outf) { - if (IsEmpty()) { - LOG_WARN("Drawing an empty tree"); - return; - } - - std::ofstream out(outf); - out << "digraph G {" << std::endl; - auto root_page_id = GetRootPageId(); - auto guard = bpm->FetchPageBasic(root_page_id); - ToGraph(guard.PageId(), guard.template As(), out); - out << "}" << std::endl; - out.close(); -} - -/** - * This method is used for debug only, You don't need to modify - */ -INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::ToGraph(page_id_t page_id, const BPlusTreePage *page, std::ofstream &out) { - std::string leaf_prefix("LEAF_"); - std::string internal_prefix("INT_"); - if (page->IsLeafPage()) { - auto *leaf = reinterpret_cast(page); - // Print node name - out << leaf_prefix << page_id; - // Print node properties - out << "[shape=plain color=green "; - // Print data of the node - out << "label=<\n"; - // Print data - out << "\n"; - out << "\n"; - out << ""; - for (int i = 0; i < leaf->GetSize(); i++) { - out << "\n"; - } - out << ""; - // Print table end - out << "
GetSize() << "\">P=" << page_id << "
GetSize() << "\">" - << "max_size=" << leaf->GetMaxSize() << ",min_size=" << leaf->GetMinSize() << ",size=" << leaf->GetSize() - << "
" << leaf->KeyAt(i) << "
>];\n"; - // Print Leaf node link if there is a next page - if (leaf->GetNextPageId() != INVALID_PAGE_ID) { - out << leaf_prefix << page_id << " -> " << leaf_prefix << leaf->GetNextPageId() << ";\n"; - out << "{rank=same " << leaf_prefix << page_id << " " << leaf_prefix << leaf->GetNextPageId() << "};\n"; - } - } else { - auto *inner = reinterpret_cast(page); - // Print node name - out << internal_prefix << page_id; - // Print node properties - out << "[shape=plain color=pink "; // why not? - // Print data of the node - out << "label=<\n"; - // Print data - out << "\n"; - out << "\n"; - out << ""; - for (int i = 0; i < inner->GetSize(); i++) { - out << "\n"; - } - out << ""; - // Print table end - out << "
GetSize() << "\">P=" << page_id << "
GetSize() << "\">" - << "max_size=" << inner->GetMaxSize() << ",min_size=" << inner->GetMinSize() << ",size=" << inner->GetSize() - << "
ValueAt(i) << "\">"; - if (i > 0) { - out << inner->KeyAt(i); - } else { - out << " "; - } - out << "
>];\n"; - // Print leaves - for (int i = 0; i < inner->GetSize(); i++) { - auto child_guard = bpm_->FetchPageBasic(inner->ValueAt(i)); - auto child_page = child_guard.template As(); - ToGraph(child_guard.PageId(), child_page, out); - if (i > 0) { - auto sibling_guard = bpm_->FetchPageBasic(inner->ValueAt(i - 1)); - auto sibling_page = sibling_guard.template As(); - if (!sibling_page->IsLeafPage() && !child_page->IsLeafPage()) { - out << "{rank=same " << internal_prefix << sibling_guard.PageId() << " " << internal_prefix - << child_guard.PageId() << "};\n"; - } - } - out << internal_prefix << page_id << ":p" << child_guard.PageId() << " -> "; - if (child_page->IsLeafPage()) { - out << leaf_prefix << child_guard.PageId() << ";\n"; - } else { - out << internal_prefix << child_guard.PageId() << ";\n"; - } - } - } -} - -INDEX_TEMPLATE_ARGUMENTS -auto BPLUSTREE_TYPE::DrawBPlusTree() -> std::string { - if (IsEmpty()) { - return "()"; - } - - PrintableBPlusTree p_root = ToPrintableBPlusTree(GetRootPageId()); - std::ostringstream out_buf; - p_root.Print(out_buf); - - return out_buf.str(); -} - -INDEX_TEMPLATE_ARGUMENTS -auto BPLUSTREE_TYPE::ToPrintableBPlusTree(page_id_t root_id) -> PrintableBPlusTree { - auto root_page_guard = bpm_->FetchPageBasic(root_id); - auto root_page = root_page_guard.template As(); - PrintableBPlusTree proot; - - if (root_page->IsLeafPage()) { - auto leaf_page = root_page_guard.template As(); - proot.keys_ = leaf_page->ToString(); - proot.size_ = proot.keys_.size() + 4; // 4 more spaces for indent - - return proot; - } - - // draw internal page - auto internal_page = root_page_guard.template As(); - proot.keys_ = internal_page->ToString(); - proot.size_ = 0; - for (int i = 0; i < internal_page->GetSize(); i++) { - page_id_t child_id = internal_page->ValueAt(i); - PrintableBPlusTree child_node = ToPrintableBPlusTree(child_id); - proot.size_ += child_node.size_; - proot.children_.push_back(child_node); - } - - return proot; -} - template class BPlusTree, RID, GenericComparator<4>>; template class BPlusTree, RID, GenericComparator<8>>; diff --git a/src/storage/index/b_plus_tree_helper.cpp b/src/storage/index/b_plus_tree_helper.cpp new file mode 100644 index 000000000..f813c76a5 --- /dev/null +++ b/src/storage/index/b_plus_tree_helper.cpp @@ -0,0 +1,244 @@ +#include +#include + +#include "common/exception.h" +#include "common/logger.h" +#include "common/rid.h" +#include "storage/index/b_plus_tree.h" + +namespace bustub { + +/***************************************************************************** + * UTILITIES AND DEBUG + *****************************************************************************/ + +/* + * This method is used for test only + * Read data from file and insert one by one + */ +INDEX_TEMPLATE_ARGUMENTS +void BPLUSTREE_TYPE::InsertFromFile(const std::string &file_name, Transaction *txn) { + int64_t key; + std::ifstream input(file_name); + while (input) { + input >> key; + + KeyType index_key; + index_key.SetFromInteger(key); + RID rid(key); + Insert(index_key, rid, txn); + } +} +/* + * This method is used for test only + * Read data from file and remove one by one + */ +INDEX_TEMPLATE_ARGUMENTS +void BPLUSTREE_TYPE::RemoveFromFile(const std::string &file_name, Transaction *txn) { + int64_t key; + std::ifstream input(file_name); + while (input) { + input >> key; + KeyType index_key; + index_key.SetFromInteger(key); + Remove(index_key, txn); + } +} + +INDEX_TEMPLATE_ARGUMENTS +void BPLUSTREE_TYPE::Print(BufferPoolManager *bpm) { + auto root_page_id = GetRootPageId(); + auto guard = bpm->FetchPageBasic(root_page_id); + PrintTree(guard.PageId(), guard.template As()); +} + +INDEX_TEMPLATE_ARGUMENTS +void BPLUSTREE_TYPE::PrintTree(page_id_t page_id, const BPlusTreePage *page) { + if (page->IsLeafPage()) { + auto *leaf = reinterpret_cast(page); + std::cout << "Leaf Page: " << page_id << "\tNext: " << leaf->GetNextPageId() << std::endl; + + // Print the contents of the leaf page. + std::cout << "Contents: "; + for (int i = 0; i < leaf->GetSize(); i++) { + std::cout << leaf->KeyAt(i); + if ((i + 1) < leaf->GetSize()) { + std::cout << ", "; + } + } + std::cout << std::endl; + std::cout << std::endl; + + } else { + auto *internal = reinterpret_cast(page); + std::cout << "Internal Page: " << page_id << std::endl; + + // Print the contents of the internal page. + std::cout << "Contents: "; + for (int i = 0; i < internal->GetSize(); i++) { + std::cout << internal->KeyAt(i) << ": " << internal->ValueAt(i); + if ((i + 1) < internal->GetSize()) { + std::cout << ", "; + } + } + std::cout << std::endl; + std::cout << std::endl; + for (int i = 0; i < internal->GetSize(); i++) { + auto guard = bpm_->FetchPageBasic(internal->ValueAt(i)); + PrintTree(guard.PageId(), guard.template As()); + } + } +} + +/** + * This method is used for debug only, You don't need to modify + */ +INDEX_TEMPLATE_ARGUMENTS +void BPLUSTREE_TYPE::Draw(BufferPoolManager *bpm, const std::string &outf) { + if (IsEmpty()) { + LOG_WARN("Drawing an empty tree"); + return; + } + + std::ofstream out(outf); + out << "digraph G {" << std::endl; + auto root_page_id = GetRootPageId(); + auto guard = bpm->FetchPageBasic(root_page_id); + ToGraph(guard.PageId(), guard.template As(), out); + out << "}" << std::endl; + out.close(); +} + +/** + * This method is used for debug only, You don't need to modify + */ +INDEX_TEMPLATE_ARGUMENTS +void BPLUSTREE_TYPE::ToGraph(page_id_t page_id, const BPlusTreePage *page, std::ofstream &out) { + std::string leaf_prefix("LEAF_"); + std::string internal_prefix("INT_"); + if (page->IsLeafPage()) { + auto *leaf = reinterpret_cast(page); + // Print node name + out << leaf_prefix << page_id; + // Print node properties + out << "[shape=plain color=green "; + // Print data of the node + out << "label=<\n"; + // Print data + out << "\n"; + out << "\n"; + out << ""; + for (int i = 0; i < leaf->GetSize(); i++) { + out << "\n"; + } + out << ""; + // Print table end + out << "
GetSize() << "\">P=" << page_id << "
GetSize() << "\">" + << "max_size=" << leaf->GetMaxSize() << ",min_size=" << leaf->GetMinSize() << ",size=" << leaf->GetSize() + << "
" << leaf->KeyAt(i) << "
>];\n"; + // Print Leaf node link if there is a next page + if (leaf->GetNextPageId() != INVALID_PAGE_ID) { + out << leaf_prefix << page_id << " -> " << leaf_prefix << leaf->GetNextPageId() << ";\n"; + out << "{rank=same " << leaf_prefix << page_id << " " << leaf_prefix << leaf->GetNextPageId() << "};\n"; + } + } else { + auto *inner = reinterpret_cast(page); + // Print node name + out << internal_prefix << page_id; + // Print node properties + out << "[shape=plain color=pink "; // why not? + // Print data of the node + out << "label=<\n"; + // Print data + out << "\n"; + out << "\n"; + out << ""; + for (int i = 0; i < inner->GetSize(); i++) { + out << "\n"; + } + out << ""; + // Print table end + out << "
GetSize() << "\">P=" << page_id << "
GetSize() << "\">" + << "max_size=" << inner->GetMaxSize() << ",min_size=" << inner->GetMinSize() << ",size=" << inner->GetSize() + << "
ValueAt(i) << "\">"; + if (i > 0) { + out << inner->KeyAt(i); + } else { + out << " "; + } + out << "
>];\n"; + // Print leaves + for (int i = 0; i < inner->GetSize(); i++) { + auto child_guard = bpm_->FetchPageBasic(inner->ValueAt(i)); + auto child_page = child_guard.template As(); + ToGraph(child_guard.PageId(), child_page, out); + if (i > 0) { + auto sibling_guard = bpm_->FetchPageBasic(inner->ValueAt(i - 1)); + auto sibling_page = sibling_guard.template As(); + if (!sibling_page->IsLeafPage() && !child_page->IsLeafPage()) { + out << "{rank=same " << internal_prefix << sibling_guard.PageId() << " " << internal_prefix + << child_guard.PageId() << "};\n"; + } + } + out << internal_prefix << page_id << ":p" << child_guard.PageId() << " -> "; + if (child_page->IsLeafPage()) { + out << leaf_prefix << child_guard.PageId() << ";\n"; + } else { + out << internal_prefix << child_guard.PageId() << ";\n"; + } + } + } +} + +INDEX_TEMPLATE_ARGUMENTS +auto BPLUSTREE_TYPE::DrawBPlusTree() -> std::string { + if (IsEmpty()) { + return "()"; + } + + PrintableBPlusTree p_root = ToPrintableBPlusTree(GetRootPageId()); + std::ostringstream out_buf; + p_root.Print(out_buf); + + return out_buf.str(); +} + +INDEX_TEMPLATE_ARGUMENTS +auto BPLUSTREE_TYPE::ToPrintableBPlusTree(page_id_t root_id) -> PrintableBPlusTree { + auto root_page_guard = bpm_->FetchPageBasic(root_id); + auto root_page = root_page_guard.template As(); + PrintableBPlusTree proot; + + if (root_page->IsLeafPage()) { + auto leaf_page = root_page_guard.template As(); + proot.keys_ = leaf_page->ToString(); + proot.size_ = proot.keys_.size() + 4; // 4 more spaces for indent + + return proot; + } + + // draw internal page + auto internal_page = root_page_guard.template As(); + proot.keys_ = internal_page->ToString(); + proot.size_ = 0; + for (int i = 0; i < internal_page->GetSize(); i++) { + page_id_t child_id = internal_page->ValueAt(i); + PrintableBPlusTree child_node = ToPrintableBPlusTree(child_id); + proot.size_ += child_node.size_; + proot.children_.push_back(child_node); + } + + return proot; +} + +template class BPlusTree, RID, GenericComparator<4>>; + +template class BPlusTree, RID, GenericComparator<8>>; + +template class BPlusTree, RID, GenericComparator<16>>; + +template class BPlusTree, RID, GenericComparator<32>>; + +template class BPlusTree, RID, GenericComparator<64>>; + +} // namespace bustub