From e1e78b0719ad8c0f5017054ec6797208dc4a59d4 Mon Sep 17 00:00:00 2001 From: Alex Chi Date: Sat, 25 Mar 2023 17:55:09 -0400 Subject: [PATCH 1/3] [DNM] B+ Tree Wrapper for Spring 2023 Signed-off-by: Alex Chi --- src/include/storage/index/b_plus_tree.h | 15 +- src/include/storage/index/index_iterator.h | 34 ++- src/storage/index/b_plus_tree.cpp | 257 +++------------------ src/storage/index/index_iterator.cpp | 23 +- 4 files changed, 71 insertions(+), 258 deletions(-) diff --git a/src/include/storage/index/b_plus_tree.h b/src/include/storage/index/b_plus_tree.h index bc1c31b27..cf17ee7bb 100644 --- a/src/include/storage/index/b_plus_tree.h +++ b/src/include/storage/index/b_plus_tree.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,7 @@ #include "common/macros.h" #include "concurrency/transaction.h" #include "storage/index/index_iterator.h" +#include "storage/index/stl_comparator_wrapper.h" #include "storage/page/b_plus_tree_header_page.h" #include "storage/page/b_plus_tree_internal_page.h" #include "storage/page/b_plus_tree_leaf_page.h" @@ -82,7 +84,7 @@ class BPlusTree { auto GetValue(const KeyType &key, std::vector *result, Transaction *txn = nullptr) -> bool; // Return the page id of the root node - auto GetRootPageId() -> page_id_t; + auto GetRootPageId() -> page_id_t { return INVALID_PAGE_ID; } // Index iterator auto Begin() -> INDEXITERATOR_TYPE; @@ -92,10 +94,10 @@ class BPlusTree { auto Begin(const KeyType &key) -> INDEXITERATOR_TYPE; // Print the B+ tree - void Print(BufferPoolManager *bpm); + void Print(BufferPoolManager *bpm) { UNREACHABLE("not implemented"); } // Draw the B+ tree - void Draw(BufferPoolManager *bpm, const std::string &outf); + void Draw(BufferPoolManager *bpm, const std::string &outf) { UNREACHABLE("not implemented"); } /** * @brief draw a B+ tree, below is a printed @@ -108,7 +110,7 @@ class BPlusTree { * * @return std::string */ - auto DrawBPlusTree() -> std::string; + auto DrawBPlusTree() -> std::string { UNREACHABLE("not implemented"); } // read data from file and insert one by one void InsertFromFile(const std::string &file_name, Transaction *txn = nullptr); @@ -133,11 +135,14 @@ class BPlusTree { // member variable std::string index_name_; BufferPoolManager *bpm_; - KeyComparator comparator_; std::vector log; // NOLINT int leaf_max_size_; int internal_max_size_; page_id_t header_page_id_; + + std::mutex lock_; + StlComparatorWrapper comparator_; + std::map> data_; }; /** diff --git a/src/include/storage/index/index_iterator.h b/src/include/storage/index/index_iterator.h index 7618823e1..cce2febcf 100644 --- a/src/include/storage/index/index_iterator.h +++ b/src/include/storage/index/index_iterator.h @@ -13,6 +13,11 @@ * For range scan of b+ tree */ #pragma once + +#include +#include + +#include "storage/index/stl_comparator_wrapper.h" #include "storage/page/b_plus_tree_leaf_page.h" namespace bustub { @@ -22,22 +27,33 @@ namespace bustub { INDEX_TEMPLATE_ARGUMENTS class IndexIterator { public: - // you may define your own constructor based on your member variables - IndexIterator(); - ~IndexIterator(); // NOLINT + IndexIterator( + const std::map> *map, + typename std::map>::const_iterator iter) + : map_(map), iter_(std::move(iter)) {} + + ~IndexIterator() = default; - auto IsEnd() -> bool; + auto IsEnd() -> bool { return iter_ != map_->cend(); } - auto operator*() -> const MappingType &; + auto operator*() -> const MappingType & { + ret_val_ = *iter_; + return ret_val_; + } - auto operator++() -> IndexIterator &; + auto operator++() -> IndexIterator & { + iter_++; + return *this; + } - auto operator==(const IndexIterator &itr) const -> bool { throw std::runtime_error("unimplemented"); } + inline auto operator==(const IndexIterator &itr) const -> bool { return itr.iter_ == iter_; } - auto operator!=(const IndexIterator &itr) const -> bool { throw std::runtime_error("unimplemented"); } + inline auto operator!=(const IndexIterator &itr) const -> bool { return !(*this == itr); } private: - // add your own private member variables here + const std::map> *map_; + typename std::map>::const_iterator iter_; + std::pair ret_val_; }; } // namespace bustub diff --git a/src/storage/index/b_plus_tree.cpp b/src/storage/index/b_plus_tree.cpp index a906af0b9..8d183b271 100644 --- a/src/storage/index/b_plus_tree.cpp +++ b/src/storage/index/b_plus_tree.cpp @@ -1,6 +1,14 @@ -#include +/** + * b_plus_tree.cpp + */ + +#include +#include +#include // NOLINT #include +#include +#include "common/config.h" #include "common/exception.h" #include "common/logger.h" #include "common/rid.h" @@ -13,20 +21,17 @@ BPLUSTREE_TYPE::BPlusTree(std::string name, page_id_t header_page_id, BufferPool const KeyComparator &comparator, int leaf_max_size, int internal_max_size) : index_name_(std::move(name)), bpm_(buffer_pool_manager), - comparator_(std::move(comparator)), leaf_max_size_(leaf_max_size), internal_max_size_(internal_max_size), - header_page_id_(header_page_id) { - WritePageGuard guard = bpm_->FetchPageWrite(header_page_id_); - auto root_page = guard.AsMut(); - root_page->root_page_id_ = INVALID_PAGE_ID; -} + header_page_id_(header_page_id), + comparator_(StlComparatorWrapper(comparator)), + data_(comparator_) {} /* * Helper function to decide whether current b+tree is empty */ INDEX_TEMPLATE_ARGUMENTS -auto BPLUSTREE_TYPE::IsEmpty() const -> bool { return true; } +auto BPLUSTREE_TYPE::IsEmpty() const -> bool { return data_.empty(); } /***************************************************************************** * SEARCH *****************************************************************************/ @@ -37,9 +42,12 @@ auto BPLUSTREE_TYPE::IsEmpty() const -> bool { return true; } */ INDEX_TEMPLATE_ARGUMENTS auto BPLUSTREE_TYPE::GetValue(const KeyType &key, std::vector *result, Transaction *txn) -> bool { - // Declaration of context instance. - Context ctx; - (void)ctx; + std::scoped_lock l(lock_); + if (data_.count(key) == 1) { + *result = std::vector{data_[key]}; + return true; + } + *result = {}; return false; } @@ -55,10 +63,12 @@ auto BPLUSTREE_TYPE::GetValue(const KeyType &key, std::vector *result */ INDEX_TEMPLATE_ARGUMENTS auto BPLUSTREE_TYPE::Insert(const KeyType &key, const ValueType &value, Transaction *txn) -> bool { - // Declaration of context instance. - Context ctx; - (void)ctx; - return false; + std::scoped_lock l(lock_); + if (data_.count(key) == 1) { + return false; + } + data_[key] = value; + return true; } /***************************************************************************** @@ -66,28 +76,27 @@ auto BPLUSTREE_TYPE::Insert(const KeyType &key, const ValueType &value, Transact *****************************************************************************/ /* * Delete key & value pair associated with input key - * If current tree is empty, return immediately. + * If current tree is empty, return immdiately. * If not, User needs to first find the right leaf page as deletion target, then * delete entry from leaf page. Remember to deal with redistribute or merge if * necessary. */ INDEX_TEMPLATE_ARGUMENTS void BPLUSTREE_TYPE::Remove(const KeyType &key, Transaction *txn) { - // Declaration of context instance. - Context ctx; - (void)ctx; + std::scoped_lock l(lock_); + data_.erase(key); } /***************************************************************************** * INDEX ITERATOR *****************************************************************************/ /* - * Input parameter is void, find the leftmost leaf page first, then construct + * Input parameter is void, find the leaftmost leaf page first, then construct * index iterator * @return : index iterator */ INDEX_TEMPLATE_ARGUMENTS -auto BPLUSTREE_TYPE::Begin() -> INDEXITERATOR_TYPE { return INDEXITERATOR_TYPE(); } +auto BPLUSTREE_TYPE::Begin() -> INDEXITERATOR_TYPE { return INDEXITERATOR_TYPE(&data_, data_.cbegin()); } /* * Input parameter is low key, find the leaf page that contains the input key @@ -95,7 +104,9 @@ auto BPLUSTREE_TYPE::Begin() -> INDEXITERATOR_TYPE { return INDEXITERATOR_TYPE() * @return : index iterator */ INDEX_TEMPLATE_ARGUMENTS -auto BPLUSTREE_TYPE::Begin(const KeyType &key) -> INDEXITERATOR_TYPE { return INDEXITERATOR_TYPE(); } +auto BPLUSTREE_TYPE::Begin(const KeyType &key) -> INDEXITERATOR_TYPE { + return INDEXITERATOR_TYPE(&data_, data_.lower_bound(key)); +} /* * Input parameter is void, construct an index iterator representing the end @@ -103,17 +114,7 @@ auto BPLUSTREE_TYPE::Begin(const KeyType &key) -> INDEXITERATOR_TYPE { return IN * @return : index iterator */ INDEX_TEMPLATE_ARGUMENTS -auto BPLUSTREE_TYPE::End() -> INDEXITERATOR_TYPE { return INDEXITERATOR_TYPE(); } - -/** - * @return Page id of the root of this tree - */ -INDEX_TEMPLATE_ARGUMENTS -auto BPLUSTREE_TYPE::GetRootPageId() -> page_id_t { return 0; } - -/***************************************************************************** - * UTILITIES AND DEBUG - *****************************************************************************/ +auto BPLUSTREE_TYPE::End() -> INDEXITERATOR_TYPE { return INDEXITERATOR_TYPE(&data_, data_.cend()); } /* * This method is used for test only @@ -148,200 +149,10 @@ void BPLUSTREE_TYPE::RemoveFromFile(const std::string &file_name, Transaction *t } } -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 diff --git a/src/storage/index/index_iterator.cpp b/src/storage/index/index_iterator.cpp index 3c2ee3ae6..d8f95f4c3 100644 --- a/src/storage/index/index_iterator.cpp +++ b/src/storage/index/index_iterator.cpp @@ -1,30 +1,11 @@ /** * index_iterator.cpp */ -#include - #include "storage/index/index_iterator.h" -namespace bustub { - -/* - * NOTE: you can change the destructor/constructor method here - * set your own input parameters - */ -INDEX_TEMPLATE_ARGUMENTS -INDEXITERATOR_TYPE::IndexIterator() = default; - -INDEX_TEMPLATE_ARGUMENTS -INDEXITERATOR_TYPE::~IndexIterator() = default; // NOLINT - -INDEX_TEMPLATE_ARGUMENTS -auto INDEXITERATOR_TYPE::IsEnd() -> bool { throw std::runtime_error("unimplemented"); } - -INDEX_TEMPLATE_ARGUMENTS -auto INDEXITERATOR_TYPE::operator*() -> const MappingType & { throw std::runtime_error("unimplemented"); } +#include -INDEX_TEMPLATE_ARGUMENTS -auto INDEXITERATOR_TYPE::operator++() -> INDEXITERATOR_TYPE & { throw std::runtime_error("unimplemented"); } +namespace bustub { template class IndexIterator, RID, GenericComparator<4>>; From 320211d5f0b6aaee298255af4445836b9ed8178b Mon Sep 17 00:00:00 2001 From: Alex Chi Date: Wed, 29 Mar 2023 17:58:04 -0400 Subject: [PATCH 2/3] Update index_iterator.h --- src/include/storage/index/index_iterator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/include/storage/index/index_iterator.h b/src/include/storage/index/index_iterator.h index cce2febcf..25664c254 100644 --- a/src/include/storage/index/index_iterator.h +++ b/src/include/storage/index/index_iterator.h @@ -34,7 +34,7 @@ class IndexIterator { ~IndexIterator() = default; - auto IsEnd() -> bool { return iter_ != map_->cend(); } + auto IsEnd() -> bool { return iter_ == map_->cend(); } auto operator*() -> const MappingType & { ret_val_ = *iter_; From e031f2cfaf3ba6fa35d2ba0e06b0c4178dccb032 Mon Sep 17 00:00:00 2001 From: NetWilliam Date: Fri, 11 Aug 2023 09:36:38 -0500 Subject: [PATCH 3/3] fix(test): add check in COW test where root value is written then removed (#590) [mod] add check in COW test where root value is written then removed --- test/primer/trie_test.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/test/primer/trie_test.cpp b/test/primer/trie_test.cpp index c3087631b..4865fb04a 100644 --- a/test/primer/trie_test.cpp +++ b/test/primer/trie_test.cpp @@ -79,11 +79,15 @@ TEST(TrieTest, BasicRemoveTest2) { ASSERT_EQ(*trie.Get("te"), 23); trie = trie.Put("tes", 233); ASSERT_EQ(*trie.Get("tes"), 233); + trie = trie.Put("", 123); + ASSERT_EQ(*trie.Get(""), 123); // Delete something + trie = trie.Remove(""); trie = trie.Remove("te"); trie = trie.Remove("tes"); trie = trie.Remove("test"); + ASSERT_EQ(trie.Get(""), nullptr); ASSERT_EQ(trie.Get("te"), nullptr); ASSERT_EQ(trie.Get("tes"), nullptr); ASSERT_EQ(trie.Get("test"), nullptr);