private BTreeNode Delete_entry(BTreeNode NodetobeDeleted, DBRecord key, BTreeNode Root) { BTreeCell cell; UInt16 offset; int indexInOffsetArray; (cell, offset, indexInOffsetArray) = NodetobeDeleted.FindBTreeCell(key, false); NodetobeDeleted.DeleteBTreeCell(cell); if (NodetobeDeleted.NumCells < MaxCell / 2 && NodetobeDeleted.ParentPage != 0) { return(Delete_leaf_redistri(NodetobeDeleted, key, Root)); } return(Root); }
private BTreeNode Delete_leaf_redistri(BTreeNode NodetobeDeleted, DBRecord key, BTreeNode Root) { BTreeCell cell; UInt16 offset; int indexInOffsetArray; MemoryPage parentPage = _pager.ReadPage((int)NodetobeDeleted.ParentPage); BTreeNode parentNode = new BTreeNode(parentPage); (cell, offset, indexInOffsetArray) = parentNode.FindBTreeCell(key); //the deleted node is not on the right page of parentNode if (cell != null) { MemoryPage brotherPage = _pager.ReadPage((int)NodetobeDeleted.RightPage); BTreeNode brotherNode = new BTreeNode(brotherPage); //If there is a node on the left of the deleted node,it need to be connected to the brother node. if (indexInOffsetArray >= 1) { InternalTableCell leftNodeCell = (InternalTableCell)parentNode.GetBTreeCell(parentNode.CellOffsetArray[indexInOffsetArray - 1]); MemoryPage leftPage = _pager.ReadPage((int)leftNodeCell.ChildPage); BTreeNode leftNode = new BTreeNode(leftPage); leftNode.RightPage = (uint)brotherNode.RawPage.PageNumber; } if (brotherNode.NumCells + NodetobeDeleted.NumCells <= MaxCell) //merge { //After the merge,one cell in the parentNode will be deleted parentNode.DeleteBTreeCell(cell); //merge two node for (int i = 0; i < NodetobeDeleted.NumCells; i++) { brotherNode.InsertBTreeCell(NodetobeDeleted.GetBTreeCell(NodetobeDeleted.CellOffsetArray[i])); } DeleteNode(NodetobeDeleted); } else //redistribute { BTreeCell movedCell = brotherNode.GetBTreeCell(brotherNode.CellOffsetArray[0]); DBRecord newKey = brotherNode.GetBTreeCell(brotherNode.CellOffsetArray[1]).Key; NodetobeDeleted.InsertBTreeCell(movedCell); brotherNode.DeleteBTreeCell(movedCell); BTreeCell parent_Cell; UInt16 parent_offset; int parent_indexInOffsetArray; (parent_Cell, parent_offset, parent_indexInOffsetArray) = parentNode.FindBTreeCell(key); InternalTableCell newCell = new InternalTableCell(newKey, (uint)NodetobeDeleted.RawPage.PageNumber); parentNode.DeleteBTreeCell(parent_Cell); parentNode.InsertBTreeCell(newCell); } } //The node to be deleted is on the rightpage,the brother node is on the left else { InternalTableCell brotherCell = (InternalTableCell)parentNode.GetBTreeCell(parentNode.CellOffsetArray[parentNode.NumCells - 1]); MemoryPage brotherPage = _pager.ReadPage((int)brotherCell.ChildPage); BTreeNode brotherNode = new BTreeNode(brotherPage); //The right page of brother node should be 0 brotherNode.RightPage = NodetobeDeleted.RightPage; if (brotherNode.NumCells + NodetobeDeleted.NumCells <= MaxCell) //merge { //After the merge,one cell in the parentNode will be deleted parentNode.DeleteBTreeCell(brotherCell); //merge two node for (int i = 0; i < NodetobeDeleted.NumCells; i++) { brotherNode.InsertBTreeCell(NodetobeDeleted.GetBTreeCell(NodetobeDeleted.CellOffsetArray[i])); } DeleteNode(NodetobeDeleted); parentNode.RightPage = (uint)brotherNode.RawPage.PageNumber; } else //redistribute { BTreeCell movedCell = brotherNode.GetBTreeCell(brotherNode.CellOffsetArray[brotherNode.NumCells - 1]); DBRecord newKey = movedCell.Key; NodetobeDeleted.InsertBTreeCell(movedCell); brotherNode.DeleteBTreeCell(movedCell); BTreeCell parent_Cell; UInt16 parent_offset; int parent_indexInOffsetArray; (parent_Cell, parent_offset, parent_indexInOffsetArray) = parentNode.FindBTreeCell(key); InternalTableCell newCell = new InternalTableCell(newKey, (uint)NodetobeDeleted.RawPage.PageNumber); parentNode.DeleteBTreeCell(parent_Cell); parentNode.InsertBTreeCell(newCell); } } NodetobeDeleted = parentNode; //deal with parent Nodes return(Delete_internal_redistribute(NodetobeDeleted, key, Root)); }
static void TestInsertIntoAndDeletionInsideBTreeNode() { string dbPath = "./testdbfile.minidb"; File.Delete(dbPath); Pager pager = new Pager(dbPath); pager.ExtendNumberOfPages(); pager.ExtendNumberOfPages(); MemoryPage page = pager.ReadPage(1); BTreeNode node = new BTreeNode(page, PageTypes.InternalIndexPage); // `keys` := (1, 6, 2, 5, 3, 4) List <DBRecord> keys = new List <DBRecord>(); keys.Add(GetTestBRecord(1)); keys.Add(GetTestBRecord(6)); keys.Add(GetTestBRecord(2)); keys.Add(GetTestBRecord(5)); keys.Add(GetTestBRecord(3)); keys.Add(GetTestBRecord(4)); // `cells` := ((1, 0), (6, 1), (2, 2), (5, 3), (3, 4), (4, 5)) List <InternalIndexCell> cells = new List <InternalIndexCell>(); cells.Add(new InternalIndexCell(keys[0], 114, GetTestBRecord(0))); cells.Add(new InternalIndexCell(keys[1], 114, GetTestBRecord(1))); cells.Add(new InternalIndexCell(keys[2], 114, GetTestBRecord(2))); cells.Add(new InternalIndexCell(keys[3], 114, GetTestBRecord(3))); cells.Add(new InternalIndexCell(keys[4], 114, GetTestBRecord(4))); cells.Add(new InternalIndexCell(keys[5], 114, GetTestBRecord(5))); byte[] raw = cells[0].Pack(); InternalIndexCell cellClone = new InternalIndexCell(raw, 0); node.InsertBTreeCell(cells[0]); node.InsertBTreeCell(cells[1]); node.InsertBTreeCell(cells[2]); node.InsertBTreeCell(cells[3]); node.InsertBTreeCell(cells[4]); node.InsertBTreeCell(cells[5]); List <ushort> offsets = node.CellOffsetArray; List <InternalIndexCell> clonecells = new List <InternalIndexCell>(); clonecells.Add((InternalIndexCell)node.GetBTreeCell(offsets[0])); clonecells.Add((InternalIndexCell)node.GetBTreeCell(offsets[1])); clonecells.Add((InternalIndexCell)node.GetBTreeCell(offsets[2])); clonecells.Add((InternalIndexCell)node.GetBTreeCell(offsets[3])); clonecells.Add((InternalIndexCell)node.GetBTreeCell(offsets[4])); clonecells.Add((InternalIndexCell)node.GetBTreeCell(offsets[5])); List <AtomValue> cloneCellKey = clonecells[0].Key.GetValues(); // all cells (not the list `cells`) inside `node`: // ((1, 0), (2, 2), (3, 4), (4, 5), (5, 3), (6, 1)) // they are all pointing to page #114 // check if the keys are stored in ascending order Assert.Equal(1, node.GetBTreeCell(offsets[0]).Key.GetValues()[0].IntegerValue); Assert.Equal(2, node.GetBTreeCell(offsets[1]).Key.GetValues()[0].IntegerValue); Assert.Equal(3, node.GetBTreeCell(offsets[2]).Key.GetValues()[0].IntegerValue); Assert.Equal(4, node.GetBTreeCell(offsets[3]).Key.GetValues()[0].IntegerValue); Assert.Equal(5, node.GetBTreeCell(offsets[4]).Key.GetValues()[0].IntegerValue); Assert.Equal(6, node.GetBTreeCell(offsets[5]).Key.GetValues()[0].IntegerValue); List <AtomValue> tmpAtomList = null; // check if `node` could be iterated by "foreach" statement int i = 1; foreach (var iteratedCell in node) { tmpAtomList = iteratedCell.Key.GetValues(); Assert.Equal(tmpAtomList[0].IntegerValue, i); i++; } // check node indexing AssertCell(node[0], cells[0]); AssertCell(node[1], cells[2]); BTreeCell cell; ushort offset; int indexInOffsetArray; // find by the keys below and check if it returns currect cells // key 6: value 1 (cell, offset, indexInOffsetArray) = node.FindBTreeCell(keys[1]); tmpAtomList = ((InternalIndexCell)cell).Key.GetValues(); Assert.Equal(6, tmpAtomList[0].IntegerValue); tmpAtomList = ((InternalIndexCell)cell).PrimaryKey.GetValues(); Assert.Equal(1, tmpAtomList[0].IntegerValue); // key 5: value 3 (cell, offset, indexInOffsetArray) = node.FindBTreeCell(keys[3]); tmpAtomList = ((InternalIndexCell)cell).Key.GetValues(); Assert.Equal(5, tmpAtomList[0].IntegerValue); tmpAtomList = ((InternalIndexCell)cell).PrimaryKey.GetValues(); Assert.Equal(3, tmpAtomList[0].IntegerValue); // key 2: value 2 (cell, offset, indexInOffsetArray) = node.FindBTreeCell(keys[2]); tmpAtomList = cell.Key.GetValues(); Assert.Equal(2, tmpAtomList[0].IntegerValue); tmpAtomList = ((InternalIndexCell)cell).PrimaryKey.GetValues(); Assert.Equal(2, tmpAtomList[0].IntegerValue); // delete cell with key == 2 node.DeleteBTreeCell(offset); // check deletion offsets = node.CellOffsetArray; tmpAtomList = node.GetBTreeCell(offsets[0]).Key.GetValues(); Assert.Equal(1, tmpAtomList[0].IntegerValue); tmpAtomList = node.GetBTreeCell(offsets[1]).Key.GetValues(); Assert.Equal(3, tmpAtomList[0].IntegerValue); tmpAtomList = node.GetBTreeCell(offsets[2]).Key.GetValues(); Assert.Equal(4, tmpAtomList[0].IntegerValue); tmpAtomList = node.GetBTreeCell(offsets[3]).Key.GetValues(); Assert.Equal(5, tmpAtomList[0].IntegerValue); tmpAtomList = node.GetBTreeCell(offsets[4]).Key.GetValues(); Assert.Equal(6, tmpAtomList[0].IntegerValue); // delete cell with key == 4 node.DeleteBTreeCell(offsets[2]); // check deletion offsets = node.CellOffsetArray; tmpAtomList = node.GetBTreeCell(offsets[0]).Key.GetValues(); Assert.Equal(1, tmpAtomList[0].IntegerValue); tmpAtomList = node.GetBTreeCell(offsets[1]).Key.GetValues(); Assert.Equal(3, tmpAtomList[0].IntegerValue); tmpAtomList = node.GetBTreeCell(offsets[2]).Key.GetValues(); Assert.Equal(5, tmpAtomList[0].IntegerValue); tmpAtomList = node.GetBTreeCell(offsets[3]).Key.GetValues(); Assert.Equal(6, tmpAtomList[0].IntegerValue); // delete by index 0 (cell with key == 1) node.DeleteBTreeCell(node[0]); tmpAtomList = node[0].Key.GetValues(); Assert.Equal(3, tmpAtomList[0].IntegerValue); tmpAtomList = node[1].Key.GetValues(); Assert.Equal(5, tmpAtomList[0].IntegerValue); tmpAtomList = node[2].Key.GetValues(); Assert.Equal(6, tmpAtomList[0].IntegerValue); // delete remaining cells node.DeleteBTreeCell(offsets[0]); offsets = node.CellOffsetArray; node.DeleteBTreeCell(offsets[0]); offsets = node.CellOffsetArray; node.DeleteBTreeCell(offsets[0]); offsets = node.CellOffsetArray; pager.Close(); }
private BTreeNode Delete_internal_redistribute(BTreeNode NodetobeDeleted, DBRecord key, BTreeNode Root) { InternalTableCell tmpCell; MemoryPage tmpPage; BTreeNode tmpNode; BTreeCell cell; UInt16 offset; int indexInOffsetArray; MemoryPage parentPage = null; BTreeNode parentNode = null; while (NodetobeDeleted.NumCells < (MaxCell + 1) / 2) { //The root if (NodetobeDeleted.ParentPage == 0) { if (NodetobeDeleted.NumCells == 0) { MemoryPage NewRootPage = _pager.ReadPage((int)NodetobeDeleted.RightPage); BTreeNode newRoot = new BTreeNode(NewRootPage); newRoot.ParentPage = 0; return(newRoot); } return(Root); } else { parentPage = _pager.ReadPage((int)NodetobeDeleted.ParentPage); parentNode = new BTreeNode(parentPage); (cell, offset, indexInOffsetArray) = parentNode.FindBTreeCell(key); //The node to be deleted isn't on the rightpage if (cell != null) { InternalTableCell brotherCell = null; MemoryPage brotherPage = null; BTreeNode brotherNode = null; //if the right brother is on rightpage if (indexInOffsetArray + 1 == parentNode.NumCells) { brotherPage = _pager.ReadPage((int)parentNode.RightPage); brotherNode = new BTreeNode(brotherPage); } else { brotherCell = (InternalTableCell)parentNode.GetBTreeCell(parentNode.CellOffsetArray[indexInOffsetArray + 1]); brotherPage = _pager.ReadPage((int)brotherCell.ChildPage); brotherNode = new BTreeNode(brotherPage); } //merge if (brotherNode.NumCells + NodetobeDeleted.NumCells < MaxCell) { for (int i = 0; i < NodetobeDeleted.NumCells; i++) { tmpCell = (InternalTableCell)NodetobeDeleted.GetBTreeCell(NodetobeDeleted.CellOffsetArray[i]); brotherNode.InsertBTreeCell(tmpCell); tmpPage = _pager.ReadPage((int)tmpCell.ChildPage); tmpNode = new BTreeNode(tmpPage); tmpNode.ParentPage = (uint)brotherNode.RawPage.PageNumber; } //move the cell in parentNode to brotherNode InternalTableCell insertCell = new InternalTableCell(cell.Key, (uint)NodetobeDeleted.RightPage); brotherNode.InsertBTreeCell(insertCell); tmpPage = _pager.ReadPage((int)insertCell.ChildPage); tmpNode = new BTreeNode(tmpPage); tmpNode.ParentPage = (uint)brotherNode.RawPage.PageNumber; DeleteNode(NodetobeDeleted); parentNode.DeleteBTreeCell(cell); } //redistribute else { InternalTableCell movedCell = (InternalTableCell)brotherNode.GetBTreeCell(brotherNode.CellOffsetArray[0]); DBRecord upperKey = movedCell.Key; DBRecord downKey = cell.Key; InternalTableCell insertDeletedCell = new InternalTableCell(downKey, (uint)NodetobeDeleted.RightPage); NodetobeDeleted.InsertBTreeCell(insertDeletedCell); NodetobeDeleted.RightPage = movedCell.ChildPage; tmpPage = _pager.ReadPage((int)movedCell.ChildPage); tmpNode = new BTreeNode(tmpPage); tmpNode.ParentPage = (uint)NodetobeDeleted.RawPage.PageNumber; InternalTableCell insertParentCell = new InternalTableCell(upperKey, (uint)NodetobeDeleted.RawPage.PageNumber); parentNode.DeleteBTreeCell(cell); parentNode.InsertBTreeCell(insertParentCell); brotherNode.DeleteBTreeCell(movedCell); } } //The node to be deleted is on the rightpage else { InternalTableCell brotherCell = null; MemoryPage brotherPage = null; BTreeNode brotherNode = null; brotherCell = (InternalTableCell)parentNode.GetBTreeCell(parentNode.CellOffsetArray[parentNode.NumCells - 1]); brotherPage = _pager.ReadPage((int)brotherCell.ChildPage); brotherNode = new BTreeNode(brotherPage); //merge if (brotherNode.NumCells + NodetobeDeleted.NumCells < MaxCell) { for (int i = 0; i < brotherNode.NumCells; i++) { tmpCell = (InternalTableCell)brotherNode.GetBTreeCell(brotherNode.CellOffsetArray[i]); NodetobeDeleted.InsertBTreeCell(tmpCell); tmpPage = _pager.ReadPage((int)tmpCell.ChildPage); tmpNode = new BTreeNode(tmpPage); tmpNode.ParentPage = (uint)NodetobeDeleted.RawPage.PageNumber; } //move the cell in parentNode to brotherNode InternalTableCell insertCell = new InternalTableCell(brotherCell.Key, (uint)brotherNode.RightPage); NodetobeDeleted.InsertBTreeCell(insertCell); tmpPage = _pager.ReadPage((int)insertCell.ChildPage); tmpNode = new BTreeNode(tmpPage); tmpNode.ParentPage = (uint)NodetobeDeleted.RawPage.PageNumber; DeleteNode(brotherNode); parentNode.DeleteBTreeCell(parentNode.GetBTreeCell(parentNode.CellOffsetArray[parentNode.NumCells - 1])); } //redistribute else { DBRecord downKey = parentNode.GetBTreeCell(parentNode.CellOffsetArray[parentNode.NumCells - 1]).Key; InternalTableCell insertDeletedCell = new InternalTableCell(downKey, brotherNode.RightPage); NodetobeDeleted.InsertBTreeCell(insertDeletedCell); InternalTableCell movedCell = (InternalTableCell)brotherNode.GetBTreeCell(brotherNode.CellOffsetArray[brotherNode.NumCells - 1]); brotherNode.RightPage = movedCell.ChildPage; DBRecord upperKey = movedCell.Key; InternalTableCell insertParentCell = new InternalTableCell(upperKey, (uint)brotherNode.RawPage.PageNumber); parentNode.InsertBTreeCell(insertParentCell); brotherNode.DeleteBTreeCell(movedCell); tmpPage = _pager.ReadPage((int)brotherNode.RightPage); tmpNode = new BTreeNode(tmpPage); tmpNode.ParentPage = (uint)brotherNode.RawPage.PageNumber; } } } NodetobeDeleted = parentNode; } return(Root); }
/// <summary> /// Split `nodeTobeSplit`, which has a `parantNode`. /// </summary> /// <param name="nodeTobeSplit">node to be split.</param> /// <param name="parantNode">parent node of `nodeTobeSplit`.</param> /// <param name="newKey">the value of new key. This could affect where to split.</param> private void SplitNode(BTreeNode nodeTobeSplit, BTreeNode parantNode, DBRecord newKey) { int i; // new node will be appended to `nodeTobeSplit` BTreeNode splitNode = GetNewNode(nodeTobeSplit.PageType); // tmp is used for the change of parentPage InternalTableCell tmpCell; MemoryPage tmpPage; // key should be the primary key to be inserted in the parent node int deleteIndex = nodeTobeSplit.NumCells / 2; // right bound of the left node DBRecord rightmostKeyInLeftNode = nodeTobeSplit[deleteIndex - 1].Key; if ((newKey.GetValues()[0] >= rightmostKeyInLeftNode.GetValues()[0]).BooleanValue) { // the new key should be put at the split node on the right deleteIndex++; rightmostKeyInLeftNode = nodeTobeSplit[deleteIndex - 1].Key; } // `deleteIndex` is now pointing to the first cell of the pending right node // loop through the right part of `nodeTobeSplit` for (i = deleteIndex; i < this.MaxCell; i++) { ushort deleteOffSet = nodeTobeSplit.CellOffsetArray[deleteIndex]; // change the parent Page fields of their children if (nodeTobeSplit.PageType == PageTypes.InternalTablePage) { tmpCell = (InternalTableCell)nodeTobeSplit.GetBTreeCell(deleteOffSet); tmpPage = _pager.ReadPage((int)tmpCell.ChildPage); new BTreeNode(tmpPage) { ParentPage = (uint)splitNode.RawPage.PageNumber }; } // move the cells in `nodeTobeSplit` belonging to the pending right node to `splitNode` splitNode.InsertBTreeCell(nodeTobeSplit.GetBTreeCell(deleteOffSet)); nodeTobeSplit.DeleteBTreeCell(deleteOffSet); } // new cell to be inserted to the parent node of `nodeTobeSplit` // If the `parentNode` need to spilt, this will be wrong. However, it could be ensured not happenning because it is a top-down process. InternalTableCell newCellToAscend = new InternalTableCell(rightmostKeyInLeftNode, (uint)nodeTobeSplit.RawPage.PageNumber); // make alias for readability BTreeNode leftNode = nodeTobeSplit; BTreeNode rightNode = splitNode; // connect two nodes `leftNode` and `rightNode` by `RightPage` pointer rightNode.RightPage = leftNode.RightPage; if (leftNode.PageType == PageTypes.LeafTablePage) { leftNode.RightPage = (uint)rightNode.RawPage.PageNumber; } // if `leftNode` is an internal node, the middle cell in the original `nodeTobeSplit` (the rightmost cell in the now `leftNode`) has to be deleted else if (leftNode.PageType == PageTypes.InternalTablePage) { // for internal node, the `ParentPage` of child page need to be changed // the rightmost child of `leftNode` sets its parent to `rightNode` tmpPage = _pager.ReadPage((int)leftNode.RightPage); new BTreeNode(tmpPage) { ParentPage = (uint)rightNode.RawPage.PageNumber }; // the rightmost Cell In the pending Left Node InternalTableCell rightmostCellInLeftNode = (InternalTableCell)leftNode[deleteIndex - 1]; // set the rightmost child of `leftNode` to the left child of `rightmostCellInLeftNode` leftNode.RightPage = rightmostCellInLeftNode.ChildPage; // leftNode.DeleteBTreeCell(rightmostCellInLeftNode); // the same as the code below, but the code below runs faster leftNode.DeleteBTreeCell(leftNode.CellOffsetArray[deleteIndex - 1]); // the deleted rightmost cell in left node sets the parent of its left child to that left node tmpPage = _pager.ReadPage((int)rightmostCellInLeftNode.ChildPage); new BTreeNode(tmpPage) { ParentPage = (uint)leftNode.RawPage.PageNumber }; } rightNode.ParentPage = (uint)parantNode.RawPage.PageNumber; // reconnect the particular cell (or right) in the parent node because of the change of the child node // This is a new empty root if (parantNode.NumCells == 0) { parantNode.RightPage = (uint)rightNode.RawPage.PageNumber; } // There are some cell in the parents node else { BTreeCell cell; (cell, _, _) = parantNode.FindBTreeCell(rightmostKeyInLeftNode); // The case when the node to be inserted into parent node is in the rightest position if (cell == null) { parantNode.RightPage = (uint)rightNode.RawPage.PageNumber; } // The normal case else { InternalTableCell tmp_cell = new InternalTableCell(cell.Key, (uint)rightNode.RawPage.PageNumber); parantNode.DeleteBTreeCell(cell); parantNode.InsertBTreeCell(tmp_cell); } } // This must be done after we reconnect the treeNode parantNode.InsertBTreeCell(newCellToAscend); }