返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

query

发布于 2024-10-12 19:15:51 字数 3630 浏览 0 评论 0 收藏 0

执行 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文