7

I need to loop through returned sql.Rows multiple times. Are my only two options:

  1. to cache the returned results in a local data structure;
  2. redo the database query?

In other words, there is no way to go back in sql.Rows (i.e., opposite of Rows.Next).

2
  • The returned cursors is indeed unidirectional. Why can't you do all the operations on a row in a single loop? Commented Apr 26, 2018 at 15:19
  • Thanks for the quick response. I am trying to abstract some error detection functionality that relies on the returned result set having a specific set of columns/rows. The stored procedure supports sending error as return values, but, unfortunately, the Go MS SQL Server driver doesn't support this functionality yet. Commented Apr 26, 2018 at 15:23

1 Answer 1

7

Another solution would be to use the decorator pattern:

// A RowsDecorator wraps sql.Rows and allows a callback to be called whenever Scan is called
type RowsDecorator struct {
    *sql.Rows
    OnScan func([]interface{}, error)
}

func Wrap(rows *sql.Rows, onScan func([]interface{}, error)) *RowsDecorator {
    return &RowsDecorator{Rows: rows, OnScan: onScan}
}

// Scan calls Rows.Scan and an optional callback
func (rows *RowsDecorator) Scan(dest ...interface{}) error {
    err := rows.Rows.Scan(dest...)
    if rows.OnScan != nil {
        rows.OnScan(dest, err)
    }
    return err
}

Used like this:

db.Exec(`CREATE TABLE example (id INTEGER, txt TEXT)`)
db.Exec(`INSERT INTO example (id, txt) VALUES (1, 'test-1'), (2, 'test-2'), (3, 'test-3') `)

rawrows, err := db.Query("SELECT id, txt FROM example")
if err != nil {
    log.Fatal(err)
}
defer rawrows.Close()

sum := 0
rows := Wrap(rawrows, func(dest []interface{}, err error) {
    if err == nil {
        sum += *dest[0].(*int)
    }
})
for rows.Next() {
    var id int
    var txt string
    err := rows.Scan(&id, &txt)
    if err != nil {
        log.Fatal(err)
    }
    log.Println(id, txt)
}
log.Println("sum", sum)

With this pattern you can write a custom function that is called as you iterate over the collection. By using an unnamed, embedded type all of the original methods (Next, Close, etc) can still be called.

Sign up to request clarification or add additional context in comments.

1 Comment

This sounds interesting, and I will look into this more. Thank you very much for sharing!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.