Postgres 19 补齐函数体验:随机日期与全局对象 DDL 查询

2026-07-31 20 预计阅读时间: 1 分钟
来源: postgr.es AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:10 分钟

Postgres 已经内置了数千个函数,但一些常见操作仍然需要拼接表达式或调用外部工具。Postgres 19 针对这些细小却高频的摩擦做了两类改进:直接生成指定区间内的随机日期与时间,以及通过 SQL 获取角色、数据库和表空间等全局对象的完整 DDL。

这些能力未必会改变查询优化器或存储引擎,却能明显简化测试数据生成、配置审计和运维脚本。需要注意的是,Postgres 19 尚处于开发周期,正式发布前应以对应版本文档和实际函数签名为准。

随机日期不再需要手工计算区间

过去要生成 2026 年内的随机日期,通常需要从年初开始加上一段随机 interval:

SELECT (
    DATE '2026-01-01'
    + random() * INTERVAL '364 days'
)::date;

这段表达式可以工作,但存在几个不够直观的地方:

  • date + interval 会把结果提升为 timestamp,所以还要显式转回 date
  • 天数需要人工计算,闰年尤其容易产生边界错误。
  • 上界是否能够取到并不直观。
  • 如果目标类型换成 timestamp with time zone,还得重新调整类型转换。

Postgres 19 为 random() 增加了时间类型的区间重载。可以把同类型的下界和上界直接传进去:

SELECT random(
    DATE '2026-01-01',
    DATE '2026-12-31'
) AS random_order_date;

输入是 date,输出仍然是 date。同样的模式也适用于另外两种常用时间类型:

SELECT random(
    TIMESTAMP '2026-01-01 00:00:00',
    TIMESTAMP '2026-12-31 23:59:59'
) AS random_local_time;

SELECT random(
    TIMESTAMPTZ '2026-01-01 00:00:00+00',
    TIMESTAMPTZ '2026-12-31 23:59:59+00'
) AS random_global_time;

显式边界让代码更容易审查,也避免了 interval 运算带来的类型变化。调用者只需要决定业务区间和时间类型,不必再维护天数换算逻辑。

用固定种子生成可复现的测试数据

随机数据只有在能够重现时,才适合进入自动化测试。Postgres 的 setseed() 可以固定当前会话中的伪随机序列。种子取值范围为 -1.01.0;在新会话中使用相同种子和相同语句顺序,可以得到相同的随机序列。

下面是一段可以在 Postgres 19 环境中直接运行或改造的测试数据脚本:

DROP TABLE IF EXISTS test_orders;

CREATE TABLE test_orders (
    order_id bigint PRIMARY KEY,
    customer_id bigint NOT NULL,
    ordered_on date NOT NULL,
    amount numeric(10, 2) NOT NULL
);

SELECT setseed(0.314159);

INSERT INTO test_orders (order_id, customer_id, ordered_on, amount)
SELECT
    n,
    floor(random() * 500 + 1)::bigint,
    random(DATE '2026-01-01', DATE '2026-12-31'),
    round((random() * 990 + 10)::numeric, 2)
FROM generate_series(1, 10000) AS g(n);

SELECT
    min(ordered_on) AS first_date,
    max(ordered_on) AS last_date,
    count(*) AS rows_created
FROM test_orders;

运行前只需要调整年份、客户数量和 generate_series 的规模。要制作百万行压测数据,可以把上限改成 1000000,但应同时评估 WAL、磁盘空间、索引维护和复制延迟。

还有一个容易忽略的边界:setseed() 适合重现测试数据,不是密码学随机源。令牌、密码、会话密钥等安全数据仍应由专门的安全随机机制生成。

全局对象的 DDL 终于能留在 SQL 里

角色、数据库和表空间属于集群级对象,不依附于某一个数据库。以往如果需要精确还原它们的定义,通常要运行 pg_dumpall,然后从集群级输出中过滤目标对象。

这种方式有几个工程问题:它依赖 shell 和外部二进制,客户端版本需要与服务端兼容,输出也无法自然地参与 SQL 的过滤、连接、存储和审计流程。

Postgres 19 增加了三类 DDL 信息函数,用于查询这些全局对象:

  • pg_get_roledef():返回角色定义及相关属性。
  • pg_get_tablespacedef():返回表空间定义,包括位置和存储设置。
  • pg_get_databasedef():返回数据库定义。

可以这样实践角色定义查询:

CREATE ROLE report_reader
    NOLOGIN
    CONNECTION LIMIT 8;

ALTER ROLE report_reader SET statement_timeout = '30s';

SELECT pg_get_roledef('report_reader');

与从整个集群转储中过滤文本相比,这种调用有三个直接收益:查询对象明确、结果可以作为 SQL 值处理、执行过程不需要数据库服务器上的 shell 权限。

出于安全考虑,角色定义函数不会在普通查询结果中暴露密码散列。这个限制很重要:可查询 DDL 不应变成低权限用户提取认证材料的渠道。实际部署时仍需检查函数执行权限,并限制谁能查看敏感的集群元数据。

数据库和表空间也采用相同思路:

SELECT pg_get_databasedef('analytics');
SELECT pg_get_tablespacedef('fast_storage');

这些查询要求目标对象已经存在,并且调用者拥有足够权限。表空间定义可能包含服务器文件系统路径,审计结果应当按照运维敏感数据管理。

把 DDL 查询接入配置审计

函数返回值可以参与普通 SQL,这是相对外部转储工具最实用的变化。可以这样实践一个最小审计表,定期记录全局对象定义:

CREATE TABLE IF NOT EXISTS global_ddl_audit (
    captured_at timestamptz NOT NULL DEFAULT clock_timestamp(),
    object_type text NOT NULL,
    object_name text NOT NULL,
    object_ddl text NOT NULL
);

INSERT INTO global_ddl_audit (object_type, object_name, object_ddl)
SELECT
    'role',
    rolname,
    pg_get_roledef(rolname)
FROM pg_roles
WHERE rolname = 'report_reader';

SELECT captured_at, object_name, object_ddl
FROM global_ddl_audit
ORDER BY captured_at DESC;

生产环境可以在此基础上增加变更哈希、操作者、工单编号和保留期限。审计表本身应设置严格权限,因为 DDL 可能暴露角色关系、表空间路径和数据库配置。

这组函数目前也不是 pg_dumppg_dumpall 的完整替代品。它们解决的是全局对象的定向查询,而不是完整备份、依赖排序、数据导出或跨集群恢复。Postgres 早已有查询函数、触发器、索引和视图定义的能力,但尚未形成覆盖所有对象类型的统一内省接口。

升级时该怎样采用

评估 Postgres 19 时,可以优先检查以下事项:

  1. 将手工拼接的随机日期表达式替换为带类型边界的 random(),并补充上下界测试。
  2. 在测试脚本开头调用 setseed(),确保失败样本能够稳定复现。
  3. 用新的 DDL 函数简化角色、数据库和表空间的定向检查,但继续保留正式备份流程。
  4. 审查 DDL 查询和审计表权限,尤其注意角色关系、数据库选项及表空间路径。
  5. 在正式迁移前核对 Postgres 19 最终文档;开发版本中的函数名称、选项和输出格式仍可能调整。

这些改进的价值不在于增加复杂能力,而在于删除不必要的仪式:随机日期变成一个类型清晰的函数调用,全局对象定义也能直接进入 SQL 工作流。对于需要维护测试夹具、配置基线和数据库审计的团队,这类小改动通常会很快转化为更短、更可靠的脚本。


相关推荐