181 lines
3.8 KiB
Go
181 lines
3.8 KiB
Go
package vm
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"os"
|
|
"strings"
|
|
|
|
. "godb/internals/buffer"
|
|
)
|
|
|
|
type MetaCommandResults int
|
|
|
|
const (
|
|
MetaCommandSuccess MetaCommandResults = iota
|
|
MetaCommandUnrecognised
|
|
)
|
|
|
|
type PrepareResults int
|
|
|
|
const (
|
|
PrepareSuccess PrepareResults = iota
|
|
PrepareUnrecognised
|
|
PrepareSyntaxError
|
|
)
|
|
|
|
type statementType int
|
|
|
|
const (
|
|
StatementSelect statementType = iota
|
|
StatementInsert
|
|
)
|
|
|
|
const (
|
|
idSize = 8
|
|
usernameSize = 32
|
|
emailSize = 255
|
|
|
|
idOffset = 0
|
|
usernameOffset = idOffset + idSize
|
|
emailOffset = usernameOffset + usernameSize
|
|
|
|
pageSize = 4096
|
|
maxPages = 100
|
|
rowSize = idSize + usernameSize + emailSize
|
|
rowsPerPage = pageSize / rowSize
|
|
maxRows = rowsPerPage * maxPages
|
|
)
|
|
|
|
type ExecuteResult int
|
|
|
|
const (
|
|
ExecuteSuccess ExecuteResult = iota
|
|
ExecuteTableFull
|
|
)
|
|
|
|
type row struct {
|
|
id uint
|
|
username string
|
|
email string
|
|
}
|
|
|
|
type Statement struct {
|
|
stype statementType
|
|
row row
|
|
}
|
|
|
|
type Table struct {
|
|
numOfRows uint
|
|
pages [maxPages]any
|
|
}
|
|
|
|
func NewStatement() *Statement {
|
|
return &Statement{0, row{0, "", ""}}
|
|
}
|
|
|
|
func NewTable() *Table {
|
|
newTable := Table{numOfRows: 0}
|
|
for i := range maxPages {
|
|
newTable.pages[i] = nil
|
|
}
|
|
return &newTable
|
|
}
|
|
|
|
func rowToUse(table *Table, rowNo uint) []byte {
|
|
pageNo := rowNo / rowsPerPage
|
|
var page []byte
|
|
if table.pages[pageNo] == nil {
|
|
page = make([]byte, pageSize)
|
|
table.pages[pageNo] = page
|
|
} else {
|
|
i := table.pages[pageNo]
|
|
if t, ok := i.([]byte); !ok {
|
|
page = []byte(t)
|
|
} else {
|
|
page = t
|
|
}
|
|
}
|
|
rowOffset := rowNo % rowsPerPage
|
|
byteOffset := rowOffset * rowSize
|
|
return page[byteOffset : byteOffset+rowSize]
|
|
}
|
|
|
|
func (row *row) seralizeRow(page []byte) {
|
|
binary.NativeEndian.PutUint64(page[idOffset:], uint64(row.id))
|
|
|
|
usernameBuff := page[usernameOffset : usernameOffset+usernameSize]
|
|
clear(usernameBuff)
|
|
copy(usernameBuff, row.username)
|
|
|
|
emailBuff := page[emailOffset : emailOffset+emailSize]
|
|
clear(emailBuff)
|
|
copy(emailBuff, row.email)
|
|
}
|
|
|
|
func (row *row) deSeralizeRow(page []byte) {
|
|
row.id = uint(binary.NativeEndian.Uint64(page[idOffset:idSize]))
|
|
row.username = string(page[usernameOffset : usernameOffset+usernameSize])
|
|
row.email = string(page[emailOffset : emailOffset+emailSize])
|
|
}
|
|
|
|
func (statement *Statement) execInsert(table *Table) ExecuteResult {
|
|
if table.numOfRows >= maxRows {
|
|
return ExecuteTableFull
|
|
}
|
|
statement.row.seralizeRow(rowToUse(table, table.numOfRows))
|
|
table.numOfRows += 1
|
|
return ExecuteSuccess
|
|
}
|
|
|
|
func (statement *Statement) execSelect(table *Table) ExecuteResult {
|
|
var row row
|
|
for i := range table.numOfRows {
|
|
row.deSeralizeRow(rowToUse(table, i))
|
|
fmt.Printf("%v\n", row)
|
|
}
|
|
return ExecuteSuccess
|
|
}
|
|
|
|
func DoMetaCommands(inputBuffer *InputBuffer) MetaCommandResults {
|
|
metacommand := MetaCommandUnrecognised
|
|
if inputBuffer.Buffer == ".exit" {
|
|
fmt.Println("bye!")
|
|
os.Exit(0)
|
|
} else {
|
|
metacommand = MetaCommandUnrecognised
|
|
}
|
|
return metacommand
|
|
}
|
|
|
|
func (statement *Statement) PrepareStatements(inputBuffer *InputBuffer) PrepareResults {
|
|
if strings.HasPrefix(inputBuffer.Buffer, "insert") {
|
|
argsAssigned, err := fmt.Sscanf(inputBuffer.Buffer, "insert %d %s %s", &statement.row.id, &statement.row.username, &statement.row.email)
|
|
// if err != nil {
|
|
// fmt.Println("error parsing insert statement")
|
|
// fmt.Println(err.Error())
|
|
// }
|
|
if err != nil || argsAssigned > 3 {
|
|
return PrepareSyntaxError
|
|
}
|
|
statement.stype = StatementInsert
|
|
return PrepareSuccess
|
|
}
|
|
if strings.HasPrefix(inputBuffer.Buffer, "select") {
|
|
statement.stype = StatementSelect
|
|
return PrepareSuccess
|
|
}
|
|
return PrepareUnrecognised
|
|
}
|
|
|
|
func (statement *Statement) ExecuteStatement(table *Table) ExecuteResult {
|
|
var result ExecuteResult
|
|
switch statement.stype {
|
|
case StatementInsert:
|
|
result = statement.execInsert(table)
|
|
case StatementSelect:
|
|
result = statement.execSelect(table)
|
|
}
|
|
return result
|
|
}
|