Вышла DuckDB 2.0.0 — релиз с долгосрочной поддержкой (LTS) как минимум до октября 2027 года. Кодовое имя — «Cinnamon Teal», в честь утки Spatula cyanoptera. С момента версии 1.5 в него вошло почти 20 000 коммитов; это крупнейший функциональный релиз проекта.
Ниже — что именно изменилось в движке и CLI, как это работает и что из этого следует для тех, кто гоняет на DuckDB аналитические запросы.
Асинхронный ввод-вывод при чтении файлов
Главное изменение для аналитических нагрузок — асинхронный ввод-вывод при чтении Parquet, CSV и собственного формата хранения DuckDB. Раньше чтение и обработка запроса были связаны; теперь слой ввода-вывода масштабируется независимо от слоя обработки запросов: чтения выдаются в фоне, и одновременно в полёте держится несколько запросов.
Практический эффект — больше параллелизма при удалённом чтении и заметно более быстрые запросы к объектным хранилищам вроде s3://. Для локальных файлов выигрыш меньше, но именно сценарий «данные лежат в объектном хранилище» здесь основной.
Quack-протокол и оператор CONNECT
Протокол Quack — это сетевой протокол, который позволяет запускать DuckDB в традиционной схеме клиент-сервер: один процесс держит базу и обслуживает подключения, а другие подключаются к ней по сети. В 2.0.0 он доработан — заявлены существенные улучшения производительности и стабильности. Конкретные проблемы, которые мотивировали переработку, в анонсе не названы.
Сервер поднимается вызовом quack_serve с токеном, клиент подключается через ATTACH с тем же токеном, после чего CONNECT переключает сессию на удалённую базу:
CALL quack_serve(token = 'my_token');
ATTACH 'quack:localhost' AS qk (TOKEN 'my_token');
CONNECT qk;После CONNECT запросы выполняются уже к таблицам удалённой базы. Оператор CONNECT не привязан к Quack: он указывает сессию на любую удалённую базу, которая это поддерживает, включая PostgreSQL и MySQL. Для них новый оптимизатор remote pushdownпередача запроса на удалённый сервер, чтобы он выполнил его у себя, а не пересылал таблицы целиком отправляет запросы прямо на удалённый сервер вместо перетаскивания таблиц по сети — так по сети передаётся результат запроса, а не все исходные строки.
Для эксплуатации важно поведение по умолчанию: сервер слушает только localhost. Чтобы принимать подключения с других машин, его оставляют на localhost и ставят за TLS-терминирующий обратный прокси. Опция allow_other_hostname, открывающая сервер напрямую, предназначена только для тестирования. В CLI появились ярлыки -serve и -connect, запускающие сервер или подключающиеся к нему из командной строки.
Тип VARIANT
VARIANTполуструктурированный тип для JSON-подобных данных стал полноценным типом колонки. DuckDB определяет общую структуру данных и «shredВ контексте типа VARIANT — раскладывает общую структуру JSON-данных по колонкам, чтобы они хорошо сжимались и быстро обрабатывались.» её — раскладывает по колонкам, — за счёт чего данные хорошо сжимаются и быстро обрабатываются. Работать с ними можно семейством функций variant_*.
CREATE TABLE events (payload VARIANT);JSON-значения вставляются приведением текста к JSON, затем к VARIANT. Для значения можно узнать его тип строкой (для объектов — OBJECT) и получить массив ключей верхнего уровня типа varchar[].
Триггеры и путь к материализованным представлениям
Появились триггеры уровня инструкции: FOR EACH STATEMENT с REFERENCING OLD TABLE AS o / NEW TABLE AS n даёт телу доступ к таблицам-переходам — строкам до и после изменения. В отличие от обычного построчного триггера, который срабатывает на каждую затронутую строку, такой триггер выполняется один раз на всю инструкцию и видит сразу весь набор изменённых строк. Это важно, когда изменение нужно обработать как единое множество: например, аудит соединяет старые и новые строки по идентификатору и записывает прежнее и новое значения одной операцией.
Отдельный сценарий — инкрементальное поддержание агрегатов. Чтобы хранить средний возраст по национальностям, таблица age_statistics держит не только average_age, но и sum_age и num_people: среднее пересчитывается без полного пересчёта таблицы. Триггер на INSERT группирует новые строки по национальности и через MERGE прибавляет сумму и количество, а при отсутствии строки вставляет новую. Триггер на DELETE работает симметрично: если удалены все люди данной национальности, запись удаляется, иначе сумма и количество вычитаются.
Авторы прямо называют это шагом к материализованным представлениям — давно числившемуся пункту в разделе «future work» roadmap. То есть триггеры приближают к ним, но не заменяют: материализованные представления остаются будущей работой.
Пользовательские репозитории расширений
Репозиторий расширений — это имя, URL-префикс и один или несколько RSA-публичных ключей, которым доверяется подпись расширений из него. Префикс может указывать на локальный путь, https, s3 — на всё, что DuckDB умеет читать.
Использование таких репозиториев сначала разрешается настройкой allow_extension_repositories: чтобы включить пользовательские репозитории, ей присваивается значение 'allowed' командой SET allow_extension_repositories = 'allowed';. Затем репозиторий создаётся командой CREATE EXTENSION REPOSITORY ... WITH PREFIX ....
При создании DuckDB загружает публичные ключи репозитория и закрепляет их в определении, печатая SHA-256 отпечаток каждого ключа — его можно сверить с опубликованным отдельно. Установка идёт через INSTALL AND LOAD ... FROM .... Функциональность ещё в разработке, и её синтаксис может измениться в будущих релизах.
Agent Mode в CLI
Coding agents вроде Claude Code, Codex и Cursor всё чаще управляют DuckDB CLI от имени пользователей. CLI 2.0 определяет, что запущен агентом, и переключается в режим агента: печатает компактные Markdown-таблицы вместо таблиц с отступами и выдаёт ошибки в JSON. Долгие запросы перед выполнением сообщают ожидаемую стоимость, чтобы агент решил, ждать ли.
Проблема, которую это решает, — объём вывода, который модели приходится читать: в экспериментах на TPC-H режим агента сократил его на 59%. Включается вручную флагом -agent, агенту можно указать отключить его флагом -no-agent.
Лямбда-синтаксис: ломающее изменение
Старый синтаксис с одиночной стрелкой (x -> x + 1) отключён по умолчанию. Вместо него используется синтаксис в стиле Python, где перед параметром ставится ключевое слово lambdaключевое слово, вводящее анонимную функцию с параметром перед двоеточием:
SELECT list_transform([1, 2, 3], lambda x: x + 1);Временно вернуть старое поведение можно настройкой SET lambda_syntax = 'enable_single_arrow';. И флаг, и синтаксис с одиночной стрелкой будут полностью удалены в v2.1 — это переходный период, а не альтернатива.
ICU: библиотека убрана
Метки времени с учётом часового пояса, календари и collationsправила сравнения и сортировки строк всегда обеспечивались библиотекой ICU. В 2.0 она удалена полностью: расширение icu теперь само реализует часовые пояса, календари и collations. Данные о часовых поясах строятся напрямую из базы IANA и сжимаются примерно до 45 kB. Поведение остаётся прежним, а нативная реализация быстрее.
Ограничения и открытые вопросы
Помимо временного характера флага lambda_syntax, авторы объявили устаревшей сборку amalgamation — она «стала слишком большой, чтобы быть реалистично полезной в большинстве случаев». Платформа osx_amd64 постепенно выводится из поддержки: DuckDB 2.1 перестанет её поддерживать. Материализованные представления, к которым приближают триггеры, остаются пунктом будущих работ.