lib/pq
GitHub: lib/pq
Go 语言标准库 database/sql 的 PostgreSQL 驱动程序,提供完整的数据库连接与交互能力。
Stars: 9910 | Forks: 968
pq 是用于 database/sql 的 Go PostgreSQL 驱动。
支持所有[受维护的 PostgreSQL 版本]。较旧的版本可能也能正常工作,
但未经过测试。[API 文档]。
## 连接
在 `sql.Open()` 调用中使用 `postgres` 驱动名称:
```
package main
import (
"database/sql"
"log"
_ "github.com/lib/pq" // To register the driver.
)
func main() {
// Or as URL: postgresql://localhost/pqgo
db, err := sql.Open("postgres", "host=localhost dbname=pqgo connect_timeout=5")
if err != nil {
log.Fatal(err)
}
defer db.Close()
// db.Open() only creates a connection pool, and doesn't actually establish
// a connection. To ensure the connection works you need to do *something*
// with a connection.
err = db.Ping()
if err != nil {
log.Fatal(err)
}
}
```
你也可以使用 `pq.Config` 结构体:
```
cfg := pq.Config{
Host: "localhost",
Port: 5432,
User: "pqgo",
ConnectTimeout: 5 * time.Second,
}
// Or: create a new Config from the defaults, environment, and DSN.
// cfg, err := pq.NewConfig("host=postgres dbname=pqgo")
// if err != nil {
// log.Fatal(err)
// }
c, err := pq.NewConnectorConfig(cfg)
if err != nil {
log.Fatal(err)
}
// Create connection pool.
db := sql.OpenDB(c)
defer db.Close()
// Make sure it works.
err = db.Ping()
if err != nil {
log.Fatal(err)
}
```
DSN 与 PostgreSQL 的 libpq 相同;支持大多数参数,
且行为应该一致。同时支持 key=value 和 postgres:// URL 样式的连接
字符串。有关完整列表和文档,请参阅 [Config 结构体]的文档注释。
最显著的区别在于,你可以在连接字符串中使用任何
[运行时参数],例如 `search_path` 或 `work_mem`。这与 libpq 不同,
后者为此使用 `options` 参数(这在 pq 中同样有效)。
例如:
```
sql.Open("postgres", "dbname=pqgo work_mem=100kB search_path=xyz")
```
libpq 的方式(在 pq 中也适用)是像这样使用 `options='-c k=v'`:
```
sql.Open("postgres", "dbname=pqgo options='-c work_mem=100kB -c search_path=xyz'")
```
建议在 DSN 中添加 `connect_timeout` —— database/sql 可能会异步建立
新连接,因此它不会使用来自 QueryContext()、
PingContext() 等的 context。默认行为是无限期等待。
## 错误
来自 PostgreSQL 的错误会作为 [pq.Error] 返回;可以使用 [pq.As] 将
错误转换为 `pq.Error`:
```
pqErr := pq.As(err, pqerror.UniqueViolation)
if pqErr != nil {
return fmt.Errorf("email %q already exsts", email)
}
```
Error() 字符串包含错误消息和代码:
```
pq: duplicate key value violates unique constraint "users_lower_idx" (23505)
```
ErrorWithDetail() 字符串还会包含 DETAIL 和 CONTEXT 字段(如果
存在)。例如,对于上述错误,它会非常有用地包含重复的
值:
```
ERROR: duplicate key value violates unique constraint "users_lower_idx" (23505)
DETAIL: Key (lower(email))=(a@example.com) already exists.
```
或者对于像这样的无效语法错误:
```
pq: invalid input syntax for type json (22P02)
```
它包含了发生此错误的上下文:
```
ERROR: invalid input syntax for type json (22P02)
DETAIL: Token "asd" is invalid.
CONTEXT: line 5, column 8:
3 | 'def',
4 | 123,
5 | 'foo', 'asd'::jsonb
^
```
## PostgreSQL 特性
### 认证
pq 开箱即用,支持 PASSWORD、MD5 和 SCRAM-SHA256 认证。如果
你需要 GSS/Kerberos 认证,则需要导入 `auth/kerberos`
模块:package:
```
import "github.com/lib/pq/auth/kerberos"
func init() {
pq.RegisterGSSProvider(func() (pq.Gss, error) { return kerberos.NewGSS() })
}
```
这是一个单独的模块,这样不需要 Kerberos 的用户(即大多数
用户)就不必添加不必要的依赖。
还支持读取[密码文件] (pgpass)。
### 使用 `COPY [..] FROM STDIN` 进行批量导入
你可以通过在事务中准备一条 `COPY [..] FROM STDIN` 语句来执行批量
导入。然后可以重复执行返回的 `sql.Stmt` 以
复制数据。处理完所有数据后,你应该调用一次不带
参数的 Exec() 来刷新所有缓冲数据。
[更多文档][copy-doc] 和[示例][copy-ex]。
### NOTICE 错误
PostgreSQL 有用于提示性消息的“NOTICE”错误。例如来自
psql CLI:
```
pqgo=# drop table if exists doesnotexist;
NOTICE: table "doesnotexist" does not exist, skipping
DROP TABLE
```
这些错误不会被返回,因为它们并不是真正的错误,而只是
通知。
你可以使用 [ConnectorWithNoticeHandler] 为这些通知注册一个回调
### 使用 `LISTEN`/`NOTIFY`
通过 [pq.Listener],通知会发送到 channel 上。例如:
```
l := pq.NewListener("dbname=pqgo", time.Second, time.Minute, nil)
defer l.Close()
err := l.Listen("coconut")
if err != nil {
log.Fatal(err)
}
for {
n := <-l.Notify:
if n == nil {
fmt.Println("nil notify: closing Listener")
return
}
fmt.Printf("notification on %q with data %q\n", n.Channel, n.Extra)
}
```
并且你会在每次 `notify coconut` 时收到一个通知。
有关更完整的示例,请参阅 API 文档。
## 注意事项
### LastInsertId
不支持 sql.Result.LastInsertId(),因为 PostgreSQL 协议没有
此功能。请改用 `insert [..] returning [cols]`:
```
db.QueryRow(`insert into tbl [..] returning id_col`).Scan(..)
// Or multiple rows:
db.Query(`insert into tbl (row1), (row2) returning id_col`)
```
这在 SQLite 和 MariaDB 中也可以使用相同的语法工作。MS-SQL 和
Oracle 也有类似的功能(语法不同)。
### 时间戳
对于带有时区的时间戳(`timestamptz`/`timestamp with time zone`),pq
像 libpq 一样使用服务器上配置的时区。你可以通过在连接
字符串中添加 `timestamp=[..]` 来更改此设置。通常建议使用
UTC。
对于不带时区的时间戳(`timestamp`/`timestamp without time zone`),
pq 总是使用 `time.FixedZone("", 0)` 作为时区;timestamp 参数
在此不起作用。这是有意设计为不等于 time.UTC,因为它并不是
UTC 时间:它是一个没有时区的时间。Go 的 time 包并不真正
支持这个概念,所以这是我们目前能做的最好方案。这会打印两次 `+0000`
(例如 `2026-03-15 17:45:47 +0000 +0000`;如果能有更清晰的名称会
更好,但这不是一个兼容性更改)。关于如何处理此问题的一些
选项,请参阅[此评论][ts]。
另请参阅[timestamptz]和[timestamp]的示例
### 使用 copy 处理 bytea
当使用 `copy [..] from
stdin` 时,所有的 `[]byte` 参数都会被编码为 `bytea`,这可能会
导致例如 `jsonb` 列出现错误。解决方案是
使用 string 代替 []byte。参见 #1023
## 开发
### 运行测试
需要在 PostgreSQL 数据库上运行测试;你可以使用 Docker compose
来启动一个:
```
docker compose up -d
```
这将启动最新版本的 PostgreSQL;使用 `docker compose up -d pg«v»` 来启动
不同的版本。
此外,你的 `/etc/hosts` 需要添加一条记录:
```
127.0.0.1 postgres postgres-invalid
```
或者你可以使用任何其他 PostgreSQL 实例;所需的设置请参见
`testdata/postgres/docker-entrypoint-initdb.d`。你可以使用
标准的 `PG*` 环境变量来控制连接细节;它
使用以下默认值:
```
PGHOST=localhost
PGDATABASE=pqgo
PGUSER=pqgo
PGSSLMODE=disable
PGCONNECT_TIMEOUT=20
```
`PQTEST_BINARY_PARAMETERS` 可用于将 `binary_parameters=yes` 添加到所有
连接字符串:
```
PQTEST_BINARY_PARAMETERS=1 go test
```
可以使用以下命令针对 pgbouncer 运行测试:
```
docker compose up -d pgbouncer pg18
PGPORT=6432 go test ./...
```
针对 pgpool 使用以下命令:
```
docker compose up -d pgpool pg18
PGPORT=7432 go test ./...
```
### 协议调试输出
你可以使用 PQGO_DEBUG=1 让驱动程序将 PostgreSQL 的通信
打印到 stderr;这在任何地方(测试或应用程序)都有效,并且
可用于调试协议问题。
例如:
```
% PQGO_DEBUG=1 go test -run TestSimpleQuery
CLIENT → Startup 69 "\x00\x03\x00\x00database\x00pqgo\x00user [..]"
SERVER ← (R) AuthRequest 4 "\x00\x00\x00\x00"
SERVER ← (S) ParamStatus 19 "in_hot_standby\x00off\x00"
[..]
SERVER ← (Z) ReadyForQuery 1 "I"
START conn.query
START conn.simpleQuery
CLIENT → (Q) Query 9 "select 1\x00"
SERVER ← (T) RowDescription 29 "\x00\x01?column?\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x17\x00\x04\xff\xff\xff\xff\x00\x00"
SERVER ← (D) DataRow 7 "\x00\x01\x00\x00\x00\x011"
END conn.simpleQuery
END conn.query
SERVER ← (C) CommandComplete 9 "SELECT 1\x00"
SERVER ← (Z) ReadyForQuery 1 "I"
CLIENT → (X) Terminate 0 ""
PASS
ok github.com/lib/pq 0.010s
```
标签:EVTX分析, HTTP, 日志审计, 测试用例, 请求拦截