
Продолжаем цикл статей с обзором изменений 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".
На этом пока всё. Впереди новости ноябрьского коммитфеста.
