- PVSM.RU - https://www.pvsm.ru -

PostgreSQL 16: Часть 1 или Коммитфест 2022-07

Август в релизном цикле PostgreSQL месяц особенный. Еще не вышла официально 15-я версия, но уже закончился первый коммитфест [1] 16-й версии. И мы можем посмотреть на самые интересные изменения.

Собираем сервер из исходного кода [2] и вперед!

dconfig server_version

List of configuration parameters
   Parameter    |  Value  
----------------+---------
 server_version | 16devel

PostgreSQL 15

После заморозки кода 15-й версии не обошлось без потерь. Откатили:

Индексы BRIN не блокируют HOT-обновления [3]
Оптимизация работы с вложенными транзакциями [4]

PostgreSQL 16

В этот обзор попали 14 изменений:

psql: pset xheader_width [5]
vacuumdb --schema и --exclude-schema [6]
Новые возможности утилиты createuser [7]
Сообщение журнала о контрольной точке дополнили номерами LSN самой точки и начала восстановления [8]
pg_prepared_statements.result_types [9]
auto_explain: параметр log_parameter_max_length [10]
Необязательные псевдонимы для подзапросов во фразе FROM [11]
REINDEX: синтаксис и не только [12]
CREATE STATISTICS: необязательно указывать имя статистики [13]
CREATE TABLE: атрибут STORAGE [14]
У созданного при инициализации кластера пользователя нельзя отобрать полномочия суперпользователя [15]
Триггеры на TRUNCATE для внешних таблиц [16]
pg_read_file/pg_read_binary_file: новая вариация указания параметров [17]
Собственные менеджеры ресурсов WAL [18]

psql: pset xheader_width [19]
commit: a45388d6 [20]

Развернутый режим вывода результатов запросов (x или pset expanded) удобен когда данные всех столбцов не помещаются на экран. Но и в развернутом режиме данные длинных столбцов также могут не помещаться в окно терминала.

Для примера в окне терминала шириной в 72 символа выполним запрос:

pset expanded on
pset pager off

SELECT version(),
       length(version()) version_length;

-[ RECORD 1 ]--+--------------------------------------------------------
-------------------------------------------------
version        | PostgreSQL 16devel on x86_64-pc-linux-gnu, compiled by 
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0, 64-bit
version_length | 104

Значение функции version не помещается по ширине, поэтому переносится на следующую строку. С этим все нормально. Но вот строка заголовка записи форматируется по самому широкому столбцу и также переносится на следующую строку. И чем длиннее самый широкий столбец, тем больше места будет занимать строка заголовка записи. Это особенно неудобно, когда выводится несколько записей и у каждой записи заголовок занимает несколько строк. (Попробуйте выполнить в развернутом режиме запрос SELECT * FROM pg_proc;)

В 16-й версии в psql появился новый параметр форматирования строки заголовка развернутого режима.

pset xheader_width

Expanded header width is 'full'.

Значение по умолчанию (full) соответствует поведению в предыдущих версиях. Другие возможные значения:

  • column ― заголовок записи обрезается до ширины первого столбца вывода;
  • page ― заголовок записи обрезается до ширины окна терминала;
  • число ― заголовок записи обрезается до указанного числа.

pset xheader_width column

Expanded header width is 'column'.

SELECT version(),
       length(version()) version_length;

-[ RECORD 1 ]--+
version        | PostgreSQL 16devel on x86_64-pc-linux-gnu, compiled by 
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0, 64-bit
version_length | 104

Заголовок записи больше не расходует напрасно полезное место окна терминала.

vacuumdb --schema и --exclude-schema [21]
commit: 7781f4e3 [22]

У vacuumdb новые параметры для очистки таблиц в указанных схемах (--schema) или таблиц, не принадлежащих указанным схемам (--exclude-schema).

Новые возможности утилиты createuser [23]
commit: 08951a7c [24]

Утилита createuser является оберткой над командой CREATE ROLE, но с ограниченным функционалом. Это изменение добавляет утилите ряд параметров, реализующих отсутствующие ранее возможности, а именно:

  • --valid-until для формирования фразы VALID UNTIL;
  • --bypassrls/--no-bypassrls для формирования атрибута BYPASSRLS/NOBYPASSRLS;
  • -m/--member для включения создаваемой роли в указанную роль;
  • -a/--admin для включения создаваемой роли в указанную роль с предложением WITH ADMIN OPTION.

Сообщение журнала о контрольной точке дополнили номерами LSN самой точки и начала восстановления [25]
commit: 62c46eee [26]

После завершения контрольной точки в WAL делается запись с номером LSN контрольной точки и номером LSN, откуда начинать восстановление в случае сбоя (redo LSN).

Для последней выполненной контрольной точки эти два номера LSN записываются в управляющий файл:

$ pg_controldata | egrep 'Latest.*location'

