Вышла sqlite-utils 4.1: вставка строк Python-кодом и STRICT-режим для таблиц SQLite

11 июля 2026 года Саймон Уиллисон, создатель Datasette и sqlite-utils, выпустил версию 4.1 своей библиотеки и CLI-утилиты для работы с базами SQLite. Это первый точечный релиз после версии 4.0, которая вышла всего за несколько дней до этого, и он добавляет несколько небольших, но практичных возможностей.

Команды insert и upsert теперь принимают опцию --code: в неё можно передать блок Python-кода (или путь к .py-файлу), который определяет функцию rows() или итерируемый объект rows со строками для вставки, это альтернатива обычной вставке данных из файла (тикет #684). Такая возможность продолжает уже существующий в sqlite-utils приём передавать блоки Python-кода прямо в аргументах командной строки: например, команда convert и раньше принимала код для преобразования значений столбца. Пример из поста: команда sqlite-utils insert data.db creatures --code с блоком, где функция rows() через yield возвращает строки вида {id: 1, name: «Cleo»} и {id: 2, name: «Suna»}, плюс флаг --pk id для указания первичного ключа.

Также insert/upsert получили опцию --type имя-столбца тип, которая позволяет вручную задать тип столбца вместо автоматического определения при создании таблицы (тикет #131). Практическая польза, например, для CSV- и TSV-колонок с почтовыми ZIP-кодами, которые выглядят как целые числа, но должны храниться как TEXT, чтобы не терять ведущие нули. Автор отмечает, что это был давний запрос пользователей, реализовать который на практике оказалось просто.

Появился новый метод table.drop_index(name) и команда drop-index для удаления индекса по имени; оба принимают ignore=True/флаг --ignore, чтобы не выдавать ошибку, если такого индекса не существует (тикет #626).

Команда query теперь может читать SQL-запрос из стандартного ввода, если вместо текста запроса передать дефис -, например, echo 'select * from dogs' | sqlite-utils query dogs.db - (тикет #765).

Ещё две небольшие возможности появились после того, как автор попросил Codex просмотреть все открытые тикеты в репозитории и выделить среди них самые простые в реализации. Во-первых, upsert теперь умеет самостоятельно определять первичный ключ существующей таблицы, поэтому флаг --pk можно не указывать, если такой ключ уже задан. Во-вторых, методы table.transform() и table.transform_sql() получили параметр strict=True/strict=False для переключения таблицы в строгий (STRICT) режим SQLite и обратно; если параметр не указан, режим таблицы сохраняется прежним (тикет #787). Соответствующая команда sqlite-utils transform тоже получила флаги --strict и --no-strict.

Функциональность STRICT появилась благодаря статье Эвана Хана (Evan Hahn) «Prefer STRICT tables in SQLite», которая в тот же день была популярна на Hacker News. Хан обратил внимание, что в SQLite нет способа перевести таблицу в строгий режим командой ALTER, для этого нужно скопировать данные из обычной таблицы в новую, строгую. Именно так устроен механизм transform в sqlite-utils, поэтому автор расширил его, добавив возможность переключать таблицы между строгим и нестрогим режимом в обе стороны.

Уиллисон опубликовал полную расшифровку сессии с моделью GPT-5.6 «Sol» в режиме рассуждений xhigh через Codex, которую он использовал для реализации STRICT-функциональности. Один из самых полезных промптов в этой сессии предписывал модели вручную проверить свою же работу: «используй uv run python -c и вручную прогоняй новую опцию .transform(strict=), попробуй найти граничные случаи или баги», то есть попросить модель протестировать код не только автоматическими тестами, которые она сама написала, но и вручную. Это выявило две небольшие ошибки, которые затем были исправлены.

Ключевые факты

  • insert/upsert получили опцию --code: блок Python-кода с функцией rows() или итерируемым объектом rows задаёт строки для вставки, альтернатива импорту из файла (тикет #684).
  • insert/upsert получили опцию --type для ручного указания типа столбца, например, чтобы хранить ZIP-коды как TEXT и не терять ведущие нули (тикет #131).
  • Новый метод table.drop_index(name) и команда drop-index удаляют индекс по имени, с флагом --ignore для случая отсутствующего индекса (тикет #626).
  • sqlite-utils query теперь читает SQL-запрос из стандартного ввода через -, а upsert научился сам определять первичный ключ уже существующей таблицы (тикет #765).
  • Методы transform/transform_sql получили параметр strict=/флаг --strict для перевода таблиц в STRICT-режим SQLite и обратно (тикет #787), идея пришла из статьи Эвана Хана, а реализацию для STRICT автор сделал с помощью модели GPT-5.6 «Sol» через Codex и вручную проверил на граничные случаи.

Почему это важно

Релиз показывает типичный для зрелого open-source-проекта цикл сопровождения: почти все новые возможности (тикеты #684, #131, #626, #765, #787) выросли из конкретных запросов и обсуждений пользователей, а не из абстрактного планирования. Отдельно интересен процесс разработки: часть небольших возможностей подсказал ИИ-агент Codex, которому поручили просмотреть все открытые тикеты и выбрать самые простые в реализации, а STRICT-функциональность написана с помощью модели GPT-5.6 «Sol» и затем вручную протестирована по прямому указанию автора, это конкретный, задокументированный пример того, как разработчик совмещает кодогенерацию через LLM с ручной проверкой перед публикацией.

Кому это важно

Python-разработчикам и дата-инженерам, которые используют SQLite и sqlite-utils/Datasette для локальной обработки, импорта и очистки данных (CSV/TSV, ETL-скрипты); всем, кто интересуется STRICT-таблицами SQLite и переводом существующих баз в этот режим; а также тем, кто следит за практикой применения ИИ-кодогенерации (Codex, GPT-5.6) в реальном сопровождении open-source-проектов.

Как это применить

Чтобы получить новые возможности, нужно обновить sqlite-utils до версии 4.1 (например, через pip или uv). Дальше можно: вставлять строки динамически через sqlite-utils insert БД таблица --code '...' с функцией rows(); задавать тип столбца вручную через --type колонка тип при импорте CSV/TSV, где нужно сохранить ведущие нули; удалять индекс командой sqlite-utils drop-index БД таблица имя_индекса --ignore; передавать SQL-запрос в query через стандартный ввод (echo 'select ...' | sqlite-utils query БД -); и переводить существующие таблицы в строгий (STRICT) режим SQLite командой sqlite-utils transform БД таблица --strict.

Можно ли доверять

Источник, официальный блог Саймона Уиллисона, создателя и основного разработчика sqlite-utils и Datasette, то есть это авторские заметки о релизе из первых рук со ссылками на конкретные номера тикетов в репозитории. Уиллисон дополнительно повышает прозрачность, публикуя полную расшифровку сессии с ИИ-моделью, которую использовал при реализации STRICT-функциональности, и открыто описывает, что было проверено вручную, а что, автоматическими тестами. Это первичный источник по собственному продукту автора; независимая проверка фактов о релизе не проводилась.

Риски и подводные камни

Опция --code выполняет произвольный переданный Python-код, та же модель, что уже использовалась в команде convert; при передаче таких блоков (особенно не из собственных скриптов) стоит понимать, что это полноценное выполнение кода, а не просто текстовый параметр. Перевод таблицы в STRICT-режим, как отмечает процитированный в посте Эван Хан, не делается штатной командой ALTER, данные копируются из старой таблицы в новую, строгую, что для больших баз данных может быть не мгновенной операцией. Наконец, даже код, написанный GPT-5.6 для STRICT-функциональности, потребовал отдельного ручного прогона тестов автором, который нашёл две небольшие ошибки, не пойманные автоматическими тестами, то есть даже в этом релизе код, сгенерированный ИИ, не публиковался без дополнительной проверки человеком.

«К сожалению, не думаю, что существует способ ALTER-ом сделать таблицу строгой (strict), похоже, для этого нужно скопировать данные из нестрогой таблицы в строгую.»

— Эван Хан (Evan Hahn), автор статьи «Prefer STRICT tables in SQLite», в цитате из поста Саймона Уиллисона