funcsql_demo()error { db, err := sql.Open("mysql", "xxxx:xxxx@tcp(localhost:3306)/xxxx?charset=utf8") if err != nil { fmt.Println(err) return err } defer db.Close() rows, err := db.Query("select id,name from sql_demo") if err != nil { fmt.Println(err) return err } defer rows.Close() var id int var name string for rows.Next() { rerr := rows.Scan(&id, &name) if rerr == nil { return rerr } } returnnil }
找bug:
是不是没找到?没关系,我们看下源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
package sql_demo
// Next prepares the next result row for reading with the Scan method. It // returns true on success, or false if there is no next result row or an error // happened while preparing it. Err should be consulted to distinguish between // the two cases. // // Every call to Scan, even the first one, must be preceded by a call to Next. func(rs *Rows)Next()bool { var doClose, ok bool withLock(rs.closemu.RLocker(), func() { doClose, ok = rs.nextLocked() }) if doClose { rs.Close() } return ok }
简单翻译一下:当 no next result row或者遇到 error的时候,Next()都会返回false, 我们需要调用Err来区分这两种情况; 所以bug找到了,迭代过程中,当遇到error时,比如网络异常等等,此时数据可能只读到了一部分,rows.Next()会返回false, 直接return nil,会把异常遗漏掉,造成一个隐形bug; 正确做法: 把return nil改为rows.Err()即可;