diff --git a/src/include/storage/index/b_plus_tree.h b/src/include/storage/index/b_plus_tree.h index 9452029b1..69a7d1c43 100644 --- a/src/include/storage/index/b_plus_tree.h +++ b/src/include/storage/index/b_plus_tree.h @@ -8,31 +8,37 @@ // Copyright (c) 2018, Carnegie Mellon University Database Group // //===----------------------------------------------------------------------===// +/** + * b_plus_tree.h + * + * Implementation of simple b+ tree data structure where internal pages direct + * the search and leaf pages contain actual data. + * (1) We only support unique key + * (2) support insert & remove + * (3) The structure should shrink and grow dynamically + * (4) Implement index iterator for range scan + */ #pragma once +#include +#include +#include // NOLINT #include #include #include +#include "common/config.h" +#include "common/exception.h" #include "concurrency/transaction.h" #include "storage/index/index_iterator.h" +#include "storage/index/stl_comparator_wrapper.h" #include "storage/page/b_plus_tree_internal_page.h" #include "storage/page/b_plus_tree_leaf_page.h" namespace bustub { #define BPLUSTREE_TYPE BPlusTree - -/** - * Main class providing the API for the Interactive B+ Tree. - * - * Implementation of simple b+ tree data structure where internal pages direct - * the search and leaf pages contain actual data. - * (1) We only support unique key - * (2) support insert & remove - * (3) The structure should shrink and grow dynamically - * (4) Implement index iterator for range scan - */ +// Main class providing the API for the Interactive B+ Tree. INDEX_TEMPLATE_ARGUMENTS class BPlusTree { using InternalPage = BPlusTreeInternalPage; @@ -49,24 +55,17 @@ class BPlusTree { auto Insert(const KeyType &key, const ValueType &value, Transaction *transaction = nullptr) -> bool; // Remove a key and its value from this B+ tree. - void Remove(const KeyType &key, Transaction *transaction = nullptr); + void Remove(const KeyType &key, Transaction *transaction); // return the value associated with a given key auto GetValue(const KeyType &key, std::vector *result, Transaction *transaction = nullptr) -> bool; - // return the page id of the root node - auto GetRootPageId() -> page_id_t; - // index iterator auto Begin() -> INDEXITERATOR_TYPE; - auto Begin(const KeyType &key) -> INDEXITERATOR_TYPE; - auto End() -> INDEXITERATOR_TYPE; - // print the B+ tree - void Print(BufferPoolManager *bpm); + auto End() -> INDEXITERATOR_TYPE; - // draw the B+ tree - void Draw(BufferPoolManager *bpm, const std::string &outf); + auto Begin(const KeyType &key) -> INDEXITERATOR_TYPE; // read data from file and insert one by one void InsertFromFile(const std::string &file_name, Transaction *transaction = nullptr); @@ -74,19 +73,30 @@ class BPlusTree { // read data from file and remove one by one void RemoveFromFile(const std::string &file_name, Transaction *transaction = nullptr); - private: - void UpdateRootPageId(int insert_record = 0); + void Print(BufferPoolManager *bpm) { throw bustub::NotImplementedException("not implemented"); } + + // draw the B+ tree + void Draw(BufferPoolManager *bpm, const std::string &outf) { + throw bustub::NotImplementedException("not implemented"); + } /* Debug Routines for FREE!! */ - void ToGraph(BPlusTreePage *page, BufferPoolManager *bpm, std::ofstream &out) const; + void ToGraph(BPlusTreePage *page, BufferPoolManager *bpm, std::ofstream &out) const { + throw bustub::NotImplementedException("not implemented"); + } + + auto FindLeafPage(const KeyType &key, bool leftMost = false) -> Page * { + throw bustub::NotImplementedException("not implemented"); + } - void ToString(BPlusTreePage *page, BufferPoolManager *bpm) const; + auto GetRootPageId() -> page_id_t { return INVALID_PAGE_ID; } - // member variable + private: + std::mutex lock_; std::string index_name_; - page_id_t root_page_id_; BufferPoolManager *buffer_pool_manager_; - KeyComparator comparator_; + StlComparatorWrapper comparator_; + std::map> data_; int leaf_max_size_; int internal_max_size_; }; diff --git a/src/include/storage/index/index_iterator.h b/src/include/storage/index/index_iterator.h index 7618823e1..25664c254 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 97d2a5cfc..68cb1b37e 100644 --- a/src/storage/index/b_plus_tree.cpp +++ b/src/storage/index/b_plus_tree.cpp @@ -1,4 +1,12 @@ +/** + * b_plus_tree.cpp + */ + +#include +#include +#include // NOLINT #include +#include #include "common/exception.h" #include "common/logger.h" @@ -11,9 +19,9 @@ INDEX_TEMPLATE_ARGUMENTS BPLUSTREE_TYPE::BPlusTree(std::string name, BufferPoolManager *buffer_pool_manager, const KeyComparator &comparator, int leaf_max_size, int internal_max_size) : index_name_(std::move(name)), - root_page_id_(INVALID_PAGE_ID), buffer_pool_manager_(buffer_pool_manager), - comparator_(comparator), + comparator_(StlComparatorWrapper(comparator)), + data_(comparator_), leaf_max_size_(leaf_max_size), internal_max_size_(internal_max_size) {} @@ -21,7 +29,7 @@ BPLUSTREE_TYPE::BPlusTree(std::string name, BufferPoolManager *buffer_pool_manag * 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 *****************************************************************************/ @@ -32,6 +40,12 @@ auto BPLUSTREE_TYPE::IsEmpty() const -> bool { return true; } */ INDEX_TEMPLATE_ARGUMENTS auto BPLUSTREE_TYPE::GetValue(const KeyType &key, std::vector *result, Transaction *transaction) -> bool { + std::scoped_lock l(lock_); + if (data_.count(key) == 1) { + *result = std::vector{data_[key]}; + return true; + } + *result = {}; return false; } @@ -47,7 +61,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 *transaction) -> bool { - return false; + std::scoped_lock l(lock_); + if (data_.count(key) == 1) { + return false; + } + data_[key] = value; + return true; } /***************************************************************************** @@ -61,7 +80,10 @@ auto BPLUSTREE_TYPE::Insert(const KeyType &key, const ValueType &value, Transact * necessary. */ INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::Remove(const KeyType &key, Transaction *transaction) {} +void BPLUSTREE_TYPE::Remove(const KeyType &key, Transaction *transaction) { + std::scoped_lock l(lock_); + data_.erase(key); +} /***************************************************************************** * INDEX ITERATOR @@ -72,7 +94,7 @@ void BPLUSTREE_TYPE::Remove(const KeyType &key, Transaction *transaction) {} * @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 @@ -80,7 +102,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 @@ -88,37 +112,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 - *****************************************************************************/ -/* - * Update/Insert root page id in header page(where page_id = 0, header_page is - * defined under include/page/header_page.h) - * Call this method everytime root page id is changed. - * @parameter: insert_record defualt value is false. When set to true, - * insert a record into header page instead of - * updating it. - */ -INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::UpdateRootPageId(int insert_record) { - auto *header_page = static_cast(buffer_pool_manager_->FetchPage(HEADER_PAGE_ID)); - if (insert_record != 0) { - // create a new record in header_page - header_page->InsertRecord(index_name_, root_page_id_); - } else { - // update root_page_id in header_page - header_page->UpdateRecord(index_name_, root_page_id_); - } - buffer_pool_manager_->UnpinPage(HEADER_PAGE_ID, true); -} +auto BPLUSTREE_TYPE::End() -> INDEXITERATOR_TYPE { return INDEXITERATOR_TYPE(&data_, data_.cend()); } /* * This method is used for test only @@ -153,161 +147,6 @@ void BPLUSTREE_TYPE::RemoveFromFile(const std::string &file_name, Transaction *t } } -/** - * 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("Draw an empty tree"); - return; - } - std::ofstream out(outf); - out << "digraph G {" << std::endl; - ToGraph(reinterpret_cast(bpm->FetchPage(root_page_id_)->GetData()), bpm, out); - out << "}" << std::endl; - out.flush(); - out.close(); -} - -/** - * This method is used for debug only, You don't need to modify - */ -INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::Print(BufferPoolManager *bpm) { - if (IsEmpty()) { - LOG_WARN("Print an empty tree"); - return; - } - ToString(reinterpret_cast(bpm->FetchPage(root_page_id_)->GetData()), bpm); -} - -/** - * This method is used for debug only, You don't need to modify - * @tparam KeyType - * @tparam ValueType - * @tparam KeyComparator - * @param page - * @param bpm - * @param out - */ -INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::ToGraph(BPlusTreePage *page, BufferPoolManager *bpm, std::ofstream &out) const { - std::string leaf_prefix("LEAF_"); - std::string internal_prefix("INT_"); - if (page->IsLeafPage()) { - auto *leaf = reinterpret_cast(page); - // Print node name - out << leaf_prefix << leaf->GetPageId(); - // 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=" << leaf->GetPageId() << "
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 << leaf->GetPageId() << " -> " << leaf_prefix << leaf->GetNextPageId() << ";\n"; - out << "{rank=same " << leaf_prefix << leaf->GetPageId() << " " << leaf_prefix << leaf->GetNextPageId() << "};\n"; - } - - // Print parent links if there is a parent - if (leaf->GetParentPageId() != INVALID_PAGE_ID) { - out << internal_prefix << leaf->GetParentPageId() << ":p" << leaf->GetPageId() << " -> " << leaf_prefix - << leaf->GetPageId() << ";\n"; - } - } else { - auto *inner = reinterpret_cast(page); - // Print node name - out << internal_prefix << inner->GetPageId(); - // 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=" << inner->GetPageId() << "
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 Parent link - if (inner->GetParentPageId() != INVALID_PAGE_ID) { - out << internal_prefix << inner->GetParentPageId() << ":p" << inner->GetPageId() << " -> " << internal_prefix - << inner->GetPageId() << ";\n"; - } - // Print leaves - for (int i = 0; i < inner->GetSize(); i++) { - auto child_page = reinterpret_cast(bpm->FetchPage(inner->ValueAt(i))->GetData()); - ToGraph(child_page, bpm, out); - if (i > 0) { - auto sibling_page = reinterpret_cast(bpm->FetchPage(inner->ValueAt(i - 1))->GetData()); - if (!sibling_page->IsLeafPage() && !child_page->IsLeafPage()) { - out << "{rank=same " << internal_prefix << sibling_page->GetPageId() << " " << internal_prefix - << child_page->GetPageId() << "};\n"; - } - bpm->UnpinPage(sibling_page->GetPageId(), false); - } - } - } - bpm->UnpinPage(page->GetPageId(), false); -} - -/** - * This function is for debug only, you don't need to modify - * @tparam KeyType - * @tparam ValueType - * @tparam KeyComparator - * @param page - * @param bpm - */ -INDEX_TEMPLATE_ARGUMENTS -void BPLUSTREE_TYPE::ToString(BPlusTreePage *page, BufferPoolManager *bpm) const { - if (page->IsLeafPage()) { - auto *leaf = reinterpret_cast(page); - std::cout << "Leaf Page: " << leaf->GetPageId() << " parent: " << leaf->GetParentPageId() - << " next: " << leaf->GetNextPageId() << std::endl; - for (int i = 0; i < leaf->GetSize(); i++) { - std::cout << leaf->KeyAt(i) << ","; - } - std::cout << std::endl; - std::cout << std::endl; - } else { - auto *internal = reinterpret_cast(page); - std::cout << "Internal Page: " << internal->GetPageId() << " parent: " << internal->GetParentPageId() << std::endl; - for (int i = 0; i < internal->GetSize(); i++) { - std::cout << internal->KeyAt(i) << ": " << internal->ValueAt(i) << ","; - } - std::cout << std::endl; - std::cout << std::endl; - for (int i = 0; i < internal->GetSize(); i++) { - ToString(reinterpret_cast(bpm->FetchPage(internal->ValueAt(i))->GetData()), bpm); - } - } - bpm->UnpinPage(page->GetPageId(), false); -} - template class BPlusTree, RID, GenericComparator<4>>; template class BPlusTree, RID, GenericComparator<8>>; template class BPlusTree, RID, GenericComparator<16>>; 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>>;