/* Recursively inserts the record based on Sedgewick's algorithm */ func (self *BTree) insert(block *KeyBlock, rec *Record, height int, dirty *dirty.DirtyBlocks) (*KeyBlock, *Record, bool) { // fmt.Println("inserting", rec, "\n", block, height) var nextb *KeyBlock if height > 0 { // at an interior node var pos ByteSlice { // we need to find the next block to search k := rec.GetKey() i, _, _, _, _ := block.Find(k) // find where the key would go in the block if i >= int(block.RecordCount()) { i-- // is it after the last key? } r, left, right, ok := block.Get(i) // get the record if ok && (k.Lt(r.GetKey())) && left != nil { pos = left // hey it goes on the left } else if ok && right != nil { pos = right // the right } else { fmt.Println("Bad block pointer in interior node PANIC, for real? ", ok) fmt.Println(block) os.Exit(4) } } // recursive insert call, s is true we a node split occured in the level below so we change our insert if b, r, s := self.insert(self.getblock(pos), rec, height-1, dirty); s { // a node split occured in the previous call // so we are going to use this new record now nextb = b rec = r } else { // no node split we return to the parent saying it has nothing to do return nil, nil, false } } // this block is changed dirty.Insert(block) if i, ok := block.Add(rec); ok { // Block isn't full record inserted, now insert pointer (if one exists) // return to parent saying it has nothing to do if nextb != nil { block.InsertPointer(i+1, nextb.Position()) } return nil, nil, false } // Block is full split the block return self.split(block, rec, nextb, dirty) }
func (self *BpTree) split(a *KeyBlock, rec *tmprec, nextb *KeyBlock, dirty *dirty.DirtyBlocks) (*KeyBlock, *tmprec, bool) { // fmt.Println("Splitting", rec.internal()) // fmt.Println("Original:\n", a) // this closure figures out which kind of block and record we need, either external or internal // we make this choice based on the mode of a because b+ trees have the property that the // allocated block always has the same node as the block to be split. b, r := func() (*KeyBlock, *Record) { if a.Mode() == self.external.Mode { return self.allocate(self.external), rec.external() } return self.allocate(self.internal), rec.internal() }() dirty.Insert(b) // m is the record to take as the "mid" point it is no longer stictly the mid point because // one must be careful to not split up duplicate keys. instead m is the closest point to // the mid point which is on the edge of the run of duplicate keys // // s is the point which the blocks should be balanced against. (ie the balance point) // // right is the block to insert into, true is a, false is b m, s, right := func() (m int, s int, right bool) { getk := func(i int) ByteSlice { r, _, _, _ := a.Get(i) return r.GetKey() } n := int(a.MaxRecordCount()) + 1 key := getk(n >> 1) l, _, _, _, _ := a.Find(key) // l = left the left side of the run of dup keys r := l // r = right the right side of the run of dup keys for ; r < n-1 && getk(r).Eq(key); r++ { } r-- lr := math.Abs(float64(l) / float64(n)) // left ratio (ie. how close is left to mid) rr := math.Abs(float64(r) / float64(n)) // right ration (ie. how close is right to mid) // we return which ever has a ratio closer to zero if lr <= rr && l != 0 { m = l s = l - 1 // since it is the left one we *must* subtract one from the balance point right = false } else { m = r s = r right = true } return }() // This dependent block finds which record or split the block on. // it also identifies which the record should point at var split_rec *Record var nextp ByteSlice { i, _, _, _, ok := a.Find(r.GetKey()) // fmt.Printf("m=%v, s=%v, i=%v, choice=%v\n", m, s, i, right) // so what is going on here is if the key is in a certain block we need to make sure // we insert our next key in that block. This is the cleanest way to do that for now. even // though it is hideously ugly. if ok && m > i { // fmt.Println("choosing a", i, m, ok) right = true } else if ok && m < i { // fmt.Println("choosing b", i, m, ok) right = false } else if m > i { // the mid point is after the spot where we would insert the key so we take the record // just before the mid point as our new record would shift the mid point over by 1 split_rec, nextp, _, _ = a.Get(m - 1) a.RemoveAtIndex(m - 1) a.RemovePointer(m - 1) } else if m < i { // the mid point is before the new record so we can take the record at the mid point as // the split record. split_rec, nextp, _, _ = a.Get(m) a.RemoveAtIndex(m) a.RemovePointer(m) } if i == m || ok && m > i || ok && m < i { // the mid point is where the new record would go so in this case the new record will be // the split record for this split, and the nextb (which is associate with our new key) // become the nextp pointer split_rec = r if nextb != nil { nextp = nextb.Position() } } else { // otherwise we now need to insert our new record into the block before it is balanced if i, ok := a.Add(r); !ok { panic("Inserting record into block failed PANIC") } else { // after it is inserted we need to associate the next block with its key (which is // the key we just inserted). if nextb != nil { a.InsertPointer(i, nextb.Position()) nextb = nil } } } } self.balance_blocks(s, a, b) // using s as the balance point // fmt.Println("AFTER BALANCE") // fmt.Println("Full:\n", a) // fmt.Println("Empty:\n", b) var return_rec *Record = split_rec var block *KeyBlock // choose which block to insert the record into if _, _, _, _, ok := a.Find(split_rec.GetKey()); ok { block = a } else if _, _, _, _, ok := b.Find(split_rec.GetKey()); ok { block = b } else if a.RecordCount() < b.RecordCount() { // fmt.Println("chose a") block = a } else { // fmt.Println("chose b") block = b } // add the record to the block if i, ok := block.Add(split_rec); !ok { fmt.Println(m, s, right) fmt.Println("Full:\n", a) fmt.Println("Empty:\n", b) fmt.Println("Rec:", r) fmt.Println("return rec:", return_rec) panic("Could not add the split_rec to the selected block PANIC") } else { // we now insert the pointer if we have one if block.Mode()&POINTERS == POINTERS && nextp != nil { // we have a block that supports a pointer and a pointer if !block.InsertPointer(i, nextp) { panic("166 pointer insert failed! PANIC") } } else if block.Mode()&POINTERS == 0 && nextp != nil { // we don't have a block that supports a pointer but we do have a pointer panic("tried to set a pointer on a block with no pointers") } else if block.Mode()&POINTERS == POINTERS && nextp == nil { // we have a block that supports a pointer but don't have a pointer panic("splitting an internal block split requires a next block to point at") } // else // we have a block that doesn't support a pointer and we don't have pointer } if rec, _, _, ok := b.Get(0); !ok { panic("Could not get the first record from block b PANIC") } else { // we change the record returned because the first record in block b will now not // be the record we split on which is ok we just need to return the correct record. return_rec = rec } // if we have an external node (leaf) then we need to hook up the pointers between the leaf // nodes to support range queries and duplicate keys if a.Mode() == self.external.Mode { tmp, _ := a.GetExtraPtr() b.SetExtraPtr(tmp) a.SetExtraPtr(b.Position()) } // fmt.Println("\n\n\nAFTER EVERYTHING") // fmt.Println("Full:\n", a) // fmt.Println("Empty:\n", b) // fmt.Println("Rec:", r) // fmt.Println("return rec:", return_rec) return b, rec_to_tmp(self, return_rec), true }
func (self *BpTree) insert(block *KeyBlock, rec *tmprec, height int, dirty *dirty.DirtyBlocks) (*KeyBlock, *tmprec, bool) { var findlastblock func(*KeyBlock, ByteSlice) *KeyBlock findlastblock = func(block *KeyBlock, key ByteSlice) *KeyBlock { p, _ := block.GetExtraPtr() if p.Eq(ByteSlice64(0)) { return block } next := self.getblock(p) if r, _, _, ok := next.Get(0); !ok { return block } else { if r.GetKey().Eq(key) { return findlastblock(next, key) } } return block } // function to take a tmprec and turn it into the appropriate type of *Record _convert := func(rec *tmprec) *Record { if block.Mode() == self.external.Mode { return rec.external() } return rec.internal() } r := _convert(rec) // fmt.Println("INSERTING ", r, block, "\n\n\n") // nextb is the block to be passed up the the caller in the case of a split at this level. var nextb *KeyBlock if height > 0 { // internal node // first we will need to find the next block to traverse down to var pos ByteSlice { // we find where in the block this key would be inserted i, _, _, _, ok := block.Find(rec.key) if i == 0 { // if that spot is zero it means that it is less than the smallest key the block // so we adjust the block appropriately if r, p, _, ok := block.Get(i); ok { dirty.Insert(block) r.SetKey(rec.key) pos = p } else { msg := fmt.Sprintf( "227 Error could not get record %v from block %v", i, block) panic(msg) } } else if ok { if _, p, _, ok := block.Get(i); ok { pos = p } else { msg := fmt.Sprintf( "235 Error could not get record %v from block %v", i, block) panic(msg) } } else { // else this spot is one to many so we get the previous spot i-- if _, p, _, ok := block.Get(i); ok { pos = p } else { msg := fmt.Sprintf( "235 Error could not get record %v from block %v", i, block) panic(msg) } } } // if pos is nil we have a serious problem if pos == nil { panic("242 Nil Pointer") } // after we have found the position we get the block // then make a recursive call to insert to insert the record into the next block if b, srec, s := self.insert(self.getblock(pos), rec, height-1, dirty); s { // if the next block split we will insert the key passed up the chain. nextb = b r = _convert(srec) rec = srec } else { return nil, nil, false } } else { // c := block.Count(rec.key) // ratio := float(c) / float(block.MaxRecordCount()) if block.Full() { firstr, _, _, _ := block.Get(0) if block.Count(firstr.GetKey()) == int(block.MaxRecordCount()) { if !r.GetKey().Lt(firstr.GetKey()) { block := findlastblock(block, firstr.GetKey()) dirty.Insert(block) // fmt.Println("Magic Heres Abouts", r, "\n", block) if block.Full() || !r.GetKey().Eq(firstr.GetKey()) { newblock := self.allocate(self.external) dirty.Insert(newblock) p, _ := block.GetExtraPtr() newblock.SetExtraPtr(p) block.SetExtraPtr(newblock.Position()) if _, ok := newblock.Add(r); !ok { panic("347 could not add to empty block") } if r.GetKey().Eq(firstr.GetKey()) { return nil, nil, false } else { // return newblock, rec_to_tmp(self, self.internal.NewRecord(firstr.GetKey().Inc())), true return newblock, rec_to_tmp(self, r), true } } else { block.Add(r) return nil, nil, false } } else { // fmt.Println("more magic", r, firstr) newblock := self.allocate(self.external) dirty.Insert(block) dirty.Insert(newblock) p, _ := block.GetExtraPtr() newblock.SetExtraPtr(p) block.SetExtraPtr(newblock.Position()) self.mv(block, newblock) if _, ok := block.Add(r); !ok { panic("366 could not add to empty block") } return newblock, rec_to_tmp(self, firstr), true } } } } // this block is changed dirty.Insert(block) if i, ok := block.Add(r); ok { // Block isn't full record inserted, now insert pointer (if one exists) // return to parent saying it has nothing to do if block.Mode()&POINTERS == POINTERS && nextb != nil { if ok := block.InsertPointer(i, nextb.Position()); !ok { panic("pointer insert failed") } } else if block.Mode()&POINTERS == 0 && nextb != nil { panic("tried to set a pointer on a block with no pointers") } return nil, nil, false } else if i == -2 { // Dotty("error.dot", self) dirty.Sync() // Dotty("error2.dot", self) msg := fmt.Sprint( "tried to insert a duplicate key into a block which does not allow that.\n", r, "\n", block) panic(msg) } // Block is full split the block return self.split(block, rec, nextb, dirty) }
/* split takes a block figures out how to split it and splits it between the two blocks, it passes back the splitting record, and a pointer new block, and whether or not it succeeded nextb is the block that will be pointed at by one of the blocks ie. it was a block that was allocated by the previous split, normally a pointer to it would have been inserted into the block that is being split, but as that block is full it needs to go into one of the blocks here the function should always return a valid btree if the record it returns becomes the record at the root level. */ func (self *BTree) split(block *KeyBlock, rec *Record, nextb *KeyBlock, dirty *dirty.DirtyBlocks) (*KeyBlock, *Record, bool) { var split_rec *Record new_block := self.allocate() dirty.Insert(new_block) i, _, _, _, _ := block.Find(rec.GetKey()) m := self.node.KeysPerBlock() >> 1 // fmt.Println("m=", m) if m > i { split_rec, _, _, _ = block.Get(m - 1) block.RemoveAtIndex(m - 1) if _, ok := block.Add(rec); !ok { fmt.Println("Inserting record into block failed PANIC") os.Exit(3) } } else if m < i { split_rec, _, _, _ = block.Get(m) block.RemoveAtIndex(m) if _, ok := block.Add(rec); !ok { fmt.Println("Inserting record into block failed PANIC") os.Exit(3) } } else { split_rec = rec } self.balance_blocks(block, new_block) dirty.Sync() // figure out how to remove if nextb != nil { // fmt.Println("NEXTB: ", nextb) nextr, _, _, _ := nextb.Get(0) if i, _, _, _, ok := block.Find(rec.GetKey()); ok { // if this pointer is going into the old block that means there will be too many pointers in this block // so we must move the last one over to the new block if p, ok := block.GetPointer(m); ok { new_block.InsertPointer(0, p) } block.RemovePointer(m) _, left, _, _ := block.Get(i) // fmt.Println("left=", left) lblock := self.getblock(left) r, _, _, _ := lblock.Get(0) // fmt.Println("empty") // fmt.Printf("nextr %v > %v r, %v\n", nextr.GetKey(), r.GetKey(), nextr.GetKey().Gt(r.GetKey())) if nextr.GetKey().Gt(r.GetKey()) { // fmt.Println("i=", i, "+1") block.InsertPointer(i+1, nextb.Position()) } else { // fmt.Println("i=", i) block.InsertPointer(i, nextb.Position()) } } else { i, _, _, _, _ := new_block.Find(rec.GetKey()) _, left, _, _ := new_block.Get(i) // fmt.Println("left=", left) lblock := self.getblock(left) r, _, _, _ := lblock.Get(0) // fmt.Println("empty") // fmt.Printf("nextr %v > %v r, %v\n", nextr.GetKey(), r.GetKey(), nextr.GetKey().Gt(r.GetKey())) if nextr.GetKey().Gt(r.GetKey()) { // fmt.Println("i=", i, "+1") new_block.InsertPointer(i+1, nextb.Position()) } else { // fmt.Println("i=", i) new_block.InsertPointer(i, nextb.Position()) } } } // j, _, _, _, _ := new_block.Find(split_rec.GetKey()) // fmt.Println("split .... ", j) // fmt.Println(new_block.Position(), split_rec, true) return new_block, split_rec, true }