Короткий ответ
Нельзя просто добавить колонку с NOT NULL без DEFAULT в таблицу с существующими строками — PostgreSQL попытается применить ограничение ко всем текущим строкам и упадёт, так как для них значения не определены; правильный путь — добавить колонку nullable, заполнить существующие строки значениями, затем отдельным шагом добавить ограничение NOT NULL.
Как это работает подробнее
ALTER TABLE ... ADD COLUMN col TYPE NOT NULL без DEFAULT требует значения для каждой существующей строки — его взять неоткуда, поэтому PostgreSQL отклоняет миграцию; добавление с DEFAULT в PostgreSQL 11+ для большинства типов быстро, но столбец всё ещё нужно сначала сделать nullable, прежде чем включать NOT NULL.
Пример кода
example.py
# Миграция 1: добавить nullable колонку с default
def upgrade():
op.add_column("users", sa.Column("country", sa.String(length=2), nullable=True))
# Отдельный backfill-скрипт/миграция данных батчами
# UPDATE users SET country = 'US' WHERE country IS NULL AND id BETWEEN 1 AND 10000;
# Миграция 2 (после подтверждения, что backfill завершён): включить NOT NULL
def upgrade():
op.alter_column("users", "country", nullable=False)Что использовать на практике
- Практика. Безопасная последовательность: (1) добавить колонку nullable; (2) выполнить backfill существующих строк батчами; (3) добавить ограничение NOT NULL отдельной миграцией после подтверждения завершения backfill — это минимизирует блокировки на большой таблице.
Подводные камни
- Ошибка. Пытаются сделать всё одной миграцией на большой production-таблице, вызывая долгую блокировку ACCESS EXCLUSIVE; забывают, что старый код приложения во время rolling deploy может создавать новые строки без новой колонки, если не предусмотрен DEFAULT.