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, 日志审计, 测试用例, 请求拦截