Skip to content

Commit

Permalink
feat: implement delete key
Browse files Browse the repository at this point in the history
  • Loading branch information
RichardKnop committed Nov 17, 2024
1 parent 1832551 commit 1aaa9c7
Show file tree
Hide file tree
Showing 10 changed files with 529 additions and 95 deletions.
27 changes: 15 additions & 12 deletions internal/pkg/minisql/cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ func (c *Cursor) LeafNodeInsert(ctx context.Context, key uint64, aRow *Row) erro
aPage.LeafNode.Cells[i] = aPage.LeafNode.Cells[i-1]
}
}

if err := saveToCell(&aPage.LeafNode.Cells[c.CellIdx], key, aRow); err != nil {
return err
}
aPage.LeafNode.Header.Cells += 1

err = saveToCell(&aPage.LeafNode.Cells[c.CellIdx], key, aRow)
return err
return nil
}

// Create a new node and move half the cells over.
Expand Down Expand Up @@ -134,16 +137,6 @@ func (c *Cursor) LeafNodeSplitInsert(ctx context.Context, key uint64, aRow *Row)
return c.Table.InternalNodeInsert(ctx, parentPageIdx, newPageIdx)
}

func saveToCell(cell *Cell, key uint64, aRow *Row) error {
rowBuf, err := aRow.Marshal()
if err != nil {
return err
}
cell.Key = key
copy(cell.Value[:], rowBuf)
return nil
}

