
sqlite-utils 4.0rc1: миграции и вложенные транзакции
Первый релиз-кандидат sqlite-utils v4 добавляет встроенные миграции и вложенные транзакции. Разбираем ключевые изменения и обратную несовместимость.
sqlite-utils 4.0rc1: миграции схем и вложенные транзакции для SQLite
Саймон Уиллисон (Simon Willison), создатель инструмента Datasette, выпустил первый релиз-кандидат sqlite-utils версии 4.0 — sqlite-utils 4.0rc1. Это важная веха: переход к мажорной версии сигнализирует о ряде незначительных, но принципиальных изменений в поведении библиотеки, которые могут сломать существующий код, написанный под ветку 3.x.
sqlite-utils — это одновременно Python-библиотека и CLI-инструмент (утилита командной строки) для работы с базами данных SQLite, предоставляющая богатый набор высокоуровневых операций поверх стандартного модуля
sqlite3.
Что такое sqlite-utils и зачем он нужен
Библиотека и утилита командной строки помогает создавать SQLite-базы данных из существующих наборов данных. Большинство функций доступны как через Python API, так и через инструмент командной строки sqlite-utils. При этом sqlite-utils не является полноценной ORM — основной акцент сделан на вспомогательных утилитах для максимально быстрого создания и наполнения базы данных.
sqlite-utils — это два инструмента в одном пакете: Python-библиотека для удобного создания и манипулирования базами данных SQLite и CLI-инструмент для работы с ними в терминале.
Проект насчитывает уже более 130 релизов с момента основания в 2018 году и активно используется в экосистеме Datasette — открытой платформы для публикации и исследования данных.
Ключевые изменения в версии 4.0
1. Встроенная поддержка миграций (migrations)
Одно из главных нововведений — интеграция системы миграций схем базы данных непосредственно в sqlite-utils. Ранее для этого требовался отдельный плагин sqlite-migrate.
Система миграций позволяет выполнять команды вида sqlite-utils migrate creatures.db path/to/migrations.py и отслеживать применённые миграции с точными временными метками.
Инструмент работает с файлами миграций. Файл миграции выглядит следующим образом: из модуля sqlite_migrate импортируется класс Migrations, которому задаётся уникальное имя — оно не должно совпадать с именами других наборов миграций, применяемых к той же базе данных.
Пример файла миграций на Python:
from sqlite_migrate import Migrations
# Уникальное имя для данного набора миграций
migration = Migrations("мой_проект")
@migration()
def create_table(db):
# db — это экземпляр sqlite_utils.Database
db["пользователи"].create({
"id": int,
"имя": str,
"email": str
}, pk="id")
@migration()
def add_age_column(db):
db["пользователи"].add_column("возраст", int)
Запуск команды выполняет миграции последовательно против указанного файла базы данных. Повторный запуск не оказывает никакого дополнительного эффекта, если в файл не были добавлены новые функции миграций.
--list для просмотра списка применённых и ожидающих миграций без их запуска. Флаг -v (verbose) покажет схему до и после применения с подробным diff-ом изменений.2. Вложенные транзакции (nested transactions)
Второе важное нововведение — поддержка вложенных транзакций через механизм SQLite savepoints (точек сохранения).
SQLite не поддерживает вложенные транзакции в классическом смысле, однако предоставляет механизм savepoints, который реализует контролируемый откат внутри транзакции.
Savepoints по сути и являются вложенными транзакциями — эффект идентичен, просто используется другой синтаксис.
Практически это означает, что теперь можно безопасно вкладывать блоки with db.transaction() друг в друга без риска получить ошибку cannot start a transaction within a transaction:
import sqlite_utils
db = sqlite_utils.Database("mydata.db")
with db.transaction():
db["заказы"].insert({"id": 1, "товар": "Ноутбук", "сумма": 75000})
# Вложенная транзакция — теперь работает корректно!
with db.transaction():
db["позиции"].insert({"заказ_id": 1, "qty": 2})
# Если здесь произойдёт ошибка, откатится только эта вложенная часть
ROLLBACK во вложенном блоке откатит только изменения этого блока, но не всей внешней транзакции.3. Обратно несовместимые изменения
Автор использует семантическое версионирование, и версия 4.0 означает наличие обратно несовместимых изменений, которые могут затронуть код, написанный под ветку 3.x. Однако эти изменения в большинстве своём незначительны: задача — не ломать существующий код там, где можно этого избежать.
Изменение в upsert-операциях
Операции upsert теперь используют синтаксис SQLite INSERT ... ON CONFLICT SET для всех версий SQLite новее 3.23.1. Это незначительное критическое изменение для приложений, зависящих от прежнего поведения INSERT OR IGNORE + UPDATE. Пользователи Python API могут переключиться на старую реализацию, передав use_old_upsert=True конструктору Database().
Автоматическое определение типов при импорте CSV
Критическое изменение: автоматическое определение типов теперь включено по умолчанию для команд insert и upsert при импорте данных CSV или TSV. Ранее все столбцы обрабатывались как TEXT, если не был передан флаг --detect-types. Для восстановления старого поведения используйте новый флаг --no-detect-types.
Удаление переменной окружения
Переменная окружения SQLITE_UTILS_DETECT_TYPES была удалена.
Исправление синтаксиса экранирования имён
В 2018 году при создании проекта автор по ошибке решил, что корректный способ создавать таблицы и столбцы с зарезервированными именами — использовать синтаксис [my table] в квадратных скобках. Это нестандартный SQL, который SQLite поддерживает для совместимости с MS Access и SQL Server. К сожалению, это было заложено в библиотеку с самого начала и с тех пор «засоряло мир» странно экранированными именами таблиц и столбцов.
Теперь sqlite-utils использует стандартный ANSI SQL с кавычками: "my_table" вместо [my_table].
Минимальная версия Python
sqlite-utils теперь требует Python версии 3.10 или выше.
Таблица изменений: что нового в 4.0
| Изменение | Версия 3.x | Версия 4.0 |
|---|---|---|
| Миграции схем | Отдельный плагин sqlite-migrate | Встроено в ядро |
| Вложенные транзакции | Не поддерживались | Через SAVEPOINT |
| upsert-операции | INSERT OR IGNORE + UPDATE | INSERT ... ON CONFLICT SET |
| Экранирование имён | [имя_таблицы] (MS Access) | "имя_таблицы" (ANSI SQL) |
| Определение типов CSV | Отключено по умолчанию | Включено по умолчанию |
| Минимальный Python | 3.8+ | 3.10+ |
Архитектура: как это работает
graph TD
A[Пользователь / Скрипт] -->|Python API или CLI| B[sqlite-utils 4.0]
B --> C[Система миграций]
B --> D[Вложенные транзакции]
B --> E[Операции с данными]
C --> F[(SQLite БД)]
D --> F
E --> F
C -->|SAVEPOINT| G[Атомарность миграций]
D -->|SAVEPOINT| H[Безопасный откат]
G --> F
H --> F
Как попробовать rc1 прямо сейчас
Установка релиз-кандидата:
# Через pip
pip install sqlite-utils==4.0rc1
# Через pipx (рекомендуется для CLI)
pipx install sqlite-utils==4.0rc1
# Через uv
uv tool install sqlite-utils==4.0rc1
Проверка версии:
sqlite-utils --version
Быстрый тест с данными:
# Создаём базу данных из JSON-данных GitHub API
curl https://api.github.com/repos/simonw/sqlite-utils/releases \
| sqlite-utils insert releases.db releases - --pk id
# Просматриваем таблицу
sqlite-utils rows releases.db releases --table
В версии 4.0 тип данных определяется автоматически:
# Числа автоматически станут INTEGER/FLOAT, а не TEXT
sqlite-utils insert data.db products products.csv --csv
# Чтобы отключить автоопределение (поведение как в 3.x):
sqlite-utils insert data.db products products.csv --csv --no-detect-types
Участие в тестировании
Автор явно призывает сообщество протестировать rc1 и сообщать о найденных проблемах. Релиз-кандидат — это почти готовая к производству версия, но до финального выпуска важно убедиться, что нет критических регрессий в существующих сценариях использования.
Это уже 128-й релиз пакета с момента его создания в 2018 году. За это время sqlite-utils стал одним из наиболее популярных Python-инструментов для работы с SQLite в экосистеме data science и журналистики данных.
Если у вас есть существующий проект на sqlite-utils 3.x, рекомендуется:
- Создать виртуальное окружение:
python -m venv test-env - Установить rc1 туда
- Запустить существующие тесты и проверить upsert-операции
- Проверить CSV-импорты — типы данных теперь определяются автоматически
- Сообщить о проблемах на GitHub Issues
Итоги
sqlite-utils 4.0rc1 — это зрелый, хорошо продуманный релиз. Несмотря на мажорную версию, большинство изменений незначительны и легко управляемы. Главные приобретения — встроенные миграции и поддержка вложенных транзакций — делают библиотеку значительно удобнее для продакшн-сценариев, где схема базы данных эволюционирует со временем.
Если вы работаете с SQLite в Python-проектах, sqlite-utils 4.0 — это апгрейд, который стоит запланировать уже сейчас.
Ссылки: