上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
query
执行 SELECT 查询,从数据库返回记录。
- 尽早调用
Rows.Close
归还连接。 - 调用
Rows.Err
检查Next
是否意外终止。 - 多结果集需数据库支持。(
Rows.NextResultSet
)
package main import ( "database/sql" "log" _ "github.com/mattn/go-sqlite3" ) func main() { log.SetFlags(log.Lshortfile) db, err := sql.Open("sqlite3", "./test.db") if err != nil { log.Fatalln(err) } defer db.Close() // ------------------ q := `SELECT name FROM user WHERE id > ?` rows, err := db.Query(q, 0) if err != nil { log.Fatalln(err) } // ------------------ // 出错会导致迭代中断。 for rows.Next() { var name string if err := rows.Scan(&name); err != nil { log.Fatalln(err) } println(name) } // 尽早关闭,归还连接。 if err := rows.Close(); err != nil { log.Fatalln(err) } // 检查 Next 错误。(可在 Close 之后) if err := rows.Err(); err != nil { log.Fatal(err) } }
源码剖析
执行查询操作后,连接和释放函数( releaseConn
)被移交给 Rows
。
// database/sql/sql.go func (db *DB) query(ctx, query, args []any, ...) (*Rows, error) { dc, err := db.conn(ctx, strategy) return db.queryDC(ctx, nil, dc, dc.releaseConn, query, args) }
func (db *DB) queryDC(..., dc, releaseConn, query, args) (*Rows, error) { // 检查接口实现,否则以 Prepare 执行。 queryerCtx, ok := dc.ci.(driver.QueryerContext) var queryer driver.Queryer if !ok { queryer, ok = dc.ci.(driver.Queryer) } // 查询! if ok { withLock(dc, func() { rowsi, err = ctxDriverQuery(..., queryer, query, nvdargs) }) if err != driver.ErrSkip { // 释放连接。 if err != nil { releaseConn(err) return nil, err } // 连接和释放都移交给 Rows 了。 rows := &Rows{ dc: dc, releaseConn: releaseConn, rowsi: rowsi, } return rows, nil } } // 以 Prepare 方式执行 ... withLock(dc, func() { si, err = ctxDriverPrepare(ctx, dc.ci, query) }) // 释放连接。 if err != nil { releaseConn(err) return nil, err } ... // 将连接和释放移交给 Rows。 rows := &Rows{ dc: dc, releaseConn: releaseConn, rowsi: rowsi, closeStmt: ds, } return rows, nil }
func (rs *Rows) close(err error) error { rs.closemu.Lock() defer rs.closemu.Unlock() if rs.closed { return nil } rs.closed = true ... rs.releaseConn(err) return err }
当 Next
出错,自动调用 Close
释放连接。
其他情况,依然要显式调用。
func (rs *Rows) Next() bool { withLock(rs.closemu.RLocker(), func() { doClose, ok = rs.nextLocked() }) if doClose { rs.Close() } return ok }
func (rs *Rows) nextLocked() (doClose, ok bool) { if rs.closed { return false, false } rs.dc.Lock() defer rs.dc.Unlock() // 读取列数据(供 Scan 使用)。 rs.lasterr = rs.rowsi.Next(rs.lastcols) // 出错时,关闭;结果集结束,关闭! if rs.lasterr != nil { if rs.lasterr != io.EOF { return true, false } // 下一结果集。 nextResultSet, ok := rs.rowsi.(driver.RowsNextResultSet) if !ok { return true, false } if !nextResultSet.HasNextResultSet() { doClose = true } return doClose, false } return false, true }
方法 Err
返回 Next
错误,而 Scan
主要是数据处理(转换)错误。
func (rs *Rows) Err() error { rs.closemu.RLock() defer rs.closemu.RUnlock() return rs.lasterrOrErrLocked(nil) } func (rs *Rows) lasterrOrErrLocked(err error) error { if rs.lasterr != nil && rs.lasterr != io.EOF { return rs.lasterr } return err }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论