Header logo.
small hallucinations
homeyearstagsaboutrss

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.

 1func GetUserId(name string) (int, error) {
 2	// (1) - BAD
 3	stmt, err := dbconn.Db.Prepare("SELECT id FROM user WHERE name=\"?\";")
 4	// (2) – GOOD
 5	stmt, err := dbconn.Db.Prepare("SELECT id FROM user WHERE name=?;") 
 6	if err != nil {
 7		log.Fatal(err)
 8	}
 9    defer stmt.Close()
10
11	var userId int
12	err = stmt.QueryRow(name).Scan(&userId) // (3)
13	if err != nil {
14		if err != sql.ErrNoRows {
15			log.Fatal(err)
16		}
17		return 0, err
18	}
19
20	return userId, nil
21}

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.

 1var jsonBlob = []byte(`[
 2	{"Name": "Platypus", "Order": "Monotremata"},
 3	{"Name": "Quoll",    "Order": "Dasyuromorphia"}
 4]`)
 5type Animal struct {
 6	Name  string
 7	Order string
 8}
 9var animals []Animal
10err := json.Unmarshal(jsonBlob, &animals) // (4)