跳至内容

2.2 用 psql 探索与取证

psql 同时服务两种不同任务:人在终端里快速理解数据库,以及脚本稳定采集证据。交互探索可以接受对齐表格、分页与版本相关的展示;自动化取证则需要明确字段、排序、格式和退出码。把两种输出混用,是很多脆弱运维脚本的起点。

本节操作均为 R0·观察。先完成 2.1 的上下文检查,再在 pg36_shop 中执行。

2.2.1 对象、权限和会话元命令

psql 元命令以反斜杠开头,由客户端解释,不会作为 SQL 发给服务器。它们最适合回答“这里大概有什么”和“下一步该查哪个目录”,不应被误认为独立于 PostgreSQL 的另一套元数据。

一张够用的探索表

元命令主要问题推荐用法常见误读
\conninfo当前客户端连接参数是什么?每次进入会话先看只显示客户端视角,不替代服务器快照
\l+有哪些数据库及其属性?观察 owner、编码、权限和大小大小统计可能慢,也不等于磁盘总占用
\dn+有哪些模式,谁拥有?确认 shop 与权限模式不是数据库
\dt+ shop.*shop 中有哪些普通表?用模式限定模式匹配不会列出所有关系类型
\d+ shop.ch02_fixture一个关系如何定义?看列、索引、约束、存储等输出格式会随版本变化
\df+ shop.*有哪些函数?限定模式和名称模式函数重载需要参数签名区分
\du+有哪些角色及属性?识别 LOGIN、SUPERUSER 等角色属性不完整呈现所有成员关系语义
\dp shop.* / \z shop.*表、序列等对象的 ACL 是什么?快速找显式授权空 ACL 与默认权限不能只看字面猜测
\encoding当前客户端编码是什么?与服务端编码一并记录客户端编码不等于数据库编码

命令中的 shop.*psql 模式匹配,不是 shell glob。仍建议放在交互会话内输入,或在 shell 中用单引号保护:

psql -X "service=pg36-admin" \
  -c '\dt+ shop.*'

若对象名包含大写字母、空格或特殊字符,模式规则与 SQL 标识符引用会变得更难读。这是本书坚持小写 snake_case 标识符的一个工程原因,而不是 PostgreSQL 的强制限制。

权限需要从三个角度看

shop.ch02_fixture 为例:

\d+ shop.ch02_fixture
\dp shop.ch02_fixture
\du+ pg36_app

它们分别展示对象定义、对象 ACL 与角色属性。实际能否执行某项操作,还可能受对象所有权、角色成员关系、模式 USAGE、行级安全和列级权限影响。最终判断应使用权限函数验证具体动作:

SELECT
    has_schema_privilege('pg36_app', 'shop', 'USAGE') AS schema_usage,
    has_table_privilege(
        'pg36_app',
        'shop.ch02_fixture',
        'SELECT'
    ) AS can_select,
    has_table_privilege(
        'pg36_ro',
        'shop.ch02_fixture',
        'UPDATE'
    ) AS ro_can_update;

期望前两项为 true,最后一项为 false。这仍不是“模拟一次完整 SQL”的万能授权检查,但比肉眼解释 ACL 字符串更适合验收。

2.2.2 扩展显示、分页、计时与查询缓冲区

探索效率常常取决于“怎样看”,而不是“还能背多少元命令”。以下设置只影响当前 psql 客户端:

\x auto
\pset null '∅'
\pset pager on
\timing on
  • \x auto 在结果太宽时自动切换为逐字段显示;
  • 自定义空值标记能区分 SQL NULL 与空字符串;
  • pager 便于人在终端阅读长结果;
  • \timing 显示客户端观察到的每条语句耗时。

这些设置不适合原样带进自动化。分页器可能等待键盘输入,装饰性空值会污染机器解析,客户端计时还包含网络传输与结果渲染。脚本应显式使用:

\pset pager off
\pset tuples_only on
\pset format unaligned

或者直接采用命令行 --no-align --tuples-only

查询缓冲区是交互式安全带

psql 会把尚未发送的 SQL 保存在查询缓冲区。常用动作是:

元命令动作何时使用
\p打印当前缓冲区执行前复核长 SQL
\e用编辑器修改缓冲区多行查询比终端编辑更安全
\r清空缓冲区放弃误输入且尚未发送的 SQL
\g发送缓冲区明确执行
\gx发送并用扩展格式显示宽结果的一次性查看
\gdesc只描述结果列,不执行结果获取预览查询输出形状

例如先写一个查询但不输入分号:

SELECT fixture_id, sku, amount
FROM shop.ch02_fixture
ORDER BY fixture_id
LIMIT 5

随后依次输入:

\p
\gdesc
\gx

\gdesc 可以检查结果列的名称和类型;它不是通用 SQL 干运行工具,更不能证明一个写语句没有副作用。不要把“描述结果形状”扩展成“可以安全预演任何 SQL”。

