On a complete tangent, are you calling a stored procedure there? I thought the database/sql package didn't support MySQL stored procedures yet?
On Monday, June 5, 2017 at 5:43:33 PM UTC+1, Ren Thraysk wrote: > > > Wrote something similar recently. > > One difference is that I moved the rows.Scan call into the passed in > function. > > > 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 > } > > > Idea being gives an opportunity to do some mapping by scanning into local > variables and then assign them into the object, for instance dealing with > NULLs. Scan into a sql.Null* variable, and modify whatever struct as see > appropriate. > > Ren > > > > > On Friday, 2 June 2017 13:55:12 UTC+1, brylant wrote: >> >> >> I've been trying hard (well.. as much as I can considering my lack of >> in-depth go knowledge or - to be perfectly honest - lack of in-depth >> knowledge of anything) to find suitable go+sql technique that would not >> require a lot of code repetition, not use reflection and not use ORMs of >> any sort... Could somebody please tell me if there's anything particularly >> wrong with the following: >> >> >> 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 >> >> >> >> -- You received this message because you are subscribed to the Google Groups "golang-nuts" group. To unsubscribe from this group and stop receiving emails from it, send an email to golang-nuts+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.