func (c *Cursor) fetchRow(ctx context.Context) (Row, error) {
aPage, err := c.Table.pager.GetPage(ctx, c.Table, c.PageIdx)
if err != nil {
Expand Down Expand Up @@ -189,3 +182,13 @@ func (c *Cursor) update(ctx context.Context, aRow *Row) error {
copy(cell.Value[:], rowBuf)
return nil
}

func saveToCell(cell *Cell, key uint64, aRow *Row) error {
rowBuf, err := aRow.Marshal()
if err != nil {
return err
}
cell.Key = key
copy(cell.Value[:], rowBuf)
return nil
}
109 changes: 109 additions & 0 deletions internal/pkg/minisql/delete_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package minisql

import (
"context"
"fmt"
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
)

func TestTable_Delete(t *testing.T) {
t.Parallel()

/*
In this test we first need to create a big enough tree to have root node,
2 internal nodes and many leaf nodes. This way we can use different deletion
use cases.
*/
var (
ctx = context.Background()
pagerMock = new(MockPager)
numRows = 20
rows = gen.MediumRows(numRows)
cells, rowSize = 0, rows[0].Size()
aRootPage = newRootLeafPageWithCells(cells, int(rowSize))
leafs = make([]*Page, 0, 5)
aTable = NewTable(testLogger, "foo", testMediumColumns, pagerMock, 0)
// These two pages will be returned as leafs by the pager as default behaviour
// for allocating a new page but will be converted to internal nodes
// aNewRightInternal = &Page{LeafNode: NewLeafNode(rowSize)}
// aNewLeftInternal = &Page{LeafNode: NewLeafNode(rowSize)}
)
for i := 0; i < numRows; i++ {
leafs = append(leafs, &Page{LeafNode: NewLeafNode(rowSize)})
}

pagerMock.On("GetPage", mock.Anything, aTable, uint32(0)).Return(aRootPage, nil)
pagerMock.On("GetPage", mock.Anything, aTable, uint32(2)).Return(leafs[0], nil)
pagerMock.On("GetPage", mock.Anything, aTable, uint32(1)).Return(leafs[1], nil)
for i := 3; i < 7; i++ {
pagerMock.On("GetPage", mock.Anything, aTable, uint32(i)).Return(leafs[i-1], nil)
}
// // Splitting root internal node causes 2 more pages to be requested, one for
// // sibling internal node, one for new root node
// pagerMock.On("GetPage", mock.Anything, aTable, uint32(5)).Return(aNewRightInternal, nil)
// pagerMock.On("GetPage", mock.Anything, aTable, uint32(6)).Return(aNewLeftInternal, nil)

totalPages := uint32(1)
pagerMock.On("TotalPages").Return(func() uint32 {
old := totalPages
totalPages += 1
return old
}, nil)

// Batch insert test rows
stmt := Statement{
Kind: Insert,
TableName: "foo",
Fields: columnNames(testMediumColumns...),
Inserts: [][]any{},
}
for _, aRow := range rows {
stmt.Inserts = append(stmt.Inserts, aRow.Values)
}

err := aTable.Insert(ctx, stmt)
require.NoError(t, err)

fmt.Println(leafs[0].LeafNode.Header.Cells, len(leafs[0].LeafNode.Cells))
fmt.Println(leafs[0].LeafNode.Cells[0].Key)
fmt.Println(leafs[0].LeafNode.Cells[1].Key)
fmt.Println(leafs[0].LeafNode.Cells[2].Key)
fmt.Println("---------")

fmt.Println(leafs[1].LeafNode.Header.Cells, len(leafs[1].LeafNode.Cells))
fmt.Println(leafs[1].LeafNode.Cells[0].Key)
fmt.Println(leafs[1].LeafNode.Cells[1].Key)
fmt.Println(leafs[1].LeafNode.Cells[2].Key)
fmt.Println("---------")

fmt.Println(leafs[2].LeafNode.Header.Cells, len(leafs[2].LeafNode.Cells))
fmt.Println(leafs[2].LeafNode.Cells[0].Key)
fmt.Println(leafs[2].LeafNode.Cells[1].Key)
fmt.Println(leafs[2].LeafNode.Cells[2].Key)
fmt.Println("---------")

fmt.Println(leafs[3].LeafNode.Header.Cells, len(leafs[3].LeafNode.Cells))
fmt.Println(leafs[3].LeafNode.Cells[0].Key)
fmt.Println(leafs[3].LeafNode.Cells[1].Key)
fmt.Println(leafs[3].LeafNode.Cells[2].Key)
fmt.Println("---------")

fmt.Println(leafs[4].LeafNode.Header.Cells, len(leafs[4].LeafNode.Cells))
fmt.Println(leafs[4].LeafNode.Cells[0].Key)
fmt.Println(leafs[4].LeafNode.Cells[1].Key)
fmt.Println(leafs[4].LeafNode.Cells[2].Key)
fmt.Println("---------")

fmt.Println(leafs[5].LeafNode.Header.Cells, len(leafs[5].LeafNode.Cells))
fmt.Println(leafs[5].LeafNode.Cells[0].Key)
fmt.Println(leafs[5].LeafNode.Cells[1].Key)
fmt.Println(leafs[5].LeafNode.Cells[2].Key)
fmt.Println(leafs[5].LeafNode.Cells[3].Key)
fmt.Println(leafs[5].LeafNode.Cells[4].Key)

assert.True(t, false)
}
7 changes: 3 additions & 4 deletions internal/pkg/minisql/insert_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,9 @@ func TestTable_Insert_SplitInternalNode_CreateNewRoot(t *testing.T) {
numRows = InternalNodeMaxCells + 2
rows = gen.BigRows(numRows)
cells, rowSize = 0, rows[0].Size()
// numberOfLeafs = numRows
aRootPage = newRootLeafPageWithCells(cells, int(rowSize))
leafs = make([]*Page, 0, numRows)
aTable = NewTable(testLogger, "foo", testBigColumns, pagerMock, 0)
aRootPage = newRootLeafPageWithCells(cells, int(rowSize))
leafs = make([]*Page, 0, numRows)
aTable = NewTable(testLogger, "foo", testBigColumns, pagerMock, 0)
// These two pages will be returned as leafs by the pager as default behaviour
// for allocating a new page but will be converted to internal nodes
aNewRightInternal = &Page{LeafNode: NewLeafNode(rowSize)}
Expand Down
25 changes: 25 additions & 0 deletions internal/pkg/minisql/internal_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -193,3 +193,28 @@ func (n *InternalNode) Unmarshal(buf []byte) (uint64, error) {

return i, nil
}

func (n *InternalNode) GetRightChildByIndex(idx uint32) uint32 {
if idx == n.Header.KeysNum-1 {
return n.Header.RightChild
}

return n.ICells[idx+1].Child
}

func (n *InternalNode) DeleteKeyByIndex(idx uint32) {
if n.Header.KeysNum == 0 {
return
}
if idx == n.Header.KeysNum {
n.Header.RightChild = n.ICells[len(n.ICells)-1].Child
n.ICells[len(n.ICells)-1] = ICell{}
} else {
for i := int(idx); i < len(n.ICells)-1; i++ {
n.ICells[i] = n.ICells[i+1]
}
}

n.ICells[idx] = ICell{}
n.Header.KeysNum -= 1
}
69 changes: 48 additions & 21 deletions internal/pkg/minisql/leaf_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,17 +64,16 @@ func (h *LeafNodeHeader) Unmarshal(buf []byte) (uint64, error) {
}

type Cell struct {
Key uint64
Value []byte // size of rowSize
RowSize uint64
Key uint64
Value []byte // size of rowSize
}

func (c *Cell) Size() uint64 {
return 8 + c.RowSize
func (c *Cell) Size(rowSize uint64) uint64 {
return 8 + rowSize
}

func (c *Cell) Marshal(buf []byte) ([]byte, error) {
size := c.Size()
func (c *Cell) Marshal(rowSize uint64, buf []byte) ([]byte, error) {
size := c.Size(rowSize)
if uint64(cap(buf)) >= size {
buf = buf[:size]
} else {
Expand All @@ -93,13 +92,13 @@ func (c *Cell) Marshal(buf []byte) ([]byte, error) {
buf[7] = byte(c.Key >> 56)
i += 8

copy(buf[i:], c.Value[0:c.RowSize])
i += c.RowSize
copy(buf[i:], c.Value[0:rowSize])
i += rowSize

return buf[:i], nil
}

func (c *Cell) Unmarshal(buf []byte) (uint64, error) {
func (c *Cell) Unmarshal(rowSize uint64, buf []byte) (uint64, error) {
i := uint64(0)

c.Key = 0 |
Expand All @@ -113,24 +112,31 @@ func (c *Cell) Unmarshal(buf []byte) (uint64, error) {
(uint64(buf[i+7]) << 56)
i += 8

copy(c.Value, buf[i:i+c.RowSize])
i += c.RowSize
copy(c.Value, buf[i:i+rowSize])
i += rowSize

return i, nil
}

type LeafNode struct {
Header LeafNodeHeader
Cells []Cell // (PageSize - (6+8)) / (rowSize+8)
Header LeafNodeHeader
Cells []Cell // (PageSize - (6+8)) / (rowSize+8)
RowSize uint64
}

func NewCell(rowSize uint64) Cell {
return Cell{
Value: make([]byte, rowSize),
}
}

func NewLeafNode(rowSize uint64, cells ...Cell) *LeafNode {
aNode := LeafNode{
Cells: make([]Cell, maxCells(rowSize)),
Cells: make([]Cell, 0, maxCells(rowSize)),
RowSize: rowSize,
}
for idx := range aNode.Cells {
aNode.Cells[idx].RowSize = rowSize
aNode.Cells[idx].Value = make([]byte, rowSize)
for i := 0; i < int(maxCells(rowSize)); i++ {
aNode.Cells = append(aNode.Cells, NewCell(aNode.RowSize))
}
if len(cells) > 0 {
aNode.Header.Cells = uint32(len(cells))
Expand All @@ -146,7 +152,7 @@ func (n *LeafNode) Size() uint64 {
size += n.Header.Size()

for idx := range n.Cells {
size += n.Cells[idx].Size()
size += n.Cells[idx].Size(n.RowSize)
}

return size
Expand All @@ -169,7 +175,7 @@ func (n *LeafNode) Marshal(buf []byte) ([]byte, error) {
i += uint64(len(hbuf))

for idx := range n.Cells {
cbuf, err := n.Cells[idx].Marshal(buf[i:])
cbuf, err := n.Cells[idx].Marshal(n.RowSize, buf[i:])
if err != nil {
return nil, err
}
Expand All @@ -189,7 +195,7 @@ func (n *LeafNode) Unmarshal(buf []byte) (uint64, error) {
i += hi

for idx := 0; idx < int(n.Header.Cells); idx++ {
ci, err := n.Cells[idx].Unmarshal(buf[i:])
ci, err := n.Cells[idx].Unmarshal(n.RowSize, buf[i:])
if err != nil {
return 0, err
}
Expand All @@ -198,3 +204,24 @@ func (n *LeafNode) Unmarshal(buf []byte) (uint64, error) {

return i, nil
}

func (n *LeafNode) AtLeastHalfFull() bool {
return int(n.Header.Cells) >= len(n.Cells)/2
}

func (n *LeafNode) MoreThanHalfFull() bool {
return int(n.Header.Cells) > len(n.Cells)/2
}

func (n *LeafNode) Delete(cellIdx uint32) {
if n.Header.Cells == 0 {
return
}

for i := int(cellIdx); i < len(n.Cells)-1; i++ {
n.Cells[cellIdx] = n.Cells[cellIdx+1]
}
n.Cells[len(n.Cells)-1] = NewCell(n.RowSize)

n.Header.Cells -= 1
}
Loading

0 comments on commit 1aaa9c7

Please sign in to comment.