Skip to content

Commit

Permalink
Добавил indexes.BTreeLeaf и пару простых тестов #76
Browse files Browse the repository at this point in the history
  • Loading branch information
unhandled-exception committed Apr 29, 2023
1 parent 6fdc13f commit 9c305bc
Show file tree
Hide file tree
Showing 5 changed files with 624 additions and 114 deletions.
261 changes: 261 additions & 0 deletions internal/pkg/indexes/btree_leaf.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
package indexes

import (
"github.com/unhandled-exception/sophiadb/internal/pkg/records"
"github.com/unhandled-exception/sophiadb/internal/pkg/scan"
"github.com/unhandled-exception/sophiadb/internal/pkg/types"
)

type BTreeLeaf struct {
trx scan.TRXInt
layout records.Layout
searchKey scan.Constant
contents *BTreePage
currentSlot types.SlotID
filename string
}

func NewBTreeLeaf(trx scan.TRXInt, block types.Block, layout records.Layout, searchKey scan.Constant) (*BTreeLeaf, error) {
contents, err := NewBTreePage(trx, block, layout)
if err != nil {
return nil, err
}

leaf := &BTreeLeaf{
trx: trx,
layout: layout,
searchKey: searchKey,
filename: block.Filename,
contents: contents,
currentSlot: -1,
}

if err = leaf.ResetSlot(); err != nil {
return nil, err
}

return leaf, nil
}

func (l *BTreeLeaf) CurrentSlot() types.SlotID {
return l.currentSlot
}

func (l *BTreeLeaf) ResetSlot() error {
currentSlot, err := l.contents.FindSlotBefore(l.searchKey)
if err != nil {
return err
}

l.currentSlot = currentSlot

return nil
}

func (l *BTreeLeaf) Close() {
l.contents.Close()
}

func (l *BTreeLeaf) RID() (types.RID, error) {
return l.contents.GetDataRID(l.currentSlot)
}

func (l *BTreeLeaf) Next() (bool, error) {
l.currentSlot++

records, err := l.contents.GetRecords()
if err != nil {
return false, err
}

if records > int64(l.currentSlot) {
dataval, err1 := l.contents.GetVal(l.currentSlot)
if err1 != nil {
return false, err1
}

if dataval.CompareTo(l.searchKey) == scan.CompEqual {
return true, nil
}
}

return l.tryOverflow()
}

func (l *BTreeLeaf) Delete(dataRID types.RID) (bool, error) {
for {
if ok, err := l.Next(); !ok || err != nil {
return false, err
}

dr, err := l.RID()
if err != nil {
return false, err
}

if dr.Equals(dataRID) {
if err1 := l.contents.Delete(l.currentSlot); err1 != nil {
return false, err
}

return true, nil
}
}
}

func (l *BTreeLeaf) Insert(dataRID types.RID) (*BTreeDirEntry, error) {
flag, err := l.contents.GetFlag()
if err != nil {
return nil, err
}

firstKey, err := l.contents.GetVal(0)
if err != nil {
return nil, err
}

// Если блок ссылается на блок переполнения и ключи меньше первого в блоке,
// то текущий блок целиком отщепляем в новый и возвращаем каталожную запись для нового блока
// dataRID вставляем в текущую страницу
if flag >= 0 && firstKey.CompareTo(l.searchKey) == scan.CompGreat {
newBlock, err1 := l.contents.Split(0, flag)
if err1 != nil {
return nil, err1
}

if err1 = l.contents.SetFlag(BTreeNewFlag); err1 != nil {
return nil, err1
}

l.currentSlot = 0

if err1 = l.contents.InsertLeaf(l.currentSlot, l.searchKey, dataRID); err1 != nil {
return nil, err1
}

return &BTreeDirEntry{
BlockNumber: newBlock.Number,
Dataval: l.searchKey,
}, nil
}

l.currentSlot++

full, err := l.contents.IsFull()
if err != nil {
return nil, err
}

// Если в блоке есть место, то вставляем запись
if !full {
return nil, l.contents.InsertLeaf(l.currentSlot, l.searchKey, dataRID)
}

// Если в блоке нет места, то расщепляем
records, err := l.contents.GetRecords()
if err != nil {
return nil, err
}

lastKey, err := l.contents.GetVal(types.SlotID(records - 1))
if err != nil {
return nil, err
}

// Если блок содержит одно значение во всех слотах, то создать блок переполнения и перенести в него все записи кроме первой
if lastKey.CompareTo(firstKey) == scan.CompEqual {
newBlock, err1 := l.contents.Split(1, flag)
if err1 != nil {
return nil, err1
}

if err1 = l.contents.SetFlag(int64(newBlock.Number)); err1 != nil {
return nil, err1
}

return nil, nil //nolint:nilnil
}

// Расщепляем блок
splitPos := types.SlotID(records / 2) //nolint:gomnd

splitKey, err := l.contents.GetVal(splitPos)
if err != nil {
return nil, err
}

// Если средний ключи равен первому, то двигаемся вправо до нового ключа
if splitKey.CompareTo(firstKey) == scan.CompEqual { //nolint:nestif
for {
splitPos++

newKey, err1 := l.contents.GetVal(splitPos)
if err1 != nil {
return nil, err1
}

if newKey.CompareTo(splitKey) != scan.CompEqual {
splitKey = newKey

break
}
}
} else {
// Иначе двигаемся влево до первой записи с текущим ключем
for {
newKey, err1 := l.contents.GetVal(splitPos - 1)
if err1 != nil {
return nil, err1
}

if newKey.CompareTo(splitKey) != scan.CompEqual {
break
}

splitPos--
}
}

newBlock, err := l.contents.Split(splitPos, BTreeNewFlag)
if err != nil {
return nil, err
}

return &BTreeDirEntry{
BlockNumber: newBlock.Number,
Dataval: splitKey,
}, nil
}

func (l *BTreeLeaf) tryOverflow() (bool, error) {
firstKey, err := l.contents.GetVal(l.currentSlot)
if err != nil {
return false, err
}

flag, err := l.contents.GetFlag()
if err != nil {
return false, err
}

if l.searchKey.CompareTo(firstKey) != scan.CompEqual || flag < 0 {
return false, nil
}

l.contents.Close()

newBlock := types.Block{
Filename: l.filename,
Number: types.BlockID(flag),
}

newContents, err := NewBTreePage(l.trx, newBlock, l.layout)
if err != nil {
return false, err
}

l.contents = newContents
l.currentSlot = 0

return true, nil
}
Loading

0 comments on commit 9c305bc

Please sign in to comment.