Latest checkpoint location:         1/1A3DEB20
Latest checkpoint's REDO location:  1/1A3DEAE8

А в 16-й версии их добавили в сообщение о завершении контрольной точки журнала сервера:

2022-08-02 12:15:17.961 MSK [198868] LOG:  checkpoint complete: wrote 0 buffers (0.0%); 0 WAL file(s) added, 0 removed, 0 recycled; write=0.001 s, sync=0.001 s, total=0.072 s; sync files=0, longest=0.000 s, average=0.000 s; distance=0 kB, estimate=418462 kB; lsn=1/1A3DEB20, redo lsn=1/1A3DEAE8

Стоит напомнить, что начиная с 15-й версии параметр log_checkpoints включен по умолчанию.

pg_prepared_statements.result_types [27]
commit: 84ad713c [28], 6ffff0fd [29]

В представление pg_prepared_statements добавили столбец result_types.

PREPARE get_booking (text) AS
SELECT * FROM bookings WHERE book_ref = $1;

SELECT * FROM pg_prepared_statements WHERE name = 'get_booking'gx

-[ RECORD 1 ]---+-----------------------------------------------
name            | get_booking
statement       | PREPARE get_booking (text) AS                 +
                | SELECT * FROM bookings WHERE book_ref = $1;
prepare_time    | 2022-08-02 16:54:14.313221+03
parameter_types | {text}
result_types    | {character,"timestamp with time zone",numeric}
from_sql        | t
generic_plans   | 0
custom_plans    | 0

Для подготовленных операторов без возвращаемого значения, столбец будет оставаться пустым.

auto_explain: параметр log_parameter_max_length [30]
commit: d4bfe412 [31]

Новый параметр расширения auto_explain.log_parameter_max_length выполняет ту же задачу, что и аналогичный log_parameter_max_length, а именно ― журналирует значения параметров запросов.

LOAD 'auto_explain';
SHOW auto_explain.log_parameter_max_length;

 auto_explain.log_parameter_max_length
---------------------------------------
 -1
(1 row)

Значение по умолчанию равно -1. Это говорит о том, что будут выводиться полные значения параметров. Значение 0 отключает вывод значений параметров, положительное число ограничивает вывод значений указанным количеством байт.

Настроим журналирование и сформируем параметризированный запрос командой EXECUTE… USING в анонимном блоке PL/pgSQL.

SET auto_explain.log_min_duration = 0;
SET auto_explain.log_nested_statements = 'on';
SET auto_explain.log_level = 'NOTICE';

DO $$BEGIN EXECUTE 'SELECT $1' USING 42; END;$$;

NOTICE:  duration: 0.003 ms  plan:
Query Text: SELECT $1
Query Parameters: $1 = '42'
Result  (cost=0.00..0.01 rows=1 width=4)
DO

В вывод добавилась строка Query Parameters, позволяющая узнать, с какими значениями выполнялся запрос в строке Query Text.

Необязательные псевдонимы для подзапросов во фразе FROM [32]
commit: bcedd8f5 [33]

Для подзапросов во фразе FROM стандарт SQL требует наличия псевдонима:

15=# SELECT * FROM (SELECT 42 AS a);

ERROR:  subquery in FROM must have an alias
LINE 1: SELECT * FROM (SELECT 42 AS a);
                    ^
HINT:  For example, FROM (SELECT ...) [AS] foo.

Однако это осложняет миграцию с других СУБД, где наличие псевдонима необязательно.

В 16-й версии такие псевдонимы не обязательны и в PostgreSQL.

16=# SELECT * FROM (SELECT 42 AS a);

 a  
----
 42
(1 row)

REINDEX: синтаксис и не только [34]
commit: 2cbc3c17 [35]

Варианты команды REINDEX DATABASE и REINDEX SYSTEM требовали указания базы данных. Но указать можно только текущую базу данных, что делает это требование излишним. В новой версии имя базы данных становится необязательным.

Кроме того, команда REINDEX DATABASE теперь переиндексирует только пользовательские индексы и пропускает индексы таблиц системного каталога. Прежнего поведения, а именно перестроения всех индексов базы данных, можно добиться выполнением двух команд REINDEX DATABASE и REINDEX SYSTEM.

Такое изменение обосновано тем, что до сих пор не было способа перестроить только пользовательские индексы во всей базе данных. К тому же перестроение системных индексов невозможно в режиме CONCURRENTLY, а в высоконагруженных системах может вызывать взаимоблокировки.

CREATE STATISTICS: необязательно указывать имя статистики [36]
commit: 624aa2a1 [37]

Расширенная статистика ― отдельный объект в базе данных и нуждается в имени. Но почему бы не разрешить опускать имя при создании, доверив его формирование самой СУБД? Точно так же, как это возможно для индексов и ограничений целостности.

Сделали:

