PostgreSQL 19:第二部分 或 CommitFest 2025-09
来源: habr.com
我们继续 PostgreSQL 19 变更回顾系列文章。这一次,我们将看看 2025 年 9 月 CommitFest 中涌现的新特性。
第一次七月 CommitFest 的重点内容可在此处查阅:[2025-07]
📑 目录
- GROUP BY ALL
- 窗口函数:NULL 值处理
- PL/Python 中的事件触发器
- 更精确的错误信息:错误的过程参数名称
- random:指定范围内的随机日期/时间
- encode 和 decode 函数的 base64url 格式
- 新增 debug_print_raw_parse 参数
- log_lock_waits 参数现在默认启用
- pg_stat_progress_basebackup:备份类型
- vacuumdb:收集分区表的统计信息
- 缓冲区缓存:使用时钟扫描算法查找空闲缓冲区
- 查询中的虚拟表别名
🔗 GROUP BY ALL
提交: [ef38a4d9756]
这将让那些不想在 GROUP BY 子句中重新列出所有 SELECT 表达式的人生活更轻松。ALL 关键字自动包含所有不使用聚合函数的 SELECT 表达式。
sql
SELECT to_char(actual_departure, 'YYYY'),
count(*)
FROM flights
GROUP BY ALL
ORDER BY 1;
to_char | count
---------+-------
2025 | 16477
2026 | 42457
| 10776
(3 rows)
如果你想在分组中添加一个新表达式,只需将其包含在 SELECT 列表中------无需修改 GROUP BY 子句:
sql
SELECT to_char(actual_departure, 'YYYY'),
status,
count(*)
FROM flights
GROUP BY ALL
ORDER BY 1;
to_char | status | count
---------+-----------+-------
2025 | Arrived | 16477
2026 | Arrived | 42438
2026 | Departed | 19
| Boarding | 5
| Scheduled | 10249
| Delayed | 9
| Cancelled | 358
| On Time | 155
(8 rows)
不过,并非所有人都同意这一更改。一些人在补丁讨论中表达了强烈反对。他们的主要论点是开发人员应该保持对查询代码的完全控制,而不是依赖系统默认值。然而,由于委员会已经批准将 GROUP BY ALL 纳入 SQL 标准的提案,关于其必要性的进一步辩论已无意义,PostgreSQL 19 将包含此功能。
另请参阅
-
Waiting for SQL:202y: GROUP BY ALL (Peter Eisentraut)
🪟 窗口函数:NULL 值处理
提交: [25a30bbd423], [2273fa32bce]
遵循 SQL 标准,lag、lead、first_value、last_value 和 nth_value 函数现在支持跳过 NULL 值。在调用这些窗口函数时,可以指定 IGNORE NULLS 或 RESPECT NULLS(默认值)。
sql
SELECT a, b,
first_value(b) RESPECT NULLS OVER w AS respect_nulls,
first_value(b) IGNORE NULLS OVER w AS ignore_nulls
FROM (VALUES ('a',NULL),('b',1),('c',2)) AS t(a,b)
WINDOW w AS (ORDER BY a ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING);
a | b | respect_nulls | ignore_nulls
---+---+---------------+--------------
a | | | 1
b | 1 | | 1
c | 2 | | 1
(3 rows)
🐍 PL/Python 中的事件触发器
提交: [53eff471c]
事件触发器函数现在可以用 PL/Python 编写。以下是一个在 DDL 操作完成时触发的触发器示例。该触发器仅显示有关已执行命令的信息:
sql
CREATE EXTENSION IF NOT EXISTS plpython3u;
CREATE OR REPLACE FUNCTION describe_ddl()
RETURNS event_trigger AS $$
for row in plpy.cursor("SELECT command_tag, object_identity FROM pg_event_trigger_ddl_commands()"):
plpy.notice(
"{}. name: {}".format(
row['command_tag'],
row['object_identity']
)
)
$$ LANGUAGE plpython3u;
CREATE EVENT TRIGGER after_ddl
ON ddl_command_end EXECUTE FUNCTION describe_ddl();
让我们测试一下:
sql
CREATE TABLE test(id integer PRIMARY KEY GENERATED ALWAYS AS IDENTITY);
NOTICE: CREATE SEQUENCE. name: public.test_id_seq
NOTICE: CREATE TABLE. name: public.test
NOTICE: CREATE INDEX. name: public.test_pkey
NOTICE: ALTER SEQUENCE. name: public.test_id_seq
CREATE TABLE
🎯 更精确的错误信息:错误的过程参数名称
提交: [83a56419457]
我们有一个带命名参数的函数。
sql
CREATE FUNCTION f(a int) RETURNS int LANGUAGE SQL RETURN a;
SELECT f(a=>42);
f
----
42
(1 row)
但是,如果在调用函数时指定了错误的参数名称,我们会收到一条通用的错误信息:
sql
18=# SELECT f(b=>42);
ERROR: function f(b => integer) does not exist
LINE 1: SELECT f(b=>42);
^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
这个提示具有误导性。确实存在具有该名称和参数类型的函数,但参数名称不同。PostgreSQL 19 中的错误信息更加精确:
sql
19=# SELECT f(b=>42);
ERROR: function f(b => integer) does not exist
LINE 1: SELECT f(b=>42);
^
DETAIL: No function of that name accepts the given argument names.
📅 random:指定范围内的随机日期/时间
提交: [faf071b5538], [9c24111c4da]
在 PostgreSQL 17 中,random 函数增加了在指定范围内生成均匀分布随机数的能力。现在,日期/时间类型也具备了相同的功能:
sql
SELECT random(current_date, current_date + 100);
random
------------
2026-03-23
(1 row)
🔐 encode 和 decode 函数的 base64url 格式
提交: [e1d917182c1]
encode 和 decode 函数现在支持 base64url 格式,该格式可安全地用于文件名和 URL。
sql
SELECT encode(decode('+-/_', 'base64url'), 'base64url');
encode
--------
--__
(1 row)
🐛 新增 debug_print_raw_parse 参数
提交: [06473f5a344]
debug_print_* 参数系列处理查询的调试输出。新增了一个 debug_print_raw_parse 参数用于显示原始解析树。
sql
\dconfig debug_print_*
List of configuration parameters
Parameter | Value
-----------------------+-------
debug_print_parse | off
debug_print_plan | off
debug_print_raw_parse | off
debug_print_rewritten | off
(4 rows)
让我们看看它为 COMMIT 命令显示了什么。
sql
SET debug_print_raw_parse = on;
SET client_min_messages = 'LOG';
COMMIT;
LOG: raw parse tree:
DETAIL: (
{RAWSTMT
:stmt
{TRANSACTIONSTMT
:kind 2
:options <>
:savepoint_name <>
:gid <>
:chain false
:location -1
}
:stmt_location 0
:stmt_len 6
}
)
WARNING: there is no transaction in progress
COMMIT
🔒 log_lock_waits 参数现在默认启用
提交: [2aac62be8cb]
长时间的锁被认为是系统运行的问题。记录锁信息应默认启用。
sql
\dconfig log_lock_waits
List of configuration parameters
Parameter | Value
----------------+-------
log_lock_waits | on
(1 row)
💾 pg_stat_progress_basebackup:备份类型
提交: [deb674454c5]
pg_stat_progress_basebackup 视图中新增的 backup_type 列指示正在创建的备份是全量备份 还是增量备份:
sql
\d pg_stat_progress_basebackup
View "pg_catalog.pg_stat_progress_basebackup"
Column | Type | Collation | Nullable | Default
----------------------+---------+-----------+----------+---------
pid | integer | | |
phase | text | | |
backup_total | bigint | | |
backup_streamed | bigint | | |
tablespaces_total | bigint | | |
tablespaces_streamed | bigint | | |
backup_type | text | | |
🧹 vacuumdb:收集分区表的统计信息
提交: [6429e5b771d]
vacuumdb 工具现在在使用 --analyze-only 和 --analyze-in-stages 选项时,除了普通表外,还会收集分区表的统计信息。
以前,收集分区表的统计信息需要在 --table 参数中显式指定它们,这并不总是很方便,尤其是在服务器升级后收集统计信息时。
💿 缓冲区缓存:使用时钟扫描算法查找空闲缓冲区
提交: [2c789405275]
以前,维护一个空闲缓冲区列表来帮助查找缓冲区缓存中的可用缓冲区。现在不再使用此列表;而是采用一种循环遍历所有缓冲区的时钟扫描算法进行搜索。消除空闲缓冲区列表预计将简化未来与 NUMA 支持相关的补丁。
从用户的角度来看,此补丁没有任何改变,可能不值得提及。但是,我们 DBA2 课程中的几个主题讨论了空闲缓冲区列表(缓冲区缓存,内存中锁)。这就是我们将此补丁包含在概览中的原因:它提醒我们,这些主题肯定需要针对 PostgreSQL 19 的课程进行更新。
🏷️ 查询中的虚拟表别名
提交: [585e31fcb], [6f79024df34], [5a170e992a4]
历史上,当查询中未显式指定名称时,规划器会为行集生成奇怪的别名。例如,"VALUES":
sql
EXPLAIN SELECT * FROM (VALUES (2),(1));
QUERY PLAN
-------------------------------------------------------------
Values Scan on "*VALUES*" (cost=0.00..0.03 rows=2 width=4)
(1 row)
你甚至可以在查询中使用这些别名:
sql
SELECT * FROM (VALUES (2),(1) ORDER BY "*VALUES*".column1);
column1
---------
1
2
(2 rows)
这些名称包括:old, new, "*SELECT*", ANY_subquery, "*MERGE*", "*RESULT*", excluded, unnamed_subquery, unnamed_join, "*GROUP*", "*TLOCRN*", "*TROCRN*", "*SELECT* %d", "*VALUES*", xmltable, json_table。
由于潜在的向后兼容性问题,用更有意义的名称替换它们被证明是具有挑战性的。事实证明,即使是 PostgreSQL 的回归测试也包含大量使用此类名称的查询,特别是 "*VALUES*"。
最终,只有少数名称被弃用:"*SELECT*"、"*SELECT* %d"、"ANY_subquery"、"*TLOCRN*"、"*TROCRN*"。它们现在被替换为遵循模式 "unnamed_subquery" 或 "unnamed_subquery_%d" 的名称。