0

I have a function that I used to iterate over a result set from a query:

func readRows(rows *sql.Rows, translator func(*sql.Rows) error) error {
    defer rows.Close()

    // Iterate over each row in the rows and scan each; if an error occurs then return
    for shouldScan := rows.Next(); shouldScan; {
        if err := translator(rows); err != nil {
            return err
        }
    }

    // Check if the rows had an error; if they did then return them. Otherwise,
    // close the rows and return an error if the close function fails
    if err := rows.Err(); err != nil {
        return err
    }

    return nil
}

The translator function is primarily responsible for calling Scan on the *sql.Rows object. An example of this is:

readRows(rows, func(scanner *sql.Rows) error {
    var entry gopb.TestObject

    // Embed the variables into a list that we can use to pull information out of the rows
    scanned := []interface{}{...}

    if err := scanner.Scan(scanned...); err != nil {
        return err
    }

    entries = append(entries, &entry)
    return nil
})

I wrote a unit test for this code:

// Create the SQL mock and the RDS reqeuster
db, mock, _ := sqlmock.New()
requester := Requester{conn: db}
defer db.Close()

// Create the rows we'll use for testing the query
rows := sqlmock.NewRows([]string{"id", "data"}).
                AddRow(0, "data")

// Verify the command order for the transaction
mock.ExpectBegin()
mock.ExpectQuery(regexp.QuoteMeta("SELECT `id`, `data`, FROM `data`")).WillReturnRows(rows)
mock.ExpectRollback()

// Attempt to get the data
data, err := requester.GetData(context.TODO())

However, it appears that Next is being called infinitely. I'm not sure if this is an sqlmock issue or an issue with my code. Any help would be appreciated.

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Woody1193
  • 7,252
  • 5
  • 40
  • 90
  • Use `for rows.Next() {`. – mkopriva Aug 14 '20 at 07:16
  • 1
    ... this is because the `for ; ; {` form of a `for` statement executes the ``, as its name suggests, only once. Alternatively you could use `for shouldScan := rows.Next(); shouldScan; shouldScan = rows.Next() {`, although I do not know of a good reason to do so. – mkopriva Aug 14 '20 at 07:18
  • 1
    See https://golang.org/ref/spec#For_statements for more details. – mkopriva Aug 14 '20 at 07:23
  • Sorry, I debugged this myself and realized that I had forgotten to update the loop invariant. I will change it to use `for rows.Next() {` instead – Woody1193 Aug 14 '20 at 08:10

0 Answers0