CREATE STATISTICS ON departure_airport, arrival_airport FROM flights;

d flights

...
Statistics objects:
    "bookings.flights_departure_airport_arrival_airport_stat" ON departure_airport, arrival_airport FROM flights

CREATE TABLE: атрибут STORAGE [38]
commit: 784cedda [39]

Предположим, мы решили хранить изображения в базе данных. Создаем таблицу:

CREATE TABLE images (filename text, data bytea);
ALTER TABLE images ALTER COLUMN data SET STORAGE external;

Во второй команде меняем стратегию хранения TOAST на external, чтобы запретить сжатие данных. Ведь файлы изображений и так сжаты.

Вопрос в том, зачем это делать отдельной командой, а не сразу указать нужную стратегию в CREATE TABLE? Ответ простой: CREATE TABLE до 16-й версии не позволяет указывать атрибут STORAGE для столбцов, что и было исправлено:

CREATE TABLE images (filename text, data bytea STORAGE external);

У созданного при инициализации кластера пользователя нельзя отобрать полномочия суперпользователя [40]
commit: e530be2c [41]

Суперпользователь, созданный при инициализации кластера (pg_authid.oid=10), мог сам у себя отобрать атрибут SUPERUSER:

15=# ALTER ROLE postgres NOSUPERUSER;

(Если другого суперпользователя нет, то чтобы вернуть атрибут придется запускать сервер в однопользовательском режиме.)

В 16-й версии случайно лишиться суперпользовательских полномочий не получится:

16=# ALTER ROLE postgres NOSUPERUSER;

ERROR:  permission denied: bootstrap user must be superuser

Кстати, удалить суперпользователя, созданного при инициализации кластера было и раньше нельзя:

CREATE ROLE root LOGIN SUPERUSER;
c - root

DROP ROLE postgres;

ERROR:  cannot drop role postgres because it is required by the database system

Общий вывод можно сделать такой. Начальный суперпользователь необходим для нормального функционирования сервера. Удалять или отнимать у него привилегии не нужно. В плане обеспечения дополнительной безопасности можно подумать [42] о том, чтобы сделать эту роль групповой, забрав атрибут LOGIN.

Триггеры на TRUNCATE для внешних таблиц [43]
commit: 3b00a944 [44]

Некоторые обертки сторонних данных поддерживают команду TRUNCATE. Этот патч добавляет возможность создавать триггер на TRUNCATE для внешних таблиц таких оберток.

pg_read_file/pg_read_binary_file: новая вариация указания параметров [45]
commit: 283129e3 [46]

Спецификация функций pg_read_file и похожей pg_read_binary_file была такой:

pg_read_file ( filename text [, offset bigint, length bigint [, missing_ok boolean ]] ) → text

Предполагается, что для чтения файла целиком нужно не указывать параметры offset и length. Но тогда не получается задать missing_ok=true для игнорирования отсутствия файла.

В 16-й версии добавили еще один перегруженный вариант функций, что привело общую спецификацию к виду:

pg_read_file ( filename text [, offset bigint, length bigint ] [, missing_ok boolean ] ) → text

Теперь можно указать только имя файла и игнорировать его отсутствие.

Собственные менеджеры ресурсов WAL [47]
commit: 5c279a6d [48]

Особую роль патча подчеркивает мой коллега, Егор Рогов. Ему и слово.

Одна из проблем, стоящих перед разработчиками табличных и индексных методов доступа — невозможность формировать журнальные записи специфического вида. Существует механизм унифицированных журнальных записей [49], который сбрасывает в WAL “разницу” между старым и новым состояниями страницы (им пользуется, например, расширение rum [50]), но он не слишком эффективен и не поддерживает логическую репликацию.

В версии 16 расширения смогут создавать собственные менеджеры ресурсов [51] и, соответственно, журнальные записи собственного формата. Обратная сторона медали в том, что восстановление экземпляра после сбоя становится зависимым от стороннего расширения.

С точки зрения пользователя патч ничего не меняет, но это еще один важный шаг на пути к появлению новых методов доступа.


На этом пока всё. Ждем следующего сентябрьского коммитфеста [52] и, конечно же, официального выхода 15-й версии.

Автор: Павел Лузанов

Источник [53]


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/postgresql/377710

Ссылки в тексте:

[1] первый коммитфест: https://commitfest.postgresql.org/38/

[2] исходного кода: https://git.postgresql.org/gitweb/?p=postgresql.git;a=summary

[3] Индексы BRIN не блокируют HOT-обновления: https://habr.com/ru/company/postgrespro/blog/596567#commit_e3fcca0d

[4] Оптимизация работы с вложенными транзакциями: https://habr.com/ru/company/postgrespro/blog/679264/#commit_06f5295a

[5] psql: pset xheader_width: #commit_a45388d6

[6] vacuumdb --schema и --exclude-schema: #commit_7781f4e3