\gexec 会把查询结果逐单元格当作 SQL 执行,后续章节偶尔用它创建可计算的 DDL。它的默认风险很高:执行顺序取决于结果排序,生成内容按字面发送,单条失败后是否继续又受 ON_ERROR_STOP 控制。使用前必须先把同一生成查询以普通 \g 输出审查,再在受控事务或隔离环境执行。

计时、重复观察与取消

\timing on
SELECT count(*) FROM shop.ch02_fixture;
\watch 2

\watch 2 每两秒重复当前查询,适合短时间观察计数或活动状态;按 Ctrl-C 取消当前查询或 watch 循环,而不是关闭整个终端。执行写语句前要先清空缓冲区,避免把它误交给 \watch

\timing 是快速反馈,不是基准测试。第一次执行的缓存状态、返回行数、终端渲染、网络与并发噪声都会改变结果。第 2.5 节会建立最小负载协议,ch26 再讨论正式测量。

发生错误后可输入:

\errverbose

它会重新显示最近一个服务端错误的完整诊断,包括 SQLSTATE、DETAIL、HINT 和错误位置(若可用)。保存证据时应同时保留标准错误,而不是只截终端最后一行。

2.2.3 元命令与系统目录查询互相验证

元命令通常在内部查询 pg_catalog。用 psql -E 启动,或在会话中设置:

\set ECHO_HIDDEN on
\d+ shop.ch02_fixture

psql 会打印它为当前服务器版本生成的目录查询。这是学习系统目录的好入口,也揭示一个重要事实:\d 的展示和内部 SQL 都可能随 PostgreSQL 版本变化,不应被 shell 脚本按列位置解析。

用目录查询复核对象

下面的查询稳定地列出本章夹具的用户列:

SELECT
    a.attnum                                      AS ordinal,
    a.attname                                     AS column_name,
    pg_catalog.format_type(a.atttypid, a.atttypmod)
                                                   AS data_type,
    a.attnotnull                                  AS not_null
FROM pg_catalog.pg_attribute AS a
WHERE a.attrelid = 'shop.ch02_fixture'::regclass
  AND a.attnum > 0
  AND NOT a.attisdropped
ORDER BY a.attnum;

\d+ 相比,它的优势不是更“原生”,而是调用方明确选择了字段、含义和顺序。regclass 转换还能在对象不存在或解析错误时直接失败;如果希望“对象缺失返回 NULL”,则用 to_regclass('shop.ch02_fixture')

再复核 owner 与关系类型:

SELECT
    n.nspname                                  AS schema_name,
    c.relname                                  AS relation_name,
    c.relkind,
    pg_catalog.pg_get_userbyid(c.relowner)     AS owner
FROM pg_catalog.pg_class AS c
JOIN pg_catalog.pg_namespace AS n
  ON n.oid = c.relnamespace
WHERE n.nspname = 'shop'
  AND c.relname = 'ch02_fixture';

pg_catalog 暴露 PostgreSQL 的完整内部元数据,字段会随版本演进;information_schema 提供更标准化、通常受当前用户可见性过滤的视图,但不会覆盖全部 PostgreSQL 特性。跨数据库工具优先考虑后者,PostgreSQL 运维与深度取证通常需要前者。

人读输出与机器证据分开

交互探索:

psql -X "service=pg36-admin" \
  -c '\d+ shop.ch02_fixture'

机器采集:

mkdir -p evidence/ch02
psql -X -w "service=pg36-admin" \
  --set=ON_ERROR_STOP=1 \
  --csv \
  --command="
    SELECT a.attnum, a.attname,
           pg_catalog.format_type(a.atttypid, a.atttypmod) AS data_type,
           a.attnotnull
    FROM pg_catalog.pg_attribute AS a
    WHERE a.attrelid = 'shop.ch02_fixture'::regclass
      AND a.attnum > 0
      AND NOT a.attisdropped
    ORDER BY a.attnum;
  " > evidence/ch02/columns.csv

机器输出应有固定列、显式排序和失败即停;文件名、采集时间、连接上下文与版本则写入清单。CSV 解决字段引用,不会自动赋予字段长期兼容承诺。

本节验收

  • 能用元命令找到 shop.ch02_fixture、owner 和 ACL;
  • 能用 has_*_privilege 验证 pg36_apppg36_ro 的实际权限;
  • 能解释 \timing 为什么不是正式基准;
  • 能用 -E 找到元命令背后的目录查询;
  • 自动化证据不解析 \d 的人读表格,而是查询明确的目录字段。

参考资料


上一节:可靠连接与上下文保护 · 返回本章目录 · 下一节:编写可靠 SQL 脚本 · 查看全书目录 · 查看索引中心

最后更新于