关于 postgres-query
用于测试、调试和性能分析的 PostgreSQL 临时查询技能。它支持显式选择数据库目标,并在每次运行时显示实际连接信息。
主要功能
- 01选择生产、开发、DataPacket 或通知数据库
- 02通过 EXPLAIN ANALYZE 检查性能
- 03从文件读取查询并输出 JSON
- 04默认只读并限制写操作
适用场景
- 01调试应用行为时检查记录
- 02比较查询计划或执行性能
- 03使用 EXPLAIN ANALYZE 检查索引使用情况
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ civitai/ civitai --skill postgres-query安装后在 Claude Code 中输入/postgres-query确认已加载。
附带文件.env.examplequery.mjs
详细介绍
PostgreSQL 查询测试
使用此技能运行临时 PostgreSQL 查询,以进行测试、调试和性能分析。
运行查询
使用随附的查询脚本,并指定目标:
node .claude/skills/postgres-query/query.mjs --prod "SELECT * FROM \"User\" LIMIT 5"
node .claude/skills/postgres-query/query.mjs --dev "SELECT * FROM \"User\" LIMIT 5"
目标
必须且只能选择一个。同一次调用中使用两个目标标志会报错,而不是按静默优先级规则处理;无法识别的选项(--devv、--data_packet)也会被拒绝,而非忽略——如果拼写错误被吞掉,命令就会落到默认目标,也就是生产环境。
| 标志 | 连接字符串 | 适用情况 |
|---|---|---|
--prod(默认) | DATABASE_REPLICA_URL,或使用 --writable 时的 DATABASE_URL | 生产主数据库 |
--dev | DEV_DATABASE_URL | dev cnpg 数据库;需要 SSH 隧道 |
--data-packet | DATABASE_DATA_PACKET_URL | DataPacket 副本(只读) |
--notifications | NOTIFICATION_DB_REPLICA_URL | notifications-db(只读);需要 SSH 隧道 |
省略目标仍会在生产环境运行,因此旧命令仍可正常工作——但运行时会打印
No target flag given, defaulted to --prod。请传入标志。
每次运行都会打印响应的数据库
连接前,脚本会向标准错误写入一行,说明目标、访问模式、pg 自身解析出的
user@host:port/database,以及对应的环境变量:
Target: PROD (read-only) -> <user>@<host>:25061/civitai [DATABASE_REPLICA_URL, timeout 30s]
即使使用 --quiet 和 --json,也会打印此信息;在所有会连接数据库的退出路径之前都会打印——包括写入操作被阻止时,因此你始终能看到刚才准备对哪个数据库执行 DELETE。此信息写入标准错误,因此将 --json 的输出管道传到文件时,文件中仍是干净的 JSON。
主机和端口来自客户端自身解析出的连接参数,而不是重新解析连接字符串,因此这行信息不会与查询实际连接的目标不一致。
🔴 请先查看这行信息,再相信查询结果。 此技能的 .env 和应用根目录下的 .env 都定义了 DATABASE_URL,而且它们指向不同的数据库——技能目录中的变量指向生产环境,根目录中的变量指向 dev 服务器使用的 dev 快照。技能目录中的 .env 优先:它会先加载,而 loadEnv 只会填充尚未存在的键(空值也算已存在,因此,如果此文件中留有一个空的 DATABASE_REPLICA_URL=,该键就不会从根目录的 .env 加载——目标会改用此文件中的 DATABASE_URL,而提示信息会显示
read-only, but pointed at the primary)。因此,直接运行 query.mjs 会从生产环境返回结果,而 dev 服务器会从 dev 返回结果。除非两行信息中的主机和端口相同,否则将此处读取的值与运行中应用读取的值进行比较,就相当于比较两个不同的数据库。2026-08-16,这导致排查耗时一小时,并得出了一个看似肯定、实际错误的根因:应用数据库中的 limit: 8 与此技能数据库中的 limit: 40 被报告为配置错误,但实际上并不存在此问题。
选项
| 标志 | 说明 |
|---|---|
--explain | 对查询运行 EXPLAIN ANALYZE |
--writable | 使用主连接而非只读副本(需要用户许可) |
--timeout <s>、-t | 查询超时时间,单位为秒(默认值:30) |
--file、-f | 从文件读取查询 |
--json | 以 JSON 格式输出结果 |
--quiet、-q | 最简输出,仅显示结果 |
同时使用 --writable 和 --data-packet 或 --notifications 会报错——两者都是只读副本,因此这样组合只能说明命令有误。
示例
# Simple query
node .claude/skills/postgres-query/query.mjs --prod "SELECT id, username FROM \"User\" LIMIT 5"
# Same query against the dev database
node .claude/skills/postgres-query/query.mjs --dev "SELECT id, username FROM \"User\" LIMIT 5"
# Check query performance
node .claude/skills/postgres-query/query.mjs --prod --explain "SELECT * FROM \"Model\" WHERE id = 1"
# Override default 30s timeout for longer queries
node .claude/skills/postgres-query/query.mjs --prod --timeout 60 "SELECT ... (complex query)"
# Query the notifications-db
node .claude/skills/postgres-query/query.mjs --notifications "SELECT count(*) FROM \"Notification\""
# Query from file
node .claude/skills/postgres-query/query.mjs --dev -f my-query.sql
# JSON output for processing (banner goes to stderr, stdout stays valid JSON)
node .claude/skills/postgres-query/query.mjs --prod --json "SELECT id, username FROM \"User\" LIMIT 3"
还有一个同名技能
apps/event-engine/.claude/skills/postgres-query/ 是一个独立副本,有自己的 .env,且不具备上述任何功能——没有目标标志,也没有提示信息。技能按目录范围生效,因此在 apps/event-engine 下工作时会使用该技能。请先按路径确认调用的是哪个脚本,再相信其输出。
查询 dev 数据库(cnpg)
dev 数据库无法直接访问,需要通过 SSH 隧道连接到内部主机。请向基础设施负责人询问连接方法;由于此仓库是公开的,本文不记录具体信息(参见 CLAUDE.md 的 Security 部分)。
建立隧道后,在 .claude/skills/postgres-query/.env 中设置 DEV_DATABASE_URL,使其指向本地转发端口。
运行 dev 查询
# Read-only (default — writes are blocked client-side)
node .claude/skills/postgres-query/query.mjs --dev "SELECT count(*) FROM \"User\""
# Writable DML (needs user permission)
node .claude/skills/postgres-query/query.mjs --dev --writable "UPDATE ..."
无论如何,--dev 都使用 DEV_DATABASE_URL——与 --prod 不同,它没有单独的副本凭据,因此 --writable 只会解除客户端侧的保护。写入是否成功取决于该 URL 使用的角色。
使用此凭据确实可以执行 DDL——2026-08-17 已在 dev 和 prod 主库上以 civitai 角色验证:ALTER TABLE ... ADD COLUMN、ADD CONSTRAINT、CREATE INDEX,以及 CREATE TABLE/DROP TABLE 往返操作均成功。可以使用 --writable 应用迁移。
本节以前曾作出相反说明,称重新分配对象所有权的 ddl_command_end 事件触发器会导致任何 CREATE TABLE 回滚,并报错 must be able to SET ROLE "<schema owner>"。没有人观察到这种情况,而且上述运行结果与之矛盾。为此向基础设施负责人升级处理其实毫无必要。如果确实遇到所有权错误,请记录你运行的内容并再次修正本节,而不要恢复笼统的说法。
⚠️ 多语句脚本即使第一条语句无害,也需要使用 --writable。 客户端侧的写入保护会检查每条语句的起始关键字,因此,以 SET lock_timeout / BEGIN 开头的迁移仍会因后续的 ALTER 而被拒绝。这是有意为之。
查询 notifications-db
notifications 数据库无法直接访问,需要通过 SSH 隧道连接到内部主机,并且必须先获准访问。
请向基础设施负责人申请访问权限并询问连接方法。 由于此仓库是公开的,本文有意不记录堡垒主机、转发目标以及凭据的存放位置——参见 CLAUDE.md 的 Security 部分。
建立隧道后,在 .claude/skills/postgres-query/.env 中设置 NOTIFICATION_DB_REPLICA_URL,使其指向本地转发端口。
运行查询
# With the tunnel open in another terminal:
node .claude/skills/postgres-query/query.mjs --notifications \
"SELECT count(*) FROM \"Notification\""
node .claude/skills/postgres-query/query.mjs --notifications --explain \
"SELECT * FROM \"UserNotification\" WHERE \"userId\" = 12345 ORDER BY \"createdAt\" DESC LIMIT 50"
可用表(只读)
Notification— 标准通知UserNotification— 面向各用户的通知分发(最大的一张表)PendingNotification— 处理队列(通常为空)
notifications_readonly 角色只有 SELECT 权限。连接池代理层也会拒绝写入(副本路由)。
安全功能
- 默认只读:
--prod使用DATABASE_REPLICA_URL,防止意外写入 - 写入保护:除非使用
--writable标志,否则会阻止 INSERT/UPDATE/DELETE/DROP - 副本目标拒绝
--writable:--notifications和--data-packet会因该标志而报错,其角色/连接池代理也会拒绝写入 - 需要明确授权:使用
--writable之前,必须征得用户许可 - 每次运行都会打印目标:避免意外将结果归到错误的数据库
⚠️ 写入保护是用于捕获拼写错误的机制,并非沙箱。 它会匹配查询的起始关键字(去除开头的注释后),以及开头的 WITH 中的 (INSERT|UPDATE|DELETE|…。更深层嵌套的写入会通过客户端检查。真正阻止写入的是你连接时使用的角色——这就是 --prod 默认使用只读副本凭据的原因。不要把未指定 --writable 当成查询无法写入的证明。
何时使用 --writable
仅在以下情况下使用 --writable 标志:
- 用户明确要求写入权限
- 需要测试写入操作
- 需要验证事务行为
重要:使用 --writable 运行之前,务必征得用户许可。
比较查询性能
要比较两种查询方式,两次运行必须使用相同的目标,否则比较毫无意义:
# Run first approach
node .claude/skills/postgres-query/query.mjs --prod --explain "SELECT ... (approach 1)"
# Run second approach
node .claude/skills/postgres-query/query.mjs --prod --explain "SELECT ... (approach 2)"
# Compare actual results
node .claude/skills/postgres-query/query.mjs --prod --json "SELECT ... (approach 1)" > /tmp/q1.json
node .claude/skills/postgres-query/query.mjs --prod --json "SELECT ... (approach 2)" > /tmp/q2.json
验证索引使用情况
使用 --explain 运行并查找:
- 良好:「Index Scan」、「Bitmap Index Scan」、「Index Only Scan」
- 不佳:大型表上的「Seq Scan」(表示缺少索引或索引未被使用)
node .claude/skills/postgres-query/query.mjs --prod --explain "SELECT * FROM \"Account\" WHERE provider = 'discord'"