
Продолжаем цикл статей с обзором изменений 19-й версии. На этот раз рассмотрим что появилось в рамках сентябрьского коммитфеста 2025 года.
Самое интересное из первого июльского коммитфеста можно прочитать здесь: 2025-07.
GROUP BY ALL
Оконные функции: обработка значений NULL
Триггеры событий на PL/Python
Более точное сообщение об ошибке при неверном указании имен параметров подпрограмм
random: случайная дата/время в заданном диапазоне
Формат base64url для функций encode и decode
Новый параметр debug_print_raw_parse
Параметр log_lock_waits включен по умолчанию
pg_stat_progress_basebackup: тип резервной копии
vacuumdb: сбор статистики по секционированным таблицам
Буферный кеш: использование алгоритма clock-sweep для поиска свободных буферов
Фейковые псевдонимы таблиц в запросах
GROUP BY ALL
commit: ef38a4d9756
Полегче станет тем, кому лень заново перечислять в предложении GROUP BY выражения из SELECT. Ключевое слово ALL подразумевает включение всех выражений из SELECT, не использующих групповые функции.
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 трогать не нужно:
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
commit: 25a30bbd423, 2273fa32bce
В соответствии со стандартом SQL для функций lag, lead, first_value, last_value, nth_value добавлена возможность не учитывать пустые значения. Для этого при вызове оконной функции можно указать IGNORE NULLS или RESPECT NULLS (по умолчанию).
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
commit: 53eff471c
Функции триггеров событий теперь можно писать на PL/Python. Пример триггера, срабатывающего по окончании операции DDL. Триггер просто выводит информацию о выполненных командах:
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();
Проверим:
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
Более точное сообщение об ошибке при неверном указании имен параметров подпрограм��
commit: 83a56419457
Есть функция с именованным параметром.
CREATE FUNCTION f(a int) RETURNS int LANGUAGE SQL RETURN a;
SELECT f(a=>42);
f
----
42
(1 row)
Но если при вызове указать неверное имя параметра, то получим стандартное на все случаи жизни сообщение об ошибке:
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.
А это неверная подсказка. Функция с таким именем и типами параметров существует, но имя у параметра другое. Детальное сообщение об ошибке в 19-й версии более точное: нет функции с таким именем и такими именами у параметров:
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: случайная дата/время в заданном диапазоне
commit: faf071b5538, 9c24111c4da
В 17-й версии функция random научилась выдавать случайные числа равномерно распределенные в заданном диапазоне. А теперь то же самое с типами даты/времени:
SELECT random(current_date, current_date + 100);
random
------------
2026-03-23
(1 row)
Формат base64url для функций encode и decode
commit: e1d917182c1
Функции encode и decode теперь поддерживают формат base64url, который можно безопасно использовать в именах файлов и адресах URL.
SELECT encode(decode('+-/_', 'base64url'), 'base64url');
encode
--------
--__
(1 row)
Новый параметр debug_print_raw_parse
commit: 06473f5a344
Параметры семейства debug_print_* включают вывод отладочной информации о запросе. К ним добавлен параметр debug_print_raw_parse для вывода необработанного дерева синтаксического анализа.
\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.
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 включен по умолчанию
commit: 2aac62be8cb
Предполагается (и вполне справедливо), что длительные блокировки — это проблема для работы системы, поэтому вывод информации о блокировках в журнал сервера должен быть включен по умолчанию.
\dconfig log_lock_waits
List of configuration parameters
Parameter | Value
----------------+-------
log_lock_waits | on
(1 row)
pg_stat_progress_basebackup: тип резервной копии
commit: deb674454c5
Новый столбец backup_type представления pg_stat_progress_basebackup показывает тип создаваемой резервной копии: полная (full) или инкрементальная (incremental):
\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: сбор статистики по секционированным таблицам
commit: 6429e5b771d
Утилита vacuumdb с параметрами --analyze-only и --analyze-in-stages будет собирать статистику не только по обычным, но и по секционированным таблицам.
Раньше для сбора статистики по секционированным таблицам нужно было явно указывать их в параметре --table, что не всегда удобно, особенно для сбора статистики после обновления сервера.
Буферный кеш: использование алгоритма clock-sweep для поиска свободных буферов
commit: 2c789405275
Раньше для поиска свободного буфера в буферном кеше поддерживался список свободных буферов. Этот список больше не используется, а для поиска применяется clock-sweep алгоритм, перебирающий по кругу все буферы. Предполагается, что отказ от списка свободных буферов упростит последующие патчи, связанные с поддержкой NUMA.
С точки зрения пользователей этот патч ничего не меняет, и о нем можно было бы не писать. Но в нескольких темах нашего курса DBA2 (Буферный кеш, Блокировки в оперативной памяти) мы рассказываем о списке свободных буферов. Поэтому включение патча в обзор — это и напоминание, что при обновлении курса на 19-ю версию в этих темах точно потребуются изменения.
Фейковые псевдонимы таблиц в запросах
commit: 585e31fcb, 6f79024df34, 5a170e992a4
Исторически планировщик «придумывает» странные псевдонимы для наборов строк, если в запросе имена явно не указаны. Например "*VALUES*":
EXPLAIN SELECT * FROM (VALUES (2),(1));
QUERY PLAN
-------------------------------------------------------------
Values Scan on "*VALUES*" (cost=0.00..0.03 rows=2 width=4)
(1 row)
Зная об этом, можно даже использовать эти псевдонимы в запросах:
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" or "unnamed_subquery_%d".
На этом пока всё. Впереди новости ноябрьского коммитфеста.
