Прежде чем нырять в типы данных, индексы, vacuum и explain analyze, полезно понять, на какой именно postgresql вы работаете и почему это важно. Версия определяет, какие команды у вас вообще есть и как ведут себя дефолты. В этом уроке мы разберем ключевые новшества PostgreSQL 15, которые мы берем за базу курса, и пройдемся по тому, что докрутили в 16 и 17. Заодно научимся ориентироваться в нумерации версий и сроках поддержки, чтобы не оказаться на ветке, которую завтра перестанут чинить.

Как это работает
С 2017 года PostgreSQL ушел от схемы вида 9.6 к простой нумерации: 15, 16, 17 - это мажорные релизы, по одному в год, обычно осенью. Третья цифра (15.4, 16.2) - это минорный, чисто исправления багов и дыр безопасности, без новых фич и без изменения формата данных на диске. Минорный накатывается простой заменой бинарников и рестартом, мажорный требует pg_upgrade или дампа, потому что внутренний формат может поменяться.
Каждый мажорный релиз живет 5 лет. Грубая прикидка на 2026 год: PG 15 поддерживается примерно до конца 2027, PG 16 - до конца 2028, PG 17 - до конца 2029. Когда ветка выходит из поддержки, минорные обновления для нее перестают выпускать, и вы остаетесь с незакрытыми уязвимостями. Сидеть на 11 или 12 в 2026 - плохая идея.
Главная философия свежих версий - меньше ручной возни и больше безопасных дефолтов. Поэтому в 15 поменяли права на схему public, выкатили команду MERGE, ускорили логическую репликацию и сжали WAL. В 16 и 17 упор сделали на масштабирование чтения, параллелизм и заметно более быстрый VACUUM.
SQL и примеры
Сначала всегда смотрите, где вы находитесь. Две формы - короткая строка и разобранные по полочкам параметры.
Код: Выделить всё
SELECT version();
SHOW server_version;
SHOW server_version_num; -- 150004 значит 15.4
Код: Выделить всё
-- в свежей базе PG 15+ обычная роль не может писать в public
GRANT CREATE, USAGE ON SCHEMA public TO app_user;
Код: Выделить всё
MERGE INTO seats AS t
USING staging_seats AS s
ON t.aircraft_code = s.aircraft_code AND t.seat_no = s.seat_no
WHEN MATCHED AND s.removed THEN
DELETE
WHEN MATCHED THEN
UPDATE SET fare_conditions = s.fare_conditions
WHEN NOT MATCHED THEN
INSERT (aircraft_code, seat_no, fare_conditions)
VALUES (s.aircraft_code, s.seat_no, s.fare_conditions);
Код: Выделить всё
CREATE TABLE flight_log (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
msg text NOT NULL,
at timestamptz NOT NULL DEFAULT now()
);
Код: Выделить всё
-- стиль SQL/JSON, активно расширявшийся в 16 и 17
SELECT JSON_OBJECT('flight': flight_no, 'status': status)
FROM flights
WHERE status = 'Arrived'
LIMIT 5;
Частые грабли
- Путают мажор и минор. Накатили 15.6 поверх 15.4 и ждут новых фич - их там нет, только багфиксы. Новые возможности приходят только с мажором.
- Обновили бинарники на новый мажор и удивляются, что база не стартует. Между мажорами нужен pg_upgrade или dump/restore, простой заменой пакетов не обойтись.
- После установки PG 15 ловят permission denied на CREATE TABLE и считают это багом. Это новое поведение public schema, нужен явный GRANT.
- Думают, что MERGE атомарно решит гонки как UPSERT. Для строгих гонок по уникальному ключу по-прежнему надежнее INSERT ... ON CONFLICT.
- Тащат в новый код serial. Он не запрещен, но GENERATED IDENTITY - более правильный и переносимый выбор на 2026.
- Сидят на снятой с поддержки версии и не накатывают минорные обновления, копя уязвимости.
- Узнайте свою версию: SELECT version(); и SHOW server_version_num; - запишите мажор и минор.
- Создайте роль: CREATE ROLE lab LOGIN PASSWORD 'lab'; и попробуйте от ее имени создать таблицу в public - поймайте отказ прав на PG 15+.
- Выдайте право GRANT CREATE, USAGE ON SCHEMA public TO lab; и повторите создание - теперь должно пройти.
- Создайте таблицу с bigint GENERATED ALWAYS AS IDENTITY и вставьте пару строк без указания id.
- Сделайте простую staging-таблицу и выполните MERGE в свою таблицу, проверив ветки MATCHED и NOT MATCHED.
- Запустите EXPLAIN на любом своем запросе и просто посмотрите на план - детально разберем его в следующих уроках.
- Зайдите на postgresql.org/support/versioning и сверьте дату конца поддержки своей версии.
- Чем отличается мажорное обновление от минорного и что требуется для каждого?
- Какое изменение в правах схемы public принес PostgreSQL 15 и как теперь дать роли возможность создавать объекты?
- Что делает команда MERGE и чем она отличается от INSERT ... ON CONFLICT?
- Почему GENERATED IDENTITY предпочтительнее serial в новом коде?
- Какие улучшения логической репликации появились в 16 и 17?
- Как узнать срок окончания поддержки вашей версии и почему на этот срок надо смотреть?