DEBUG.WIKI
雨云;给项目一台云服务器
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
ZgoCloud:多地区部署,按需选节点;日本、美国、香港、德国

pgx 5.11 升级后连错主机,检查密码里的 @

用 fmt 拼接连接 URL,密码里的多个@在新解析器下改变了主机分段。使用 URL 组件编码,保留原来的 TLS 与查询参数。

升级 pgx 5.11.0 后,数据库用户名和密码没改,连接却尝试访问带着一截密码的主机。若连接串用 fmt.Sprintf("postgres://%s:%s@%s/...", ...) 拼接,先检查密码里的@是否经过 URL 组件编码。

5.11 换用了接近 libpq 的 URI 解析规则:userinfo 以路径之前的第一个@结束;旧 net/url 路径采用最后一个@。未经编码的密码恰好包含多个@时,旧字符串能用,不意味着它在新规则下仍具有相同分段。

用组件构造 URL,不直接拼入秘密

已有连接配置可在构造入口分别提供用户信息与主机。例如下面是 Go 函数内的局部片段,appdb 为固定示例库名,queryOptions 应来自原有配置:

u := url.URL{
    Scheme:   "postgres",
    User:     url.UserPassword(user, password),
    Host:     net.JoinHostPort(host, port),
    Path:     "/appdb",
    RawQuery: queryOptions.Encode(),
}
cfg, err := pgconn.ParseConfig(u.String())
if err != nil {
    return err
}

需导入 net、net/url 及 pgconn。user、password、host、port 是应用已有配置,queryOptions 是保留原选项的 url.Values。不要把示例库名照搬到生产,也不要漏掉原来的 sslmode、证书路径、连接目标与池参数。这个主机构造示例是单主机入口;多主机列表有自身的位置配对规则,不能把整串逗号列表交给 JoinHostPort 便认为等价。

UserPassword 负责用户信息中的特殊字符,URL.String 生成 URI 表示。不要先对密码做 QueryEscape 再交给 UserPassword,那可能重复编码;也不要把整份连接 URL 当成一个查询值编码。

先核对解析结果,再尝试连接

在受控范围比较 cfg.Host、端口和数据库名是否与配置一致,密码只核对一致性,不写入日志。完整连接 URL、未脱敏配置和某些解析错误都可能暴露秘密;诊断记录应只保留必要的字段形状。

同一次升级还改变了其他边界,例如查询值里的加号不再自动当空格、重复查询参数采用最后一个值。密码含@的修正不代表所有历史拼接 URL 都已兼容,应覆盖真实使用的特殊字符和参数。

不要通过改 DNS、给错误主机开放权限或删除 TLS 选项让连接“成功”。先修正地址的表达。保留原配置生成代码与锁文件,在隔离连接入口核对后切换;需要回退时恢复整套配置与依赖组合,避免旧实例和新实例分别使用不同编码规则。