gob decoder returns only first element in the array

Issue

So I was trying to create a mock DB, and in the current implementation, I am trying to make an insert and select which insert rows and select returns them. I decided to use a bytes.Buffer to help maintain a memory block I could insert a slice of rows in, and deserialize that memory block when I call select but it seems select just returns the first row instead of all the rows passed to the array.

main.go


func main() {
    inputBuffer := compiler.NewInputBuffer()
    scanner := bufio.NewScanner(os.Stdin)
    for {
        PrintPrompt()
        scanner.Scan()
        command := scanner.Text()
        inputBuffer.Buffer = command

        if strings.HasPrefix(inputBuffer.Buffer, ".") {
            switch compiler.DoMetaCommand(inputBuffer) {
            case compiler.MetaCommandSuccess:
                continue
            case compiler.MetaCommandUnrecognizedCommand:
                fmt.Printf("Unrecognized command %q \n", inputBuffer.Buffer)
                continue
            }
        }

        var statement compiler.Statement
        switch compiler.PrepareStatement(inputBuffer, &statement) {
        case compiler.PrepareSuccess:

        case compiler.PrepareUnrecognizedStatement:
            fmt.Printf("Unrecognized command at start of %q \n", inputBuffer.Buffer)
            continue

        case compiler.PrepareSyntaxError:
            fmt.Println("Syntax error. Could not parse statement.")
            continue
        }

        compiler.ExecuteStatement(statement)
        fmt.Println("Executed")

    }
}

func PrintPrompt() {
    fmt.Printf("db > ")
}

Above is the code responsible for collecting user input etc.

package compiler

import (
    "bytes"
    "log"
    "os"
    "strconv"
    "strings"
)


type Row struct {
    ID       int32
    Username string
    Email    string
}



type Statement struct {
    RowToInsert Row
    Type        StatementType
}

var (
    RowsTable       = make([]Row, 0)
    RowsTableBuffer bytes.Buffer
)

func DoMetaCommand(buffer InputBuffer) MetaCommandResult {
    if buffer.Buffer == ".exit" {
        os.Exit(0)
    } else {
        return MetaCommandUnrecognizedCommand
    }
    return MetaCommandSuccess
}

func PrepareStatement(buffer InputBuffer, statement *Statement) PrepareResult {
    if len(buffer.Buffer) > 6 {
        bufferArguments := strings.Fields(buffer.Buffer)
        if bufferArguments[0] == "insert" {
            statement.Type = StatementInsert
            if len(bufferArguments) < 4 {
                return PrepareSyntaxError
            } else {
                i, err := strconv.Atoi(bufferArguments[1])
                if err != nil {
                    log.Printf("%q is not a valid id\n", bufferArguments[1])
                    return PrepareSyntaxError
                } else {
                    statement.RowToInsert.ID = int32(i)
                }
                statement.RowToInsert.Username = bufferArguments[2]
                statement.RowToInsert.Email = bufferArguments[3]
            }
            RowsTable = append(RowsTable, statement.RowToInsert)
            
            return PrepareSuccess
        }
    }

    if buffer.Buffer == "select" {
        statement.Type = StatementSelect
        return PrepareSuccess
    }

    return PrepareUnrecognizedStatement
}

func ExecuteStatement(statement Statement) {
    switch statement.Type {
    case (StatementInsert):
        SerializeRow(RowsTable)
    case (StatementSelect):
        DeserializeRow()
    }
}

The code above is for parsing and appending the entries into statements and depending on the keywords, it’s either an insert or select [Took the code for defining enums out and left core logic]


func SerializeRow(r []Row) {
    encoder := gob.NewEncoder(&RowsTableBuffer)
    err := encoder.Encode(r)
    if err != nil {
        log.Println("encode error:", err)
    }
}

func DeserializeRow() {
    var rowsBuffer = RowsTableBuffer
    rowsTable := make([]Row, 0)
    decoder := gob.NewDecoder(&rowsBuffer)
    err := decoder.Decode(&rowsTable)
    if err != nil {
            log.Println("decode error:", err)
        }
    fmt.Println(rowsTable)
}

So the code above uses a global buffer in which the slice being appended to in PrepareStatement()will be encoded after an insert is done. A select ought to return the slice of all rows but just returns the first element for some reason.

Example (in terminal):
If I make 2 inserts:
db > insert 1 john c@mail.com
db > insert 2 collins k@mail.com

Then I make a select:
select
=> it returns [{1 john c@mail.com}] only.

Is there anything I am missing here? Thanks for your support.

Solution

So the answer was pretty simple. We were creating a new encoder in the SerializeRow function instead of creating it once. We pulled it out of the function and created a global.

var (
    encoder = gob.NewEncoder(&RowsTableBuffer)
    decoder = gob.NewDecoder(&RowsTableBuffer)
)

func SerializeRow(r Row) {
    err := encoder.Encode(r)
    if err != nil {
        log.Println("encode error:", err)
    }

}

func DeserializeRow() {

    var rows Row

    err := decoder.Decode(&rows)

    for err == nil {
        if err != nil {
            log.Fatal("decode error:", err)
        }
        fmt.Printf("%d %s %s\n", rows.ID, rows.Username, rows.Email)
        err = decoder.Decode(&rows)

    }

}

Answered By – Kwaku Biney

Answer Checked By – David Marino (GoLangFix Volunteer)

Leave a Reply

Your email address will not be published.