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 StatementUnrecognized ) 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 ExecuteFail ) 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 { statement := &Statement{} return statement } 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 { statement.stype = StatementUnrecognized 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 = ExecuteFail switch statement.stype { case StatementInsert: result = statement.execInsert(table) case StatementSelect: result = statement.execSelect(table) } return result }