PostgreSQL 18: Часть 4 или Коммитфест 2025-01
Продолжаем следить за новостями из мира PostgreSQL 18. Январский коммитфест принес много полезных изменений в области мониторинга и не только.
А самое интересное из предыдущих коммитфестов можно прочитать здесь: 2024–07, 2024–09, 2024–11.
EXPLAIN (analyze): buffers on по умолчанию
pg_stat_io: статистика ввода/вывода в байтах вместо страниц
pg_stat_io: статистика WAL
pg_stat_get_backend_io: статистика ввода/вывода отдельного процесса
VACUUM (verbose): информация из карты видимости
Суммарное время на очистку и анализ в разрезе таблиц
autovacuum: изменение количества рабочих процессов без перезапуска сервера
psql: информация о службе соединения
psql: расширенный режим для команд \d*
psql: признак leakproof в выводе команд \df*
jsonb: приведение значения null к другим типам
Алгоритм шифрования MD5: подготовка к снятию с поддержки
Новая функция uuidv7
postgres_fdw: использование аутентификации SCRAM без хранения пароля
passwordcheck: минимальная длина пароля
Новая функция casefold и правило сортировки pg_unicode_fast
Команды DML: RETURNING с OLD и NEW
to_number: преобразование строки с римскими цифрами в число
EXPLAIN (analyze): buffers on по умолчанию
commit: c2a4078eb
После долгих обсуждений победили сторонники включения по умолчанию вывода информации о буферах в команде EXPLAIN ANALYZE.
Привыкаем к более многословному выводу:
EXPLAIN (analyze, costs off)
SELECT * FROM bookings;
QUERY PLAN
------------------------------------------------------------------------
Seq Scan on bookings (actual time=0.086..119.142 rows=2111110 loops=1)
Buffers: shared hit=160 read=13287
Planning:
Buffers: shared hit=57
Planning Time: 0.543 ms
Execution Time: 180.270 ms
(6 rows)
Впрочем, часть сообщения о коммите посвящена сомнениям относительно принятого решения вплоть до возможности откатить патч до официального выпуска 18-й версии, если будет много возражений.
См. также
Waiting for PostgreSQL 18 — Enable BUFFERS with EXPLAIN ANALYZE by default (Hubert 'depesz' Lubaczewski)
pg_stat_io: статистика ввода/вывода в байтах вместо страниц
commit: f92c854cf
В предоставлении pg_stat_io три новых столбца: read_bytes, write_bytes, extend_bytes. В них накапливается объем соответствующих операций чтения, записи и расширения файлов. Количество операций по-прежнему можно получить из столбцов: reads, writes, extends.
Раньше для вычисления размера в байтах был необходим столбец op_bytes. В нем хранился размер страницы. Умножая op_bytes на reads, writes и extends, вычисляли объем операций в байтах. Теперь для размера в байтах есть отдельные столбцы, поэтому op_bytes был удален.
Это изменение открывает возможность добавлять в pg_stat_io другие операции ввода/вывода, не связанные с буферным кешем. Например, операции с WAL, где размер отдельной операции не связан с размером страницы.
Да и с буферным кешем не всегда всё строго в терминах страниц. Появившийся в 17-й версии параметр io_combine_limit позволяет группировать некоторые операции чтения более крупными кусками. К таким операциям относятся последовательное чтение таблиц, сбор статистики (analyze) и некоторые другие.
Посмотрим на влияние io_combine_limit на последовательное чтение таблицы в деле. Для теста потребуется небольшой psql-скрипт:
$ cat pg18/pg_stat_io.sql
\o /dev/null
SELECT pg_stat_reset_shared('io');
SET io_combine_limit = :'io_combine_limit';
CREATE TABLE bookings_copy
AS SELECT * FROM bookings;
DROP TABLE bookings_copy;
\o
SELECT context,
reads, read_bytes,
writes, write_bytes,
extends, extend_bytes
FROM pg_stat_io
WHERE backend_type = 'client backend' AND
object = 'relation' AND
(reads > 0 OR writes > 0);
В начале скрипта сбрасывается статистика и устанавливается io_combine_limit, затем выполняется CREATE TABLE AS SELECT. Для чтения таблицы bookings будет использовано последовательное сканирование. Последний запрос показывает статистику ввода/вывода.
Запустим скрипт со значением io_combine_limit по умолчанию:
$ psql -f pg18/pg_stat_io.sql -v io_combine_limit='128kB'
context | reads | read_bytes | writes | write_bytes | extends | extend_bytes
-----------+-------+------------+--------+-------------+---------+--------------
bulkread | 789 | 99753984 | 0 | 0 | |
bulkwrite | 0 | 0 | 11456 | 93847552 | 217 | 110624768
(2 rows)
При увеличении io_combine_limit в два раза, примерно в два раза уменьшается количество операций чтения:
$ psql -f pg18/pg_stat_io.sql -v io_combine_limit='256kB'
context | reads | read_bytes | writes | write_bytes | extends | extend_bytes
-----------+-------+------------+--------+-------------+---------+--------------
bulkread | 396 | 99491840 | 0 | 0 | |
bulkwrite | 0 | 0 | 11456 | 93847552 | 217 | 110624768
(2 rows)
pg_stat_io: статистика WAL
commit: a051e71e2
Благодаря предыдущему изменению, статистика ввода/вывода больше не привязана к размеру страницы. Это позволило добавить в pg_stat_io статистику работы с WAL.
Работа с WAL учитывается для разных типов процессов и в двух контекстах:
- normal ― операции с уже созданным сегментом WAL,
- init ― операции, когда сначала инициализируется новый сегмент WAL.
Пример статистики записи в WAL:
SELECT backend_type, object, context,
writes, write_bytes, pg_size_pretty(round(write_bytes/writes)) bytes_per_op
FROM pg_stat_io
WHERE object = 'wal' AND writes > 0;
backend_type | object | context | writes | write_bytes | bytes_per_op
-------------------+--------+---------+--------+-------------+--------------
client backend | wal | init | 2 | 33554432 | 16 MB
client backend | wal | normal | 207883 | 2198847488 | 10 kB
autovacuum worker | wal | init | 4 | 67108864 | 16 MB
autovacuum worker | wal | normal | 25595 | 1446313984 | 55 kB
background worker | wal | normal | 19012 | 155746304 | 8192 bytes
background writer | wal | normal | 1 | 8192 | 8192 bytes
checkpointer | wal | init | 1 | 16777216 | 16 MB
checkpointer | wal | normal | 12 | 8495104 | 691 kB
walwriter | wal | normal | 314 | 452460544 | 1407 kB
(9 rows)
pg_stat_get_backend_io: статистика ввода/вывода отдельного процесса
commit: 9aea73fc6
Новая функция pg_stat_get_backend_io выдает статистику ввода/вывода отдельного клиентского процесса. На входе функция принимает идентификатор процесса, а на выходе возвращает набор строк такой же структуры, как представление pg_stat_io.
Пример запроса статистики для текущего процесса:
SELECT object, context,
reads, read_bytes,
writes, write_bytes
FROM pg_stat_get_backend_io(pg_backend_pid())
WHERE reads > 0 OR writes > 0;
object | context | reads | read_bytes | writes | write_bytes
----------+---------+-------+------------+--------+-------------
relation | normal | 543 | 4489216 | 0 | 0
relation | vacuum | 76666 | 1122197504 | 34301 | 280993792
wal | normal | 0 | 0 | 256 | 283475968
(3 rows)
Функция работает только с клиентскими процессами. Статистика накапливается, пока процесс работает. По завершении процесса статистика удаляется. Сбрасывать статистику во время работы процесса можно функцией pg_stat_reset_backend_stats.
Соединяя pg_stat_get_backend_io с pg_stat_activity, можно получить много новой полезной информации: не просто какой процесс дольше всех работает, а какой из действующих процессов больше всего обращается к диску.
См. также
Postgres backend statistics (Part 1): I/O statistics (Bertrand Drouvot)
VACUUM (verbose): информация из карты видимости
commit: dc6acfd91, 4b565a198
Для только что созданной таблицы еще нет карты видимости, поэтому оба счетчика all-visible и all-frozen равны 0.
CREATE EXTENSION pg_visibility;
CREATE TABLE bookings_copy WITH (autovacuum_enabled = off)
AS SELECT * FROM bookings;
SELECT * FROM pg_visibility_map_summary('bookings_copy'::regclass);
all_visible | all_frozen
-------------+------------
0 | 0
(1 row)
В вывод команды VACUUM с параметром verbose добавлена строка visibility map
, показывающая итоговые значения этих счетчиков после очистки, а также количество страниц all-visible перед очисткой (значение 0 в скобках). Это позволяет посчитать количество страниц, помеченных как all-visible за время очистки.
VACUUM(freeze, verbose) bookings_copy;
INFO: aggressively vacuuming "demo.bookings.bookings_copy"
INFO: finished vacuuming "demo.bookings.bookings_copy": index scans: 0
pages: 0 removed, 13504 remain, 13504 scanned (100.00% of total), 0 eagerly scanned
tuples: 0 removed, 2111110 remain, 0 are dead but not yet removable
removable cutoff: 964, which was 0 XIDs old when operation ended
new relfrozenxid: 964, which is 1 XIDs ahead of previous value
frozen: 13447 pages from table (99.58% of total) had 2111110 tuples frozen
visibility map: 13447 pages set all-visible, 13447 pages set all-frozen (0 were all-visible)
index scan not needed: 0 pages from table (0.00% of total) had 0 dead item identifiers removed
avg read rate: 269.712 MB/s, avg write rate: 269.806 MB/s
buffer usage: 15623 hits, 11456 reads, 11460 dirtied
WAL usage: 26898 records, 4 full page images, 5963212 bytes
system usage: CPU: user: 0.19 s, system: 0.03 s, elapsed: 0.33 s
VACUUM
Теперь строки всех страниц заморожены и видны всем транзакциям.
SELECT * FROM pg_visibility_map_summary('bookings_copy'::regclass);
all_visible | all_frozen
-------------+------------
13447 | 13447
(1 row)
Суммарное время на очистку и анализ в разрезе таблиц
commit: 30a6ed0ce
В представлении pg_stat_all_table четыре новых столбца, начинающихся на total: total_vacuum_time, total_autovacuum_time, total_analyze_time, total_autoanalyze_time. В этих столбцах для каждой таблицы накапливается время, затраченное на очистку, автоочистку, анализ и автоанализ соответственно.
Учитывая, что в таблице уже есть счетчики количества операций, можно получить и статистику среднего времени выполнения. Следующий запрос показывает таблицу, на очистку и анализ которой было затрачено больше всего времени:
SELECT relname,
vacuum_count,
total_vacuum_time,
total_vacuum_time/nullif(vacuum_count,0) avg_vacuum_time,
autovacuum_count,
total_autovacuum_time,
total_autovacuum_time/nullif(autovacuum_count,0) avg_autovacuum_time,
analyze_count,
total_analyze_time,
total_analyze_time/nullif(analyze_count,0) avg_analyze_time,
autoanalyze_count,
total_autoanalyze_time,
total_autoanalyze_time/nullif(autoanalyze_count,0) avg_autoanalyze_time
FROM pg_stat_all_tables
ORDER BY total_autovacuum_time + total_vacuum_time +
total_analyze_time + total_autoanalyze_time DESC
LIMIT 1
\gx
-[ RECORD 1 ]----------+------------------
relname | ticket_flights
vacuum_count | 3
total_vacuum_time | 2992
avg_vacuum_time | 997.3333333333334
autovacuum_count | 0
total_autovacuum_time | 0
avg_autovacuum_time |
analyze_count | 2
total_analyze_time | 594
avg_analyze_time | 297
autoanalyze_count | 0
total_autoanalyze_time | 0
avg_autoanalyze_time |
Время везде в миллисекундах, поэтому очевидно, что в этой тестовой базе данных очистка и анализ не создают хлопот.
См. также
PostgreSQL 18: Per-relation cumulative statistics for [auto]vacuum and [auto]analyze (Daniel Westermann)
autovacuum: изменение количества рабочих процессов без перезапуска сервера
commit: c758119e5
Количество рабочих процессов автоочистки теперь можно изменять без перезапуска сервера. Достаточно изменить значение параметра autovacuum_max_workers и перечитать конфигурацию. Но количество рабочих процессов не должно превышать количество слотов, зарезервированных в новом параметре autovacuum_worker_slots. А вот изменение количества слотов уже потребует перезапуска.
\dconfig+ autovacuum*worker*
List of configuration parameters
Parameter | Value | Type | Context | Access privileges
-------------------------+-------+---------+------------+-------------------
autovacuum_max_workers | 3 | integer | sighup |
autovacuum_worker_slots | 16 | integer | postmaster |
(2 rows)
См. также
Waiting for PostgreSQL 18 — Allow changing autovacuum_max_workers without restarting.(Hubert 'depesz' Lubaczewski)
psql: информация о службе соединения
commit: 477728b5d
Файл службы соединения (pg_service.conf) позволяет подключаться к разным базам данных без необходимости каждый раз вводить параметры подключения.
$ grep -A 6 '^\[replica\]' ~/.pg_service.conf
[replica]
# Реплика основного сервера
host=localhost
port=5433
user=postgres
dbname=demo
options=-c search_path=bookings
Любое приложение, использующее libpq, может подключаться по имени службы replica. В psql теперь можно получить имя службы из переменной SERVICE:
$ psql service=replica -c '\echo :SERVICE'
replica
А также включать имя службы в приглашение psql, используя специальную последовательность %s
.
См. также
Waiting for PostgreSQL 18 — psql: Add more information about service name (Hubert 'depesz' Lubaczewski)
psql: расширенный режим для команд \d*
commit: 00f4c2959
Для того чтобы команды \d* выводили дополнительную информацию, приходилось перед командой включать расширенный режим, а после команды ― отключать. И писать не удобно, и лишний вывод о включении/выключении режима не нужен:
\x \du+ \x
Expanded display is on.
List of roles
-[ RECORD 1 ]-----------------------------------------------------------
Role name | postgres
Attributes | Superuser, Create role, Create DB, Replication, Bypass RLS
Description |
Expanded display is off.
Теперь в командах, выводящих списки объектов, можно использовать символ x
для включения расширенного режима только для этой команды:
\dux+
List of roles
-[ RECORD 1 ]-----------------------------------------------------------
Role name | postgres
Attributes | Superuser, Create role, Create DB, Replication, Bypass RLS
Description |
Поведение аналогично включению расширенного режима для вывода результата запроса при помощи \gx. При этом символы x
и +
могут быть указаны в любом порядке.
\dt+x tickets
List of tables
-[ RECORD 1 ]-+----------
Schema | bookings
Name | tickets
Type | table
Owner | postgres
Persistence | permanent
Access method | heap
Size | 386 MB
Description | Tickets
psql: признак leakproof в выводе команд \df*
commit: 2355e5111
В вывод команд \df+, \do+, \dAo+ и \dC+ добавлен признак leakproof. А с новым модификатором x
это легко продемонстрировать:
\df+x bookings.now
List of functions
-[ RECORD 1 ]-------+--------------------------------------------------------
Schema | bookings
Name | now
Result data type | timestamp with time zone
Argument data types |
Type | func
Volatility | immutable
Parallel | unsafe
Owner | postgres
Security | invoker
Leakproof? | no
Access privileges |
Language | sql
Internal name |
Description | Point in time according to which the data are generated
jsonb: приведение значения null к другим типам
commit: a5579a90a
После приведения JSON-значения null к SQL-типу text для элемента jsonb получается строка 'null'
. Но приведение к другому скалярному типу, например int, float, etc, вызывает ошибку:
17=# WITH t AS (
SELECT '{"a": null}'::jsonb col
)
SELECT t.col['a']::int null_as_int,
t.col['a']::text null_as_text
FROM t;
ERROR: cannot cast jsonb null to type integer
Больше этой ошибки не будет, JSON-значение null будет приведено к SQL-значению NULL:
18=# WITH t AS (
SELECT '{"a": null}'::jsonb col
)
SELECT t.col['a']::int null_as_int,
t.col['a']::text null_as_text
FROM t;
null_as_int | null_as_text
-------------+--------------
| null
(1 row)
Алгоритм шифрования MD5: подготовка к снятию с поддержки
commit: db6a4a985
Проблемы с безопасностью паролей, зашифрованных алгоритмом MD5, известны, на этом не стоит останавливаться. Поэтому в 19-й версии этот алгоритм не будет поддерживаться.
В 18-й версии MD5 еще работает, но в документации уже объявлен как устаревший, а при попытке создать пароль с алгоритмом MD5 будет выдаваться соответствующее предупреждение:
SET password_encryption = 'md5';
CREATE ROLE alice LOGIN PASSWORD 'secret';
WARNING: setting an MD5-encrypted password
DETAIL: MD5 password support is deprecated and will be removed in a future release of PostgreSQL.
HINT: Refer to the PostgreSQL documentation for details about migrating to another password type.
CREATE ROLE
Впрочем, предупреждение можно отключить параметром md5_password_warnings, но готовиться к отказу от MD5 стоит уже сейчас.
Новая функция uuidv7
commit: 78c5e141e
Существующая функция gen_random_uuid возвращает значение UUID версии 4. Значения формируются случайным образом и попытка их упорядочить не даст ответа на вопрос, в каком порядке они были получены:
SELECT g.x, gen_random_uuid()
from generate_series(1,5) AS g(x)
ORDER BY 2;
x | gen_random_uuid
---+--------------------------------------
4 | 00743ce3-1e99-4cf6-8d74-6c057018b7d6
3 | 26d9a9bc-f7a0-4c91-8afe-945960f2f59b
5 | 384869c0-8d7d-4201-945c-ee339b62e6b5
1 | c970dce9-d883-440a-8f41-0d47cc818328
2 | dd5cc4ed-adcc-4dbe-8926-d2222b826316
(5 rows)
В спецификации для 7-й версии UUID говорится, что значения должны начинаться с метки времени эпохи Unix в миллисекундах, поэтому генерируемые значения монотонно возрастают. В чем легко убедиться с новой функцией uuidv7, гарантирующей монотонное возрастание в пределах клиентского процесса:
SELECT g.x, uuidv7()
from generate_series(1,5) AS g(x)
ORDER BY 2;
x | uuidv7
---+--------------------------------------
1 | 01953813-d813-7c2f-81b0-9f74e90928b8
2 | 01953813-d813-7cb9-b5bb-1df8003f07b6
3 | 01953813-d813-7ce1-b13b-4f87ceb29651
4 | 01953813-d813-7d11-a4e6-cc1f343741b8
5 | 01953813-d813-7d37-94b0-bb2977fb3f4f
(5 rows)
Кроме того, при помощи функции uuid_extract_timestamp можно получить дату и время формирования UUID, а при вызове uuidv7 можно «сдвинуть время», передав параметром интервал сдвига:
SELECT g.x,
uuid_extract_timestamp(
uuidv7(format('%s day',g.x)::interval)
)
from generate_series(-2,2) AS g(x)
ORDER BY 2;
x | uuid_extract_timestamp
----+----------------------------
-2 | 2025-02-22 16:22:06.566+03
-1 | 2025-02-23 16:22:06.566+03
0 | 2025-02-24 16:22:06.566+03
1 | 2025-02-25 16:22:06.566+03
2 | 2025-02-26 16:22:06.566+03
(5 rows)
См. также
Waiting for PostgreSQL 18 — Add UUID version 7 generation function.(Hubert 'depesz' Lubaczewski)
postgres_fdw: использование аутентификации SCRAM без хранения пароля
commit: 761c79508
Если на удаленном сервере использовалась парольная аутентификация, то в настройках сопоставления ролей (CREATE USER MAPPINGS) приходилось явно указывать пароль. Чтобы избежать хранения пароля в открытом виде, теперь можно воспользоваться новым параметром use_scram_passthrough в настройках внешнего сервера или таблицы.
На удаленном сервере создана роль alice с паролем, зашифрованным алгоритмом scram-sha-256. В pg_hba.conf для этой роли настроен только один способ подключения: аутентификация по методу SCRAM.
remote=# SELECT * FROM pg_hba_file_rules LIMIT 1
-[ RECORD 1 ]------------------------------------------------
rule_number | 1
file_name | /home/pal/master/pg18/remote_server/pg_hba.conf
line_number | 1
type | host
database | {all}
user_name | {alice}
address | 127.0.0.1
netmask | 255.255.255.255
auth_method | scram-sha-256
options |
error |
Настройка postgres_fdw на локальном сервере будет выглядеть следующим образом:
local=# CREATE EXTENSION postgres_fdw;
В настройках сервера включим новый параметр use_scram_passthrough:
local=# CREATE SERVER remote_server
FOREIGN DATA WRAPPER postgres_fdw
OPTIONS (
host 'localhost',
port '5400',
dbname 'postgres',
use_scram_passthrough 'true'
);
Использовать внешний сервер также будет роль alice. Пароль для alice установим позже. Важно, что в сопоставлении ролей не нужно указывать пароль роли alice с удаленного сервера:
local=# CREATE ROLE alice LOGIN;
local=# CREATE USER MAPPING FOR alice
SERVER remote_server
OPTIONS (
user 'alice'
);
local=# GRANT USAGE ON FOREIGN SERVER remote_server TO alice;
Теперь для локальной роли alice установим точно такой же пароль, как у удаленной роли alice. Нужно не просто ввести такую же строку, а прямо скопировать с удаленного сервера зашифрованное значение пароля из таблицы pg_authid:
remote=# SELECT rolpassword FROM pg_authid WHERE rolname = 'alice';
И подставить это значение в качестве пароля локальной роли alice:
local=# ALTER ROLE alice PASSWORD '...';
Теперь alice на локальном сервере может создавать и использовать внешние таблицы, подключаясь к удаленному серверу без ввода пароля.
passwordcheck: минимальная длина пароля
commit: f7e1b3828
Новый параметр расширения passwordcheck позволяет указать минимальную длину пароля.
SET passwordcheck.min_password_length = 8;
ALTER ROLE postgres PASSWORD '123';
ERROR: password is too short
DETAIL: password must be at least "passwordcheck.min_password_length" (8) bytes long
Но отправлять пароли в открытом виде небезопасно, поэтому многие клиентские приложения предоставляют возможности для отправки пароля на сервер в предварительно зашифрованном виде. Например, в psql для этого используется команда \password. А с предварительно зашифрованными паролями параметр passwordcheck.min_password_length работать не будет.
Новая функция casefold и правило сортировки pg_unicode_fast
commit: bfc599206, d3d098316, 286a365b9
Функция casefold предназначена для регистронезависимого поиска в тех случаях, когда преобразование сравниваемых строк к нижнему (или верхнему) регистру не помогает. В зависимости от правила сортировки, для одного символа могут быть определены несколько вариантов в нижнем регистре.
Пример вызова функции для знака эсцет:
SELECT c, lower(c), casefold(c), upper(c)
FROM (VALUES('ß' collate "pg_unicode_fast")) AS t(c);
c | lower | casefold | upper
---+-------+----------+-------
ß | ß | ss | SS
(1 row)
Здесь используется новое правило сортировки pg_unicode_fast, которое более точно соответствует определенному в стандарте SQL правилу сортировки ucs_basic.
Команды DML: RETURNING с OLD и NEW
commit: 80feb727c
В командах INSERT, UPDATE, DELETE и MERGE во фразе RETURNING появилась возможность получать как старые значения (до выполнения команды), так и новые значения. Для этого нужно обращаться к столбцам при помощи конструкций OLD и NEW. (Очень похоже на работу со старыми и новыми значениями в триггерах уровня строки.)
CREATE TABLE t(
id integer PRIMARY KEY,
s text
);
Для INSERT обращаться к старым значениям обычно нет смысла, старые значения всегда NULL.
INSERT INTO t
VALUES (1,'aaa'), (2, 'bbb')
RETURNING old.*, new.*;
id | s | id | s
----+---+----+-----
| | 1 | aaa
| | 2 | bbb
(2 rows)
Для DELETE отсутствуют новые значения:
DELETE FROM t
WHERE id > 1
RETURNING old.*, new.*;
id | s | id | s
----+-----+----+---
2 | bbb | |
(1 row)
А вот для UPDATE доступ к старым и новым значениям будет полезен для анализа изменений:
UPDATE t SET s = 'ccc'
WHERE id = 1
RETURNING old.*, new.*,
format('%s->%s', old.s, new.s) AS change;
id | s | id | s | change
----+-----+----+-----+----------
1 | aaa | 1 | ccc | aaa->ccc
(1 row)
То же относится к варианту INSERT с ON CONFLICT:
INSERT INTO t
VALUES (1,'ddd'), (3,'eee')
ON CONFLICT(id) DO UPDATE SET s = EXCLUDED.s
RETURNING to_json(old.*) AS old,
to_json(new.*) AS new;
old | new
--------------------+--------------------
{"id":1,"s":"ccc"} | {"id":1,"s":"ddd"}
| {"id":3,"s":"eee"}
(2 rows)
См. также
Waiting for PostgreSQL 18 — Add OLD/NEW support to RETURNING in DML queries.(Hubert 'depesz' Lubaczewski)
to_number: преобразование строки с римскими цифрами в число
commit: 172e6b3ad
Преобразовать натуральное число в строку с римскими цифрами уже давно можно при помощи функции to_char. Теперь появился обратный способ конвертации строки с римскими цифрами в число при помощи to_number:
SELECT to_char(42, 'RN'),
to_number('XLII', 'RN');
to_char | to_number
-----------------+-----------
XLII | 42
(1 row)
См. также
Waiting for PostgreSQL 18 — Support RN (roman-numeral format) in to_number ().(Hubert 'depesz' Lubaczewski)
На этом пока всё. Впереди основные события последнего мартовского коммитфеста 18-й версии.