sqlite-utils 4.0rc1: миграции и вложенные транзакции
sqlite-utils, Python-библиотека и CLI для работы с SQLite, предоставляющая высокоуровневые операции над БД. Версия 4.0rc1 добавляет две крупные фичи: систему миграций (портированную из отдельного пакета sqlite-migrate) и API для вложенных транзакций через savepoints. Релиз содержит несколько breaking changes: изменен тип FLOAT на REAL, изменилась логика вставки данных, переход на pyproject.toml, новая синтаксис INSERT ... ON CONFLICT для всех версий SQLite после 3.23.1.
Ключевые факты
- Встроенная система миграций на основе декораторов с поддержкой применения через Python API или CLI
- Новый API db.atomic() для управления вложенными транзакциями через savepoints, заимствованный из Django
- Несколько breaking changes: FLOAT→REAL, новая синтаксис вставки данных, отказ от Python 3.8, перенос TUI в отдельный плагин
- Миграции спроектированы без функции отката, ошибки исправляются новой миграцией вперед
- Тестирование на SQLite 3.23.1 (2018 год) гарантирует совместимость со старыми версиями
Ред. Главная фича релиза это решение не делать откат миграций. Удобство, продаваемое как принципиальность.
Почему это важно
Миграции БД, стандартный паттерн в production. Раньше sqlite-utils оставлял управление транзакциями пользователю. Теперь версия 4.0 интегрирует проверенный механизм миграций (предшественник использовался LLM и другими проектами годы) и упрощает работу с транзакциями через savepoints. Breaking changes говорят об амбициях авторов переделать фундамент для лучшей совместимости с SQL-стандартами.
Ред. "Амбиции переделать фундамент" звучит гордо, но на практике это значит, что ваш рабочий код сломается ради чистоты SQL-стандарта.
Кому это важно
Python-разработчикам, работающим с SQLite через ORM или прямым доступом. Особенно полезно для небольших приложений и скриптов, где полноценные системы миграций (как Django) кажутся избыточны. LLM-проектам, которые используют SQLite как встроенное хранилище, упростится управление схемой.
Ред. Тем, кто выбрал SQLite именно потому, что не хотел возиться с миграциями. Теперь придётся.
Как это применить
Миграции подключаются через декоратор @migrations() в файле migrations.py. Каждая функция получает объект Database и может создавать таблицы или менять схему. Запуск: либо db = Database(...); migrations.apply(db) в Python, либо команда sqlite-utils migrate creatures.db migrations.py в CLI. Вложенные транзакции используют with db.atomic(): контекст, внутри можно еще раз вложить db.atomic() для savepoint. Установка: pip install sqlite-utils==4.0rc1 или uvx --with sqlite-utils==4.0rc1.
Ред. Инструкция выглядит просто, пока миграция не упадёт на середине. Тогда вспомните, что отката тут нет, и пишите вторую миграцию поверх первой.
Можно ли доверять
Дизайн миграций проверен годами (из sqlite-migrate). Неподвижно реализован в sqlite-utils 4.0 RC, это первый стабильный кандидат перед финальным выпуском. Вложенные транзакции, абстракция над SQLite savepoints, стандартного механизма, но меньше тестирована. Авторы (Simon Willison) активно просят обратную связь через Discord и GitHub Issues перед финальным релизом.
Ред. Дизайн проверен годами, реализация в 4.0 это RC. То есть проверено всё, кроме того, что вы поставите.
Риски и подводные камни
Breaking changes потребуют правок в коде существующих приложений. Миграции без отката означают, что ошибку нужно исправлять новой миграцией (forward-only). Вложенные транзакции могут дорого обойтись на больших объемах, savepoints во время интенсивных операций замедляют работу. Отказ от Python 3.8 отсечет старые окружения. Изменение синтаксиса имен столбцов с [square-braces] на "double-quotes" может сломать скрипты, зависящие от точной схемы.
Ред. Forward-only миграции это не философия минимализма, а перекладывание ответственности на того, кто будет чинить прод в три часа ночи. Плюс savepoints, которые тихо съедают производительность ровно тогда, когда данных много.
«The system is deliberately small: it doesn't provide reverse migrations, so any mistakes you make should be fixed by deploying a fresh migration to undo them.»
— Simon Willison, sqlite-utils документация