[7] Новые возможности утилиты createuser: #commit_08951a7c

[8] Сообщение журнала о контрольной точке дополнили номерами LSN самой точки и начала восстановления: #commit_62c46eee

[9] pg_prepared_statements.result_types: #commit_84ad713c

[10] auto_explain: параметр log_parameter_max_length: #commit_d4bfe412

[11] Необязательные псевдонимы для подзапросов во фразе FROM: #commit_bcedd8f5

[12] REINDEX: синтаксис и не только: #commit_2cbc3c17

[13] CREATE STATISTICS: необязательно указывать имя статистики: #commit_624aa2a1

[14] CREATE TABLE: атрибут STORAGE: #commit_784cedda

[15] У созданного при инициализации кластера пользователя нельзя отобрать полномочия суперпользователя: #commit_e530be2c

[16] Триггеры на TRUNCATE для внешних таблиц: #commit_3b00a944

[17] pg_read_file/pg_read_binary_file: новая вариация указания параметров: #commit_283129e3

[18] Собственные менеджеры ресурсов WAL: #commit_5c279a6d

[19] psql: pset xheader_width: https://commitfest.postgresql.org/38/3295/

[20] a45388d6: https://github.com/postgres/postgres/commit/a45388d6

[21] vacuumdb --schema и --exclude-schema: https://commitfest.postgresql.org/38/3612/

[22] 7781f4e3: https://github.com/postgres/postgres/commit/7781f4e3

[23] Новые возможности утилиты createuser: https://commitfest.postgresql.org/38/3618/

[24] 08951a7c: https://github.com/postgres/postgres/commit/08951a7c

[25] Сообщение журнала о контрольной точке дополнили номерами LSN самой точки и начала восстановления: https://commitfest.postgresql.org/38/3474/

[26] 62c46eee: https://github.com/postgres/postgres/commit/62c46eee

[27] pg_prepared_statements.result_types: https://commitfest.postgresql.org/38/3644/

[28] 84ad713c: https://github.com/postgres/postgres/commit/84ad713c

[29] 6ffff0fd: https://github.com/postgres/postgres/commit/6ffff0fd

[30] auto_explain: параметр log_parameter_max_length: https://commitfest.postgresql.org/38/3660/

[31] d4bfe412: https://github.com/postgres/postgres/commit/d4bfe412

[32] Необязательные псевдонимы для подзапросов во фразе FROM: https://commitfest.postgresql.org/38/3714/

[33] bcedd8f5: https://github.com/postgres/postgres/commit/bcedd8f5

[34] REINDEX: синтаксис и не только: https://commitfest.postgresql.org/38/3636/

[35] 2cbc3c17: https://github.com/postgres/postgres/commit/2cbc3c17

[36] CREATE STATISTICS: необязательно указывать имя статистики: https://commitfest.postgresql.org/38/3708/

[37] 624aa2a1: https://github.com/postgres/postgres/commit/624aa2a1

[38] CREATE TABLE: атрибут STORAGE: https://commitfest.postgresql.org/38/3479/

[39] 784cedda: https://github.com/postgres/postgres/commit/784cedda

[40] У созданного при инициализации кластера пользователя нельзя отобрать полномочия суперпользователя: https://www.postgresql.org/message-id/flat/CA%2BTgmoZirCwArJms_fgvLBFrC6b%3DHdxmG7iAhv%2Bkt_%3DNBA7tEw%40mail.gmail.com

[41] e530be2c: https://github.com/postgres/postgres/commit/e530be2c

[42] подумать: https://www.postgresql.org/message-id/f941fa4a-a294-165a-82c4-947d3c8425f6%40joeconway.com

[43] Триггеры на TRUNCATE для внешних таблиц: https://www.postgresql.org/message-id/flat/20220630193848.5b02e0d6076b86617a915682%40sraoss.co.jp

[44] 3b00a944: https://github.com/postgres/postgres/commit/3b00a944

[45] pg_read_file/pg_read_binary_file: новая вариация указания параметров: https://commitfest.postgresql.org/38/3730/

[46] 283129e3: https://github.com/postgres/postgres/commit/283129e3

[47] Собственные менеджеры ресурсов WAL: https://commitfest.postgresql.org/38/3469/

[48] 5c279a6d: https://github.com/postgres/postgres/commit/5c279a6d

[49] унифицированных журнальных записей: https://postgrespro.ru/docs/postgresql/14/generic-wal

[50] rum: https://github.com/postgrespro/rum

[51] собственные менеджеры ресурсов: https://www.postgresql.org/docs/15/custom-rmgr.html

[52] сентябрьского коммитфеста: https://commitfest.postgresql.org/39/

[53] Источник: https://habr.com/ru/post/681164/?utm_source=habrahabr&utm_medium=rss&utm_campaign=681164