postgresql - pgx lib中的命名预处理语句,它是如何工作的?
问题描述
介绍
数据库/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
还指出(强调我的):
准备是幂等的;即使用相同的名称和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
}
}
- PosgreSQL 本身需要它来做某事(假设)。
解决方案
@mkopriva指出该sql
文本误导了我。它在这里具有双重功能。如果变量与映射中sql
的键不匹配,则准备好包含在 中的查询,并为 指定一个新结构。如果它确实匹配了一个键,则该变量将指向映射的一个条目。c.preparedStatements[sql]
sql
*PreparedStatement
ps
ps
因此,您可以有效地执行以下操作:
package main
import (
"fmt"
"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)
}
if _, err := db.Prepare("my-query", "select $1::int"); err != nil {
panic(err)
}
row := db.QueryRow("my-query", 10)
var i int
if err := row.Scan(&i); err != nil {
panic(err)
}
fmt.Println(i)
}
推荐阅读
- android - 立即完成当前活动
- android - 将方法调用到另一个活动的片段中
- c# - 剃须刀内的写入条件是否已弃用?
- python - 尝试从命令行调用文件路径
- javascript - 按列值搜索电子表格,返回行然后将内容发布到外部 API:JavaScript
- php - 如何在浏览器中添加打开的电子邮件,以最简单的方式,php,mailto
- javascript - 数据更新后不调用角同级ngInit方法
- swiftui - SwiftUI - 用于提醒和采取行动的按钮操作
- powershell - 如何在 Power BI 中获取与工作区关联的所有数据集
- python - Python3 - argparse 开关和文件