DeepSeek总结的PostgreSQL 19新功能:第二部分

PostgreSQL 19:第二部分 或 CommitFest 2025-09

来源: habr.com

原文地址:https://postgr.es/p/7tE

我们继续 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 标准,lagleadfirst_valuelast_valuenth_value 函数现在支持跳过 NULL 值。在调用这些窗口函数时,可以指定 IGNORE NULLSRESPECT 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

encodedecode 函数现在支持 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" 的名称。


相关推荐
三言老师4 小时前
CentOS7.9:Redis‑Cluster集群部署结构化实战教程
linux·运维·服务器·数据库
旺仔学长 哈哈4 小时前
Spring Boot 智能停车场管理系统---附源码+数据库文档
数据库·spring boot·后端·智能停车场
ClickHouseDB4 小时前
ClickHouse托管Postgres:OLTP+OLAP,新能力解锁最佳数据平台
java·前端·数据库
数智化管理手记5 小时前
主数据重复、错漏频发?一站式主数据管理平台如何落地?
大数据·运维·数据库·人工智能·数据挖掘
吴声子夜歌5 小时前
MongoDB 4.2——选择片键
数据库·mongodb
天天进步一点点6 小时前
QSqlQuery删除sqlite数据库执行drop数据表报错database table is locked unable to fetch row问题
数据库·oracle·sqlite
音视频工程实战6 小时前
PromptQL 新手入门与实战指南
数据库·sql·算法
清川渡水6 小时前
NL2SQL 的正确打开方式:从「AI 猜 SQL」到「置信度闭环」的工程化落地
数据库·人工智能·sql
TDengine (老段)6 小时前
TDengine Go 与 Rust 连接器 — 高性能异步访问
大数据·数据库·物联网·golang·rust·时序数据库·tdengine
东方佑7 小时前
MA-RMSNorm:打破“缩放换智能”的魔咒,大模型归一化的一次范式革命!
数据库·人工智能·计算机视觉