public BTree(uint topSid, SegmentManager sm, IKey keyFactory) { this.m_top_sid = topSid; this.m_sgManager = sm; this.m_keyFactory = keyFactory; this.m_nodeFactory = new BNode(); }
/// <summary> /// Create the internal storage for the dbFile. /// Note: /// 1. a helper file for segment managment b-tree is also created if this database file /// does not exist. /// </summary> /// <param name="dbFile"></param> public ObjectStore(string dbFileName) { m_dbFile = new DiskFile(dbFileName); m_indexFile = new DiskFile(dbFileName+".idx"); m_indexPageManager = new memSegmentManager(m_indexFile); if (m_indexPageManager.Header.InitializeNeeded) { m_indexPageManager.Header.NextSegmentId = 0; m_indexPageManager.Header.NextObjectId = 0; m_indexPageManager.Header.NextClassId = 0; //initialize the segment b-tree's top node id OOD.Imp.Storage.BNode segTreeTop = new OOD.Imp.Storage.BNode(); m_indexPageManager.GetNewSegment(segTreeTop); segTreeTop.SetOrder(m_segTreeNodeOrder); segTreeTop.Leaf = true; m_indexPageManager.Header.SegmentTreeTopNodeSId = segTreeTop.SegmentID; //initialize the space b-tree's top node id BNode spaceTreeTop = new BNode(); m_indexPageManager.GetNewSegment(spaceTreeTop); spaceTreeTop.SetOrder(m_spaceTreeNodeOrder); spaceTreeTop.Leaf = true; m_indexPageManager.Header.FreeSpaceTreeTopNodeSId = spaceTreeTop.SegmentID; } m_segmentIndexTree = new SegTree(m_indexPageManager.Header.SegmentTreeTopNodeSId, m_indexPageManager); m_spaceIndexTree = new SpaceTree(m_indexPageManager.Header.FreeSpaceTreeTopNodeSId, m_indexPageManager, m_dbFile); m_dbSegmentManager = new dbSegmentManager( m_indexPageManager.Header.NextSegmentId, m_segmentIndexTree, m_spaceIndexTree, m_dbFile); if (m_indexPageManager.Header.InitializeNeeded) { BNode catalogTop = new BNode(); m_dbSegmentManager.GetNewSegment(catalogTop); catalogTop.SetOrder(m_catalogTreeNodeOrder); catalogTop.Leaf = true; m_indexPageManager.Header.CatalogTreeTopNodeSId = catalogTop.SegmentID; } m_catalogTree = new CatalogTree( m_indexPageManager.Header.CatalogTreeTopNodeSId, m_dbSegmentManager, m_indexPageManager.Header.NextClassId); m_classCache = new LRUHashtable(m_cache_size, m_classCacheLimit); m_objectTreeCache = new LRUHashtable(m_cache_size, m_objectTreeCacheLimit); //bring top node's segment tree, space tree, catalog tree into cache, they will be needed anyway if (!m_indexPageManager.Header.InitializeNeeded) { m_indexPageManager.GetSegment(m_segmentIndexTree.TopNodSegId, new BNode(), new KSegId()); m_indexPageManager.GetSegment(m_spaceIndexTree.TopNodeSId, new BNode(), new KOffset()); m_dbSegmentManager.GetSegment(m_catalogTree.TopNodeSId, new BNode(), new KCatalog()); } }
/* operations regarding class information, scheme */ public bool InsertClassIno(string className, string[] fieldNames, ref uint assignedCId, ref uint topNodeSId) { if (m_catalogTree.Search(className) != null) return false; //reserve a segment id for this class clustering's top node BNode classTopSid = new BNode(); m_dbSegmentManager.GetNewSegment(classTopSid); classTopSid.SetOrder(m_classTreeNodeOrder); classTopSid.Leaf = true; topNodeSId = classTopSid.SegmentID; if (m_catalogTree.Insert(className, fieldNames, classTopSid.SegmentID, ref assignedCId) == false) throw new OOD.Exception.ProgramLogicError( this, "Inserting class information failed."); return true; }
public bool MoveNext() { m_current_key_no ++; if (m_current_key_no < m_current_node.KeyNums) { m_current = m_current_node.GetKeyAt(m_current_key_no); } else { //this node is used up, try next one if (m_to_be_visited.Count >0) { m_current_node = (BNode)m_tree.m_sgManager.GetSegment( (uint)m_to_be_visited.Dequeue(), m_tree.m_nodeFactory, m_tree.m_keyFactory); Debug.Assert(m_current_node.KeyNums>0); if (!m_current_node.Leaf) { for (int i=0; i<=m_current_node.KeyNums; i++) { Debug.Assert(m_current_node.GetChildAt(i) >= 0); m_to_be_visited.Enqueue(m_current_node.GetChildAt(i)); } } //locate the current to be the first one in the new node m_current_key_no = 0; m_current = m_current_node.GetKeyAt(0); } else m_current = null; } return m_current != null; }
public BTEnumerator(BTree tree) { m_tree = tree; m_to_be_visited = new System.Collections.Queue(); m_current = null; m_current_node = (BNode) m_tree.m_sgManager.GetSegment(m_tree.m_top_sid, m_tree.m_nodeFactory, m_tree.m_keyFactory); if (m_current_node.KeyNums >0) { for (int i=0; i<=m_current_node.KeyNums; i++) { m_to_be_visited.Enqueue(m_current_node.GetChildAt(i)); } } m_current_key_no = -1; }
/// <summary> /// Given a key, search the node where the key is stored in the tree with root as the root node. /// </summary> /// <param name="root"></param> /// <param name="key"></param> /// <param name="pos">the location the key is stored in</param> /// <param name="visisted">all the visisted parent is saved in the stack.</param> /// <returns></returns> public BNode FindNode(BNode root, IKey key, ref int pos, System.Collections.Stack visited, System.Collections.Stack via) { Debug.Assert(visited != null); BNode n = root; pos = -1; IKey temp = null; while (!n.Leaf) { temp = n.SearchKey(key, ref pos); if (temp == null) { uint nextNodeId = n.GetChildAt(pos); visited.Push(n); via.Push(pos); n = (BNode)m_sgManager.GetSegment(nextNodeId, m_nodeFactory, m_keyFactory); } else return n; } //n is leaf temp = n.SearchKey(key, ref pos); if (temp == null) return null; else return n; }
/// <summary> /// Insert the newKey into this B-tree, /// </summary> /// <param name="newKey"></param> /// <returns></returns> public bool Insert(IKey newKey) { //find the leaf where the newKey should be in BNode n = m_top; System.Collections.Stack visited = new System.Collections.Stack(); int pos = -1; while (!n.Leaf) { IKey temp = n.SearchKey(newKey, ref pos); if (temp == null) { uint nextNodeId = n.GetChildAt(pos); visited.Push(n); n = (BNode)m_sgManager.GetSegment(nextNodeId, m_nodeFactory, m_keyFactory); } else return false; } //now BNode n is the leaf where insert should happen IKey t_temp = n.SearchKey(newKey, ref pos); if (t_temp == null) { //not exists, go ahead to insert the new key if (!n.IsFull) { n.InsertAtLeaf(newKey, pos); return true; } else { //split needed for this node BNode right = new BNode(); m_sgManager.GetNewSegment(right); IKey median = null; n.SplitAtLeaf(newKey, pos, ref median, ref right); //this split is at leaf bool finished = false; //now n holds the left half of the items, //right holds the right half items, median is the middle key while (!finished) { //parent is node middle key will be inserted BNode parent = (visited.Count >0 ? (BNode)visited.Pop() : null); if (parent == null) { //new top node is needed BNode new_top = new BNode(); m_sgManager.GetNewSegment(new_top); new_top.SetOrder(m_top.Order); new_top.Leaf = false; new_top.InsertFirstKey(median, n.SegmentID, right.SegmentID); this.m_top_sid = new_top.SegmentID; return true; } else { IKey tt = parent.SearchKey(median, ref pos); if (tt != null) return false; if (!parent.IsFull) { parent.InsertAtInternal(median, pos, right.SegmentID); return true; } else { //parent is full again BNode newRight = new BNode(); m_sgManager.GetNewSegment(newRight); newRight.SetOrder(parent.Order); newRight.Leaf = parent.Leaf; //this split will insert median into the parent, then split and new middle key is newMedian IKey newMedian; parent.SplitAtInternal(median, pos, right.SegmentID, out newMedian, newRight); n = parent; median = newMedian; right = newRight; } } } } } else return false; return false; }
/// <summary> /// Distribute one key from left to right /// </summary> /// <param name="pos"></param> /// <param name="left"></param> /// <param name="right"></param> public void RedistributeLeft2Right(int pos, BNode left, BNode right) { Debug.Assert(pos >=0 && pos < m_keys.Length); Debug.Assert(m_children[pos] == left.SegmentID); Debug.Assert(m_children[pos+1] == right.SegmentID); Debug.Assert(m_keys[pos].CompareTo(left.m_keys[left.m_keyNums-1])>0); Debug.Assert(m_keys[pos].CompareTo(right.m_keys[right.m_keyNums-1])<0); Debug.Assert(right.KeyNums < m_order - 1); //shift everything in right for the new key for (int i=right.KeyNums; i>0; i--) { right.m_keys[i] = right.m_keys[i-1]; right.m_children[i+1] = right.m_children[i]; } right.m_children[1] = right.m_children[0]; //copy middle key in parent to the first one of right right.m_keys[0] = m_keys[pos]; right.m_children[0] = left.m_children[left.KeyNums]; //copy last pointer to be the first pointer in new right node right.m_keyNums++; //copy the last key in left into parent m_keys[pos] = left.m_keys[left.m_keyNums-1]; //remove the last key from left left.m_keyNums--; this.m_dirty = true; left.m_dirty = true; right.m_dirty = true; }
/// <summary> /// Note: /// 1. helper should be the realy key used in the b-tree. /// </summary> /// <param name="segmentId"></param> /// <param name="helper"></param> /// <param name="bytes"></param> /// <param name="offset"></param> /// <param name="count"></param> public override Segment Deserialize(uint segmentId, object helper, byte[] bytes, int offset, int count) { IKey keyFactory = (IKey)helper; int pos = offset; short order = OOD.Utility.Bytes.Unpack2(bytes, pos); BNode result = new BNode(segmentId, order); pos += 2; result.m_leaf = (bytes[pos++] == 1); result.m_keyNums = OOD.Utility.Bytes.Unpack2(bytes, pos); pos += 2; for (int i=0; i<result.m_keyNums; i++) { int length = OOD.Utility.Bytes.Unpack2(bytes, pos); pos += 2; result.m_keys[i] = keyFactory.Deserialize(bytes, pos, length); pos += length; } for (int i=0; i<=result.m_keyNums; i++) { result.m_children[i] = OOD.Utility.Bytes.Unpack4U(bytes, pos); pos += 4; } result.m_dirty = false; return result; }
/// <summary> /// Combine the left, right, along with the key in pos of this node into the new one, /// and keep left, clear right one. /// </summary> /// <param name="pos">the location of the connecting key in this node(parent)</param> /// <param name="left"></param> /// <param name="right"></param> public void CombineChildren(int pos, BNode left, BNode right) { Debug.Assert(pos >=0 && pos < m_keys.Length); Debug.Assert(m_children[pos] == left.SegmentID); Debug.Assert(m_children[pos+1] == right.SegmentID); Debug.Assert(m_keys[pos].CompareTo(left.m_keys[left.m_keyNums-1])>0); Debug.Assert(m_keys[pos].CompareTo(right.m_keys[right.m_keyNums-1])<0); left.m_keys[left.m_keyNums] = m_keys[pos]; left.m_children[left.m_keyNums+1] = right.m_children[0]; left.m_keyNums ++ ; for (int i=0; i<right.m_keyNums; i++) { left.m_keys[left.m_keyNums++] = right.m_keys[i]; left.m_children[left.m_keyNums] = right.m_children[i+1]; } right.m_keyNums = 0; //remove pos record in the parent Remove(pos); this.m_dirty = true; left.m_dirty = true; right.m_dirty = true; }
/// <summary> /// Redistribute one from right to left /// </summary> /// <param name="pos">pos is pointing the middle connecting key in between the left and right.</param> /// <param name="left"></param> /// <param name="right"></param> public void RedistributeRight2Left(int pos, BNode left, BNode right) { Debug.Assert(pos >=0 && pos < m_keys.Length); Debug.Assert(m_children[pos] == left.SegmentID); Debug.Assert(m_children[pos+1] == right.SegmentID); Debug.Assert(m_keys[pos].CompareTo(left.m_keys[left.m_keyNums-1])>0); Debug.Assert(m_keys[pos].CompareTo(right.m_keys[right.m_keyNums-1])<0); //copy middle key in parent to tail of left left.m_keys[left.m_keyNums++] = m_keys[pos]; left.m_children[left.m_keyNums] = right.m_children[0]; //move first pointer in right to be the last in new left //replace the middle key with the first key in right m_keys[pos] = right.m_keys[0]; //remove the first key along with the first pointer from right for (int i=1; i<right.KeyNums; i++) { right.m_keys[i-1] = right.m_keys[i]; right.m_children[i-1] = right.m_children[i]; } right.m_keyNums --; //move the last one right.m_children[right.m_keyNums] = right.m_children[right.m_keyNums+1]; this.m_dirty = true; left.m_dirty = true; right.m_dirty = true; }
/// <summary> /// Split the internal node with the newKey which is trying to insert this internal node /// </summary> /// <param name="newKey"></param> /// <param name="pos"></param> /// <param name="right"></param> /// <param name="newMedian"></param> /// <param name="newRight"></param> public void SplitAtInternal(IKey newKey, int pos, uint rightSon, out IKey newMedian, BNode newRight) { Debug.Assert(this.m_leaf == false); Debug.Assert(this.IsFull); IKey[] temp = new IKey[m_order]; uint[] t_pointer = new uint[m_order + 1]; //copy old data into new arrays Array.Copy(m_keys, 0, temp, 0, m_order-1); Array.Copy(m_children, 0, t_pointer, 0, m_order); //shift everything after this key right for (int i=this.m_keyNums; i>pos; i--) { temp[i] = temp[i-1]; t_pointer[i+1] = t_pointer[i]; } temp[pos] = newKey; t_pointer[pos+1] = rightSon; //copy left half back this node int mid = m_order >> 1; Array.Copy(temp,0,m_keys,0,mid); Array.Copy(t_pointer,0,m_children,0,mid+1); newMedian = temp[mid]; Array.Copy(temp,mid+1,newRight.m_keys,0,m_order-mid-1); Array.Copy(t_pointer,mid+1,newRight.m_children,0,m_order-mid); m_keyNums = (short)mid; newRight.m_keyNums = (short)(m_order - mid -1); this.m_dirty = true; newRight.m_dirty = true; }
/// <summary> /// split the leaf into two, leave left half in current leaf, and move right half into right node /// </summary> /// <param name="newKey"></param> /// <param name="median">the middle key is popped up</param> /// <param name="right"></param> public void SplitAtLeaf(IKey newKey, int pos, ref IKey median, ref BNode right) { Debug.Assert(this.m_leaf); Debug.Assert(this.IsFull); right.SetOrder(this.m_order); right.m_leaf = this.m_leaf; int mid = m_order >> 1; //copy current keys into a bigger array IKey[] temp = new IKey[m_order]; Array.Copy(m_keys,0,temp,0,m_order - 1); //insert the newKey into the pos location //shift later keys to the right for (int i=this.m_keyNums; i>pos; i--) { temp[i] = temp[i-1]; } temp[pos] = newKey; median = temp[mid]; //copy left half to current leaf for (int i=0; i<mid; i++) { m_keys[i] = temp[i]; } //copy right half to right leaf for (int i=mid+1; i<temp.Length; i++) { right.m_keys[i-mid-1] = temp[i]; } m_keyNums = (short)mid; right.m_keyNums = (short)(m_order - 1 - mid); this.m_dirty = true; right.m_dirty = true; }
/// <summary> /// Create the internal storage for the dbFile. /// Note: /// 1. a helper file for segment managment b-tree is also created if this database file /// does not exist. /// </summary> /// <param name="dbFile"></param> public ObjectStore(string dbFileName) { m_dbFile = new DiskFile(dbFileName); m_indexFile = new DiskFile(dbFileName + ".idx"); m_indexPageManager = new memSegmentManager(m_indexFile); if (m_indexPageManager.Header.InitializeNeeded) { m_indexPageManager.Header.NextSegmentId = 0; m_indexPageManager.Header.NextObjectId = 0; m_indexPageManager.Header.NextClassId = 0; //initialize the segment b-tree's top node id OOD.Imp.Storage.BNode segTreeTop = new OOD.Imp.Storage.BNode(); m_indexPageManager.GetNewSegment(segTreeTop); segTreeTop.SetOrder(m_segTreeNodeOrder); segTreeTop.Leaf = true; m_indexPageManager.Header.SegmentTreeTopNodeSId = segTreeTop.SegmentID; //initialize the space b-tree's top node id BNode spaceTreeTop = new BNode(); m_indexPageManager.GetNewSegment(spaceTreeTop); spaceTreeTop.SetOrder(m_spaceTreeNodeOrder); spaceTreeTop.Leaf = true; m_indexPageManager.Header.FreeSpaceTreeTopNodeSId = spaceTreeTop.SegmentID; } m_segmentIndexTree = new SegTree(m_indexPageManager.Header.SegmentTreeTopNodeSId, m_indexPageManager); m_spaceIndexTree = new SpaceTree(m_indexPageManager.Header.FreeSpaceTreeTopNodeSId, m_indexPageManager, m_dbFile); m_dbSegmentManager = new dbSegmentManager( m_indexPageManager.Header.NextSegmentId, m_segmentIndexTree, m_spaceIndexTree, m_dbFile); if (m_indexPageManager.Header.InitializeNeeded) { BNode catalogTop = new BNode(); m_dbSegmentManager.GetNewSegment(catalogTop); catalogTop.SetOrder(m_catalogTreeNodeOrder); catalogTop.Leaf = true; m_indexPageManager.Header.CatalogTreeTopNodeSId = catalogTop.SegmentID; } m_catalogTree = new CatalogTree( m_indexPageManager.Header.CatalogTreeTopNodeSId, m_dbSegmentManager, m_indexPageManager.Header.NextClassId); m_classCache = new LRUHashtable(m_cache_size, m_classCacheLimit); m_objectTreeCache = new LRUHashtable(m_cache_size, m_objectTreeCacheLimit); //bring top node's segment tree, space tree, catalog tree into cache, they will be needed anyway if (!m_indexPageManager.Header.InitializeNeeded) { m_indexPageManager.GetSegment(m_segmentIndexTree.TopNodSegId, new BNode(), new KSegId()); m_indexPageManager.GetSegment(m_spaceIndexTree.TopNodeSId, new BNode(), new KOffset()); m_dbSegmentManager.GetSegment(m_catalogTree.TopNodeSId, new BNode(), new KCatalog()); } }
/// <summary> /// Remove the key from the tree. /// </summary> /// <param name="key"></param> /// <returns></returns> public bool Delete(IKey key) { int pos = -1; System.Collections.Stack visited = new System.Collections.Stack(); System.Collections.Stack viaLinks = new System.Collections.Stack(); //find the node which contains the key BNode n = FindNode(m_top, key, ref pos, visited, viaLinks); if (n == null) { return(false); } else { if (n.Leaf) { n.RemoveAtLeaf(key, pos); } else { visited.Push(n); uint nextNodeId = n.GetChildAt(pos + 1); viaLinks.Push(pos + 1); BNode t_node = (BNode)m_sgManager.GetSegment(nextNodeId, m_nodeFactory, m_keyFactory); //find the leaf most leaf in its right sub-tree while (!t_node.Leaf) { visited.Push(t_node); nextNodeId = t_node.GetChildAt(0); viaLinks.Push(0); t_node = (BNode)m_sgManager.GetSegment(nextNodeId, m_nodeFactory, m_keyFactory); } Debug.Assert(t_node.Leaf); IKey successor = t_node.GetKeyAt(0); //replace the key&data in n with the successor n.ReplaceKeyDataWSuccessor(key, successor, pos); //remove successor from the leaf node t_node.RemoveAtLeaf(successor, 0); n = t_node; } } //now n is the leaf node where the real deletion happens //visited keep all the parents visited so far, viaLinks keeps which links we followed while (n.IsUnderflow && n.SegmentID != m_top_sid) { BNode parent = (BNode)visited.Pop(); //get left/right brother int followed = (int)viaLinks.Pop(); BNode left = (followed > 0? (BNode)m_sgManager.GetSegment(parent.GetChildAt(followed - 1), m_nodeFactory, m_keyFactory) : null); BNode right = (followed < parent.KeyNums ? (BNode)m_sgManager.GetSegment(parent.GetChildAt(followed + 1), m_nodeFactory, m_keyFactory) : null); Debug.Assert(left != null || right != null); bool combined = false; //try combin with right first if (right != null && right.KeyNums == right.ReqMinimum) { //combine with the right parent.CombineChildren(followed, n, right); Debug.Assert(right.KeyNums == 0); Debug.Assert(n.KeyNums > n.ReqMinimum); m_sgManager.FreeSegment(right); combined = true; if (parent.KeyNums == 0) { Debug.Assert(parent.Leaf == false); Debug.Assert(parent.SegmentID == this.m_top_sid); //tree will shrink this.m_top_sid = n.SegmentID; m_sgManager.FreeSegment(parent); break; } } else if (left != null && left.KeyNums == left.ReqMinimum) { //combine with the left parent.CombineChildren(followed - 1, left, n); Debug.Assert(n.KeyNums == 0); Debug.Assert(left.KeyNums > left.ReqMinimum); m_sgManager.FreeSegment(n); combined = true; if (parent.KeyNums == 0) { Debug.Assert(parent.Leaf == false); Debug.Assert(parent.SegmentID == this.m_top_sid); //tree will shrink this.m_top_sid = left.SegmentID; m_sgManager.FreeSegment(parent); break; } } if (!combined) { //try redistrubute if combine is not possible if (right != null && right.KeyNums > right.ReqMinimum) { //redistribute one entry from right node parent.RedistributeRight2Left(followed, n, right); } else if (left != null && left.KeyNums > left.ReqMinimum) { //redistribute with left parent.RedistributeLeft2Right(followed - 1, left, n); } } else { n = parent; } } return(true); }
/// <summary> /// Insert the newKey into this B-tree, /// </summary> /// <param name="newKey"></param> /// <returns></returns> public bool Insert(IKey newKey) { //find the leaf where the newKey should be in BNode n = m_top; System.Collections.Stack visited = new System.Collections.Stack(); int pos = -1; while (!n.Leaf) { IKey temp = n.SearchKey(newKey, ref pos); if (temp == null) { uint nextNodeId = n.GetChildAt(pos); visited.Push(n); n = (BNode)m_sgManager.GetSegment(nextNodeId, m_nodeFactory, m_keyFactory); } else { return(false); } } //now BNode n is the leaf where insert should happen IKey t_temp = n.SearchKey(newKey, ref pos); if (t_temp == null) { //not exists, go ahead to insert the new key if (!n.IsFull) { n.InsertAtLeaf(newKey, pos); return(true); } else { //split needed for this node BNode right = new BNode(); m_sgManager.GetNewSegment(right); IKey median = null; n.SplitAtLeaf(newKey, pos, ref median, ref right); //this split is at leaf bool finished = false; //now n holds the left half of the items, //right holds the right half items, median is the middle key while (!finished) { //parent is node middle key will be inserted BNode parent = (visited.Count > 0 ? (BNode)visited.Pop() : null); if (parent == null) { //new top node is needed BNode new_top = new BNode(); m_sgManager.GetNewSegment(new_top); new_top.SetOrder(m_top.Order); new_top.Leaf = false; new_top.InsertFirstKey(median, n.SegmentID, right.SegmentID); this.m_top_sid = new_top.SegmentID; return(true); } else { IKey tt = parent.SearchKey(median, ref pos); if (tt != null) { return(false); } if (!parent.IsFull) { parent.InsertAtInternal(median, pos, right.SegmentID); return(true); } else { //parent is full again BNode newRight = new BNode(); m_sgManager.GetNewSegment(newRight); newRight.SetOrder(parent.Order); newRight.Leaf = parent.Leaf; //this split will insert median into the parent, then split and new middle key is newMedian IKey newMedian; parent.SplitAtInternal(median, pos, right.SegmentID, out newMedian, newRight); n = parent; median = newMedian; right = newRight; } } } } } else { return(false); } return(false); }