Header logo.
A study of bugs
HomeArchiveTagsAboutFeed

Using prepared statements & pointers in Golang

I changed the name of this blog to “a study of bugs”. This makes it easier for me to think of what to write about -- bugs, of course.

In MySQL, you can use a question mark (?) in a prepared statement to stand in for a value.

func GetUserId(name string) (int, error) {
    // (1) - BAD
    stmt, err := dbconn.Db.Prepare("SELECT id FROM user WHERE name=\"?\";")
    // (2) – GOOD
    stmt, err := dbconn.Db.Prepare("SELECT id FROM user WHERE name=?;") 
    if err != nil {
        log.Fatal(err)
    }
    defer stmt.Close()

    var userId int
    err = stmt.QueryRow(name).Scan(&userId) // (3)
    if err != nil {
        if err != sql.ErrNoRows {
            log.Fatal(err)
        }
        return 0, err
    }

    return userId, nil
}

In the Go code above, dbconn is a connection to a MySQL server. Line 1 defines a prepared statement. And Line 3 queries the table for a row where the name column matches the value of variable name. I assumed the ? in this query would be interpolated with an actual string. I added quotation marks since they are needed around strings in MySQL CLI.

This fails to return anything. Removing the quotation marks solved the problem. The correct code is on Line 2.

I also find Line 3 quite interesting. Here, first, stmt.QueryRow() returns a pointer to a sql.Row object. Its Scan() method then does two fascinating things: a) it sets a “dest” to the output value and b) returns an error if there's an error or nil if there's no error.

Thingy a is quite interesting. In this case, Scan() method will find the memory address of variable userId and write the output value (the user's ID) there.

It feels as if getting the value from a DB is a side effect.

The same goes when you decode a JSON string. In this example, on Line 4, Unmarshal() method unpacks the JSON string and writes it to &animals, the address of animals variable. And this is a side effect. The return value is again, either an error or nil — if there is an error, you simply can't ignore it.

var jsonBlob = []byte(`[
    {"Name": "Platypus", "Order": "Monotremata"},
    {"Name": "Quoll",    "Order": "Dasyuromorphia"}
]`)
type Animal struct {
    Name  string
    Order string
}
var animals []Animal
err := json.Unmarshal(jsonBlob, &animals) // (4)