介绍
database/sql
在 Go 标准 sql 库中,*Stmt
类型有一些方法的定义,例如:
func (s *Stmt) Exec(args ...interface{}) (Result, error)
func (s *Stmt) Query(args ...interface{}) (*Rows, error)
一个新的(未命名的)声明由以下人员准备:
func (db *DB) Prepare(query string) (*Stmt, error)
- 连接池是抽象的,不直接可访问。
- 事务在单个连接上准备。
- 如果连接在语句执行时不可用,则会在新连接上重新准备。
pgx
PreparedStatement
类型没有定义任何方法。可以通过以下方式准备新的命名准备好的语句:
func (p *ConnPool) Prepare(name, sql string) (*PreparedStatement, error)
- 操作直接在连接池上进行
- 事务在池中的所有连接上准备
- 没有明确的方法来执行准备好的语句
在Github评论中,作者更好地解释了pgx和database/sql之间的架构差异。在Prepare
文档中也指出(我强调):
Prepare是幂等的;也就是说可以安全地使用相同的名称和sql参数多次调用Prepare。这允许一个代码路径准备、查询/执行/PrepareEx而不必考虑该语句是否已经准备好。
小例子
package main
import (
"github.com/jackc/pgx"
)
func main() {
conf := pgx.ConnPoolConfig{
ConnConfig: pgx.ConnConfig{
Host: "/run/postgresql",
User: "postgres",
Database: "test",
},
MaxConnections: 5,
}
db, err := pgx.NewConnPool(conf)
if err != nil {
panic(err)
}
_, err = db.Prepare("my-query", "select $1")
if err != nil {
panic(err)
}
// What to do with the prepared statement?
}
问题
name
参数给我留下了它可以通过调用name
来执行的印象,但是如何做到呢?- 文档让人感觉
Query
/Exec
方法在某种程度上利用了准备好的语句。然而,这些方法不接受name
参数。那么它们是如何匹配的? - 据推测,匹配是根据查询内容进行的。那么命名语句的整个意义是什么?
可能的答案
这是我自己得出的结论:
- 没有任何方法引用名称来引用查询(猜想)
- 在
conn.ExecEx()
中,匹配是根据查询主体完成的。如果尚未准备好,则会准备好。
ps, ok := c.preparedStatements[sql]
if !ok {
var err error
ps, err = c.prepareEx("", sql, nil)
if err != nil {
return "", err
}
}
sql
参数传递给Query/QueryRow/Exec
方法。这个链接 和 这个链接证实了这一点,并且sendPreparedQuery
方法会在名称前附加EXECUTE
。sql
将保存准备好的语句的名称,这时您就可以在第2步链接的行上获得匹配。 - mkopriva