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.

Reply via email to