type ScannerFunc func() []interface{}
func (db *DB) ScanSome(stmt string, sf ScannerFunc, params ...interface{}) error {
rows, err := db.Query(stmt, params...)
if err != nil {
return err
}
defer rows.Close()
for rows.Next() {
err = rows.Scan(sf()...)
if err != nil {
return err
}
}
if err = rows.Err(); err != nil {
return err
}
return nil
}
Having the above I could then implement the following for each of my 'models' (User being an example below). This could easily be 'go generate'-d for each model
type User struct {
UserID int64
Name string
Role int
// (...)
}
func ScanUsersFunc(users *[]*User) ScannerFunc {
return ScannerFunc(func() []interface{}) {
u := User{}
*users = append(*users, &u)
var r []interface{} = []interface{}{&u.UserID, &u.Name, &u.Role, (more properties)}
return r
}
}
and finally use it like this:
const (
sqlUsersByRole = "SELECT user_id,name,role, (more if needed) FROM user WHERE role=?"
sqlAllUsers = "SELECT user_id,name,role FROM user"
)
func (db *DB) UsersByRole(role int) ([]*User, error) {
users := make([]*User, 0)
err := db.ScanSome(sqlUsersByRole, ScanUsersFunc(&users), role)
if err != nil {
return nil, err
}
return users, nil
}
func (db *DB) AllUsers() ([]*User, error) {
users := make([]*User, 0)
err := db.ScanSome(sqlAllUsers, ScanUsersFunc(&users))
if err != nil {
return nil, err
}
return users, nil
}Alternatively (to avoid scanning/returning all results) a callback could be provided to ScanSome and called after each scan.
Obviously I could also implement ScanOne for situations where I only expect one row of results...
So - any obvious issues with the above 'technique'...?
Thanks,
adam
type Scannable interface { Scan(...interface{}) error}
func scanIntoUser(u *store.User) func(s Scannable) error {
return func(s Scannable) error {
return s.Scan(&u.Id, &u.Name, &u.PasswordHash, &u.Email)
}
}
func (s *userStore) UserByName(name string) (*store.User, error) {
u := &store.User{}
err := ExpectOneRow(s.db, scanIntoUser(u), "CALL spUserByName(?)", name)
if err != nil {
return nil, err
}
return u, nil
}On a complete tangent, are you calling a stored procedure there? I thought the database/sql package didn't support MySQL stored procedures yet?