I need to loop through returned sql.Rows
multiple times. Are my only two options:
In other words, there is no way to go back in sql.Rows
(i.e., opposite of Rows.Next
).
The ITERATE statement is used to cause the flow of control to return to the beginning of a labeled LOOP statement.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With