diff --git a/src/observer/storage/common/bplus_tree.cpp b/src/observer/storage/common/bplus_tree.cpp index e2e614508365e1be21f82f6e586e095861dd9e81..ff5d313185c148f449ab035fb69a09f020d2618a 100644 --- a/src/observer/storage/common/bplus_tree.cpp +++ b/src/observer/storage/common/bplus_tree.cpp @@ -20,2439 +20,1925 @@ See the Mulan PSL v2 for more details. */ #define FIRST_INDEX_PAGE 1 -int float_compare(float f1, float f2) -{ - float result = f1 - f2; - if (-1e-6 < result && result < 1e-6) { - return 0; - } - return result > 0 ? 1 : -1; -} - -int attribute_comp(const char *first, const char *second, AttrType attr_type, int attr_length) -{ // 简化 - int i1, i2; - float f1, f2; - const char *s1, *s2; - switch (attr_type) { - case INTS: { - i1 = *(int *)first; - i2 = *(int *)second; - return i1 - i2; - } break; - case FLOATS: { - f1 = *(float *)first; - f2 = *(float *)second; - return float_compare(f1, f2); - } break; - case CHARS: { - s1 = first; - s2 = second; - return strncmp(s1, s2, attr_length); - } break; - default: { - LOG_PANIC("Unknown attr type: %d", attr_type); - } - } - return -2; // This means error happens -} -int key_compare(AttrType attr_type, int attr_length, const char *first, const char *second) -{ - int result = attribute_comp(first, second, attr_type, attr_length); - if (0 != result) { - return result; - } - RID *rid1 = (RID *)(first + attr_length); - RID *rid2 = (RID *)(second + attr_length); - return RID::compare(rid1, rid2); -} -int lower_bound(AttrType attr_type, int attr_length, const char *data, const int item_length, const int item_num, const char *key, bool *_found = nullptr) -{ - int left = 0; - int right = item_num; - int med = 0; - bool last_is_bigger = false; - bool found = false; - for ( ; left < right; ) { - med = (left + right) / 2; - const char *item = data + item_length * med; - int cmp_result = key_compare(attr_type, attr_length, key, item); - if (cmp_result == 0) { - last_is_bigger = false; - found = true; - break; - } - if (cmp_result > 0) { - last_is_bigger = true; - left = med + 1; - } else { - last_is_bigger = false; - right = med; - } - } - - if (_found) - *_found = found; - - if (last_is_bigger) - return med + 1; - return med; -} - -int get_page_index_capacity(int attr_length) +int calc_internal_page_capacity(int attr_length) { + int item_size = attr_length + sizeof(RID) + sizeof(PageNum); int capacity = - ((int)BP_PAGE_DATA_SIZE - sizeof(IndexFileHeader) - sizeof(IndexNode)) / (attr_length + 2 * sizeof(RID)); - // Here is some tricks - // 1. reserver one pair of kV for insert operation - // 2. make sure capacity % 2 == 0, otherwise it is likeyly to occur problem when split node - capacity = ((capacity - RECORD_RESERVER_PAIR_NUM) / 2) * 2; + ((int)BP_PAGE_DATA_SIZE - InternalIndexNode::HEADER_SIZE) / item_size; return capacity; } -IndexNode *BplusTreeHandler::get_index_node(char *page_data) const +int calc_leaf_page_capacity(int attr_length) { - IndexNode *node = (IndexNode *)(page_data + sizeof(IndexFileHeader)); - node->keys = (char *)node + sizeof(IndexNode); - node->rids = (RID *)(node->keys + (file_header_.order + RECORD_RESERVER_PAIR_NUM) * file_header_.key_length); - return node; + int item_size = attr_length + sizeof(RID) + sizeof(RID); + int capacity = + ((int)BP_PAGE_DATA_SIZE - LeafIndexNode::HEADER_SIZE) / item_size; + return capacity; } -RC BplusTreeHandler::sync() +///////////////////////////////////////////////////////////////////////////////// +IndexNodeHandler::IndexNodeHandler(const IndexFileHeader &header, BPPageHandle &page_handle) + : header_(header), page_num_(page_handle.page_num()), node_((IndexNode *)page_handle.data()) +{} + +bool IndexNodeHandler::is_leaf() const { - return disk_buffer_pool_->purge_all_pages(file_id_); + return node_->is_leaf; +} +void IndexNodeHandler::init_empty(bool leaf) +{ + node_->is_leaf = leaf; + node_->key_num = 0; + node_->parent = BP_INVALID_PAGE_NUM; +} +PageNum IndexNodeHandler::page_num() const +{ + return page_num_; } -RC BplusTreeHandler::create(const char *file_name, AttrType attr_type, int attr_length) +int IndexNodeHandler::key_size() const { - DiskBufferPool *disk_buffer_pool = theGlobalDiskBufferPool(); - RC rc = disk_buffer_pool->create_file(file_name); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to create file. file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); - return rc; - } - LOG_INFO("Successfully create index file:%s", file_name); + return header_.key_length; +} - int file_id; - rc = disk_buffer_pool->open_file(file_name, &file_id); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to open file. file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); - return rc; - } - LOG_INFO("Successfully open index file %s.", file_name); +int IndexNodeHandler::value_size() const +{ + // return header_.value_size; + return sizeof(RID); +} - rc = disk_buffer_pool->allocate_page(file_id, &root_page_handle_); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to allocate page. file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); - disk_buffer_pool->close_file(file_id); - return rc; - } +int IndexNodeHandler::item_size() const +{ + return key_size() + value_size(); +} - char *pdata; - rc = disk_buffer_pool->get_data(&root_page_handle_, &pdata); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get data. file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); - disk_buffer_pool->close_file(file_id); - return rc; - } +int IndexNodeHandler::size() const +{ + return node_->key_num; +} - PageNum page_num; - rc = disk_buffer_pool->get_page_num(&root_page_handle_, &page_num); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get page num. file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); - disk_buffer_pool->close_file(file_id); - return rc; - } +void IndexNodeHandler::increase_size(int n) +{ + node_->key_num += n; +} - IndexFileHeader *file_header = (IndexFileHeader *)pdata; - file_header->attr_length = attr_length; - file_header->key_length = attr_length + sizeof(RID); - file_header->attr_type = attr_type; - file_header->order = get_page_index_capacity(attr_length); - file_header->root_page = page_num; +PageNum IndexNodeHandler::parent_page_num() const +{ + return node_->parent; +} - root_node_ = get_index_node(pdata); - root_node_->init_empty(*file_header); +void IndexNodeHandler::set_parent_page_num(PageNum page_num) +{ + this->node_->parent = page_num; +} +std::string to_string(const IndexNodeHandler &handler) +{ + std::stringstream ss; - disk_buffer_pool->mark_dirty(&root_page_handle_); + ss << "PageNum:" << handler.page_num() + << ",is_leaf:" << handler.is_leaf() << "," + << "key_num:" << handler.size() << "," + << "parent:" << handler.parent_page_num() << ","; - disk_buffer_pool_ = disk_buffer_pool; - file_id_ = file_id; + return ss.str(); +} - memcpy(&file_header_, pdata, sizeof(file_header_)); - header_dirty_ = false; +bool IndexNodeHandler::validate() const +{ + if (parent_page_num() == BP_INVALID_PAGE_NUM) { + // this is a root page + if (size() < 1) { + LOG_WARN("root page has no item"); + return false; + } - mem_pool_item_ = new common::MemPoolItem(file_name); - if (mem_pool_item_->init(file_header->key_length) < 0) { - LOG_WARN("Failed to init memory pool for index %s", file_name); - close(); - return RC::NOMEM; + if (!is_leaf() && size() < 2) { + LOG_WARN("root page internal node has less than 2 child. size=%d", size()); + return false; + } } - - LOG_INFO("Successfully create index %s", file_name); - return RC::SUCCESS; + return true; } -RC BplusTreeHandler::open(const char *file_name) +///////////////////////////////////////////////////////////////////////////////// +LeafIndexNodeHandler::LeafIndexNodeHandler(const IndexFileHeader &header, BPPageHandle &page_handle) + : IndexNodeHandler(header, page_handle), leaf_node_((LeafIndexNode *)page_handle.data()) +{} + +void LeafIndexNodeHandler::init_empty() { - if (file_id_ > 0) { - LOG_WARN("%s has been opened before index.open.", file_name); - return RC::RECORD_OPENNED; - } + IndexNodeHandler::init_empty(true); + leaf_node_->prev_brother = BP_INVALID_PAGE_NUM; + leaf_node_->next_brother = BP_INVALID_PAGE_NUM; +} - DiskBufferPool *disk_buffer_pool = theGlobalDiskBufferPool(); - int file_id = 0; - RC rc = disk_buffer_pool->open_file(file_name, &file_id); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to open file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); - return rc; - } +void LeafIndexNodeHandler::set_next_page(PageNum page_num) +{ + leaf_node_->next_brother = page_num; +} - BPPageHandle page_handle; - rc = disk_buffer_pool->get_this_page(file_id, FIRST_INDEX_PAGE, &page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get first page file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); - disk_buffer_pool_->close_file(file_id); - return rc; - } +void LeafIndexNodeHandler::set_prev_page(PageNum page_num) +{ + leaf_node_->prev_brother = page_num; +} +PageNum LeafIndexNodeHandler::next_page() const +{ + return leaf_node_->next_brother; +} +PageNum LeafIndexNodeHandler::prev_page() const +{ + return leaf_node_->prev_brother; +} - char *pdata; - rc = disk_buffer_pool->get_data(&page_handle, &pdata); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get first page data. file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); - disk_buffer_pool_->close_file(file_id); - return rc; - } +char *LeafIndexNodeHandler::key_at(int index) +{ + assert(index >= 0 && index < size()); + return __key_at(index); +} - memcpy(&file_header_, pdata, sizeof(IndexFileHeader)); - header_dirty_ = false; - disk_buffer_pool_ = disk_buffer_pool; - file_id_ = file_id; +char *LeafIndexNodeHandler::value_at(int index) +{ + assert(index >= 0 && index < size()); + return __value_at(index); +} - mem_pool_item_ = new common::MemPoolItem(file_name); - if (mem_pool_item_->init(file_header_.key_length) < 0) { - LOG_WARN("Failed to init memory pool for index %s", file_name); - close(); - return RC::NOMEM; - } +int LeafIndexNodeHandler::max_size() const +{ + return header_.leaf_max_size; +} - if (file_header_.root_page == FIRST_INDEX_PAGE) { - root_node_ = get_index_node(pdata); - root_page_handle_ = page_handle; +int LeafIndexNodeHandler::min_size() const +{ + return header_.leaf_max_size - header_.leaf_max_size / 2; +} - LOG_INFO("Successfully open index %s", file_name); - return RC::SUCCESS; +int LeafIndexNodeHandler::lookup(const KeyComparator &comparator, const char *key, bool *found /* = nullptr */) const +{ + const int size = this->size(); + int i = 0; + for ( ; i < size; i++) { + int result = comparator(key, __key_at(i)); + if (0 == result) { + if (found) { + *found = true; + } + return i; + } + if (result < 0) { + break; + } } - - // close old page_handle - disk_buffer_pool->unpin_page(&page_handle); - - LOG_INFO("Begin to load root page of index:%s, root_page:%d.", file_name, file_header_.root_page); - rc = disk_buffer_pool->get_this_page(file_id, file_header_.root_page, &root_page_handle_); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get first page file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); - disk_buffer_pool_->close_file(file_id); - return rc; + if (found) { + *found = false; } + return i; +} - rc = disk_buffer_pool->get_data(&root_page_handle_, &pdata); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get first page data. file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); - disk_buffer_pool_->close_file(file_id); - return rc; +void LeafIndexNodeHandler::insert(int index, const char *key, const char *value) +{ + if (index < size()) { + memmove(__item_at(index + 1), __item_at(index), (size() - index) * item_size()); + } + memcpy(__item_at(index), key, key_size()); + memcpy(__item_at(index) + key_size(), value, value_size()); + increase_size(1); +} +void LeafIndexNodeHandler::remove(int index) +{ + assert(index >= 0 && index < size()); + if (index < size() - 1) { + memmove(__item_at(index), __item_at(index + 1), (size() - index - 1) * item_size()); } - root_node_ = get_index_node(pdata); + increase_size(-1); +} - LOG_INFO("Successfully open index %s", file_name); - return RC::SUCCESS; +int LeafIndexNodeHandler::remove(const char *key, const KeyComparator &comparator) +{ + bool found = false; + int index = lookup(comparator, key, &found); + if (found) { + this->remove(index); + return 1; + } + return 0; } -RC BplusTreeHandler::close() +RC LeafIndexNodeHandler::move_half_to(LeafIndexNodeHandler &other, DiskBufferPool *bp, int file_id) { - if (file_id_ != -1) { - disk_buffer_pool_->unpin_page(&root_page_handle_); - root_node_ = nullptr; + const int size = this->size(); + const int move_index = size / 2; - disk_buffer_pool_->close_file(file_id_); - file_id_ = -1; + memcpy(other.__item_at(0), this->__item_at(move_index), item_size() * (size - move_index)); + other.increase_size(size - move_index); + this->increase_size(- ( size - move_index)); + return RC::SUCCESS; +} +RC LeafIndexNodeHandler::move_first_to_end(LeafIndexNodeHandler &other, DiskBufferPool *disk_buffer_pool, int file_id) +{ + other.append(__item_at(0)); - delete mem_pool_item_; - mem_pool_item_ = nullptr; + if (size() >= 1) { + memmove(__item_at(0), __item_at(1), (size() - 1) * item_size() ); } - - disk_buffer_pool_ = nullptr; + increase_size(-1); return RC::SUCCESS; } -RC BplusTreeHandler::print_node(IndexNode *node, PageNum page_num) +RC LeafIndexNodeHandler::move_last_to_front(LeafIndexNodeHandler &other, DiskBufferPool *bp, int file_id) { - LOG_INFO("PageNum:%d, node {%s}\n", page_num, node->to_string(file_header_).c_str()); + other.preappend(__item_at(size() - 1)); - if (node->is_leaf == false) { - for (int i = 0; i <= node->key_num; i++) { - PageNum child_page_num = node->rids[i].page_num; - BPPageHandle page_handle; - RC rc = disk_buffer_pool_->get_this_page(file_id_, child_page_num, &page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to load page file_id:%d, page_num:%d", file_id_, child_page_num); - continue; - } + increase_size(-1); + return RC::SUCCESS; +} +/** + * move all items to left page + */ +RC LeafIndexNodeHandler::move_to(LeafIndexNodeHandler &other, DiskBufferPool *bp, int file_id) +{ + memcpy(other.__item_at(other.size()), this->__item_at(0), this->size() * item_size()); + other.increase_size(this->size()); + this->increase_size(- this->size()); - char *pdata; - disk_buffer_pool_->get_data(&page_handle, &pdata); - IndexNode *child = get_index_node(pdata); - print_node(child, child_page_num); - disk_buffer_pool_->unpin_page(&page_handle); + other.set_next_page(this->next_page()); + + PageNum next_right_page_num = this->next_page(); + if (next_right_page_num != BP_INVALID_PAGE_NUM) { + BPPageHandle next_right_page_handle; + RC rc = bp->get_this_page(file_id, next_right_page_num, &next_right_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch next right page. page number:%d. rc=%d:%s", next_right_page_num, rc, strrc(rc)); + return rc; } - } + LeafIndexNodeHandler next_right_node(header_, next_right_page_handle); + next_right_node.set_prev_page(other.page_num()); + next_right_page_handle.mark_dirty(); + bp->unpin_page(&next_right_page_handle); + } return RC::SUCCESS; } -RC BplusTreeHandler::print_tree() +void LeafIndexNodeHandler::append(const char *item) { - if (file_id_ < 0) { - LOG_WARN("Index hasn't been created or opened, fail to print"); - return RC::SUCCESS; - } + memcpy(__item_at(size()), item, item_size()); + increase_size(1); +} - int page_count; - RC rc = disk_buffer_pool_->get_page_count(file_id_, &page_count); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get page count of index %d", file_id_); - return rc; +void LeafIndexNodeHandler::preappend(const char *item) +{ + if (size() > 0) { + memmove(__item_at(1), __item_at(0), size() * item_size()); } + memcpy(__item_at(0), item, item_size()); + increase_size(1); +} - LOG_INFO("\n\n\n !!!! Begin to print index %s:%d, page_count:%d, file_header:%s\n\n\n", - mem_pool_item_->get_name().c_str(), - file_id_, - page_count, - file_header_.to_string().c_str()); +char *LeafIndexNodeHandler::__item_at(int index) const +{ + return leaf_node_->array + (index * item_size()); +} +char *LeafIndexNodeHandler::__key_at(int index) const +{ + return __item_at(index); +} +char *LeafIndexNodeHandler::__value_at(int index) const +{ + return __item_at(index) + key_size(); +} - print_node(root_node_, file_header_.root_page); - return RC::SUCCESS; +std::string to_string(const LeafIndexNodeHandler &handler, const KeyPrinter &printer) +{ + std::stringstream ss; + ss << to_string((const IndexNodeHandler &)handler) + << ",prev page:" << handler.prev_page() + << ",next page:" << handler.next_page(); + ss << ",values=[" << printer(handler.__key_at(0)) ; + for (int i = 1; i < handler.size(); i++) { + ss << "," << printer(handler.__key_at(i)); + } + ss << "]"; + return ss.str(); } -RC BplusTreeHandler::print_leafs() +bool LeafIndexNodeHandler::validate(const KeyComparator &comparator, DiskBufferPool *bp, int file_id) const { - PageNum page_num; - get_first_leaf_page(&page_num); + bool result = IndexNodeHandler::validate(); + if (false == result) { + return false; + } - IndexNode *node; - BPPageHandle page_handle; - RC rc; - while (page_num != -1) { - rc = disk_buffer_pool_->get_this_page(file_id_, page_num, &page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to print leafs, due to failed to load. "); - return rc; + const int node_size = size(); + for (int i = 1; i < node_size; i++) { + if (comparator(__key_at(i - 1), __key_at(i)) >= 0) { + LOG_WARN("page number = %d, invalid key order. id1=%d,id2=%d, this=%s", + page_num(), i-1, i, to_string(*this).c_str()); + return false; } - char *pdata; - disk_buffer_pool_->get_data(&page_handle, &pdata); - node = get_index_node(pdata); - LOG_INFO("Page:%d, Node:%s", page_num, node->to_string(file_header_).c_str()); - page_num = node->next_brother; - disk_buffer_pool_->unpin_page(&page_handle); } - return RC::SUCCESS; -} + PageNum parent_page_num = this->parent_page_num(); + if (parent_page_num == BP_INVALID_PAGE_NUM) { + return true; + } -bool BplusTreeHandler::validate_node(IndexNode *node) -{ - if (node->key_num > file_header_.order) { - LOG_WARN("NODE %s 's key number is invalid", node->to_string(file_header_).c_str()); + BPPageHandle parent_page_handle; + RC rc = bp->get_this_page(file_id, parent_page_num, &parent_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch parent page. page num=%d, rc=%d:%s", + parent_page_num, rc, strrc(rc)); + return false; + } + + InternalIndexNodeHandler parent_node(header_, parent_page_handle); + int index_in_parent = parent_node.value_index(this->page_num()); + if (index_in_parent < 0) { + LOG_WARN("invalid leaf node. cannot find index in parent. this page num=%d, parent page num=%d", + this->page_num(), parent_page_num); + bp->unpin_page(&parent_page_handle); return false; } - if (node->parent != -1) { - if (node->key_num < file_header_.order / 2) { - LOG_WARN("NODE %s 's key number is invalid", node->to_string(file_header_).c_str()); + + if (0 != index_in_parent) { + int cmp_result = comparator(__key_at(0), parent_node.key_at(index_in_parent)); + if (cmp_result < 0) { + LOG_WARN("invalid leaf node. first item should be greate than or equal to parent item. " \ + "this page num=%d, parent page num=%d, index in parent=%d", + this->page_num(), parent_node.page_num(), index_in_parent); + bp->unpin_page(&parent_page_handle); return false; } - } else { - // node is root - if (node->is_leaf == false) { + } - if (node->key_num < 1) { - LOG_WARN("NODE %s 's key number is invalid", node->to_string(file_header_).c_str()); - return false; - } + if (index_in_parent < parent_node.size() - 1) { + int cmp_result = comparator(__key_at(size() - 1), parent_node.key_at(index_in_parent + 1)); + if (cmp_result >= 0) { + LOG_WARN("invalid leaf node. last item should be less than the item at the first after item in parent." \ + "this page num=%d, parent page num=%d, parent item to compare=%d", + this->page_num(), parent_node.page_num(), index_in_parent + 1); + bp->unpin_page(&parent_page_handle); + return false; } } + bp->unpin_page(&parent_page_handle); + return true; +} - if (node->is_leaf && node->prev_brother != -1) { - char *first_key = node->keys; - bool found = false; - - PageNum parent_page = node->parent; - while (parent_page != -1) { - BPPageHandle parent_handle; - RC rc = disk_buffer_pool_->get_this_page(file_id_, parent_page, &parent_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to check parent's keys, file_id:%d", file_id_); +///////////////////////////////////////////////////////////////////////////////// +InternalIndexNodeHandler::InternalIndexNodeHandler(const IndexFileHeader &header, BPPageHandle &page_handle) + : IndexNodeHandler(header, page_handle), internal_node_((InternalIndexNode *)page_handle.data()) +{} - return false; - } +std::string to_string(const InternalIndexNodeHandler &node, const KeyPrinter &printer) +{ + std::stringstream ss; + ss << to_string((const IndexNodeHandler &)node); + ss << ",children:[" + << "{key:" << printer(node.__key_at(0)) << "," + << "value:" << *(PageNum *)node.__value_at(0) << "}"; + + for (int i = 1; i < node.size(); i++) { + ss << ",{key:" << printer(node.__key_at(i)) + << ",value:"<< *(PageNum *)node.__value_at(i) << "}"; + } + ss << "]"; + return ss.str(); +} - char *pdata; - disk_buffer_pool_->get_data(&parent_handle, &pdata); - IndexNode *parent = get_index_node(pdata); - for (int i = 0; i < parent->key_num; i++) { - char *cur_key = parent->keys + i * file_header_.key_length; - int tmp = key_compare(file_header_.attr_type, file_header_.attr_length, first_key, cur_key); - if (tmp == 0) { - found = true; - - break; - } else if (tmp < 0) { - break; - } - } - disk_buffer_pool_->unpin_page(&parent_handle); - if (found == true) { - break; - } +void InternalIndexNodeHandler::init_empty() +{ + IndexNodeHandler::init_empty(false); +} +void InternalIndexNodeHandler::create_new_root(PageNum first_page_num, const char *key, PageNum page_num) +{ + memset(__key_at(0), 0, key_size()); + memcpy(__value_at(0), &first_page_num, value_size()); + memcpy(__item_at(1), key, key_size()); + memcpy(__value_at(1), &page_num, value_size()); + increase_size(2); +} - parent_page = parent->parent; - } +/** + * insert one entry + * the entry to be inserted will never at the first slot. + * the right child page after split will always have bigger keys. + */ +void InternalIndexNodeHandler::insert(const char *key, PageNum page_num, const KeyComparator &comparator) +{ + int insert_position = -1; + lookup(comparator, key, nullptr, &insert_position); + if (insert_position < size()) { + memmove(__item_at(insert_position + 1), __item_at(insert_position), (size() - insert_position) * item_size()); + } + memcpy(__item_at(insert_position), key, key_size()); + memcpy(__value_at(insert_position), &page_num, value_size()); + increase_size(1); +} - if (found == false) { - LOG_WARN("Failed to find leaf's first key in internal node. leaf:%s, file_id:%d", - node->to_string(file_header_).c_str(), - file_id_); - return false; - } +RC InternalIndexNodeHandler::move_half_to(InternalIndexNodeHandler &other, DiskBufferPool *bp, int file_id) +{ + const int size = this->size(); + const int move_index = size / 2; + RC rc = other.copy_from(this->__item_at(move_index), size - move_index, bp, file_id); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to copy item to new node. rc=%d:%s", rc, strrc(rc)); + return rc; } - bool ret = false; - char *last_key = node->keys; - char *cur_key; - for (int i = 0; i < node->key_num; i++) { - int tmp; - cur_key = node->keys + i * file_header_.key_length; - if (i > 0) { - tmp = key_compare(file_header_.attr_type, file_header_.attr_length, cur_key, last_key); - if (tmp < 0) { - LOG_WARN("NODE %s 's key sequence is wrong", node->to_string(file_header_).c_str()); - return false; - } - } - last_key = cur_key; - if (node->is_leaf) { - continue; - } + increase_size(- (size - move_index)); + return rc; +} - PageNum child_page = node->rids[i].page_num; - BPPageHandle child_handle; - RC rc = disk_buffer_pool_->get_this_page(file_id_, child_page, &child_handle); - if (rc != RC::SUCCESS) { - LOG_WARN( - "Failed to validte node's child %d, file_id:%d, node:%s", i, file_id_, node->to_string(file_header_).c_str()); - continue; - } - char *pdata; - disk_buffer_pool_->get_data(&child_handle, &pdata); - IndexNode *child = get_index_node(pdata); - - char *child_last_key = child->keys + (child->key_num - 1) * file_header_.key_length; - tmp = key_compare(file_header_.attr_type, file_header_.attr_length, cur_key, child_last_key); - if (tmp <= 0) { - LOG_WARN("Child's last key is bigger than current key, child:%s, current:%s, file_id:%d", - child->to_string(file_header_).c_str(), - node->to_string(file_header_).c_str(), - file_id_); - disk_buffer_pool_->unpin_page(&child_handle); - return false; - } +int InternalIndexNodeHandler::max_size() const +{ + return header_.internal_max_size; +} - ret = validate_node(child); - if (ret == false) { - disk_buffer_pool_->unpin_page(&child_handle); - return false; - } +int InternalIndexNodeHandler::min_size() const +{ + return header_.internal_max_size - header_.internal_max_size / 2; +} - BPPageHandle next_child_handle; - PageNum next_child_page = node->rids[i + 1].page_num; - rc = disk_buffer_pool_->get_this_page(file_id_, next_child_page, &next_child_handle); - if (rc != RC::SUCCESS) { - LOG_WARN( - "Failed to validte node's child %d, file_id:%d, node:%s", i, file_id_, node->to_string(file_header_).c_str()); - disk_buffer_pool_->unpin_page(&child_handle); - continue; - } - disk_buffer_pool_->get_data(&next_child_handle, &pdata); - IndexNode *next_child = get_index_node(pdata); - - char *first_next_child_key = next_child->keys; - tmp = key_compare(file_header_.attr_type, file_header_.attr_length, cur_key, first_next_child_key); - if (next_child->is_leaf) { - if (tmp != 0) { - LOG_WARN("Next child's first key isn't equal current key, next_child:%s, current:%s, file_id:%d", - next_child->to_string(file_header_).c_str(), - node->to_string(file_header_).c_str(), - file_id_); - disk_buffer_pool_->unpin_page(&next_child_handle); - disk_buffer_pool_->unpin_page(&child_handle); - return false; +/** + * lookup the first item which key <= item + * @return unlike the leafNode, the return value is not the insert position, + * but only the index of child to find. + */ +int InternalIndexNodeHandler::lookup(const KeyComparator &comparator, const char *key, + bool *found /* = nullptr */, int *insert_position /*= nullptr */) const +{ + int i = 1; + const int size = this->size(); + for ( ; i < size; i++) { + int result = comparator(key, __key_at(i)); + if (result == 0) { + if (found) { + *found = true; } - } else { - if (tmp >= 0) { - LOG_WARN("Next child's first key isn't equal current key, next_child:%s, current:%s, file_id:%d", - next_child->to_string(file_header_).c_str(), - node->to_string(file_header_).c_str(), - file_id_); - disk_buffer_pool_->unpin_page(&next_child_handle); - disk_buffer_pool_->unpin_page(&child_handle); - return false; + if (insert_position) { + *insert_position = i; } + return i; } - - if (i == node->key_num - 1) { - ret = validate_node(next_child); - if (ret == false) { - LOG_WARN("Next child is invalid, next_child:%s, current:%s, file_id:%d", - next_child->to_string(file_header_).c_str(), - node->to_string(file_header_).c_str(), - file_id_); - disk_buffer_pool_->unpin_page(&next_child_handle); - disk_buffer_pool_->unpin_page(&child_handle); - return false; + if (result < 0) { + if (found) { + *found = false; } - } - if (child->is_leaf) { - if (child->next_brother != next_child_page || next_child->prev_brother != child_page) { - LOG_WARN("The child 's next brother or the next child's previous brother isn't correct, child:%s, " - "next_child:%s, file_id:%d", - child->to_string(file_header_).c_str(), - next_child->to_string(file_header_).c_str(), - file_id_); - disk_buffer_pool_->unpin_page(&next_child_handle); - disk_buffer_pool_->unpin_page(&child_handle); - return false; + if (insert_position) { + *insert_position = i; } + + return i - 1; } - disk_buffer_pool_->unpin_page(&next_child_handle); - disk_buffer_pool_->unpin_page(&child_handle); } + if (found) { + *found = false; + } + if (insert_position) { + *insert_position = size; + } + return size - 1; +} - return true; +char *InternalIndexNodeHandler::key_at(int index) +{ + assert(index >= 0 && index < size()); + return __key_at(index); +} + +void InternalIndexNodeHandler::set_key_at(int index, const char *key) +{ + assert(index >= 0 && index < size()); + memcpy(__key_at(index), key, key_size()); +} + +PageNum InternalIndexNodeHandler::value_at(int index) +{ + assert(index >= 0 && index < size()); + return *(PageNum *)__value_at(index); +} + +int InternalIndexNodeHandler::value_index(PageNum page_num) +{ + for (int i = 0; i < size(); i++) { + if (page_num == *(PageNum*)__value_at(i)) { + return i; + } + } + return -1; +} + +void InternalIndexNodeHandler::remove(int index) +{ + assert(index >= 0 && index < size()); + if (index < size() - 1) { + memmove(__item_at(index), __item_at(index + 1), (size() - index - 1) * item_size()); + } + increase_size(-1); +} + +RC InternalIndexNodeHandler::move_to(InternalIndexNodeHandler &other, DiskBufferPool *disk_buffer_pool, int file_id) +{ + RC rc = other.copy_from(__item_at(0), size(), disk_buffer_pool, file_id); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to copy items to other node. rc=%d:%s", rc, strrc(rc)); + return rc; + } + + increase_size(- this->size()); + return RC::SUCCESS; } -bool BplusTreeHandler::validate_leaf_link() +RC InternalIndexNodeHandler::move_first_to_end(InternalIndexNodeHandler &other, DiskBufferPool *disk_buffer_pool, int file_id) { - BPPageHandle first_leaf_handle; - IndexNode *first_leaf = root_node_; - PageNum first_page; - RC rc; - - while (first_leaf->is_leaf == false) { - if (first_leaf_handle.open) { - disk_buffer_pool_->unpin_page(&first_leaf_handle); - } - first_page = first_leaf->rids[0].page_num; - rc = disk_buffer_pool_->get_this_page(file_id_, first_page, &first_leaf_handle); - if (rc != RC::SUCCESS) { - return false; - } - - char *pdata; - disk_buffer_pool_->get_data(&first_leaf_handle, &pdata); - first_leaf = get_index_node(pdata); + RC rc = other.append(__item_at(0), disk_buffer_pool, file_id); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to append item to others."); + return rc; } - if (first_leaf_handle.open == false) { - // only root node - if (first_leaf->prev_brother != -1 || first_leaf->next_brother != -1) { - LOG_WARN("root node is the only node, but either root node's previous brother or next brother is wrong, root:%s, " - "file_id:%s", - first_leaf->to_string(file_header_).c_str(), - file_id_); - return false; - } - return true; + if (size() >= 1) { + memmove(__item_at(0), __item_at(1), (size() - 1) * item_size() ); } + increase_size(-1); + return rc; +} - if (first_leaf->prev_brother != -1 || first_leaf->next_brother == -1) { - LOG_WARN("First leaf is invalid, node:%s, file_id:%d", first_leaf->to_string(file_header_).c_str(), file_id_); - disk_buffer_pool_->unpin_page(&first_leaf_handle); - return false; +RC InternalIndexNodeHandler::move_last_to_front(InternalIndexNodeHandler &other, DiskBufferPool *bp, int file_id) +{ + RC rc = other.preappend(__item_at(size() - 1), bp, file_id); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to preappend to others"); + return rc; } - BPPageHandle last_leaf_handle; - IndexNode *last_leaf = root_node_; - PageNum last_page = -1; + increase_size(-1); + return rc; +} +/** + * copy items from other node to self's right + */ +RC InternalIndexNodeHandler::copy_from(const char *items, int num, DiskBufferPool *disk_buffer_pool, int file_id) +{ + memcpy(__item_at(this->size()), items, num * item_size()); - while (last_leaf->is_leaf == false) { - if (last_leaf_handle.open) { - disk_buffer_pool_->unpin_page(&last_leaf_handle); - } - last_page = last_leaf->rids[last_leaf->key_num].page_num; - rc = disk_buffer_pool_->get_this_page(file_id_, last_page, &last_leaf_handle); + RC rc = RC::SUCCESS; + PageNum this_page_num = this->page_num(); + BPPageHandle page_handle; + for (int i = 0; i < num; i++) { + const PageNum page_num = *(const PageNum *)((items + i * item_size()) + key_size()); + rc = disk_buffer_pool->get_this_page(file_id, page_num, &page_handle); if (rc != RC::SUCCESS) { - disk_buffer_pool_->unpin_page(&first_leaf_handle); - return false; + LOG_WARN("failed to set child's page num. child page num:%d, this page num=%d, rc=%d:%s", + page_num, this_page_num, rc, strrc(rc)); + return rc; } - - char *pdata; - disk_buffer_pool_->get_data(&last_leaf_handle, &pdata); - last_leaf = get_index_node(pdata); + IndexNodeHandler child_node(header_, page_handle); + child_node.set_parent_page_num(this_page_num); + page_handle.mark_dirty(); + disk_buffer_pool->unpin_page(&page_handle); } + increase_size(num); + return rc; +} - if (last_page == -1) { - LOG_WARN("The last leaf is invalid, last leaf is root:%s, file_id:%d", - last_leaf->to_string(file_header_).c_str(), - file_id_); - disk_buffer_pool_->unpin_page(&first_leaf_handle); - return false; - } +RC InternalIndexNodeHandler::append(const char *item, DiskBufferPool *bp, int file_id) +{ + return this->copy_from(item, 1, bp, file_id); +} - if (last_leaf->next_brother != -1 || last_leaf->prev_brother == -1) { - LOG_WARN( - "The last leaf is invalid, last leaf:%s, file_id:%d", last_leaf->to_string(file_header_).c_str(), file_id_); - disk_buffer_pool_->unpin_page(&first_leaf_handle); - disk_buffer_pool_->unpin_page(&last_leaf_handle); - return false; +RC InternalIndexNodeHandler::preappend(const char *item, DiskBufferPool *bp, int file_id) +{ + PageNum child_page_num = *(PageNum *)(item + key_size()); + BPPageHandle page_handle; + RC rc = bp->get_this_page(file_id, child_page_num, &page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch child page. rc=%d:%s", rc, strrc(rc)); + return rc; } - std::set leaf_pages; - leaf_pages.insert(first_page); + IndexNodeHandler child_node(header_, page_handle); + child_node.set_parent_page_num(this->page_num()); - BPPageHandle current_handle; - IndexNode *cur_node = first_leaf; - PageNum cur_page = first_page; + page_handle.mark_dirty(); + bp->unpin_page(&page_handle); - BPPageHandle next_handle; - IndexNode *next_node = nullptr; - PageNum next_page = cur_node->next_brother; + if (this->size() > 0) { + memmove(__item_at(1), __item_at(0), this->size() * item_size()); + } - bool found = false; - bool ret = false; + memcpy(__item_at(0), item, item_size()); + increase_size(1); + return RC::SUCCESS; +} - while (next_page != -1) { - rc = disk_buffer_pool_->get_this_page(file_id_, next_page, &next_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to check leaf link "); - goto cleanup; - } +char *InternalIndexNodeHandler::__item_at(int index) const +{ + return internal_node_->array + (index * item_size()); +} - char *pdata; - disk_buffer_pool_->get_data(&next_handle, &pdata); - next_node = get_index_node(pdata); - - if (cur_node->next_brother != next_page || next_node->prev_brother != cur_page) { - LOG_WARN("The leaf 's next brother or the next leaf's previous brother isn't correct, child:%s, next_child:%s, " - "file_id:%d", - cur_node->to_string(file_header_).c_str(), - next_node->to_string(file_header_).c_str(), - file_id_); - disk_buffer_pool_->unpin_page(&next_handle); - goto cleanup; - } +char *InternalIndexNodeHandler::__key_at(int index) const +{ + return __item_at(index); +} - if (next_page == last_page) { - found = true; - disk_buffer_pool_->unpin_page(&next_handle); - break; - } +char *InternalIndexNodeHandler::__value_at(int index) const +{ + return __item_at(index) + key_size(); +} - if (leaf_pages.find(next_page) != leaf_pages.end()) { - LOG_WARN( - "Leaf links occur loop, current node:%s, file_id:%d", cur_node->to_string(file_header_).c_str(), file_id_); - disk_buffer_pool_->unpin_page(&next_handle); - goto cleanup; - } else { - leaf_pages.insert(next_page); - } +int InternalIndexNodeHandler::value_size() const +{ + return sizeof(PageNum); +} - if (current_handle.open) { - disk_buffer_pool_->unpin_page(¤t_handle); - } - current_handle = next_handle; - cur_node = next_node; - cur_page = next_page; - next_page = cur_node->next_brother; - } +int InternalIndexNodeHandler::item_size() const +{ + return key_size() + this->value_size(); +} - if (found == true) { - ret = true; +bool InternalIndexNodeHandler::validate(const KeyComparator &comparator, DiskBufferPool *bp, int file_id) const +{ + bool result = IndexNodeHandler::validate(); + if (false == result) { + return false; } -cleanup: - if (first_leaf_handle.open) { - disk_buffer_pool_->unpin_page(&first_leaf_handle); + const int node_size = size(); + for (int i = 2; i < node_size; i++) { + if (comparator(__key_at(i - 1), __key_at(i)) >= 0) { + LOG_WARN("page number = %d, invalid key order. id1=%d,id2=%d, this=%s", + page_num(), i-1, i, to_string(*this).c_str()); + return false; + } } - if (last_leaf_handle.open) { - disk_buffer_pool_->unpin_page(&last_leaf_handle); + for (int i = 0; result && i < node_size; i++) { + PageNum page_num = *(PageNum *)__value_at(i); + if (page_num < 0) { + LOG_WARN("this page num=%d, got invalid child page. page num=%d", this->page_num(), page_num); + } else { + BPPageHandle child_page_handle; + RC rc = bp->get_this_page(file_id, page_num, &child_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch child page while validate internal page. page num=%d, rc=%d:%s", + page_num, rc, strrc(rc)); + } else { + IndexNodeHandler child_node(header_, child_page_handle); + if (child_node.parent_page_num() != this->page_num()) { + LOG_WARN("child's parent page num is invalid. child page num=%d, parent page num=%d, this page num=%d", + child_node.page_num(), child_node.parent_page_num(), this->page_num()); + result = false; + } + bp->unpin_page(&child_page_handle); + } + } } - if (current_handle.open) { - disk_buffer_pool_->unpin_page(¤t_handle); + if (!result) { + return result; } - return ret; -} + const PageNum parent_page_num = this->parent_page_num(); + if (parent_page_num == BP_INVALID_PAGE_NUM) { + return result; + } -bool BplusTreeHandler::validate_tree() -{ - IndexNode *node = root_node_; - if (validate_node(node) == false || validate_leaf_link() == false) { - LOG_WARN("Current B+ Tree is invalid"); - print_tree(); + BPPageHandle parent_page_handle; + RC rc = bp->get_this_page(file_id, parent_page_num, &parent_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch parent page. page num=%d, rc=%d:%s", parent_page_num, rc, strrc(rc)); return false; } - return true; -} -RC BplusTreeHandler::find_leaf(const char *pkey, PageNum *leaf_page) -{ - BPPageHandle page_handle; - IndexNode *node = root_node_; - while (false == node->is_leaf) { - char *pdata; - int i; - //for (i = 0; i < node->key_num; i++) { - // int tmp = - // key_compare(file_header_.attr_type, file_header_.attr_length, pkey, node->keys + i * file_header_.key_length); - // if (tmp < 0) - // break; - //} - i = lower_bound(file_header_.attr_type, file_header_.attr_length, node->keys, file_header_.key_length, node->key_num, pkey); - - if (page_handle.open == true) { - disk_buffer_pool_->unpin_page(&page_handle); - } + InternalIndexNodeHandler parent_node(header_, parent_page_handle); + int index_in_parent = parent_node.value_index(this->page_num()); + if (index_in_parent < 0) { + LOG_WARN("invalid internal node. cannot find index in parent. this page num=%d, parent page num=%d", + this->page_num(), parent_page_num); + bp->unpin_page(&parent_page_handle); + return false; + } - RC rc = disk_buffer_pool_->get_this_page(file_id_, node->rids[i].page_num, &page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to load page file_id:%d, page_num:%d", file_id_, node->rids[i].page_num); - return rc; + if (0 != index_in_parent) { + int cmp_result = comparator(__key_at(1), parent_node.key_at(index_in_parent)); + if (cmp_result < 0) { + LOG_WARN("invalid internal node. the second item should be greate than or equal to parent item. " \ + "this page num=%d, parent page num=%d, index in parent=%d", + this->page_num(), parent_node.page_num(), index_in_parent); + bp->unpin_page(&parent_page_handle); + return false; } - disk_buffer_pool_->get_data(&page_handle, &pdata); - - node = get_index_node(pdata); } - if (page_handle.open == false) { - *leaf_page = file_header_.root_page; - return RC::SUCCESS; + if (index_in_parent < parent_node.size() - 1) { + int cmp_result = comparator(__key_at(size() - 1), parent_node.key_at(index_in_parent + 1)); + if (cmp_result >= 0) { + LOG_WARN("invalid internal node. last item should be less than the item at the first after item in parent." \ + "this page num=%d, parent page num=%d, parent item to compare=%d", + this->page_num(), parent_node.page_num(), index_in_parent + 1); + bp->unpin_page(&parent_page_handle); + return false; + } } + bp->unpin_page(&parent_page_handle); - disk_buffer_pool_->get_page_num(&page_handle, leaf_page); - disk_buffer_pool_->unpin_page(&page_handle); - - return RC::SUCCESS; + return result; } -RC BplusTreeHandler::insert_entry_into_node(IndexNode *node, const char *pkey, const RID *rid, PageNum left_page) -{ - int insert_pos = 0, tmp; - - //for (; insert_pos < node->key_num; insert_pos++) { - // tmp = key_compare( - // file_header_.attr_type, file_header_.attr_length, pkey, node->keys + insert_pos * file_header_.key_length); - // if (tmp == 0) { - // LOG_TRACE("Insert into %d occur duplicated key, rid:%s.", file_id_, node->rids[insert_pos].to_string().c_str()); - // return RC::RECORD_DUPLICATE_KEY; - // } - // if (tmp < 0) - // break; - //} - - bool found = false; - insert_pos = lower_bound(file_header_.attr_type, file_header_.attr_length, node->keys, file_header_.key_length, - node->key_num, pkey, &found); - if (found) { - LOG_TRACE("Insert into %d occur duplicated key, rid:%s.", file_id_, node->rids[insert_pos].to_string().c_str()); - return RC::RECORD_DUPLICATE_KEY; - } - - char *from = node->keys + insert_pos * file_header_.key_length; - char *to = from + file_header_.key_length; - int len = (node->key_num - insert_pos) * file_header_.key_length; - memmove(to, from, len); - memcpy(node->keys + insert_pos * file_header_.key_length, pkey, file_header_.key_length); - - if (node->is_leaf) { - len = (node->key_num - insert_pos) * sizeof(RID); - memmove(node->rids + insert_pos + 1, node->rids + insert_pos, len); - memcpy(node->rids + insert_pos, rid, sizeof(RID)); - - change_leaf_parent_key_insert(node, insert_pos, left_page); - } else { - - len = (node->key_num - insert_pos) * sizeof(RID); - memmove(node->rids + insert_pos + 2, node->rids + insert_pos + 1, len); - memcpy(node->rids + insert_pos + 1, rid, sizeof(RID)); - } +///////////////////////////////////////////////////////////////////////////////// - node->key_num++; //叶子结点增加一条记录 - return RC::SUCCESS; +RC BplusTreeHandler::sync() +{ + return disk_buffer_pool_->purge_all_pages(file_id_); } -RC BplusTreeHandler::split_leaf(BPPageHandle &leaf_page_handle) +RC BplusTreeHandler::create(const char *file_name, AttrType attr_type, int attr_length, + int internal_max_size /* = -1*/, int leaf_max_size /* = -1 */) { - PageNum leaf_page; - disk_buffer_pool_->get_page_num(&leaf_page_handle, &leaf_page); - - char *pdata; - RC rc = disk_buffer_pool_->get_data(&leaf_page_handle, &pdata); + DiskBufferPool *disk_buffer_pool = theGlobalDiskBufferPool(); + RC rc = disk_buffer_pool->create_file(file_name); if (rc != RC::SUCCESS) { + LOG_WARN("Failed to create file. file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); return rc; } - IndexNode *old_node = get_index_node(pdata); - - char *new_parent_key = (char *)mem_pool_item_->alloc(); - if (new_parent_key == nullptr) { - LOG_WARN("Failed to alloc memory for new key. size=%d", file_header_.key_length); - return RC::NOMEM; - } + LOG_INFO("Successfully create index file:%s", file_name); - // add a new node - BPPageHandle page_handle2; - rc = disk_buffer_pool_->allocate_page(file_id_, &page_handle2); + int file_id; + rc = disk_buffer_pool->open_file(file_name, &file_id); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to split index page due to failed to allocate page, file_id:%d ", file_id_); + LOG_WARN("Failed to open file. file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); return rc; } - PageNum new_page; - disk_buffer_pool_->get_page_num(&page_handle2, &new_page); - disk_buffer_pool_->get_data(&page_handle2, &pdata); - IndexNode *new_node = get_index_node(pdata); - new_node->init_empty(file_header_); - new_node->parent = old_node->parent; - new_node->prev_brother = leaf_page; - new_node->next_brother = old_node->next_brother; - old_node->next_brother = new_page; - - // begin to move data from leaf_node to new_node - split_node(old_node, new_node, leaf_page, new_page, new_parent_key); - disk_buffer_pool_->mark_dirty(&leaf_page_handle); - disk_buffer_pool_->mark_dirty(&page_handle2); - - PageNum parent_page = old_node->parent; - rc = insert_into_parent(parent_page, leaf_page_handle, new_parent_key, page_handle2); + LOG_INFO("Successfully open index file %s.", file_name); + + BPPageHandle header_page_handle; + rc = disk_buffer_pool->allocate_page(file_id, &header_page_handle); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to insert into parent of index %d", file_id_); - // restore status before insert into parent - // merge_nodes function will move left node into right node - merge_nodes(old_node, new_node, new_page, new_parent_key); - copy_node(old_node, new_node); - change_insert_leaf_link(old_node, new_node, leaf_page); - - mem_pool_item_->free(new_parent_key); - disk_buffer_pool_->unpin_page(&page_handle2); - disk_buffer_pool_->dispose_page(file_id_, new_page); + LOG_WARN("failed to allocate header page for bplus tree. rc=%d:%s", rc, strrc(rc)); + disk_buffer_pool->close_file(file_id); return rc; } - mem_pool_item_->free(new_parent_key); - disk_buffer_pool_->unpin_page(&page_handle2); - return RC::SUCCESS; -} - -RC BplusTreeHandler::insert_intern_node( - BPPageHandle &parent_page_handle, BPPageHandle &left_page_handle, BPPageHandle &right_page_handle, const char *pkey) -{ - PageNum left_page; - disk_buffer_pool_->get_page_num(&left_page_handle, &left_page); - PageNum right_page; - disk_buffer_pool_->get_page_num(&right_page_handle, &right_page); + if (header_page_handle.page_num() != FIRST_INDEX_PAGE) { + LOG_WARN("header page num should be %d but got %d. is it a new file : %s", + FIRST_INDEX_PAGE, header_page_handle.page_num(), file_name); + disk_buffer_pool->close_file(file_id); + return RC::INTERNAL; + } - char *pdata; - RC rc = disk_buffer_pool_->get_data(&parent_page_handle, &pdata); - if (rc != RC::SUCCESS) { - return rc; + if (internal_max_size < 0) { + internal_max_size = calc_internal_page_capacity(attr_length); } + if (leaf_max_size < 0) { + leaf_max_size = calc_leaf_page_capacity(attr_length); + } + char *pdata = header_page_handle.data(); + IndexFileHeader *file_header = (IndexFileHeader *)pdata; + file_header->attr_length = attr_length; + file_header->key_length = attr_length + sizeof(RID); + file_header->attr_type = attr_type; + file_header->internal_max_size = internal_max_size; + file_header->leaf_max_size = leaf_max_size; + file_header->root_page = BP_INVALID_PAGE_NUM; - IndexNode *node = get_index_node(pdata); + header_page_handle.mark_dirty(); + disk_buffer_pool->unpin_page(&header_page_handle); - RID rid; - rid.page_num = right_page; - rid.slot_num = BP_INVALID_PAGE_NUM; // change to invalid page num + disk_buffer_pool_ = disk_buffer_pool; + file_id_ = file_id; - insert_entry_into_node(node, pkey, &rid, right_page); + memcpy(&file_header_, pdata, sizeof(file_header_)); + header_dirty_ = false; - disk_buffer_pool_->mark_dirty(&parent_page_handle); + mem_pool_item_ = new common::MemPoolItem(file_name); + if (mem_pool_item_->init(file_header->key_length) < 0) { + LOG_WARN("Failed to init memory pool for index %s", file_name); + close(); + return RC::NOMEM; + } + key_comparator_.init(file_header->attr_type, file_header->attr_length); + key_printer_.init(file_header->attr_type, file_header->attr_length); + LOG_INFO("Successfully create index %s", file_name); return RC::SUCCESS; } -RC BplusTreeHandler::split_intern_node(BPPageHandle &inter_page_handle, const char *pkey) +RC BplusTreeHandler::open(const char *file_name) { - PageNum inter_page_num; - disk_buffer_pool_->get_page_num(&inter_page_handle, &inter_page_num); + if (file_id_ >= 0) { + LOG_WARN("%s has been opened before index.open.", file_name); + return RC::RECORD_OPENNED; + } - char *pdata; - RC rc = disk_buffer_pool_->get_data(&inter_page_handle, &pdata); + DiskBufferPool *disk_buffer_pool = theGlobalDiskBufferPool(); + int file_id = 0; + RC rc = disk_buffer_pool->open_file(file_name, &file_id); if (rc != RC::SUCCESS) { + LOG_WARN("Failed to open file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); return rc; } - IndexNode *inter_node = get_index_node(pdata); - - char *new_parent_key = (char *)mem_pool_item_->alloc(); - if (new_parent_key == nullptr) { - LOG_WARN("Failed to alloc memory for new key when split intern node index %d", file_id_); - return RC::NOMEM; - } - - // add a new node - BPPageHandle new_page_handle; - rc = disk_buffer_pool_->allocate_page(file_id_, &new_page_handle); + BPPageHandle page_handle; + rc = disk_buffer_pool->get_this_page(file_id, FIRST_INDEX_PAGE, &page_handle); if (rc != RC::SUCCESS) { - LOG_WARN("Faild to alloc new page when split inter node of index, file_id:%d", file_id_); - mem_pool_item_->free(new_parent_key); + LOG_WARN("Failed to get first page file name=%s, rc=%d:%s", file_name, rc, strrc(rc)); + disk_buffer_pool_->close_file(file_id); return rc; } - disk_buffer_pool_->get_data(&new_page_handle, &pdata); - - PageNum new_page; - disk_buffer_pool_->get_page_num(&new_page_handle, &new_page); - - IndexNode *new_node = get_index_node(pdata); - new_node->init_empty(file_header_); - new_node->is_leaf = false; - new_node->parent = inter_node->parent; - - split_node(inter_node, new_node, inter_page_num, new_page, new_parent_key); - disk_buffer_pool_->mark_dirty(&inter_page_handle); - disk_buffer_pool_->mark_dirty(&new_page_handle); + char *pdata = page_handle.data(); + memcpy(&file_header_, pdata, sizeof(IndexFileHeader)); + header_dirty_ = false; + disk_buffer_pool_ = disk_buffer_pool; + file_id_ = file_id; - // print(); - PageNum parent_page = inter_node->parent; - rc = insert_into_parent(parent_page, inter_page_handle, new_parent_key, new_page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to insert key to parents, file_id:%d", file_id_); - merge_nodes(inter_node, new_node, new_page, new_parent_key); - copy_node(inter_node, new_node); - change_children_parent(inter_node->rids, inter_node->key_num + 1, inter_page_num); + mem_pool_item_ = new common::MemPoolItem(file_name); + if (mem_pool_item_->init(file_header_.key_length) < 0) { + LOG_WARN("Failed to init memory pool for index %s", file_name); + close(); + return RC::NOMEM; + } - mem_pool_item_->free(new_parent_key); - disk_buffer_pool_->unpin_page(&new_page_handle); - disk_buffer_pool_->dispose_page(file_id_, new_page); + // close old page_handle + disk_buffer_pool->unpin_page(&page_handle); - return rc; - } - mem_pool_item_->free(new_parent_key); - disk_buffer_pool_->unpin_page(&new_page_handle); - return rc; + key_comparator_.init(file_header_.attr_type, file_header_.attr_length); + LOG_INFO("Successfully open index %s", file_name); + return RC::SUCCESS; } -RC BplusTreeHandler::insert_into_parent( - PageNum parent_page, BPPageHandle &left_page_handle, const char *pkey, BPPageHandle &right_page_handle) +RC BplusTreeHandler::close() { - if (parent_page == -1) { - return insert_into_new_root(left_page_handle, pkey, right_page_handle); - } - - BPPageHandle page_handle; - RC rc = disk_buffer_pool_->get_this_page(file_id_, parent_page, &page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get parent page file_id:%d, page:%d", file_id_, parent_page); - return rc; - } + if (file_id_ != -1) { - char *pdata; - disk_buffer_pool_->get_data(&page_handle, &pdata); - IndexNode *node = get_index_node(pdata); + disk_buffer_pool_->close_file(file_id_); + file_id_ = -1; - rc = insert_intern_node(page_handle, left_page_handle, right_page_handle, pkey); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to insert intern node of index :%d", file_id_); - return rc; - } - if (node->key_num > file_header_.order) { - rc = split_intern_node(page_handle, pkey); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to split intern node of index %d", file_id_); - int delete_index; - delete_entry_from_node(node, pkey, delete_index); - } + delete mem_pool_item_; + mem_pool_item_ = nullptr; } - disk_buffer_pool_->unpin_page(&page_handle); - return rc; + disk_buffer_pool_ = nullptr; + return RC::SUCCESS; } -void BplusTreeHandler::swith_root(BPPageHandle &new_root_page_handle, IndexNode *root, PageNum root_page) +RC BplusTreeHandler::print_leaf(BPPageHandle &page_handle) { - //@@@ TODO here should add lock - - disk_buffer_pool_->unpin_page(&root_page_handle_); - root_page_handle_ = new_root_page_handle; - root_node_ = root; - file_header_.root_page = root_page; - header_dirty_ = true; + LeafIndexNodeHandler leaf_node(file_header_, page_handle); + LOG_INFO("leaf node: %s", to_string(leaf_node, key_printer_).c_str()); + disk_buffer_pool_->unpin_page(&page_handle); + return RC::SUCCESS; } -/** - * Create one new root node - * @param left_page_handle - * @param pkey - * @param right_page_handle - * @return - */ -RC BplusTreeHandler::insert_into_new_root( - BPPageHandle &left_page_handle, const char *pkey, BPPageHandle &right_page_handle) +RC BplusTreeHandler::print_internal_node_recursive(BPPageHandle &page_handle) { - BPPageHandle new_root_page_handle; - RC rc = disk_buffer_pool_->allocate_page(file_id_, &new_root_page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to alloc new page for the new root node of index, file_id:%d", file_id_); - return rc; - } - - PageNum root_page; - disk_buffer_pool_->get_page_num(&new_root_page_handle, &root_page); - - // modify the left node - PageNum left_page; - char *pdata; - disk_buffer_pool_->get_page_num(&left_page_handle, &left_page); - disk_buffer_pool_->get_data(&left_page_handle, &pdata); - IndexNode *left = get_index_node(pdata); - left->parent = root_page; - disk_buffer_pool_->mark_dirty(&left_page_handle); - - // modify the right node - PageNum right_page; - disk_buffer_pool_->get_page_num(&right_page_handle, &right_page); - disk_buffer_pool_->get_data(&right_page_handle, &pdata); - IndexNode *right = get_index_node(pdata); - right->parent = root_page; - disk_buffer_pool_->mark_dirty(&right_page_handle); - - // handle the root node - disk_buffer_pool_->get_data(&new_root_page_handle, &pdata); - IndexNode *root = get_index_node(pdata); - root->init_empty(file_header_); - root->is_leaf = false; - root->key_num = 1; - memcpy(root->keys, pkey, file_header_.key_length); - - RID rid; - rid.page_num = left_page; - rid.slot_num = EMPTY_RID_SLOT_NUM; - memcpy(root->rids, &rid, sizeof(RID)); - rid.page_num = right_page; - rid.slot_num = EMPTY_RID_SLOT_NUM; - memcpy(root->rids + root->key_num, &rid, sizeof(RID)); + RC rc = RC::SUCCESS; + LOG_INFO("bplus tree. file header: %s", file_header_.to_string().c_str()); + InternalIndexNodeHandler internal_node(file_header_, page_handle); + LOG_INFO("internal node: %s", to_string(internal_node, key_printer_).c_str()); + + int node_size = internal_node.size(); + for (int i = 0; i < node_size; i++) { + PageNum page_num = internal_node.value_at(i); + BPPageHandle child_page_handle; + rc = disk_buffer_pool_->get_this_page(file_id_, page_num, &child_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch child page. page id=%d, rc=%d:%s", page_num, rc, strrc(rc)); + disk_buffer_pool_->unpin_page(&page_handle); + return rc; + } - disk_buffer_pool_->mark_dirty(&new_root_page_handle); - swith_root(new_root_page_handle, root, root_page); + IndexNodeHandler node(file_header_, child_page_handle); + if (node.is_leaf()) { + rc = print_leaf(child_page_handle); + } else { + rc = print_internal_node_recursive(child_page_handle); + } + if (rc != RC::SUCCESS) { + LOG_WARN("failed to print node. page id=%d, rc=%d:%s", child_page_handle.page_num(), rc, strrc(rc)); + disk_buffer_pool_->unpin_page(&page_handle); + return rc; + } + } + disk_buffer_pool_->unpin_page(&page_handle); return RC::SUCCESS; } -RC BplusTreeHandler::insert_entry(const char *pkey, const RID *rid) +RC BplusTreeHandler::print_tree() { - if (file_id_ < 0) { - LOG_WARN("Index isn't ready!"); - return RC::RECORD_CLOSED; + LOG_WARN("Index hasn't been created or opened, fail to print"); + return RC::SUCCESS; } - - if (pkey == nullptr || rid == nullptr) { - LOG_WARN("Invalid arguments, key is empty or rid is empty"); - return RC::INVALID_ARGUMENT; + if (is_empty()) { + LOG_INFO("tree is empty"); + return RC::SUCCESS; } - char *key = (char *)mem_pool_item_->alloc(); - if (key == nullptr) { - LOG_WARN("Failed to alloc memory for key. file_id:%d", file_id_); - return RC::NOMEM; + BPPageHandle page_handle; + PageNum page_num = file_header_.root_page; + RC rc = disk_buffer_pool_->get_this_page(file_id_, page_num, &page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch page. page id=%d, rc=%d:%s", page_num, rc, strrc(rc)); + return rc; + } + + IndexNodeHandler node(file_header_, page_handle); + if (node.is_leaf()) { + rc = print_leaf(page_handle); + } else { + rc = print_internal_node_recursive(page_handle); } - memcpy(key, pkey, file_header_.attr_length); - memcpy(key + file_header_.attr_length, rid, sizeof(*rid)); + return rc; +} - PageNum leaf_page; - RC rc = find_leaf(key, &leaf_page); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to find leaf file_id:%d, %s", file_id_, rid->to_string().c_str()); - mem_pool_item_->free(key); - return rc; +RC BplusTreeHandler::print_leafs() +{ + if (is_empty()) { + LOG_INFO("empty tree"); + return RC::SUCCESS; } BPPageHandle page_handle; - rc = disk_buffer_pool_->get_this_page(file_id_, leaf_page, &page_handle); + + RC rc = left_most_page(page_handle); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to load leaf file_id:%d, page_num:%d", file_id_, leaf_page); - mem_pool_item_->free(key); + LOG_WARN("failed to get left most page. rc=%d:%s", rc, strrc(rc)); return rc; } - char *pdata; - disk_buffer_pool_->get_data(&page_handle, &pdata); + while (page_handle.page_num() != BP_INVALID_PAGE_NUM) { + LeafIndexNodeHandler leaf_node(file_header_, page_handle); + LOG_INFO("leaf info: %s", to_string(leaf_node, key_printer_).c_str()); - IndexNode *leaf = get_index_node(pdata); - rc = insert_entry_into_node(leaf, key, rid, leaf_page); - if (rc != RC::SUCCESS) { - LOG_TRACE("Failed to insert into leaf of index %d, rid:%s", file_id_, rid->to_string().c_str()); + PageNum next_page_num = leaf_node.next_page(); disk_buffer_pool_->unpin_page(&page_handle); - mem_pool_item_->free(key); - return rc; - } - disk_buffer_pool_->mark_dirty(&page_handle); - if (leaf->key_num > file_header_.order) { + if (next_page_num == BP_INVALID_PAGE_NUM) { + break; + } - rc = split_leaf(page_handle); + rc = disk_buffer_pool_->get_this_page(file_id_, next_page_num, &page_handle); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to insert index of %d, failed to split for rid:%s", file_id_, rid->to_string().c_str()); - int delete_index = 0; - delete_entry_from_node(leaf, key, delete_index); - - disk_buffer_pool_->unpin_page(&page_handle); - mem_pool_item_->free(key); + LOG_WARN("failed to get next page. page id=%d, rc=%d:%s", next_page_num, rc, strrc(rc)); return rc; } } - - disk_buffer_pool_->unpin_page(&page_handle); - mem_pool_item_->free(key); - return RC::SUCCESS; + return rc; } -void BplusTreeHandler::get_entry_from_leaf( - IndexNode *node, const char *pkey, std::list &rids, bool &continue_check) +bool BplusTreeHandler::validate_node_recursive(BPPageHandle &page_handle) { - for (int i = node->key_num - 1; i >= 0; i--) { - int tmp = attribute_comp( - pkey, node->keys + (i * file_header_.key_length), file_header_.attr_type, file_header_.attr_length); - if (tmp < 0) { - if (continue_check == true) { - LOG_WARN("Something is wrong, the sequence is wrong."); - print_tree(); - continue_check = false; + bool result = true; + IndexNodeHandler node(file_header_, page_handle); + if (node.is_leaf()) { + LeafIndexNodeHandler leaf_node(file_header_, page_handle); + result = leaf_node.validate(key_comparator_, disk_buffer_pool_, file_id_); + } else { + InternalIndexNodeHandler internal_node(file_header_, page_handle); + result = internal_node.validate(key_comparator_, disk_buffer_pool_, file_id_); + for (int i = 0; result && i < internal_node.size(); i++) { + PageNum page_num = internal_node.value_at(i); + BPPageHandle child_page_handle; + RC rc = disk_buffer_pool_->get_this_page(file_id_, page_num, &child_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch child page.page id=%d, rc=%d:%s", page_num, rc, strrc(rc)); + result = false; break; - } else { - continue; } - } else if (tmp == 0) { - rids.push_back(node->rids[i]); - continue_check = true; - } else { - continue_check = false; - break; + + result = validate_node_recursive(child_page_handle); } } + + disk_buffer_pool_->unpin_page(&page_handle); + return result; } -RC BplusTreeHandler::get_entry(const char *pkey, std::list &rids) +bool BplusTreeHandler::validate_leaf_link() { - if (file_id_ < 0) { - LOG_WARN("Index isn't ready!"); - return RC::RECORD_CLOSED; + if (is_empty()) { + return true; } - char *key = (char *)mem_pool_item_->alloc(); - if (key == nullptr) { - LOG_WARN("Failed to alloc memory for key. size=%d", file_header_.key_length); - return RC::NOMEM; + BPPageHandle page_handle; + RC rc = left_most_page(page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch left most page. rc=%d:%s", rc, strrc(rc)); + return false; } - memcpy(key, pkey, file_header_.attr_length); - RC rc; + PageNum prev_page_num = BP_INVALID_PAGE_NUM; - BPPageHandle page_handle; - char *pdata; - IndexNode *node = root_node_; - while (false == node->is_leaf) { - - int i; - for (i = 0; i < node->key_num; i++) { - int tmp = attribute_comp( - pkey, node->keys + i * file_header_.key_length, file_header_.attr_type, file_header_.attr_length); - if (tmp < 0) - break; - } + LeafIndexNodeHandler leaf_node(file_header_, page_handle); + if (leaf_node.prev_page() != prev_page_num) { + LOG_WARN("invalid page. current_page_num=%d, prev page num should be %d but got %d", + page_handle.page_num(), prev_page_num, leaf_node.prev_page()); + return false; + } + PageNum next_page_num = leaf_node.next_page(); - if (page_handle.open == true) { - disk_buffer_pool_->unpin_page(&page_handle); - } + prev_page_num = page_handle.page_num(); + char *prev_key = (char *)mem_pool_item_->alloc(); + memcpy(prev_key, leaf_node.key_at(leaf_node.size() - 1), file_header_.key_length); + disk_buffer_pool_->unpin_page(&page_handle); - rc = disk_buffer_pool_->get_this_page(file_id_, node->rids[i].page_num, &page_handle); + bool result = true; + while (result && next_page_num != BP_INVALID_PAGE_NUM) { + rc = disk_buffer_pool_->get_this_page(file_id_, next_page_num, &page_handle); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to load page file_id:%d, page_num:%d", file_id_, node->rids[i].page_num); - return rc; + LOG_WARN("failed to fetch next page. page num=%d, rc=%d:%s", next_page_num, rc, strrc(rc)); + return false; } - disk_buffer_pool_->get_data(&page_handle, &pdata); - node = get_index_node(pdata); - } - - bool continue_check = false; - get_entry_from_leaf(node, key, rids, continue_check); - - while (continue_check == true) { - PageNum prev_brother = node->prev_brother; - if (prev_brother == EMPTY_RID_PAGE_NUM) { - break; + LeafIndexNodeHandler leaf_node(file_header_, page_handle); + if (leaf_node.prev_page() != prev_page_num) { + LOG_WARN("invalid page. current_page_num=%d, prev page num should be %d but got %d", + page_handle.page_num(), prev_page_num, leaf_node.prev_page()); + result = false; } - if (page_handle.open) { - disk_buffer_pool_->unpin_page(&page_handle); - } - - rc = disk_buffer_pool_->get_this_page(file_id_, prev_brother, &page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Skip load the previous page, file_id:%d", file_id_); - break; + if (key_comparator_(prev_key, leaf_node.key_at(0)) >= 0) { + LOG_WARN("invalid page. current first key is not bigger than last"); + result = false; } - disk_buffer_pool_->get_data(&page_handle, &pdata); - node = get_index_node(pdata); - - get_entry_from_leaf(node, key, rids, continue_check); - } - if (page_handle.open) { + next_page_num = leaf_node.next_page(); + memcpy(prev_key, leaf_node.key_at(leaf_node.size() - 1), file_header_.key_length); + prev_page_num = page_handle.page_num(); disk_buffer_pool_->unpin_page(&page_handle); } - mem_pool_item_->free(key); - return RC::SUCCESS; + + free_key(prev_key); + // can do more things + return result; } -void BplusTreeHandler::delete_entry_from_node(IndexNode *node, const int delete_index) +bool BplusTreeHandler::validate_tree() { - char *from = node->keys + (delete_index + 1) * file_header_.key_length; - char *to = from - file_header_.key_length; - int len = (node->key_num - delete_index - 1) * file_header_.key_length; - memmove(to, from, len); - - RID *from_rid = node->rids + (delete_index + 1); - RID *to_rid = from_rid - 1; - len = (node->key_num - delete_index - 1) * sizeof(RID); - if (node->is_leaf == false) { - len += sizeof(RID); + if (is_empty()) { + return true; } - memmove(to_rid, from_rid, len); - - node->key_num--; -} -RC BplusTreeHandler::get_parent_changed_index( - BPPageHandle &parent_handle, IndexNode *&parent, IndexNode *node, PageNum page_num, int &changed_index) -{ - RC rc = disk_buffer_pool_->get_this_page(file_id_, node->parent, &parent_handle); + BPPageHandle page_handle; + RC rc = disk_buffer_pool_->get_this_page(file_id_, file_header_.root_page, &page_handle); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to delete index, due to failed to get pareent page, file_id:%d, parent_page:%d", - file_id_, - node->parent); + LOG_WARN("failed to fetch root page. page id=%d, rc=%d:%s", file_header_.root_page, rc, strrc(rc)); return rc; } - char *pdata; - disk_buffer_pool_->get_data(&parent_handle, &pdata); - parent = get_index_node(pdata); - - while (changed_index <= parent->key_num) { - if ((parent->rids[changed_index].page_num) == page_num) - break; - changed_index++; - } - if (changed_index == parent->key_num + 1) { - LOG_WARN("Something is wrong, failed to find the target page %d in parent, node:%s file_id:%d", - page_num, - node->to_string(file_header_).c_str(), - file_id_); + if (!validate_node_recursive(page_handle) || !validate_leaf_link()) { + LOG_WARN("Current B+ Tree is invalid"); print_tree(); - return RC::RECORD_CLOSED; + return false; } - return RC::SUCCESS; + LOG_INFO("great! current tree is valid"); + return true; } -RC BplusTreeHandler::change_leaf_parent_key_insert(IndexNode *node, int changed_indx, PageNum page_num) +bool BplusTreeHandler::is_empty() const { - if (changed_indx != 0) { - return RC::SUCCESS; - } - - if (node->is_leaf == false) { - return RC::SUCCESS; - } - - if (node->parent == -1) { - return RC::SUCCESS; - } - - if (node->key_num == 0) { - return RC::SUCCESS; - } - - if (node->prev_brother == -1) { - return RC::SUCCESS; - } - - int parent_changed_index = 0; - BPPageHandle parent_handle; - IndexNode *parent = nullptr; + return file_header_.root_page == BP_INVALID_PAGE_NUM; +} - RC rc = get_parent_changed_index(parent_handle, parent, node, page_num, parent_changed_index); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get parent's delete index, file_id:%d, child's page_num:%d", file_id_, page_num); - if (parent_handle.open) { - disk_buffer_pool_->unpin_page(&parent_handle); - return rc; - } - } - if (parent_changed_index > 0) { - memcpy(parent->keys + (parent_changed_index - 1) * file_header_.key_length, node->keys, file_header_.key_length); - } +RC BplusTreeHandler::find_leaf(const char *key, BPPageHandle &page_handle) +{ + return find_leaf_internal( + [&](InternalIndexNodeHandler &internal_node) { + return internal_node.value_at(internal_node.lookup(key_comparator_, key)); + }, + page_handle); +} - disk_buffer_pool_->unpin_page(&parent_handle); - return RC::SUCCESS; +RC BplusTreeHandler::left_most_page(BPPageHandle &page_handle) +{ + return find_leaf_internal( + [&](InternalIndexNodeHandler &internal_node) { + return internal_node.value_at(0); + }, + page_handle + ); } -RC BplusTreeHandler::change_leaf_parent_key_delete(IndexNode *leaf, int delete_indx, const char *old_first_key) +RC BplusTreeHandler::right_most_page(BPPageHandle &page_handle) { - if (delete_indx != 0) { - return RC::SUCCESS; - } - - if (leaf->is_leaf == false) { - return RC::SUCCESS; - } + return find_leaf_internal( + [&](InternalIndexNodeHandler &internal_node) { + return internal_node.value_at(internal_node.size() - 1); + }, + page_handle + ); +} - if (leaf->parent == -1) { - return RC::SUCCESS; +RC BplusTreeHandler::find_leaf_internal(const std::function &child_page_getter, + BPPageHandle &page_handle) +{ + if (is_empty()) { + return RC::EMPTY; } - if (leaf->prev_brother == -1) { - return RC::SUCCESS; + RC rc = disk_buffer_pool_->get_this_page(file_id_, file_header_.root_page, &page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch root page. page id=%d, rc=%d:%s", file_header_.root_page, rc, strrc(rc)); + return rc; } - if (leaf->key_num == 0) { - return RC::SUCCESS; - } + IndexNode *node = (IndexNode *)page_handle.data(); + while (false == node->is_leaf) { + InternalIndexNodeHandler internal_node(file_header_, page_handle); + PageNum page_num = child_page_getter(internal_node); - IndexNode *node = leaf; - bool found = false; - while (node->parent != -1) { - int index = 0; - BPPageHandle parent_handle; + disk_buffer_pool_->unpin_page(&page_handle); - RC rc = disk_buffer_pool_->get_this_page(file_id_, node->parent, &parent_handle); + rc = disk_buffer_pool_->get_this_page(file_id_, page_num, &page_handle); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to delete index, due to failed to get pareent page, file_id:%d, parent_page:%d", - file_id_, - node->parent); + LOG_WARN("Failed to load page file_id:%d, page_num:%d", file_id_, page_num); return rc; } - char *pdata; - disk_buffer_pool_->get_data(&parent_handle, &pdata); - node = get_index_node(pdata); - - int tmp = 0; - while (index < node->key_num) { - tmp = key_compare(file_header_.attr_type, - file_header_.attr_length, - old_first_key, - node->keys + index * file_header_.key_length); - if (tmp == 0) { - found = true; - memcpy(node->keys + index * file_header_.key_length, leaf->keys, file_header_.key_length); - break; - } else if (tmp > 0) { - index++; - continue; - } else { - break; - } - } - - disk_buffer_pool_->unpin_page(&parent_handle); - - if (found == true) { - return RC::SUCCESS; - } + node = (IndexNode *)page_handle.data(); } - if (found == false) { - LOG_INFO("The old fist key has been changed, leaf:%s", leaf->to_string(file_header_).c_str()); - print_tree(); - } return RC::SUCCESS; } -RC BplusTreeHandler::delete_entry_from_node(IndexNode *node, const char *pkey, int &node_delete_index) -{ - int delete_index, tmp; - for (delete_index = 0; delete_index < node->key_num; delete_index++) { - tmp = key_compare( - file_header_.attr_type, file_header_.attr_length, pkey, node->keys + delete_index * file_header_.key_length); - if (tmp == 0) { - node_delete_index = delete_index; - break; - } - } - if (delete_index >= node->key_num) { - // LOG_WARN("Failed to delete index of %d", file_id_); - return RC::RECORD_INVALID_KEY; +RC BplusTreeHandler::insert_entry_into_leaf_node(BPPageHandle &page_handle, const char *key, const RID *rid) +{ + LeafIndexNodeHandler leaf_node(file_header_, page_handle); + bool exists = false; + int insert_position = leaf_node.lookup(key_comparator_, key, &exists); + if (exists) { + LOG_TRACE("entry exists"); + return RC::RECORD_DUPLICATE_KEY; } - delete_entry_from_node(node, delete_index); - - // change parent's key - change_leaf_parent_key_delete(node, delete_index, pkey); - return RC::SUCCESS; -} - -RC BplusTreeHandler::change_insert_leaf_link(IndexNode *left, IndexNode *right, PageNum right_page) -{ - if (left->is_leaf == false) { + if (leaf_node.size() < leaf_node.max_size()) { + leaf_node.insert(insert_position, key, (const char *)rid); + page_handle.mark_dirty(); + disk_buffer_pool_->unpin_page(&page_handle); return RC::SUCCESS; } - if (right->next_brother != -1) { - PageNum next_right_page = right->next_brother; - BPPageHandle next_right_handle; - RC rc = disk_buffer_pool_->get_this_page(file_id_, next_right_page, &next_right_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to set link for leaf for node %s, file_id:%d", file_id_, right->to_string(file_header_).c_str()); - return rc; - } - - char *pdata; - disk_buffer_pool_->get_data(&next_right_handle, &pdata); - IndexNode *next_right = get_index_node(pdata); - next_right->prev_brother = right_page; - disk_buffer_pool_->mark_dirty(&next_right_handle); - disk_buffer_pool_->unpin_page(&next_right_handle); + BPPageHandle new_page_handle; + RC rc = split(page_handle, new_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to split leaf node. rc=%d:%s", rc, strrc(rc)); + return rc; } - return RC::SUCCESS; -} - -RC BplusTreeHandler::change_delete_leaf_link(IndexNode *left, IndexNode *right, PageNum right_page) -{ - if (left->is_leaf == false) { - return RC::SUCCESS; - } + LeafIndexNodeHandler new_index_node(file_header_, new_page_handle); + new_index_node.set_prev_page(page_handle.page_num()); + new_index_node.set_next_page(leaf_node.next_page()); + new_index_node.set_parent_page_num(leaf_node.parent_page_num()); + leaf_node.set_next_page(new_page_handle.page_num()); - right->prev_brother = left->prev_brother; - if (left->prev_brother != -1) { - PageNum prev_left_page = left->prev_brother; - BPPageHandle prev_left_handle; - RC rc = disk_buffer_pool_->get_this_page(file_id_, prev_left_page, &prev_left_handle); + PageNum next_page_num = new_index_node.next_page(); + if (next_page_num != BP_INVALID_PAGE_NUM) { + BPPageHandle next_page_handle; + rc = disk_buffer_pool_->get_this_page(file_id_, next_page_num, &next_page_handle); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to set link for leaf for node %s, file_id:%d", file_id_, right->to_string(file_header_).c_str()); + LOG_WARN("failed to fetch next page. page num=%d, rc=%d:%s", next_page_num, rc, strrc(rc)); return rc; } - char *pdata; - disk_buffer_pool_->get_data(&prev_left_handle, &pdata); - IndexNode *prev_left = get_index_node(pdata); - prev_left->next_brother = right_page; - disk_buffer_pool_->mark_dirty(&prev_left_handle); - disk_buffer_pool_->unpin_page(&prev_left_handle); - } - - return RC::SUCCESS; -} - -/** - * merge left node into right node. - * @param parent_handle - * @param left_handle - * @param right_handle - * @param delete_index - * @return - */ -RC BplusTreeHandler::coalesce_node(BPPageHandle &parent_handle, BPPageHandle &left_handle, BPPageHandle &right_handle, - int delete_index, bool check_change_leaf_key, int node_delete_index, const char *pkey) -{ - PageNum left_page, right_page, parent_page; - IndexNode *left, *right, *parent; - char *pdata, *parent_key; - RC rc; - - disk_buffer_pool_->get_page_num(&left_handle, &left_page); - disk_buffer_pool_->get_data(&left_handle, &pdata); - left = get_index_node(pdata); - - disk_buffer_pool_->get_page_num(&right_handle, &right_page); - disk_buffer_pool_->get_data(&right_handle, &pdata); - right = get_index_node(pdata); - - parent_page = left->parent; - disk_buffer_pool_->get_data(&parent_handle, &pdata); - parent = get_index_node(pdata); - - parent_key = (char *)mem_pool_item_->alloc(); - if (parent_key == nullptr) { - LOG_WARN("Failed to alloc memory for key. size=%d", file_header_.key_length); - return RC::NOMEM; - } - - memcpy(parent_key, parent->keys + delete_index * file_header_.key_length, file_header_.key_length); - merge_nodes(left, right, right_page, parent_key); - disk_buffer_pool_->mark_dirty(&left_handle); - disk_buffer_pool_->mark_dirty(&right_handle); - - change_delete_leaf_link(left, right, right_page); - if (check_change_leaf_key) { - change_leaf_parent_key_delete(right, node_delete_index, pkey); + LeafIndexNodeHandler next_node(file_header_, next_page_handle); + next_node.set_prev_page(new_page_handle.page_num()); + disk_buffer_pool_->unpin_page(&next_page_handle); } - rc = delete_entry_internal(parent_page, parent_key); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to delete internal entry of index ", file_id_); - - // restore status - copy_node(left, right); - right->key_num = 0; - split_node(left, right, left_page, right_page, parent_key); - change_delete_leaf_link(left, right, left_page); - left->next_brother = right_page; - right->prev_brother = left_page; - - mem_pool_item_->free(parent_key); - return rc; + if (insert_position < leaf_node.size()) { + leaf_node.insert(insert_position, key, (const char *)rid); + } else { + new_index_node.insert(insert_position - leaf_node.size(), key, (const char *)rid); } - mem_pool_item_->free(parent_key); - return RC::SUCCESS; + return insert_entry_into_parent(page_handle, new_page_handle, new_index_node.key_at(0)); } -void BplusTreeHandler::change_children_parent(RID *rids, int rid_len, PageNum new_parent_page) +RC BplusTreeHandler::insert_entry_into_parent(BPPageHandle &page_handle, BPPageHandle &new_page_handle, const char *key) { - for (int i = 0; i < rid_len; i++) { - RID rid = rids[i]; + RC rc = RC::SUCCESS; - PageNum page_num = rid.page_num; + IndexNodeHandler node_handler(file_header_, page_handle); + IndexNodeHandler new_node_handler(file_header_, new_page_handle); + PageNum parent_page_num = node_handler.parent_page_num(); - BPPageHandle child_page_handle; - RC rc = disk_buffer_pool_->get_this_page(file_id_, page_num, &child_page_handle); + if (parent_page_num == BP_INVALID_PAGE_NUM) { + + // create new root page + BPPageHandle root_page; + rc = disk_buffer_pool_->allocate_page(file_id_, &root_page); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to load child page %d of index %d when change child's parent.", file_id_, page_num); - continue; + LOG_WARN("failed to allocate new root page. rc=%d:%s", rc, strrc(rc)); + return rc; } - char *pdata; - disk_buffer_pool_->get_data(&child_page_handle, &pdata); + InternalIndexNodeHandler root_node(file_header_, root_page); + root_node.init_empty(); + root_node.create_new_root(page_handle.page_num(), key, new_page_handle.page_num()); + node_handler.set_parent_page_num(root_page.page_num()); + new_node_handler.set_parent_page_num(root_page.page_num()); - IndexNode *child_node = get_index_node(pdata); - child_node->parent = new_parent_page; + page_handle.mark_dirty(); + new_page_handle.mark_dirty(); + disk_buffer_pool_->unpin_page(&page_handle); + disk_buffer_pool_->unpin_page(&new_page_handle); - disk_buffer_pool_->mark_dirty(&child_page_handle); - disk_buffer_pool_->unpin_page(&child_page_handle); - } -} + file_header_.root_page = root_page.page_num(); + update_root_page_num(); // TODO + root_page.mark_dirty(); + disk_buffer_pool_->unpin_page(&root_page); -/** - * merge left node into right node; - * - * This function is contrary to split_node - */ -void BplusTreeHandler::merge_nodes(IndexNode *left_node, IndexNode *right_node, PageNum right_page, char *parent_key) -{ - bool is_leaf = left_node->is_leaf; - int old_left_key_num = left_node->key_num; - int old_right_key_num = right_node->key_num; - int new_left_key_num = 0; - int new_right_key_num = old_left_key_num + old_right_key_num; - if (is_leaf == false) { - new_right_key_num++; - } - left_node->key_num = new_left_key_num; - right_node->key_num = new_right_key_num; - - if (is_leaf) { - int delta = new_right_key_num - old_right_key_num; - char *from = right_node->keys; - char *to = right_node->keys + delta * file_header_.key_length; - int len = old_right_key_num * file_header_.key_length; - memmove(to, from, len); - - RID *from_rid = right_node->rids; - RID *to_rid = right_node->rids + delta; - len = old_right_key_num * sizeof(RID); - memmove(to_rid, from_rid, len); - - from = left_node->keys; - to = right_node->keys; - len = old_left_key_num * file_header_.key_length; - memmove(to, from, len); - - from_rid = left_node->rids; - to_rid = right_node->rids; - len = old_left_key_num * sizeof(RID); - memmove(to_rid, from_rid, len); + return RC::SUCCESS; } else { - int delta = new_right_key_num - old_right_key_num; - char *from = right_node->keys; - char *to = right_node->keys + delta * file_header_.key_length; - int len = old_right_key_num * file_header_.key_length; - memmove(to, from, len); - RID *from_rid = right_node->rids; - RID *to_rid = right_node->rids + delta; - len = (old_right_key_num + 1) * sizeof(RID); - memmove(to_rid, from_rid, len); + BPPageHandle parent_page_handle; + rc = disk_buffer_pool_->get_this_page(file_id_, parent_page_num, &parent_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to insert entry into leaf. rc=%d:%s", rc, strrc(rc)); + // should do more things to recover + return rc; + } + + InternalIndexNodeHandler node(file_header_, parent_page_handle); - memcpy(right_node->keys + (delta - 1) * file_header_.key_length, parent_key, file_header_.key_length); + /// current node is not in full mode, insert the entry and return + if (node.size() < node.max_size()) { + node.insert(key, new_page_handle.page_num(), key_comparator_); + new_node_handler.set_parent_page_num(parent_page_num); - from = left_node->keys; - to = right_node->keys; - len = old_left_key_num * file_header_.key_length; - memmove(to, from, len); + page_handle.mark_dirty(); + new_page_handle.mark_dirty(); + parent_page_handle.mark_dirty(); + disk_buffer_pool_->unpin_page(&page_handle); + disk_buffer_pool_->unpin_page(&new_page_handle); + disk_buffer_pool_->unpin_page(&parent_page_handle); - from_rid = left_node->rids; - to_rid = right_node->rids; - len = (old_left_key_num + 1) * sizeof(RID); - memmove(to_rid, from_rid, len); + } else { - change_children_parent(to_rid, len / sizeof(RID), right_page); + // we should split the node and insert the entry and then insert new entry to current node's parent + BPPageHandle new_parent_page_handle; + rc = split(parent_page_handle, new_parent_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to split internal node. rc=%d:%s", rc, strrc(rc)); + disk_buffer_pool_->unpin_page(&page_handle); + disk_buffer_pool_->unpin_page(&new_page_handle); + disk_buffer_pool_->unpin_page(&parent_page_handle); + } else { + // insert into left or right ? decide by key compare result + InternalIndexNodeHandler new_node(file_header_, new_parent_page_handle); + if (key_comparator_(key, new_node.key_at(0)) > 0) { + new_node.insert(key, new_page_handle.page_num(), key_comparator_); + new_node_handler.set_parent_page_num(new_node.page_num()); + } else { + node.insert(key, new_page_handle.page_num(), key_comparator_); + new_node_handler.set_parent_page_num(node.page_num()); + } + + disk_buffer_pool_->unpin_page(&page_handle); + disk_buffer_pool_->unpin_page(&new_page_handle); + + rc = insert_entry_into_parent(parent_page_handle, new_parent_page_handle, new_node.key_at(0)); + } + } } + return rc; } /** - * split left node to two node - * This function is contrary to merge_node + * split one full node into two + * @param page_handle[inout] the node to split + * @param new_page_handle[out] the new node after split + * @param intert_position the intert position of new key */ -void BplusTreeHandler::split_node( - IndexNode *left_node, IndexNode *right_node, PageNum left_page, PageNum right_page, char *new_parent_key) -{ - bool is_leaf = left_node->is_leaf; - int old_left_key_num = left_node->key_num; - int old_right_key_num = right_node->key_num; // right_node->key_num should be zero - int total_key_num = left_node->key_num + right_node->key_num; - - int mid, new_left_key_num, new_right_key_num; - - /** - * if node is leaf, all key will be distributed both in left and right node - * if node is intern node, all keys except the middle key will be distributed both in the left and the right node - */ - if (is_leaf == true) { - new_left_key_num = total_key_num / 2; - mid = new_left_key_num; - new_right_key_num = total_key_num - mid; - } else { - new_left_key_num = (total_key_num - 1) / 2; - mid = new_left_key_num + 1; - new_right_key_num = (total_key_num - mid); - } - - left_node->key_num = new_left_key_num; - right_node->key_num = new_right_key_num; +template +RC BplusTreeHandler::split(BPPageHandle &page_handle, BPPageHandle &new_page_handle) +{ + IndexNodeHandlerType old_node(file_header_, page_handle); - if (is_leaf) { - memcpy(new_parent_key, left_node->keys + new_left_key_num * file_header_.key_length, file_header_.key_length); - } else { - memmove(new_parent_key, left_node->keys + new_left_key_num * file_header_.key_length, file_header_.key_length); + char *new_parent_key = (char *)mem_pool_item_->alloc(); + if (new_parent_key == nullptr) { + LOG_WARN("Failed to alloc memory for new key. size=%d", file_header_.key_length); + return RC::NOMEM; } - char *from = left_node->keys + mid * file_header_.key_length; - char *to = right_node->keys; - int len = new_right_key_num * file_header_.key_length; - memmove(to, from, len); - - RID *from_rid = left_node->rids + mid; - RID *to_rid = right_node->rids; - len = new_right_key_num * sizeof(RID); - memmove(to_rid, from_rid, len); + // add a new node + RC rc = disk_buffer_pool_->allocate_page(file_id_, &new_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("Failed to split index page due to failed to allocate page, file_id:%d. rc=%d:%s", file_id_, rc, strrc(rc)); + return rc; + } - // handle the last rid - if (is_leaf == false) { + IndexNodeHandlerType new_node(file_header_, new_page_handle); + new_node.init_empty(); + new_node.set_parent_page_num(old_node.parent_page_num()); - RID *changed_rids = to_rid; - int changed_rids_len = len; - PageNum changed_page = right_page; + old_node.move_half_to(new_node, disk_buffer_pool_, file_id_); - if (old_right_key_num == 0) { - memmove(right_node->rids + new_right_key_num, left_node->rids + old_left_key_num, sizeof(RID)); - changed_rids_len += sizeof(RID); - } + page_handle.mark_dirty(); + new_page_handle.mark_dirty(); + return RC::SUCCESS; +} - change_children_parent(changed_rids, changed_rids_len / sizeof(RID), changed_page); - } else { - change_insert_leaf_link(left_node, right_node, right_page); +RC BplusTreeHandler::update_root_page_num() +{ + BPPageHandle header_page_handle; + RC rc = disk_buffer_pool_->get_this_page(file_id_, FIRST_INDEX_PAGE, &header_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch header page. rc=%d:%s", rc, strrc(rc)); + return rc; } - return; + IndexFileHeader *header = (IndexFileHeader *)header_page_handle.data(); + header->root_page = file_header_.root_page; + header_page_handle.mark_dirty(); + disk_buffer_pool_->unpin_page(&header_page_handle); + return rc; } -void BplusTreeHandler::copy_node(IndexNode *to, IndexNode *from) -{ - memcpy(to->keys, from->keys, from->key_num * file_header_.key_length); - memcpy(to->rids, from->rids, (from->key_num + 1) * sizeof(RID)); - memcpy(to, from, sizeof(IndexNode)); -} -void BplusTreeHandler::redistribute_nodes( - IndexNode *left_node, IndexNode *right_node, PageNum left_page, PageNum right_page, char *parent_key) +RC BplusTreeHandler::create_new_tree(const char *key, const RID *rid) { - bool is_leaf = left_node->is_leaf; - int old_left_key_num = left_node->key_num; - int old_right_key_num = right_node->key_num; - int total_key_num = left_node->key_num + right_node->key_num; - if (is_leaf == false) { - total_key_num++; + RC rc = RC::SUCCESS; + if (file_header_.root_page != BP_INVALID_PAGE_NUM) { + rc = RC::INTERNAL; + LOG_WARN("cannot create new tree while root page is valid. root page id=%d", file_header_.root_page); + return rc; } - // mid represent the parent key's position - int mid, new_left_key_num, new_right_key_num; - - /** - * if node is leaf, all key will be distributed both in left and right node - * if node is intern node, all keys except the middle key will be distributed both in the left and the right node - */ - if (is_leaf == true) { - new_left_key_num = total_key_num / 2; - mid = new_left_key_num; - new_right_key_num = total_key_num - mid; - } else { - new_left_key_num = (total_key_num - 1) / 2; - mid = new_left_key_num + 1; - new_right_key_num = (total_key_num - mid); - } - - left_node->key_num = new_left_key_num; - right_node->key_num = new_right_key_num; - - RID *changed_rids = nullptr; - int changed_rids_len = 0; - PageNum changed_page = 0; - - int delta = old_left_key_num - new_left_key_num; - if (delta == 0) { - return; - } else if (delta > 0) { - // move kv from left to right - delta = new_right_key_num - old_right_key_num; - char *from = right_node->keys; - char *to = right_node->keys + delta * file_header_.key_length; - int len = old_right_key_num * file_header_.key_length; - memmove(to, from, len); - - RID *from_rid = right_node->rids; - RID *to_rid = right_node->rids + delta; - len = old_right_key_num * sizeof(RID); - if (left_node->is_leaf == false) { - len += sizeof(RID); - } - memmove(to_rid, from_rid, len); - - if (is_leaf == false) { - memcpy(left_node->keys + old_left_key_num * file_header_.key_length, parent_key, file_header_.key_length); - } - - delta = old_left_key_num - new_left_key_num; - - from = left_node->keys + mid * file_header_.key_length; - to = right_node->keys; - len = delta * file_header_.key_length; - memmove(to, from, len); - - from_rid = left_node->rids + mid; - to_rid = right_node->rids; - len = delta * sizeof(RID); - memmove(to_rid, from_rid, len); - - changed_rids = to_rid; - changed_rids_len = len; - changed_page = right_page; - - if (is_leaf) { - memcpy(parent_key, right_node->keys, file_header_.key_length); - } else { - memmove(parent_key, left_node->keys + new_left_key_num * file_header_.key_length, file_header_.key_length); - } - - } else { - // move kv from right to left - if (is_leaf == false) { - memcpy(left_node->keys + old_left_key_num * file_header_.key_length, parent_key, file_header_.key_length); - } - int start_pos = old_left_key_num; - int len = (new_left_key_num - old_left_key_num); - if (is_leaf == false) { - start_pos++; - len--; - } - - char *from = right_node->keys; - char *to = left_node->keys + start_pos * file_header_.key_length; - memmove(to, from, len * file_header_.key_length); - - RID *from_rid = right_node->rids; - RID *to_rid = left_node->rids + start_pos; - memmove(to_rid, from_rid, len * sizeof(RID)); - - changed_rids = to_rid; - changed_rids_len = (new_left_key_num - old_left_key_num) * sizeof(RID); - changed_page = left_page; - - if (is_leaf == false) { - memcpy(parent_key, right_node->keys + len * file_header_.key_length, file_header_.key_length); - memcpy(left_node->rids + new_left_key_num, right_node->rids + len, sizeof(RID)); - } else { - memcpy(parent_key, right_node->keys + len * file_header_.key_length, file_header_.key_length); - } - - delta = old_right_key_num - new_right_key_num; - from = right_node->keys + delta * file_header_.key_length; - to = right_node->keys; - len = new_right_key_num * file_header_.key_length; - memmove(to, from, len); - - from_rid = right_node->rids + delta; - to_rid = right_node->rids; - len = new_right_key_num * sizeof(RID); - if (left_node->is_leaf == false) { - len += sizeof(RID); - } - memmove(to_rid, from_rid, len); + BPPageHandle page_handle; + rc = disk_buffer_pool_->allocate_page(file_id_, &page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to allocate root page. rc=%d:%s", rc, strrc(rc)); + return rc; } - // handle the last rid - if (left_node->is_leaf == false) { - change_children_parent(changed_rids, changed_rids_len / sizeof(RID), changed_page); - } + LeafIndexNodeHandler leaf_node(file_header_, page_handle); + leaf_node.init_empty(); + leaf_node.insert(0, key, (const char *)rid); + file_header_.root_page = page_handle.page_num(); + page_handle.mark_dirty(); + disk_buffer_pool_->unpin_page(&page_handle); - return; + rc = update_root_page_num(); + // disk_buffer_pool_->check_all_pages_unpinned(file_id_); + return rc; } -RC BplusTreeHandler::redistribute_nodes( - BPPageHandle &parent_handle, BPPageHandle &left_handle, BPPageHandle &right_handle) +char *BplusTreeHandler::make_key(const char *user_key, const RID &rid) { + char *key = (char *)mem_pool_item_->alloc(); + if (key == nullptr) { + LOG_WARN("Failed to alloc memory for key. file_id:%d", file_id_); + return nullptr; + } + memcpy(key, user_key, file_header_.attr_length); + memcpy(key + file_header_.attr_length, &rid, sizeof(rid)); + return key; +} - PageNum left_page, right_page; - IndexNode *left, *right, *parent; - char *pdata; - - disk_buffer_pool_->get_page_num(&left_handle, &left_page); - disk_buffer_pool_->get_data(&left_handle, &pdata); - left = get_index_node(pdata); - - disk_buffer_pool_->get_page_num(&right_handle, &right_page); - disk_buffer_pool_->get_data(&right_handle, &pdata); - right = get_index_node(pdata); - - disk_buffer_pool_->get_data(&parent_handle, &pdata); - parent = get_index_node(pdata); - - int parent_change_pos = -1; - for (int k = 0; k < parent->key_num; k++) { +void BplusTreeHandler::free_key(char *key) +{ + mem_pool_item_->free(key); +} - if (parent->rids[k].page_num == left_page) { - parent_change_pos = k; - break; - } +RC BplusTreeHandler::insert_entry(const char *user_key, const RID *rid) +{ + if (file_id_ < 0) { + LOG_WARN("Index isn't ready!"); + return RC::RECORD_CLOSED; } - if (parent_change_pos == -1) { - LOG_WARN("Failed to find the parent pos during redistribute node"); - return RC::RECORD_INVALID_KEY; + if (user_key == nullptr || rid == nullptr) { + LOG_WARN("Invalid arguments, key is empty or rid is empty"); + return RC::INVALID_ARGUMENT; } - char *parent_key = parent->keys + parent_change_pos * file_header_.key_length; - redistribute_nodes(left, right, left_page, right_page, parent_key); - - disk_buffer_pool_->mark_dirty(&left_handle); - disk_buffer_pool_->mark_dirty(&right_handle); - disk_buffer_pool_->mark_dirty(&parent_handle); - - return RC::SUCCESS; -} + char *key = make_key(user_key, *rid); + if (key == nullptr) { + LOG_WARN("Failed to alloc memory for key. file_id:%d", file_id_); + return RC::NOMEM; + } -RC BplusTreeHandler::clean_root_after_delete(IndexNode *old_root) -{ - if (old_root->key_num > 0) { - return RC::SUCCESS; + if (is_empty()) { + return create_new_tree(key, rid); } - BPPageHandle root_handle; - RC rc = disk_buffer_pool_->get_this_page(file_id_, old_root->rids[0].page_num, &root_handle); + BPPageHandle page_handle; + RC rc = find_leaf(key, page_handle); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get new root page %d of index %d", old_root->rids[0].page_num, file_id_); + LOG_WARN("Failed to find leaf file_id:%d, %s. rc=%d:%s", file_id_, rid->to_string().c_str(), rc, strrc(rc)); + mem_pool_item_->free(key); return rc; } - char *pdata; - disk_buffer_pool_->get_data(&root_handle, &pdata); - IndexNode *root = get_index_node(pdata); - root->parent = -1; - disk_buffer_pool_->mark_dirty(&root_handle); - swith_root(root_handle, root, old_root->rids[0].page_num); - - return RC::SUCCESS; -} - -RC BplusTreeHandler::can_merge_with_other(BPPageHandle *page_handle, PageNum page_num, bool *can_merge) -{ - RC rc = disk_buffer_pool_->get_this_page(file_id_, page_num, page_handle); + rc = insert_entry_into_leaf_node(page_handle, key, rid); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to delete index, due to failed to get page of current delete page, file_id:%d, page:%d", - file_id_, - page_num); + LOG_TRACE("Failed to insert into leaf of index %d, rid:%s", file_id_, rid->to_string().c_str()); + disk_buffer_pool_->unpin_page(&page_handle); + mem_pool_item_->free(key); + // disk_buffer_pool_->check_all_pages_unpinned(file_id_); return rc; } - char *pdata; - disk_buffer_pool_->get_data(page_handle, &pdata); - IndexNode *node = get_index_node(pdata); - *can_merge = node->key_num > (file_header_.order / 2); + mem_pool_item_->free(key); + LOG_TRACE("insert entry success"); + // disk_buffer_pool_->check_all_pages_unpinned(file_id_); return RC::SUCCESS; } -RC BplusTreeHandler::delete_entry_internal(PageNum page_num, const char *pkey) +RC BplusTreeHandler::get_entry(const char *user_key, std::list &rids) { - BPPageHandle page_handle; - RC rc = disk_buffer_pool_->get_this_page(file_id_, page_num, &page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN( - "Failed to delete entry in index node, due to failed to get page!, file_id:%d, page:%d", file_id_, page_num); - return rc; + if (file_id_ < 0) { + LOG_WARN("Index isn't ready!"); + return RC::RECORD_CLOSED; } - char *pdata; - rc = disk_buffer_pool_->get_data(&page_handle, &pdata); + LOG_INFO("before get entry"); + disk_buffer_pool_->check_all_pages_unpinned(file_id_); + + BplusTreeScanner scanner(*this); + RC rc = scanner.open(user_key, true/*left_inclusive*/, user_key, true/*right_inclusive*/); if (rc != RC::SUCCESS) { + LOG_WARN("failed to open scanner. rc=%d:%s", rc, strrc(rc)); return rc; } - IndexNode *node = get_index_node(pdata); - int node_delete_index = -1; - rc = delete_entry_from_node(node, pkey, node_delete_index); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to delete index %d", file_id_); - return rc; + RID rid; + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + rids.push_back(rid); } - disk_buffer_pool_->mark_dirty(&page_handle); + scanner.close(); + if (rc != RC::RECORD_EOF) { + LOG_WARN("scanner return error. rc=%d:%s", rc, strrc(rc)); + } else { + rc = RC::SUCCESS; + } + LOG_INFO("after get entry"); + disk_buffer_pool_->check_all_pages_unpinned(file_id_); + return rc; +} - int min_key = file_header_.order / 2; - if (node->key_num >= min_key) { - disk_buffer_pool_->unpin_page(&page_handle); +RC BplusTreeHandler::adjust_root(BPPageHandle &root_page_handle) +{ + IndexNodeHandler root_node(file_header_, root_page_handle); + if (root_node.is_leaf() && root_node.size() > 0) { + root_page_handle.mark_dirty(); + disk_buffer_pool_->unpin_page(&root_page_handle); return RC::SUCCESS; } - if (node->parent == -1) { - if (node->key_num == 0 && node->is_leaf == false) { - rc = clean_root_after_delete(node); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to clean root after delete all entry in the root, file_id:%d", file_id_); - insert_entry_into_node(node, pkey, (RID *)(pkey + file_header_.attr_length), page_num); - disk_buffer_pool_->unpin_page(&page_handle); - return rc; - } - disk_buffer_pool_->unpin_page(&page_handle); - disk_buffer_pool_->dispose_page(file_id_, page_num); + if (root_node.is_leaf()) { + // this is a leaf and an empty node + file_header_.root_page = BP_INVALID_PAGE_NUM; + } else { + // this is an internal node and has only one child node + InternalIndexNodeHandler internal_node(file_header_, root_page_handle); - return RC::SUCCESS; + const PageNum child_page_num = internal_node.value_at(0); + BPPageHandle child_page_handle; + RC rc = disk_buffer_pool_->get_this_page(file_id_, child_page_num, &child_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch child page. page num=%d, rc=%d:%s", child_page_num, rc, strrc(rc)); + return rc; } - disk_buffer_pool_->unpin_page(&page_handle); - return RC::SUCCESS; + IndexNodeHandler child_node(file_header_, child_page_handle); + child_node.set_parent_page_num(BP_INVALID_PAGE_NUM); + disk_buffer_pool_->unpin_page(&child_page_handle); + + file_header_.root_page = child_page_num; } - int delete_index = 0; - BPPageHandle parent_handle; - IndexNode *parent = nullptr; + update_root_page_num(); - rc = get_parent_changed_index(parent_handle, parent, node, page_num, delete_index); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get parent delete index"); - insert_entry_into_node(node, pkey, (RID *)(pkey + file_header_.attr_length), page_num); + PageNum old_root_page_num = root_page_handle.page_num(); + disk_buffer_pool_->unpin_page(&root_page_handle); + disk_buffer_pool_->dispose_page(file_id_, old_root_page_num); + return RC::SUCCESS; +} +template +RC BplusTreeHandler::coalesce_or_redistribute(BPPageHandle &page_handle) +{ + IndexNodeHandlerType index_node(file_header_, page_handle); + if (index_node.size() >= index_node.min_size()) { disk_buffer_pool_->unpin_page(&page_handle); - return rc; + return RC::SUCCESS; } - bool can_merge_with_right = false; - bool force_collapse_with_right = false; - bool can_merge_with_left = false; - // bool force_collapse_with_left = false; - PageNum left_page = 0, right_page = 0; - BPPageHandle right_handle, left_handle; - if (delete_index == 0) { - right_page = parent->rids[delete_index + 1].page_num; - rc = can_merge_with_other(&right_handle, right_page, &can_merge_with_right); - if (rc != RC::SUCCESS) { - goto cleanup; - } - - if (can_merge_with_right == false) { - force_collapse_with_right = true; - } - } else { - left_page = parent->rids[delete_index - 1].page_num; - rc = can_merge_with_other(&left_handle, left_page, &can_merge_with_left); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed delete index, due to failed to get page, file_id:%d, page:%d", file_id_, left_page); - goto cleanup; - } - if (can_merge_with_left == false) { - // begin to merge with right - // force_collapse_with_left = true; - if (delete_index < parent->key_num) { - - right_page = parent->rids[delete_index + 1].page_num; - rc = can_merge_with_other(&right_handle, right_page, &can_merge_with_right); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to delete index, due to failed to get right page of current delete page, file_id:%d, " - "right_page:$d", - file_id_, - right_page); - - goto cleanup; - } - - } // delete_index < parent->key_num - 1 - } // if can_merge_with_left = false - } // delete_index = 0 - - if (can_merge_with_left) { - rc = redistribute_nodes(parent_handle, left_handle, page_handle); - } else if (can_merge_with_right) { - rc = redistribute_nodes(parent_handle, page_handle, right_handle); - change_leaf_parent_key_delete(node, node_delete_index, pkey); - } else if (force_collapse_with_right) { - rc = coalesce_node(parent_handle, page_handle, right_handle, delete_index, true, node_delete_index, pkey); - if (rc == RC::SUCCESS) { + const PageNum parent_page_num = index_node.parent_page_num(); + if (BP_INVALID_PAGE_NUM == parent_page_num) { + // this is the root page + if (index_node.size() > 1) { disk_buffer_pool_->unpin_page(&page_handle); - disk_buffer_pool_->dispose_page(file_id_, page_num); - page_handle.open = false; - } - } else { - rc = coalesce_node(parent_handle, left_handle, page_handle, delete_index - 1, false, node_delete_index, pkey); - if (rc == RC::SUCCESS) { - disk_buffer_pool_->unpin_page(&left_handle); - disk_buffer_pool_->dispose_page(file_id_, left_page); - left_handle.open = false; + } else { + // adjust the root node + adjust_root(page_handle); } + return RC::SUCCESS; } -cleanup: + BPPageHandle parent_page_handle; + RC rc = disk_buffer_pool_->get_this_page(file_id_, parent_page_num, &parent_page_handle); if (rc != RC::SUCCESS) { - insert_entry_into_node(node, pkey, (RID *)(pkey + file_header_.attr_length), page_num); - } - if (right_handle.open) { - disk_buffer_pool_->unpin_page(&right_handle); - } - if (left_handle.open) { - disk_buffer_pool_->unpin_page(&left_handle); - } - disk_buffer_pool_->unpin_page(&parent_handle); - if (page_handle.open) { + LOG_WARN("failed to fetch parent page. page id=%d, rc=%d:%s", parent_page_num, rc, strrc(rc)); disk_buffer_pool_->unpin_page(&page_handle); + return rc; } - return rc; -} - -RC BplusTreeHandler::delete_entry(const char *data, const RID *rid) -{ - if (file_id_ < 0) { - LOG_WARN("Failed to delete index entry, due to index is't ready"); - return RC::RECORD_CLOSED; + InternalIndexNodeHandler parent_index_node(file_header_, parent_page_handle); + int index = parent_index_node.lookup(key_comparator_, index_node.key_at(index_node.size() - 1)); + if (parent_index_node.value_at(index) != page_handle.page_num()) { + LOG_ERROR("lookup return an invalid value. index=%d, this page num=%d, but got %d", + index, page_handle.page_num(), parent_index_node.value_at(index)); } - - char *pkey = (char *)mem_pool_item_->alloc(); - if (nullptr == pkey) { - LOG_WARN("Failed to alloc memory for key. size=%d", file_header_.key_length); - return RC::NOMEM; + PageNum neighbor_page_num; + if (index == 0) { + neighbor_page_num = parent_index_node.value_at(1); + } else { + neighbor_page_num = parent_index_node.value_at(index - 1); } - memcpy(pkey, data, file_header_.attr_length); - memcpy(pkey + file_header_.attr_length, rid, sizeof(*rid)); - PageNum leaf_page; - RC rc = find_leaf(pkey, &leaf_page); + BPPageHandle neighbor_page_handle; + rc = disk_buffer_pool_->get_this_page(file_id_, neighbor_page_num, &neighbor_page_handle); if (rc != RC::SUCCESS) { - mem_pool_item_->free(pkey); + LOG_WARN("failed to fetch neighbor page. page id=%d, rc=%d:%s", neighbor_page_num, rc, strrc(rc)); + // TODO do more thing to release resource + disk_buffer_pool_->unpin_page(&page_handle); + disk_buffer_pool_->unpin_page(&parent_page_handle); return rc; } - rc = delete_entry_internal(leaf_page, pkey); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to delete index %d", file_id_); - mem_pool_item_->free(pkey); - return rc; + + IndexNodeHandlerType neighbor_node(file_header_, neighbor_page_handle); + if (index_node.size() + neighbor_node.size() > index_node.max_size()) { + rc = redistribute(neighbor_page_handle, page_handle, parent_page_handle, index); + } else { + rc = coalesce(neighbor_page_handle, page_handle, parent_page_handle, index); } - mem_pool_item_->free(pkey); - return RC::SUCCESS; + return rc; } -RC BplusTreeHandler::find_first_index_satisfied(CompOp compop, const char *key, PageNum *page_num, int *rididx) +template +RC BplusTreeHandler::coalesce(BPPageHandle &neighbor_page_handle, BPPageHandle &page_handle, + BPPageHandle &parent_page_handle, int index) { - BPPageHandle page_handle; - IndexNode *node; - PageNum leaf_page, next; - char *pdata, *pkey; - RC rc; - int i, tmp; - RID rid; - if (compop == LESS_THAN || compop == LESS_EQUAL || compop == NOT_EQUAL) { - rc = get_first_leaf_page(page_num); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get first leaf page, index:%d", file_id_); - return rc; - } - *rididx = 0; - return RC::SUCCESS; - } - rid.page_num = -1; - rid.slot_num = -1; - pkey = (char *)mem_pool_item_->alloc(); - if (pkey == nullptr) { - LOG_WARN("Failed to alloc memory for key. size=%d", file_header_.key_length); - return RC::NOMEM; + IndexNodeHandlerType neighbor_node(file_header_, neighbor_page_handle); + IndexNodeHandlerType node(file_header_, page_handle); + + InternalIndexNodeHandler parent_node(file_header_, parent_page_handle); + + BPPageHandle *left_page_handle = nullptr; + BPPageHandle *right_page_handle = nullptr; + if (index == 0) { + // neighbor node is at right + left_page_handle = &page_handle; + right_page_handle = &neighbor_page_handle; + index++; + } else { + left_page_handle = &neighbor_page_handle; + right_page_handle = &page_handle; + // neighbor is at left } - memcpy(pkey, key, file_header_.attr_length); - memcpy(pkey + file_header_.attr_length, &rid, sizeof(RID)); - rc = find_leaf(pkey, &leaf_page); + IndexNodeHandlerType left_node(file_header_, *left_page_handle); + IndexNodeHandlerType right_node(file_header_, *right_page_handle); + + parent_node.remove(index); + // parent_node.validate(key_comparator_, disk_buffer_pool_, file_id_); + RC rc = right_node.move_to(left_node, disk_buffer_pool_, file_id_); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to find leaf page of index %d", file_id_); - mem_pool_item_->free(pkey); + LOG_WARN("failed to move right node to left. rc=%d:%s", rc, strrc(rc)); return rc; } - mem_pool_item_->free(pkey); + // left_node.validate(key_comparator_); - next = leaf_page; + if (left_node.is_leaf()) { + LeafIndexNodeHandler left_leaf_node(file_header_, *left_page_handle); + LeafIndexNodeHandler right_leaf_node(file_header_, *right_page_handle); + left_leaf_node.set_next_page(right_leaf_node.next_page()); - while (next > 0) { - rc = disk_buffer_pool_->get_this_page(file_id_, next, &page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to scan index due to failed to load page %d of index %d", next, file_id_); - return rc; - } - disk_buffer_pool_->get_data(&page_handle, &pdata); - - node = get_index_node(pdata); - for (i = 0; i < node->key_num; i++) { - tmp = attribute_comp( - node->keys + i * file_header_.key_length, key, file_header_.attr_type, file_header_.attr_length); - if (compop == EQUAL_TO || compop == GREAT_EQUAL) { - if (tmp >= 0) { - disk_buffer_pool_->get_page_num(&page_handle, page_num); - - *rididx = i; - disk_buffer_pool_->unpin_page(&page_handle); - return RC::SUCCESS; - } - } - if (compop == GREAT_THAN) { - if (tmp > 0) { - disk_buffer_pool_->get_page_num(&page_handle, page_num); - *rididx = i; - disk_buffer_pool_->unpin_page(&page_handle); - return RC::SUCCESS; - } + PageNum next_right_page_num = right_leaf_node.next_page(); + if (next_right_page_num != BP_INVALID_PAGE_NUM) { + BPPageHandle next_right_page_handle; + rc = disk_buffer_pool_->get_this_page(file_id_, next_right_page_num, &next_right_page_handle); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch next right page. page number:%d. rc=%d:%s", next_right_page_num, rc, strrc(rc)); + disk_buffer_pool_->unpin_page(&page_handle); + disk_buffer_pool_->unpin_page(&neighbor_page_handle); + disk_buffer_pool_->unpin_page(&parent_page_handle); + return rc; } + + LeafIndexNodeHandler next_right_node(file_header_, next_right_page_handle); + next_right_node.set_prev_page(left_node.page_num()); + disk_buffer_pool_->unpin_page(&next_right_page_handle); } - next = node->next_brother; + } - disk_buffer_pool_->unpin_page(&page_handle); - return RC::RECORD_EOF; + PageNum right_page_num = right_page_handle->page_num(); + disk_buffer_pool_->unpin_page(left_page_handle); + disk_buffer_pool_->unpin_page(right_page_handle); + disk_buffer_pool_->dispose_page(file_id_, right_page_num); + return coalesce_or_redistribute(parent_page_handle); } -RC BplusTreeHandler::get_first_leaf_page(PageNum *leaf_page) +template +RC BplusTreeHandler::redistribute(BPPageHandle &neighbor_page_handle, BPPageHandle &page_handle, + BPPageHandle &parent_page_handle, int index) { - RC rc; - BPPageHandle page_handle; - PageNum page_num; - IndexNode *node; - char *pdata; - - node = root_node_; - - while (node->is_leaf == false) { - page_num = node->rids[0].page_num; - if (page_handle.open) { - disk_buffer_pool_->unpin_page(&page_handle); - } + InternalIndexNodeHandler parent_node(file_header_, parent_page_handle); + IndexNodeHandlerType neighbor_node(file_header_, neighbor_page_handle); + IndexNodeHandlerType node(file_header_, page_handle); + if (neighbor_node.size() < node.size()) { + LOG_ERROR("got invalid nodes. neighbor node size %d, this node size %d", + neighbor_node.size(), node.size()); + } + if (index == 0) { + // the neighbor is at right + neighbor_node.move_first_to_end(node, disk_buffer_pool_, file_id_); + // neighbor_node.validate(key_comparator_, disk_buffer_pool_, file_id_); + // node.validate(key_comparator_, disk_buffer_pool_, file_id_); + parent_node.set_key_at(index + 1, neighbor_node.key_at(0)); + // parent_node.validate(key_comparator_, disk_buffer_pool_, file_id_); + } else { + // the neighbor is at left + neighbor_node.move_last_to_front(node, disk_buffer_pool_, file_id_); + // neighbor_node.validate(key_comparator_, disk_buffer_pool_, file_id_); + // node.validate(key_comparator_, disk_buffer_pool_, file_id_); + parent_node.set_key_at(index, node.key_at(0)); + // parent_node.validate(key_comparator_, disk_buffer_pool_, file_id_); + } + + neighbor_page_handle.mark_dirty(); + page_handle.mark_dirty(); + parent_page_handle.mark_dirty(); + disk_buffer_pool_->unpin_page(&parent_page_handle); + disk_buffer_pool_->unpin_page(&neighbor_page_handle); + disk_buffer_pool_->unpin_page(&page_handle); + return RC::SUCCESS; +} - rc = disk_buffer_pool_->get_this_page(file_id_, page_num, &page_handle); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to load page %d of index %d", page_num, file_id_); - return rc; - } - disk_buffer_pool_->get_data(&page_handle, &pdata); +RC BplusTreeHandler::delete_entry_internal(BPPageHandle &leaf_page_handle, const char *key) +{ + LeafIndexNodeHandler leaf_index_node(file_header_, leaf_page_handle); - node = get_index_node(pdata); + const int remove_count = leaf_index_node.remove(key, key_comparator_); + if (remove_count == 0) { + LOG_TRACE("no data to remove"); + disk_buffer_pool_->unpin_page(&leaf_page_handle); + return RC::RECORD_RECORD_NOT_EXIST; } - if (page_handle.open) { - disk_buffer_pool_->get_page_num(&page_handle, leaf_page); + // leaf_index_node.validate(key_comparator_, disk_buffer_pool_, file_id_); - disk_buffer_pool_->unpin_page(&page_handle); - } else { - disk_buffer_pool_->get_page_num(&root_page_handle_, leaf_page); + leaf_page_handle.mark_dirty(); + + if (leaf_index_node.size() >= leaf_index_node.min_size()) { + disk_buffer_pool_->unpin_page(&leaf_page_handle); + return RC::SUCCESS; } - return RC::SUCCESS; + return coalesce_or_redistribute(leaf_page_handle); } -BplusTreeScanner::BplusTreeScanner(BplusTreeHandler &index_handler) : index_handler_(index_handler) -{} - -RC BplusTreeScanner::open(CompOp comp_op, const char *value) +RC BplusTreeHandler::delete_entry(const char *user_key, const RID *rid) { - RC rc; - if (opened_) { - return RC::RECORD_OPENNED; + if (file_id_ < 0) { + LOG_WARN("Failed to delete index entry, due to index is't ready"); + return RC::RECORD_CLOSED; } - comp_op_ = comp_op; - - char *value_copy = (char *)malloc(index_handler_.file_header_.attr_length); - if (value_copy == nullptr) { - LOG_WARN("Failed to alloc memory for value. size=%d", index_handler_.file_header_.attr_length); + char *key = (char *)mem_pool_item_->alloc(); + if (nullptr == key) { + LOG_WARN("Failed to alloc memory for key. size=%d", file_header_.key_length); return RC::NOMEM; } - memcpy(value_copy, value, index_handler_.file_header_.attr_length); - value_ = value_copy; // mem_pool_item_->free value_ - rc = index_handler_.find_first_index_satisfied(comp_op, value, &next_page_num_, &index_in_node_); + memcpy(key, user_key, file_header_.attr_length); + memcpy(key + file_header_.attr_length, rid, sizeof(*rid)); + + LOG_INFO("before delete"); + disk_buffer_pool_->check_all_pages_unpinned(file_id_); + BPPageHandle leaf_page_handle; + RC rc = find_leaf(key, leaf_page_handle); if (rc != RC::SUCCESS) { - if (rc == RC::RECORD_EOF) { - next_page_num_ = -1; - index_in_node_ = -1; - } else - return rc; + LOG_WARN("failed to find leaf page. rc =%d:%s", rc, strrc(rc)); + mem_pool_item_->free(key); + return rc; + } + rc = delete_entry_internal(leaf_page_handle, key); + if (rc != RC::SUCCESS) { + LOG_WARN("Failed to delete index %d", file_id_); + mem_pool_item_->free(key); + return rc; } - num_fixed_pages_ = 1; - next_index_of_page_handle_ = 0; - pinned_page_count_ = 0; - opened_ = true; + mem_pool_item_->free(key); + LOG_INFO("after delete"); + disk_buffer_pool_->check_all_pages_unpinned(file_id_); return RC::SUCCESS; } -RC BplusTreeScanner::close() +BplusTreeScanner::BplusTreeScanner(BplusTreeHandler &tree_handler) : tree_handler_(tree_handler) +{} + +BplusTreeScanner::~BplusTreeScanner() { - if (!opened_) { - return RC::RECORD_SCANCLOSED; - } - free((void *)value_); - value_ = nullptr; - opened_ = false; - return RC::SUCCESS; + close(); } -RC BplusTreeScanner::next_entry(RID *rid) +RC BplusTreeScanner::open(const char *left_user_key, bool left_inclusive, + const char *right_user_key, bool right_inclusive) { - RC rc; - if (!opened_) { - return RC::RECORD_CLOSED; + RC rc = RC::SUCCESS; + if (inited_) { + LOG_WARN("tree scanner has been inited"); + return RC::INTERNAL; } - rc = get_next_idx_in_memory(rid); //和RM中一样,有可能有错误,一次只查当前页和当前页的下一页,有待确定 - if (rc == RC::RECORD_NO_MORE_IDX_IN_MEM) { - rc = find_idx_pages(); + + inited_ = true; + + // 校验输入的键值是否是合法范围 + if (left_user_key && right_user_key) { + const auto &attr_comparator = tree_handler_.key_comparator_.attr_comparator(); + const int result = attr_comparator(left_user_key, right_user_key); + if (result > 0 || // left < right + // left == right but is (left,right)/[left,right) or (left,right] + (result == 0 && (left_inclusive == false || right_inclusive == false))) { + return RC::INVALID_ARGUMENT; + } + } + + if (nullptr == left_user_key) { + rc = tree_handler_.left_most_page(left_page_handle_); if (rc != RC::SUCCESS) { + LOG_WARN("failed to find left most page. rc=%d:%s", rc, strrc(rc)); return rc; } - rc = get_next_idx_in_memory(rid); - if (rc == RC::RECORD_NO_MORE_IDX_IN_MEM) { - rc = RC::RECORD_EOF; - } - return rc; + + iter_index_ = 0; } else { + char *left_key = nullptr; + if (left_inclusive) { + left_key = tree_handler_.make_key(left_user_key, *RID::min()); + } else { + left_key = tree_handler_.make_key(left_user_key, *RID::max()); + } + rc = tree_handler_.find_leaf(left_key, left_page_handle_); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to find left page. rc=%d:%s", rc, strrc(rc)); + tree_handler_.free_key(left_key); return rc; } - } - return RC::SUCCESS; -} + LeafIndexNodeHandler left_node(tree_handler_.file_header_, left_page_handle_); + int left_index = left_node.lookup(tree_handler_.key_comparator_, left_key); + tree_handler_.free_key(left_key); + // lookup 返回的是适合插入的位置,还需要判断一下是否在合适的边界范围内 + if (left_index >= left_node.size()) { // 超出了当前页,就需要向后移动一个位置 + const PageNum next_page_num = left_node.next_page(); + if (next_page_num == BP_INVALID_PAGE_NUM) { // 这里已经是最后一页,说明当前扫描,没有数据 + return RC::SUCCESS; + } -RC BplusTreeScanner::find_idx_pages() -{ - RC rc; - if (!opened_) { - return RC::RECORD_CLOSED; - } - if (pinned_page_count_ > 0) { - for (int i = 0; i < pinned_page_count_; i++) { - rc = index_handler_.disk_buffer_pool_->unpin_page(page_handles_ + i); + tree_handler_.disk_buffer_pool_->unpin_page(&left_page_handle_); + rc = tree_handler_.disk_buffer_pool_->get_this_page(tree_handler_.file_id_, next_page_num, &left_page_handle_); if (rc != RC::SUCCESS) { - return rc; + LOG_WARN("failed to fetch next page. page num=%d, rc=%d:%s", next_page_num, rc, strrc(rc)); + return rc; } + + left_index = 0; } + iter_index_ = left_index; } - next_index_of_page_handle_ = 0; - pinned_page_count_ = 0; - for (int i = 0; i < num_fixed_pages_; i++) { - if (next_page_num_ <= 0) - break; - rc = index_handler_.disk_buffer_pool_->get_this_page(index_handler_.file_id_, next_page_num_, page_handles_ + i); + // 没有指定右边界范围,那么就返回右边界最大值 + if (nullptr == right_user_key) { + rc = tree_handler_.right_most_page(right_page_handle_); if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch right most page. rc=%d:%s", rc, strrc(rc)); return rc; } - char *pdata; - rc = index_handler_.disk_buffer_pool_->get_data(page_handles_ + i, &pdata); + + LeafIndexNodeHandler node(tree_handler_.file_header_, right_page_handle_); + end_index_ = node.size() - 1; + } else { + + char *right_key = nullptr; + if (right_inclusive) { + right_key = tree_handler_.make_key(right_user_key, *RID::max()); + } else { + right_key = tree_handler_.make_key(right_user_key, *RID::min()); + } + + rc = tree_handler_.find_leaf(right_key, right_page_handle_); if (rc != RC::SUCCESS) { + LOG_WARN("failed to find left page. rc=%d:%s", rc, strrc(rc)); + tree_handler_.free_key(right_key); return rc; } - IndexNode *node = index_handler_.get_index_node(pdata); - pinned_page_count_++; - next_page_num_ = node->next_brother; + LeafIndexNodeHandler right_node(tree_handler_.file_header_, right_page_handle_); + int right_index = right_node.lookup(tree_handler_.key_comparator_, right_key); + tree_handler_.free_key(right_key); + // lookup 返回的是适合插入的位置,需要根据实际情况做调整 + // 通常情况下需要找到上一个位置 + if (right_index > 0) { + right_index--; + } else { + // 实际上,只有最左边的叶子节点查找时,lookup 才可能返回0 + // 其它的叶子节点都不可能返回0,所以这段逻辑其实是可以简化的 + const PageNum prev_page_num = right_node.prev_page(); + if (prev_page_num == BP_INVALID_PAGE_NUM) { + end_index_ = -1; + return RC::SUCCESS; + } + + tree_handler_.disk_buffer_pool_->unpin_page(&right_page_handle_); + rc = tree_handler_.disk_buffer_pool_->get_this_page(tree_handler_.file_id_, prev_page_num, &right_page_handle_); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch prev page num. page num=%d, rc=%d:%s", prev_page_num, rc, strrc(rc)); + return rc; + } + + LeafIndexNodeHandler tmp_node(tree_handler_.file_header_, right_page_handle_); + right_index = tmp_node.size() - 1; + } + end_index_ = right_index; + } + + // 判断是否左边界比右边界要靠后 + // 两个边界最多会多一页 + // 查找不存在的元素,或者不存在的范围数据时,可能会存在这个问题 + if (left_page_handle_.page_num() == right_page_handle_.page_num() && + iter_index_ > end_index_) { + end_index_ = -1; + } else { + LeafIndexNodeHandler left_node(tree_handler_.file_header_, left_page_handle_); + LeafIndexNodeHandler right_node(tree_handler_.file_header_, right_page_handle_); + if (left_node.prev_page() == right_node.page_num()) { + end_index_ = -1; + } } - if (pinned_page_count_ > 0) - return RC::SUCCESS; - return RC::RECORD_EOF; + return RC::SUCCESS; } -RC BplusTreeScanner::get_next_idx_in_memory(RID *rid) +RC BplusTreeScanner::next_entry(RID *rid) { - char *pdata; - IndexNode *node; - RC rc; - if (next_index_of_page_handle_ >= pinned_page_count_) { - return RC::RECORD_NO_MORE_IDX_IN_MEM; + if (-1 == end_index_) { + return RC::RECORD_EOF; } - if (next_page_num_ == -1 && index_in_node_ == -1) { - return RC::RECORD_EOF; + LeafIndexNodeHandler node(tree_handler_.file_header_, left_page_handle_); + memcpy(rid, node.value_at(iter_index_), sizeof(*rid)); + + if (left_page_handle_.page_num() == right_page_handle_.page_num() && + iter_index_ == end_index_) { + end_index_ = -1; + return RC::SUCCESS; } - for (; next_index_of_page_handle_ < pinned_page_count_; next_index_of_page_handle_++) { - rc = index_handler_.disk_buffer_pool_->get_data(page_handles_ + next_index_of_page_handle_, &pdata); - if (rc != RC::SUCCESS) { - LOG_WARN("Failed to get data from disk buffer pool. rc=%s", strrc); - return rc; - } + if (iter_index_ < node.size() - 1) { + ++iter_index_; + return RC::SUCCESS; + } - node = index_handler_.get_index_node(pdata); - for (; index_in_node_ < node->key_num; index_in_node_++) { - if (satisfy_condition(node->keys + index_in_node_ * index_handler_.file_header_.key_length)) { - memcpy(rid, node->rids + index_in_node_, sizeof(RID)); - index_in_node_++; - return RC::SUCCESS; + RC rc = RC::SUCCESS; + if (left_page_handle_.page_num() != right_page_handle_.page_num()) { + PageNum page_num = node.next_page(); + tree_handler_.disk_buffer_pool_->unpin_page(&left_page_handle_); + if (page_num == BP_INVALID_PAGE_NUM) { + LOG_WARN("got invalid next page. page num=%d", page_num); + rc = RC::INTERNAL; + } else { + rc = tree_handler_.disk_buffer_pool_->get_this_page(tree_handler_.file_id_, page_num, &left_page_handle_); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to fetch next page. page num=%d, rc=%d:%s", page_num, rc, strrc(rc)); + return rc; } - } - index_in_node_ = 0; + iter_index_ = 0; + } + } else if (end_index_ != -1) { + LOG_WARN("should have more pages but not. left page=%d, right page=%d", + left_page_handle_.page_num(), right_page_handle_.page_num()); + rc = RC::INTERNAL; } - return RC::RECORD_NO_MORE_IDX_IN_MEM; + return rc; } -bool BplusTreeScanner::satisfy_condition(const char *pkey) -{ - int i1 = 0, i2 = 0; - float f1 = 0, f2 = 0; - const char *s1 = nullptr, *s2 = nullptr; - if (comp_op_ == NO_OP) { - return true; +RC BplusTreeScanner::close() +{ + if (left_page_handle_.open) { + tree_handler_.disk_buffer_pool_->unpin_page(&left_page_handle_); } - - AttrType attr_type = index_handler_.file_header_.attr_type; - switch (attr_type) { - case INTS: - i1 = *(int *)pkey; - i2 = *(int *)value_; - break; - case FLOATS: - f1 = *(float *)pkey; - f2 = *(float *)value_; - break; - case CHARS: - s1 = pkey; - s2 = value_; - break; - default: - LOG_PANIC("Unknown attr type: %d", attr_type); - } - - bool flag = false; - - int attr_length = index_handler_.file_header_.attr_length; - switch (comp_op_) { - case EQUAL_TO: - switch (attr_type) { - case INTS: - flag = (i1 == i2); - break; - case FLOATS: - flag = 0 == float_compare(f1, f2); - break; - case CHARS: - flag = (strncmp(s1, s2, attr_length) == 0); - break; - default: - LOG_PANIC("Unknown attr type: %d", attr_type); - } - break; - case LESS_THAN: - switch (attr_type) { - case INTS: - flag = (i1 < i2); - break; - case FLOATS: - flag = (f1 < f2); - break; - case CHARS: - flag = (strncmp(s1, s2, attr_length) < 0); - break; - default: - LOG_PANIC("Unknown attr type: %d", attr_type); - } - break; - case GREAT_THAN: - switch (attr_type) { - case INTS: - flag = (i1 > i2); - break; - case FLOATS: - flag = (f1 > f2); - break; - case CHARS: - flag = (strncmp(s1, s2, attr_length) > 0); - break; - default: - LOG_PANIC("Unknown attr type: %d", attr_type); - } - break; - case LESS_EQUAL: - switch (attr_type) { - case INTS: - flag = (i1 <= i2); - break; - case FLOATS: - flag = (f1 <= f2); - break; - case CHARS: - flag = (strncmp(s1, s2, attr_length) <= 0); - break; - default: - LOG_PANIC("Unknown attr type: %d", attr_type); - } - break; - case GREAT_EQUAL: - switch (attr_type) { - case INTS: - flag = (i1 >= i2); - break; - case FLOATS: - flag = (f1 >= f2); - break; - case CHARS: - flag = (strncmp(s1, s2, attr_length) >= 0); - break; - default: - LOG_PANIC("Unknown attr type: %d", attr_type); - } - break; - case NOT_EQUAL: - switch (attr_type) { - case INTS: - flag = (i1 != i2); - break; - case FLOATS: - flag = 0 != float_compare(f1, f2); - break; - case CHARS: - flag = (strncmp(s1, s2, attr_length) != 0); - break; - default: - LOG_PANIC("Unknown attr type: %d", attr_type); - } - break; - default: - LOG_PANIC("Unknown comp op: %d", comp_op_); + if (right_page_handle_.open) { + tree_handler_.disk_buffer_pool_->unpin_page(&right_page_handle_); } - return flag; + end_index_ = -1; + inited_ = false; + LOG_INFO("bplus tree scanner closed"); + return RC::SUCCESS; } diff --git a/src/observer/storage/common/bplus_tree.h b/src/observer/storage/common/bplus_tree.h index ab9fe586ec3986e4de6fdb17ed4e4614e3686794..673c44be86df6ef2df087e67c37919af5b90b51b 100644 --- a/src/observer/storage/common/bplus_tree.h +++ b/src/observer/storage/common/bplus_tree.h @@ -17,6 +17,7 @@ See the Mulan PSL v2 for more details. */ #ifndef __OBSERVER_STORAGE_COMMON_INDEX_MANAGER_H_ #define __OBSERVER_STORAGE_COMMON_INDEX_MANAGER_H_ +#include #include #include "record_manager.h" @@ -26,16 +27,151 @@ See the Mulan PSL v2 for more details. */ #define EMPTY_RID_PAGE_NUM -1 #define EMPTY_RID_SLOT_NUM -1 +class AttrComparator +{ +public: + void init(AttrType type, int length) + { + attr_type_ = type; + attr_length_ = length; + } + + int attr_length() const { + return attr_length_; + } + + int operator()(const char *v1, const char *v2) const { + switch (attr_type_) { + case INTS: { + return *(int *)v1 - *(int *)v2; + } + break; + case FLOATS: { + float result = *(float *)v1 - *(float *)v2; + if (-1e-6 < result && result < 1e-6) { + return 0; + } + return result > 0 ? 1 : -1; + } + case CHARS: { + return strncmp(v1, v2, attr_length_); + } + default:{ + LOG_ERROR("unknown attr type. %d", attr_type_); + abort(); + } + } + } +private: + AttrType attr_type_; + int attr_length_; +}; + +class KeyComparator +{ +public: + void init(AttrType type, int length) + { + attr_comparator_.init(type, length); + } + + const AttrComparator &attr_comparator() const { + return attr_comparator_; + } + + int operator() (const char *v1, const char *v2) const { + int result = attr_comparator_(v1, v2); + if (result != 0) { + return result; + } + + const RID *rid1 = (const RID *)(v1 + attr_comparator_.attr_length()); + const RID *rid2 = (const RID *)(v2 + attr_comparator_.attr_length()); + return RID::compare(rid1, rid2); + } + +private: + AttrComparator attr_comparator_; +}; + +class AttrPrinter +{ +public: + void init(AttrType type, int length) + { + attr_type_ = type; + attr_length_ = length; + } + + int attr_length() const { + return attr_length_; + } + + std::string operator()(const char *v) const { + switch (attr_type_) { + case INTS: { + return std::to_string(*(int*)v); + } + break; + case FLOATS: { + return std::to_string(*(float*)v); + } + case CHARS: { + return std::string(v, attr_length_); + } + default:{ + LOG_ERROR("unknown attr type. %d", attr_type_); + abort(); + } + } + } +private: + AttrType attr_type_; + int attr_length_; +}; + +class KeyPrinter +{ +public: + void init(AttrType type, int length) + { + attr_printer_.init(type, length); + } + + const AttrPrinter &attr_printer() const { + return attr_printer_; + } + + std::string operator() (const char *v) const { + std::stringstream ss; + ss << "{key:" << attr_printer_(v) << ","; + + const RID *rid = (const RID *)(v + attr_printer_.attr_length()); + ss << "rid:{" << rid->to_string() << "}}"; + return ss.str(); + } + +private: + AttrPrinter attr_printer_; +}; + +/** + * the meta information of bplus tree + * this is the first page of bplus tree. + * only one field can be supported, can you extend it to multi-fields? + */ struct IndexFileHeader { IndexFileHeader() { memset(this, 0, sizeof(IndexFileHeader)); + root_page = BP_INVALID_PAGE_NUM; } - int attr_length; - int key_length; + PageNum root_page; + int32_t internal_max_size; + int32_t leaf_max_size; + int32_t attr_length; + int32_t key_length; // attr length + sizeof(RID) AttrType attr_type; - PageNum root_page; - int order; const std::string to_string() { @@ -45,74 +181,197 @@ struct IndexFileHeader { << "key_length:" << key_length << "," << "attr_type:" << attr_type << "," << "root_page:" << root_page << "," - << "order:" << order << ";"; + << "internal_max_size:" << internal_max_size << "," + << "leaf_max_size:" << leaf_max_size << ";"; return ss.str(); } }; #define RECORD_RESERVER_PAIR_NUM 2 +/** + * the common part of page describtion of bplus tree + * storage format: + * | page type | item number | parent page id | + */ struct IndexNode { + static constexpr int HEADER_SIZE = 12; + bool is_leaf; int key_num; PageNum parent; - PageNum prev_brother; // valid when is_leaf = true - PageNum next_brother; // valid when is_leaf = true +}; + +/** + * leaf page of bplus tree + * storage format: + * | common header | prev page id | next page id | + * | key0, rid0 | key1, rid1 | ... | keyn, ridn | + * + * the key is in format: the key value of record and rid. + * so the key in leaf page must be unique. + * the value is rid. + * can you implenment a cluster index ? + */ +struct LeafIndexNode : public IndexNode { + static constexpr int HEADER_SIZE = IndexNode::HEADER_SIZE + 8; + + PageNum prev_brother; + PageNum next_brother; /** * leaf can store order keys and rids at most + */ + char array[0]; +}; + +/** + * internal page of bplus tree + * storage format: + * | common header | + * | key(0),page_id(0) | key(1), page_id(1) | ... | key(n), page_id(n) | + * + * the first key is ignored(key0). + * so it will waste space, can you fix this? + */ +struct InternalIndexNode : public IndexNode { + static constexpr int HEADER_SIZE = IndexNode::HEADER_SIZE; + + /** * internal node just store order -1 keys and order rids, the last rid is last rght child. */ - char *keys; + char array[0]; +}; + +class IndexNodeHandler { +public: + IndexNodeHandler(const IndexFileHeader &header, BPPageHandle &page_handle); + + void init_empty(bool leaf); + + bool is_leaf() const; + int key_size() const; + int value_size() const; + int item_size() const; + + void increase_size(int n); + int size() const; + void set_parent_page_num(PageNum page_num); + PageNum parent_page_num() const; + + PageNum page_num() const; + + bool validate() const; + + friend std::string to_string(const IndexNodeHandler &handler); + +protected: + const IndexFileHeader &header_; + PageNum page_num_; + IndexNode *node_; +}; + +class LeafIndexNodeHandler : public IndexNodeHandler { +public: + LeafIndexNodeHandler(const IndexFileHeader &header, BPPageHandle &page_handle); + + void init_empty(); + void set_next_page(PageNum page_num); + void set_prev_page(PageNum page_num); + PageNum next_page() const; + PageNum prev_page() const; + + char *key_at(int index); + char *value_at(int index); + + /** + * 查找指定key的插入位置(注意不是key本身) + * 如果key已经存在,会设置found的值 + * NOTE: 当前lookup的实现效率非常低,你是否可以优化它? + */ + int lookup(const KeyComparator &comparator, const char *key, bool *found = nullptr) const; + + void insert(int index, const char *key, const char *value); + void remove(int index); + int remove(const char *key, const KeyComparator &comparator); + RC move_half_to(LeafIndexNodeHandler &other, DiskBufferPool *bp, int file_id); + RC move_first_to_end(LeafIndexNodeHandler &other, DiskBufferPool *disk_buffer_pool, int file_id); + RC move_last_to_front(LeafIndexNodeHandler &other, DiskBufferPool *bp, int file_id); /** - * In the node which isn't leaf, the rids point to child's page, - * rids[i] is keys[i]'s left child, rids[key_num] is the last right child. - * In the node which is leaf, the rids point to record's rid. + * move all items to left page */ - RID *rids; + RC move_to(LeafIndexNodeHandler &other, DiskBufferPool *bp, int file_id); - void init_empty(IndexFileHeader &file_header) - { - is_leaf = true; - key_num = 0; - parent = -1; - prev_brother = -1; - next_brother = -1; - keys = (char *)(this + 1); - rids = (RID *)(keys + (file_header.order + RECORD_RESERVER_PAIR_NUM) * file_header.key_length); - } + int max_size() const; + int min_size() const; - std::string to_string(IndexFileHeader &file_header) - { - std::stringstream ss; + bool validate(const KeyComparator &comparator, DiskBufferPool *bp, int file_id) const; - ss << "is_leaf:" << is_leaf << "," - << "key_num:" << key_num << "," - << "parent:" << parent << "," - << "prev_brother:" << prev_brother << "," - << "next_brother:" << next_brother << ","; - - if (file_header.attr_type == INTS) { // CHARS, INTS, FLOATS - - ss << "start_key:" << *(int *)(keys) << "," - << "end_key:" << *(int *)(keys + (key_num - 1) * file_header.key_length) << ";"; - } else if (file_header.attr_type == FLOATS) { - ss << "start_key:" << *(float *)(keys) << "," - << "end_key:" << *(float *)(keys + (key_num - 1) * file_header.key_length) << ";"; - } else if (file_header.attr_type == CHARS) { - char *temp = (char *)malloc(file_header.attr_length + 1); - memset(temp, 0, file_header.attr_length + 1); - memcpy(temp, keys, file_header.attr_length); - ss << "start_key:" << temp << ","; - memcpy(temp, keys + (key_num - 1) * file_header.key_length, file_header.attr_length); - ss << "end_key:" << temp << ";"; - - free(temp); - } else { - ss << "Unkown key range." << std::endl; - } + friend std::string to_string(const LeafIndexNodeHandler &handler, const KeyPrinter &printer); +private: + char *__item_at(int index) const; + char *__key_at(int index) const; + char *__value_at(int index) const; - return ss.str(); - } + void append(const char *item); + void preappend(const char *item); + +private: + LeafIndexNode *leaf_node_; +}; + +class InternalIndexNodeHandler : public IndexNodeHandler { +public: + InternalIndexNodeHandler(const IndexFileHeader &header, BPPageHandle &page_handle); + + void init_empty(); + void create_new_root(PageNum first_page_num, const char *key, PageNum page_num); + + void insert(const char *key, PageNum page_num, const KeyComparator &comparator); + RC move_half_to(LeafIndexNodeHandler &other, DiskBufferPool *bp, int file_id); + char *key_at(int index); + PageNum value_at(int index); + + /** + * 返回指定子节点在当前节点中的索引 + */ + int value_index(PageNum page_num); + void set_key_at(int index, const char *key); + void remove(int index); + + /** + * 与Leaf节点不同,lookup返回指定key应该属于哪个子节点,返回这个子节点在当前节点中的索引 + * 如果想要返回插入位置,就提供 `insert_position` 参数 + * NOTE: 查找效率不高,你可以优化它吗? + */ + int lookup(const KeyComparator &comparator, const char *key, + bool *found = nullptr, int *insert_position = nullptr) const; + + int max_size() const; + int min_size() const; + + RC move_to(InternalIndexNodeHandler &other, DiskBufferPool *disk_buffer_pool, int file_id); + RC move_first_to_end(InternalIndexNodeHandler &other, DiskBufferPool *disk_buffer_pool, int file_id); + RC move_last_to_front(InternalIndexNodeHandler &other, DiskBufferPool *bp, int file_id); + RC move_half_to(InternalIndexNodeHandler &other, DiskBufferPool *bp, int file_id); + + bool validate(const KeyComparator &comparator, DiskBufferPool *bp, int file_id) const; + + friend std::string to_string(const InternalIndexNodeHandler &handler, const KeyPrinter &printer); +private: + RC copy_from(const char *items, int num, DiskBufferPool *disk_buffer_pool, int file_id); + RC append(const char *item, DiskBufferPool *bp, int file_id); + RC preappend(const char *item, DiskBufferPool *bp, int file_id); + +private: + char *__item_at(int index) const; + char *__key_at(int index) const; + char *__value_at(int index) const; + + int value_size() const; + int item_size() const; + +private: + InternalIndexNode *internal_node_; }; class BplusTreeHandler { @@ -121,7 +380,8 @@ public: * 此函数创建一个名为fileName的索引。 * attrType描述被索引属性的类型,attrLength描述被索引属性的长度 */ - RC create(const char *file_name, AttrType attr_type, int attr_length); + RC create(const char *file_name, AttrType attr_type, int attr_length, + int internal_max_size = -1, int leaf_max_size = -1); /** * 打开名为fileName的索引文件。 @@ -137,22 +397,24 @@ public: /** * 此函数向IndexHandle对应的索引中插入一个索引项。 - * 参数pData指向要插入的属性值,参数rid标识该索引项对应的元组, - * 即向索引中插入一个值为(*pData,rid)的键值对 + * 参数user_key指向要插入的属性值,参数rid标识该索引项对应的元组, + * 即向索引中插入一个值为(user_key,rid)的键值对 */ - RC insert_entry(const char *pkey, const RID *rid); + RC insert_entry(const char *user_key, const RID *rid); /** * 从IndexHandle句柄对应的索引中删除一个值为(*pData,rid)的索引项 * @return RECORD_INVALID_KEY 指定值不存在 */ - RC delete_entry(const char *pkey, const RID *rid); + RC delete_entry(const char *user_key, const RID *rid); + + bool is_empty() const; /** * 获取指定值的record * @param rid 返回值,记录记录所在的页面号和slot */ - RC get_entry(const char *pkey, std::list &rids); + RC get_entry(const char *user_key, std::list &rids); RC sync(); @@ -170,65 +432,60 @@ public: public: RC print_tree(); - RC print_node(IndexNode *node, PageNum page_num); RC print_leafs(); +private: + RC print_leaf(BPPageHandle &page_handle); + RC print_internal_node_recursive(BPPageHandle &page_handle); + bool validate_node(IndexNode *node); bool validate_leaf_link(); + bool validate_node_recursive(BPPageHandle &page_handle); protected: - RC find_leaf(const char *pkey, PageNum *leaf_page); + RC find_leaf(const char *key, BPPageHandle &page_handle); + RC left_most_page(BPPageHandle &page_handle); + RC right_most_page(BPPageHandle &page_handle); + RC find_leaf_internal(const std::function &child_page_getter, + BPPageHandle &page_handle); RC insert_into_parent( PageNum parent_page, BPPageHandle &left_page_handle, const char *pkey, BPPageHandle &right_page_handle); - RC insert_intern_node(BPPageHandle &parent_page_handle, BPPageHandle &left_page_handle, - BPPageHandle &right_page_handle, const char *pkey); RC split_leaf(BPPageHandle &leaf_page_handle); - RC split_intern_node(BPPageHandle &parent_page_handle, const char *pkey); - RC delete_entry_internal(PageNum page_num, const char *pkey); - RC coalesce_node(BPPageHandle &parent_handle, BPPageHandle &left_handle, BPPageHandle &right_handle, int delete_index, - bool check_change_leaf_key, int node_delete_index, const char *pkey); + RC delete_entry_internal(BPPageHandle &leaf_page_handle, const char *key); RC insert_into_new_root(BPPageHandle &left_page_handle, const char *pkey, BPPageHandle &right_page_handle); - RC clean_root_after_delete(IndexNode *old_root); - - RC insert_entry_into_node(IndexNode *node, const char *pkey, const RID *rid, PageNum left_page); - RC delete_entry_from_node(IndexNode *node, const char *pkey, int &node_delete_index); - void delete_entry_from_node(IndexNode *node, const int delete_index); - - RC redistribute_nodes(BPPageHandle &parent_handle, BPPageHandle &left_handle, BPPageHandle &right_handle); - void redistribute_nodes( - IndexNode *left_node, IndexNode *right_node, PageNum left_page, PageNum right_page, char *new_key); - void merge_nodes(IndexNode *left_node, IndexNode *right_node, PageNum left_page, char *parent_key); - RC can_merge_with_other(BPPageHandle *page_handle, PageNum page_num, bool *can_merge); - void split_node( - IndexNode *left_node, IndexNode *right_node, PageNum left_page, PageNum right_page, char *new_parent_key); - void copy_node(IndexNode *to, IndexNode *from); - - void get_entry_from_leaf(IndexNode *node, const char *pkey, std::list &rids, bool &continue_check); - RC find_first_index_satisfied(CompOp comp_op, const char *pkey, PageNum *page_num, int *rididx); - RC get_first_leaf_page(PageNum *leaf_page); - - IndexNode *get_index_node(char *page_data) const; - void swith_root(BPPageHandle &new_root_page_handle, IndexNode *root, PageNum root_page); - - void change_children_parent(RID *rid, int rid_len, PageNum new_parent_page); - RC get_parent_changed_index( - BPPageHandle &parent_handle, IndexNode *&parent, IndexNode *node, PageNum page_num, int &changed_index); - RC change_leaf_parent_key_insert(IndexNode *node, int changed_indx, PageNum page_num); - RC change_leaf_parent_key_delete(IndexNode *leaf, int delete_indx, const char *old_first_key); - RC change_insert_leaf_link(IndexNode *left, IndexNode *right, PageNum right_page); - RC change_delete_leaf_link(IndexNode *left, IndexNode *right, PageNum right_page); + template + RC split(BPPageHandle &page_handle, BPPageHandle &new_page_handle); + template + RC coalesce_or_redistribute(BPPageHandle &page_handle); + template + RC coalesce(BPPageHandle &neighbor_page_handle, BPPageHandle &page_handle, + BPPageHandle &parent_page_handle, int index); + template + RC redistribute(BPPageHandle &neighbor_page_handle, BPPageHandle &page_handle, + BPPageHandle &parent_page_handle, int index); + + RC insert_entry_into_parent(BPPageHandle &page_handle, BPPageHandle &new_page_handle, const char *key); + RC insert_entry_into_leaf_node(BPPageHandle &page_handle, const char *pkey, const RID *rid); + RC update_root_page_num(); + RC create_new_tree(const char *key, const RID *rid); + + RC adjust_root(BPPageHandle &root_page_handle); + +private: + char *make_key(const char *user_key, const RID &rid); + void free_key(char *key); protected: DiskBufferPool *disk_buffer_pool_ = nullptr; int file_id_ = -1; bool header_dirty_ = false; IndexFileHeader file_header_; - BPPageHandle root_page_handle_; - IndexNode *root_node_ = nullptr; + KeyComparator key_comparator_; + KeyPrinter key_printer_; common::MemPoolItem *mem_pool_item_ = nullptr; @@ -239,73 +496,33 @@ private: class BplusTreeScanner { public: - BplusTreeScanner(BplusTreeHandler &index_handler); + BplusTreeScanner(BplusTreeHandler &tree_handler); + ~BplusTreeScanner(); /** - * 用于在indexHandle对应的索引上初始化一个基于条件的扫描。 - * compOp和*value指定比较符和比较值,indexScan为初始化后的索引扫描结构指针 - * 没有带两个边界的范围扫描 + * 扫描指定范围的数据 + * @param left_key 扫描范围的左边界,如果是null,则没有左边界 + * @param left_inclusive 左边界的值是否包含在内 + * @param right_key 扫描范围的右边界。如果是null,则没有右边界 + * @param right_inclusive 右边界的值是否包含在内 */ - RC open(CompOp comp_op, const char *value); + RC open(const char *left_user_key, bool left_inclusive, + const char *right_user_key, bool right_inclusive); - /** - * 用于继续索引扫描,获得下一个满足条件的索引项, - * 并返回该索引项对应的记录的ID - */ RC next_entry(RID *rid); - /** - * 关闭一个索引扫描,释放相应的资源 - */ RC close(); - /** - * 获取由fileName指定的B+树索引内容,返回指向B+树的指针。 - * 此函数提供给测试程序调用,用于检查B+树索引内容的正确性 - */ - // RC getIndexTree(char *fileName, Tree *index); - -private: - RC get_next_idx_in_memory(RID *rid); - RC find_idx_pages(); - bool satisfy_condition(const char *key); - private: - BplusTreeHandler &index_handler_; - bool opened_ = false; - CompOp comp_op_ = NO_OP; // 用于比较的操作符 - const char *value_ = nullptr; // 与属性行比较的值 - int num_fixed_pages_ = -1; // 固定在缓冲区中的页,与指定的页面固定策略有关 - int pinned_page_count_ = 0; // 实际固定在缓冲区的页面数 - BPPageHandle page_handles_[BP_BUFFER_SIZE]; // 固定在缓冲区页面所对应的页面操作列表 - int next_index_of_page_handle_ = -1; // 当前被扫描页面的操作索引 - int index_in_node_ = -1; // 当前B+ Tree页面上的key index - PageNum next_page_num_ = -1; // 下一个将要被读入的页面号 -}; - -class BplusTreeTester { -public: - BplusTreeTester(BplusTreeHandler &index_handler) : index_handler_(index_handler) - {} - ~BplusTreeTester() = default; - - void set_order(int order) - { - if (order >= 2 && order % 2 == 0) { - index_handler_.file_header_.order = order; - LOG_INFO("Successfully set index %d's order as %d", index_handler_.file_id_, order); - } else { - LOG_INFO("Invalid input order argument %d", order); - } - } - - const int get_oder() - { - return index_handler_.file_header_.order; - } - -protected: - BplusTreeHandler &index_handler_; + bool inited_ = false; + BplusTreeHandler &tree_handler_; + + /// 使用左右叶子节点和位置来表示扫描的起始位置和终止位置 + /// 起始位置和终止位置都是有效的数据 + BPPageHandle left_page_handle_; + BPPageHandle right_page_handle_; + int iter_index_ = -1; + int end_index_ = -1; // use -1 for end of scan }; -#endif //__OBSERVER_STORAGE_COMMON_INDEX_MANAGER_H_ \ No newline at end of file +#endif //__OBSERVER_STORAGE_COMMON_INDEX_MANAGER_H_ diff --git a/src/observer/storage/common/bplus_tree_index.cpp b/src/observer/storage/common/bplus_tree_index.cpp index f16e6c936e0ef3024c7e3d5c7dc8fe7ff9351e3f..903fe3c4f5dc73c8cd6f60c39bde734dbda337bf 100644 --- a/src/observer/storage/common/bplus_tree_index.cpp +++ b/src/observer/storage/common/bplus_tree_index.cpp @@ -100,17 +100,16 @@ RC BplusTreeIndex::delete_entry(const char *record, const RID *rid) return index_handler_.delete_entry(record + field_meta_.offset(), rid); } -IndexScanner *BplusTreeIndex::create_scanner(CompOp comp_op, const char *value) +IndexScanner *BplusTreeIndex::create_scanner(const char *left_key, bool left_inclusive, + const char *right_key, bool right_inclusive) { - BplusTreeScanner *bplus_tree_scanner = new BplusTreeScanner(index_handler_); - RC rc = bplus_tree_scanner->open(comp_op, value); + BplusTreeIndexScanner *index_scanner = new BplusTreeIndexScanner(index_handler_); + RC rc = index_scanner->open(left_key, left_inclusive, right_key, right_inclusive); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to open index scanner. file_id:%d, rc=%d:%s", index_handler_.get_file_id(), rc, strrc(rc)); - delete bplus_tree_scanner; + LOG_WARN("failed to open index scanner. rc=%d:%s", rc, strrc(rc)); + delete index_scanner; return nullptr; } - - BplusTreeIndexScanner *index_scanner = new BplusTreeIndexScanner(bplus_tree_scanner); return index_scanner; } @@ -120,22 +119,26 @@ RC BplusTreeIndex::sync() } //////////////////////////////////////////////////////////////////////////////// -BplusTreeIndexScanner::BplusTreeIndexScanner(BplusTreeScanner *tree_scanner) : tree_scanner_(tree_scanner) +BplusTreeIndexScanner::BplusTreeIndexScanner(BplusTreeHandler &tree_handler) : tree_scanner_(tree_handler) {} BplusTreeIndexScanner::~BplusTreeIndexScanner() noexcept { - tree_scanner_->close(); - delete tree_scanner_; + tree_scanner_.close(); +} + +RC BplusTreeIndexScanner::open(const char *left_key, bool left_inclusive, const char *right_key, bool right_inclusive) +{ + return tree_scanner_.open(left_key, left_inclusive, right_key, right_inclusive); } RC BplusTreeIndexScanner::next_entry(RID *rid) { - return tree_scanner_->next_entry(rid); + return tree_scanner_.next_entry(rid); } RC BplusTreeIndexScanner::destroy() { delete this; return RC::SUCCESS; -} \ No newline at end of file +} diff --git a/src/observer/storage/common/bplus_tree_index.h b/src/observer/storage/common/bplus_tree_index.h index 19fe9305d880b49c1778522360e219373271d735..eb686fa989f74113dcd4a8ade7b3ecb38ece54f4 100644 --- a/src/observer/storage/common/bplus_tree_index.h +++ b/src/observer/storage/common/bplus_tree_index.h @@ -30,7 +30,11 @@ public: RC insert_entry(const char *record, const RID *rid) override; RC delete_entry(const char *record, const RID *rid) override; - IndexScanner *create_scanner(CompOp comp_op, const char *value) override; + /** + * 扫描指定范围的数据 + */ + IndexScanner *create_scanner(const char *left_key, bool left_inclusive, + const char *right_key, bool right_inclusive) override; RC sync() override; @@ -41,14 +45,15 @@ private: class BplusTreeIndexScanner : public IndexScanner { public: - BplusTreeIndexScanner(BplusTreeScanner *tree_scanner); + BplusTreeIndexScanner(BplusTreeHandler &tree_handle); ~BplusTreeIndexScanner() noexcept override; RC next_entry(RID *rid) override; RC destroy() override; + RC open(const char *left_key, bool left_inclusive, const char *right_key, bool right_inclusive); private: - BplusTreeScanner *tree_scanner_; + BplusTreeScanner tree_scanner_; }; #endif //__OBSERVER_STORAGE_COMMON_BPLUS_TREE_INDEX_H_ diff --git a/src/observer/storage/common/index.h b/src/observer/storage/common/index.h index 76420d101fe0ee591b20b2b60617c52976b0cec8..d66a09327de2559047fa510db59e143d1a7cca4b 100644 --- a/src/observer/storage/common/index.h +++ b/src/observer/storage/common/index.h @@ -46,7 +46,8 @@ public: virtual RC insert_entry(const char *record, const RID *rid) = 0; virtual RC delete_entry(const char *record, const RID *rid) = 0; - virtual IndexScanner *create_scanner(CompOp comp_op, const char *value) = 0; + virtual IndexScanner *create_scanner(const char *left_key, bool left_inclusive, + const char *right_key, bool right_inclusive) = 0; virtual RC sync() = 0; @@ -63,8 +64,12 @@ public: IndexScanner() = default; virtual ~IndexScanner() = default; + /** + * 遍历元素数据 + * 如果没有更多的元素,返回RECORD_EOF + */ virtual RC next_entry(RID *rid) = 0; virtual RC destroy() = 0; }; -#endif // __OBSERVER_STORAGE_COMMON_INDEX_H_ \ No newline at end of file +#endif // __OBSERVER_STORAGE_COMMON_INDEX_H_ diff --git a/src/observer/storage/common/record_manager.h b/src/observer/storage/common/record_manager.h index 9e8a34d58873e418dcca298399bd24442acaee14..1552129b43350a6a19d450222064e968117fc2cb 100644 --- a/src/observer/storage/common/record_manager.h +++ b/src/observer/storage/common/record_manager.h @@ -17,7 +17,7 @@ See the Mulan PSL v2 for more details. */ #include #include "storage/default/disk_buffer_pool.h" -typedef int SlotNum; +typedef int32_t SlotNum; class ConditionFilter; @@ -48,6 +48,11 @@ struct RID { return page_num == other.page_num && slot_num == other.slot_num; } + bool operator!=(const RID &other) const + { + return !(*this == other); + } + static int compare(const RID *rid1, const RID *rid2) { int page_diff = rid1->page_num - rid2->page_num; @@ -57,6 +62,22 @@ struct RID { return rid1->slot_num - rid2->slot_num; } } + + /** + * 返回一个不可能出现的最小的RID + * 虽然page num 0和slot num 0都是合法的,但是page num 0通常用于存放meta数据,所以对数据部分来说都是 + * 不合法的. 这里在bplus tree中查找时会用到。 + */ + static RID *min() + { + static RID rid{0, 0}; + return &rid; + } + static RID *max() + { + static RID rid{std::numeric_limits::max(), std::numeric_limits::max()}; + return &rid; + } }; class RidDigest { @@ -222,4 +243,4 @@ private: RecordPageHandler record_page_handler_; }; -#endif //__OBSERVER_STORAGE_COMMON_RECORD_MANAGER_H_ \ No newline at end of file +#endif //__OBSERVER_STORAGE_COMMON_RECORD_MANAGER_H_ diff --git a/src/observer/storage/common/table.cpp b/src/observer/storage/common/table.cpp index b8d11e22a2dc03e2efb8bb796711ec89bd9e7ea9..caba93c6feee3ecffb824a6514c29945a7108251 100644 --- a/src/observer/storage/common/table.cpp +++ b/src/observer/storage/common/table.cpp @@ -768,7 +768,43 @@ IndexScanner *Table::find_index_for_scan(const DefaultConditionFilter &filter) return nullptr; } - return index->create_scanner(filter.comp_op(), (const char *)value_cond_desc->value); + const char *left_key = nullptr; + const char *right_key = nullptr; + bool left_inclusive = false; + bool right_inclusive = false; + switch (filter.comp_op()) { + case EQUAL_TO: { + left_key = (const char *)value_cond_desc->value; + right_key = (const char *)value_cond_desc->value; + left_inclusive = true; + right_inclusive = true; + } + break; + case LESS_EQUAL: { + right_key = (const char *)value_cond_desc->value; + right_inclusive = true; + } + break; + case GREAT_EQUAL: { + left_key = (const char *)value_cond_desc->value; + left_inclusive = true; + } + break; + case LESS_THAN: { + right_key = (const char *)value_cond_desc->value; + right_inclusive = false; + } + break; + case GREAT_THAN: { + left_key = (const char *)value_cond_desc->value; + left_inclusive = false; + } + break; + default: { + return nullptr; + } + } + return index->create_scanner(left_key, left_inclusive, right_key, right_inclusive); } IndexScanner *Table::find_index_for_scan(const ConditionFilter *filter) diff --git a/src/observer/storage/default/disk_buffer_pool.cpp b/src/observer/storage/default/disk_buffer_pool.cpp index 705c0273d6309c3af09e86055d3fdf0baf748f7a..6bbc66357bc3b941868cfa91164ffe75ed2802e8 100644 --- a/src/observer/storage/default/disk_buffer_pool.cpp +++ b/src/observer/storage/default/disk_buffer_pool.cpp @@ -478,7 +478,7 @@ RC DiskBufferPool::purge_page(int file_id, PageNum page_num) RC DiskBufferPool::purge_page(Frame *buf) { if (buf->pin_count > 0) { - LOG_INFO("Begin to free page %d of %d, but it's pinned, pin_count:%d.", + LOG_INFO("Begin to free page %d of %d(file id), but it's pinned, pin_count:%d.", buf->page.page_num, buf->file_desc, buf->pin_count); @@ -488,12 +488,12 @@ RC DiskBufferPool::purge_page(Frame *buf) if (buf->dirty) { RC rc = flush_page(buf); if (rc != RC::SUCCESS) { - LOG_WARN("Failed to flush page %d of %d during purge page.", buf->page.page_num, buf->file_desc); + LOG_WARN("Failed to flush page %d of %d(file desc) during purge page.", buf->page.page_num, buf->file_desc); return rc; } } - LOG_DEBUG("Successfully purge frame =%p, page %d of %d", buf, buf->page.page_num, buf->file_desc); + LOG_DEBUG("Successfully purge frame =%p, page %d of %d(file desc)", buf, buf->page.page_num, buf->file_desc); bp_manager_.free(buf); return RC::SUCCESS; } @@ -533,7 +533,8 @@ RC DiskBufferPool::purge_all_pages(BPFileHandle *file_handle) for (std::list::iterator it = used.begin(); it != used.end(); ++it) { Frame *frame = *it; if (frame->pin_count > 0) { - LOG_WARN("The page has been pinned, file_id:%d, pagenum:%d", frame->file_desc, frame->page.page_num); + LOG_WARN("The page has been pinned, file_desc:%d, pagenum:%d, pin_count=%d", + frame->file_desc, frame->page.page_num, frame->pin_count); continue; } if (frame->dirty) { @@ -548,6 +549,29 @@ RC DiskBufferPool::purge_all_pages(BPFileHandle *file_handle) return RC::SUCCESS; } +RC DiskBufferPool::check_all_pages_unpinned(int file_id) +{ + RC rc = check_file_id(file_id); + if (rc != RC::SUCCESS) { + LOG_ERROR("Failed to flush pages due to invalid file_id %d", file_id); + return rc; + } + + BPFileHandle *file_handle = open_list_[file_id]; + std::list frames = bp_manager_.find_list(file_handle->file_desc); + for (auto & frame : frames) { + if (frame->page.page_num == 0 && frame->pin_count > 1) { + LOG_WARN("This page has been pinned. file id=%d, page num:%d, pin count=%d", + file_id, frame->page.page_num, frame->pin_count); + } else if (frame->page.page_num != 0 && frame->pin_count > 0) { + LOG_WARN("This page has been pinned. file id=%d, page num:%d, pin count=%d", + file_id, frame->page.page_num, frame->pin_count); + } + } + LOG_INFO("all pages have been checked of file id %d", file_id); + return RC::SUCCESS; +} + RC DiskBufferPool::flush_page(Frame *frame) { // The better way is use mmap the block into memory, diff --git a/src/observer/storage/default/disk_buffer_pool.h b/src/observer/storage/default/disk_buffer_pool.h index 81d00889303ea6e07c6570b09487dc6716b15995..90881919a9379b9ee85cae8c85218a112eca4293 100644 --- a/src/observer/storage/default/disk_buffer_pool.h +++ b/src/observer/storage/default/disk_buffer_pool.h @@ -69,6 +69,16 @@ typedef struct BPPageHandle { BPPageHandle() : open(false), frame(nullptr) {} + PageNum page_num() const { + return frame->page.page_num; + } + void mark_dirty() { + this->frame->dirty = true; + } + + char *data() { + return this->frame->page.data; + } bool open; Frame *frame; } BPPageHandle; @@ -195,6 +205,8 @@ public: RC purge_all_pages(int file_id); + RC check_all_pages_unpinned(int file_id); + protected: RC allocate_page(Frame **buf); diff --git a/unitest/bplus_tree_test.cpp b/unitest/bplus_tree_test.cpp index e41f2c9694cd1cc81675de76e12328ad2cf17438..76a24be614264d674be90e4038d443091a2e738d 100644 --- a/unitest/bplus_tree_test.cpp +++ b/unitest/bplus_tree_test.cpp @@ -40,6 +40,7 @@ int k = 0; void test_insert() { + RC rc = RC::SUCCESS; for (int i = 0; i < insert_num; i++) { rid.page_num = i / page_size; @@ -51,10 +52,11 @@ void test_insert() LOG_INFO("Begin to insert the page's num %s", rid.to_string().c_str()); } } else { - LOG_INFO("Insert %d", i); + LOG_INFO("Insert %d. rid=%s", i, rid.to_string().c_str()); } - RC rc = handler->insert_entry((const char *)&i, &rid); + rc = handler->insert_entry((const char *)&i, &rid); ASSERT_EQ(RC::SUCCESS, rc); + handler->print_tree(); ASSERT_EQ(true, handler->validate_tree()); } } @@ -71,10 +73,11 @@ void test_insert() LOG_INFO("Begin to insert the page's num %s", rid.to_string().c_str()); } } else { - LOG_INFO("Insert %d", i); + LOG_INFO("Insert %d. rid=%s", i, rid.to_string().c_str()); } - RC rc = handler->insert_entry((const char *)&i, &rid); + rc = handler->insert_entry((const char *)&i, &rid); ASSERT_EQ(RC::SUCCESS, rc); + handler->print_tree(); ASSERT_EQ(true, handler->validate_tree()); } } @@ -91,9 +94,9 @@ void test_insert() LOG_INFO("Begin to insert the page's num %s", rid.to_string().c_str()); } } else { - LOG_INFO("Insert %d", i); + LOG_INFO("Insert %d. rid=%s", i, rid.to_string().c_str()); } - RC rc = handler->insert_entry((const char *)&i, &rid); + rc = handler->insert_entry((const char *)&i, &rid); ASSERT_EQ(RC::SUCCESS, rc); ASSERT_EQ(true, handler->validate_tree()); } @@ -114,11 +117,14 @@ void test_insert() LOG_INFO("Begin to check duplicated insert the page's num %s", rid.to_string().c_str()); } } else { - LOG_INFO("Check duplicate insert %d", i); + LOG_INFO("check duplicate Insert %d. rid=%s. i%TIMES=%d", i, rid.to_string().c_str(), i%TIMES); } - RC rc = handler->insert_entry((const char *)&i, &rid); + rc = handler->insert_entry((const char *)&i, &rid); int t = i % TIMES; if (t == 0 || t == 1 || t == 2) { + if (rc != RC::RECORD_DUPLICATE_KEY) { + LOG_WARN("insert duplicate key success"); + } ASSERT_EQ(RC::RECORD_DUPLICATE_KEY, rc); } else { ASSERT_EQ(RC::SUCCESS, rc); @@ -154,6 +160,7 @@ void test_get() void test_delete() { + RC rc = RC::SUCCESS; std::list rids; for (int i = 0; i < insert_num / 2; i++) { @@ -164,16 +171,19 @@ void test_delete() if (t == 0 || t == 1) { if (insert_num > page_size) { if (k++ % 100 == 0) { - LOG_INFO("Begin to delete entry of index, rid: %s", rid.to_string().c_str()); + LOG_INFO("Begin to delete entry of index, i=%d rid: %s", i, rid.to_string().c_str()); } } else { - LOG_INFO("Begin to delete entry of index, rid: %s", rid.to_string().c_str()); + LOG_INFO("Begin to delete entry of index, i=%d, rid: %s", i, rid.to_string().c_str()); } - RC rc = handler->delete_entry((const char *)&i, &rid); + rc = handler->delete_entry((const char *)&i, &rid); + if (rc != RC::SUCCESS) { + LOG_WARN("failed to delete entry. i=%d, rid=%s", i, rid.to_string().c_str()); + } + ASSERT_EQ(RC::SUCCESS, rc); ASSERT_EQ(true, handler->validate_tree()); - ASSERT_EQ(RC::SUCCESS, rc); } } @@ -192,7 +202,7 @@ void test_delete() } else { LOG_INFO("Begin to delete entry of index, rid: %s", rid.to_string().c_str()); } - RC rc = handler->delete_entry((const char *)&i, &rid); + rc = handler->delete_entry((const char *)&i, &rid); ASSERT_EQ(true, handler->validate_tree()); ASSERT_EQ(RC::SUCCESS, rc); @@ -205,20 +215,26 @@ void test_delete() rid.slot_num = i % page_size; if (insert_num > page_size) { if (k++ % 100 == 0) { - LOG_INFO("Begin to get entry of index, rid: %s", rid.to_string().c_str()); + LOG_INFO("Begin to get entry of index, i=%d,rid: %s", i, rid.to_string().c_str()); } } else { - LOG_INFO("Begin to get entry of index, rid: %s", rid.to_string().c_str()); + LOG_INFO("Begin to get entry of index, i=%d, rid: %s", i, rid.to_string().c_str()); } rids.clear(); - RC rc = handler->get_entry((const char *)&i, rids); + rc = handler->get_entry((const char *)&i, rids); ASSERT_EQ(RC::SUCCESS, rc); int t = i % TIMES; if (t == 0 || t == 1) { ASSERT_EQ(0, rids.size()); } else { + if (rids.size() != 1) { + LOG_WARN("invalid. i=%d, rid=%s, check rid=%s", i, rid.to_string().c_str(), check_rid.to_string().c_str()); + } ASSERT_EQ(1, rids.size()); check_rid = rids.front(); + if (rid != check_rid) { + LOG_WARN("invalid. i=%d, rid=%s, check rid=%s", i, rid.to_string().c_str(), check_rid.to_string().c_str()); + } ASSERT_EQ(rid.page_num, check_rid.page_num); ASSERT_EQ(rid.slot_num, check_rid.slot_num); ASSERT_EQ(true, handler->validate_tree()); @@ -239,7 +255,7 @@ void test_delete() } else { LOG_INFO("Begin to delete entry of index, rid: %s", rid.to_string().c_str()); } - RC rc = handler->delete_entry((const char *)&i, &rid); + rc = handler->delete_entry((const char *)&i, &rid); ASSERT_EQ(true, handler->validate_tree()); ASSERT_EQ(RC::SUCCESS, rc); @@ -261,7 +277,7 @@ void test_delete() } else { LOG_INFO("Begin to delete entry of index, rid: %s", rid.to_string().c_str()); } - RC rc = handler->delete_entry((const char *)&i, &rid); + rc = handler->delete_entry((const char *)&i, &rid); ASSERT_EQ(true, handler->validate_tree()); ASSERT_EQ(RC::SUCCESS, rc); @@ -280,7 +296,7 @@ void test_delete() } else { LOG_INFO("Begin to insert entry of index, rid: %s", rid.to_string().c_str()); } - RC rc = handler->insert_entry((const char *)&i, &rid); + rc = handler->insert_entry((const char *)&i, &rid); int t = i % TIMES; if (t == 0 || t == 1 || t == 2) { ASSERT_EQ(RC::SUCCESS, rc); @@ -292,6 +308,385 @@ void test_delete() handler->print_tree(); } +TEST(test_bplus_tree, test_leaf_index_node_handle) +{ + LoggerFactory::init_default("test.log"); + + IndexFileHeader index_file_header; + index_file_header.root_page = BP_INVALID_PAGE_NUM; + index_file_header.internal_max_size = 5; + index_file_header.leaf_max_size = 5; + index_file_header.attr_length = 4; + index_file_header.key_length = 4 + sizeof(RID); + index_file_header.attr_type = INTS; + + Frame frame; + frame.dirty = false; + frame.pin_count = 0; + frame.acc_time = 0; + frame.file_desc = 0; + frame.page.page_num = 100; + + BPPageHandle page_handle; + page_handle.open = true; + page_handle.frame = &frame; + + KeyComparator key_comparator; + key_comparator.init(INTS, 4); + + LeafIndexNodeHandler leaf_node(index_file_header, page_handle); + leaf_node.init_empty(); + ASSERT_EQ(0, leaf_node.size()); + + bool found; + int index; + char key_mem[4 + sizeof(RID)]; + int &key = *(int *)key_mem; + RID &rid = *(RID *)(key_mem + 4); + rid.page_num = 0; + rid.slot_num = 0; + for (int i = 0; i < 5; i++) { + key = i * 2 + 1; + index = leaf_node.lookup(key_comparator, key_mem, &found); + ASSERT_EQ(false, found); + leaf_node.insert(index, (const char *)&key, (const char *)&rid); + } + + ASSERT_EQ(5, leaf_node.size()); + + for (int i = 0; i < 5; i++) { + key = i * 2; + index = leaf_node.lookup(key_comparator, key_mem, &found); + ASSERT_EQ(false, found); + ASSERT_EQ(i, index); + } + + key = 12; + index = leaf_node.lookup(key_comparator, key_mem, &found); + ASSERT_EQ(false, found); + ASSERT_EQ(5, index); + + for (int i = 0; i < 5; i++) { + key = i * 2 + 1; + index = leaf_node.lookup(key_comparator, key_mem, &found); + if (!found || i != index) { + printf("found=%d, index=%d, key=%d", found, index, key); + } + ASSERT_EQ(true, found); + ASSERT_EQ(i, index); + } +} +TEST(test_bplus_tree, test_internal_index_node_handle) +{ + LoggerFactory::init_default("test.log"); + + IndexFileHeader index_file_header; + index_file_header.root_page = BP_INVALID_PAGE_NUM; + index_file_header.internal_max_size = 5; + index_file_header.leaf_max_size = 5; + index_file_header.attr_length = 4; + index_file_header.key_length = 4 + sizeof(RID); + index_file_header.attr_type = INTS; + + Frame frame; + frame.dirty = false; + frame.pin_count = 0; + frame.acc_time = 0; + frame.file_desc = 0; + frame.page.page_num = 100; + + BPPageHandle page_handle; + page_handle.open = true; + page_handle.frame = &frame; + + KeyComparator key_comparator; + key_comparator.init(INTS, 4); + + InternalIndexNodeHandler internal_node(index_file_header, page_handle); + internal_node.init_empty(); + ASSERT_EQ(0, internal_node.size()); + + bool found; + int index; + int insert_position; + char key_mem[4 + sizeof(RID)]; + int &key = *(int *)key_mem; + RID &rid = *(RID *)(key_mem + 4); + rid.page_num = 0; + rid.slot_num = 0; + + key = 3; + internal_node.create_new_root(1, key_mem, key); + for (int i = 2; i < 5; i++) { + key = i * 2 + 1; + internal_node.insert((const char *)&key, (PageNum)key, key_comparator); + } + + ASSERT_EQ(5, internal_node.size()); + + for (int i = 1; i < 5; i++) { + key = i * 2 + 1; + int real_key = *(int*)internal_node.key_at(i); + ASSERT_EQ(key, real_key); + } + + key = 0; + index = internal_node.lookup(key_comparator, key_mem, &found, &insert_position); + ASSERT_EQ(false, found); + ASSERT_EQ(0, index); + ASSERT_EQ(1, insert_position); + + key = 2; + index = internal_node.lookup(key_comparator, key_mem, &found, &insert_position); + ASSERT_EQ(false, found); + ASSERT_EQ(0, index); + ASSERT_EQ(1, insert_position); + + key = 4; + index = internal_node.lookup(key_comparator, key_mem, &found, &insert_position); + ASSERT_EQ(false, found); + ASSERT_EQ(1, index); + ASSERT_EQ(2, insert_position); + + key = 8; + index = internal_node.lookup(key_comparator, key_mem, &found, &insert_position); + ASSERT_EQ(false, found); + ASSERT_EQ(3, index); + ASSERT_EQ(4, insert_position); + + key = 10; + index = internal_node.lookup(key_comparator, key_mem, &found, &insert_position); + ASSERT_EQ(false, found); + ASSERT_EQ(4, index); + ASSERT_EQ(5, insert_position); + + key = 12; + index = internal_node.lookup(key_comparator, key_mem, &found); + ASSERT_EQ(false, found); + ASSERT_EQ(4, index); + ASSERT_EQ(5, insert_position); + + for (int i = 1; i < 5; i++) { + key = i * 2 + 1; + index = internal_node.lookup(key_comparator, key_mem, &found); + if (!found || i != index) { + printf("found=%d, index=%d, key=%d", found, index, key); + } + ASSERT_EQ(true, found); + ASSERT_EQ(i, index); + } +} +TEST(test_bplus_tree, test_scanner) +{ + LoggerFactory::init_default("test.log"); + + DiskBufferPool::set_pool_num(POOL_NUM); + + const char *index_name = "scanner.btree"; + ::remove(index_name); + handler = new BplusTreeHandler(); + handler->create(index_name, INTS, sizeof(int), ORDER, ORDER); + + int count = 0; + RC rc = RC::SUCCESS; + RID rid; + for (int i = 0; i < 100; i++) { + int key = i * 2 + 1; + rid.page_num = 0; + rid.slot_num = key; + rc = handler->insert_entry((const char *)&key, &rid); + ASSERT_EQ(RC::SUCCESS, rc); + } + + handler->print_tree(); + + BplusTreeScanner scanner(*handler); + + int begin = -100; + int end = -20; + rc = scanner.open((const char *)&begin, false, (const char *)&end, false); + ASSERT_EQ(RC::SUCCESS, rc); + + rc = scanner.next_entry(&rid); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = -100; + end = 1; + rc = scanner.open((const char *)&begin, false, (const char *)&end, false); + ASSERT_EQ(RC::SUCCESS, rc); + rc = scanner.next_entry(&rid); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = -100; + end = 1; + rc = scanner.open((const char *)&begin, false, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + rc = scanner.next_entry(&rid); + ASSERT_EQ(RC::SUCCESS, rc); + rc = scanner.next_entry(&rid); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 1; + end = 3; + rc = scanner.open((const char *)&begin, false, (const char *)&end, false/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + rc = scanner.next_entry(&rid); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 1; + end = 3; + rc = scanner.open((const char *)&begin, true, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + count++; + } + ASSERT_EQ(2, count); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 0; + end = 3; + rc = scanner.open((const char *)&begin, true, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + count = 0; + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + count++; + } + ASSERT_EQ(2, count); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 11; + end = 21; + rc = scanner.open((const char *)&begin, true, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + count = 0; + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + count++; + } + ASSERT_EQ((end - begin) / 2 + 1, count); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 11; + end = 91; + rc = scanner.open((const char *)&begin, true, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + count = 0; + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + count++; + } + ASSERT_EQ((end - begin) / 2 + 1, count); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 191; + end = 199; + rc = scanner.open((const char *)&begin, true, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + count = 0; + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + count++; + } + ASSERT_EQ((end - begin) / 2 + 1, count); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 191; + end = 201; + rc = scanner.open((const char *)&begin, true, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + count = 0; + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + count++; + } + ASSERT_EQ(5, count); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 200; + end = 301; + rc = scanner.open((const char *)&begin, true, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + rc = scanner.next_entry(&rid); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 300; + end = 201; + rc = scanner.open((const char *)&begin, true, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::INVALID_ARGUMENT, rc); + + scanner.close(); + + begin = 300; + end = 201; + rc = scanner.open(nullptr, true, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + count = 0; + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + count++; + } + ASSERT_EQ(100, count); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 300; + end = 10; + rc = scanner.open(nullptr, true, (const char *)&end, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + count = 0; + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + count++; + } + ASSERT_EQ(5, count); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 190; + end = 10; + rc = scanner.open((const char *)&begin, true, nullptr, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + count = 0; + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + count++; + } + ASSERT_EQ(5, count); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); + + begin = 190; + end = 10; + rc = scanner.open(nullptr, true, nullptr, true/*inclusive*/); + ASSERT_EQ(RC::SUCCESS, rc); + count = 0; + while ((rc = scanner.next_entry(&rid)) == RC::SUCCESS) { + count++; + } + ASSERT_EQ(100, count); + ASSERT_EQ(RC::RECORD_EOF, rc); + + scanner.close(); +} + TEST(test_bplus_tree, test_bplus_tree_insert) { @@ -301,10 +696,7 @@ TEST(test_bplus_tree, test_bplus_tree_insert) ::remove(index_name); handler = new BplusTreeHandler(); - handler->create(index_name, INTS, sizeof(int)); - - BplusTreeTester bplus_tree_tester(*handler); - bplus_tree_tester.set_order(ORDER); + handler->create(index_name, INTS, sizeof(int), ORDER, ORDER); test_insert(); @@ -329,4 +721,4 @@ int main(int argc, char **argv) int rc = RUN_ALL_TESTS(); return rc; -} \ No newline at end of file +}