DuckDB v2.0 выходит осенью под именем «Cyanoptera» — в честь коричнево-красной утки с запада Америки. Это большой мажорный релиз: за полгода с v1.5 накопилось больше 10 000 коммитов. Внутри — новый SQL-парсер, новый формат хранения, переработанный C API и несколько осознанных ломающих изменений. Но главное — это релиз, который открывает эру DuckDB как сервера.
Расширение quack реализует нативный протокол DuckDB и выходит в стабильной версии. Любой процесс DuckDB может отдавать свои базы по сети, а другой DuckDB — подключаться к ним через новую команду CONNECT. Причём CONNECT работает и с внешними базами: новый оптимизатор remote pushdown умеет отправлять SQL прямо в PostgreSQL и MySQL, а не тянуть таблицы по проводу. В DuckDB с самого начала были MVCC и транзакции, но без клиент-сервера это было почти не нужно. Теперь механизм транзакций раскрывается в долгоживущих многопользовательских сервисах, а в v2.0 улучшили метрики, логи и наблюдаемость.
Тип VARIANT, появившийся в v1.5, в v2.0 доработали до конца: данные «шредятся» прямо при чтении из хранилища, pushdown работает в сканы, есть чтение и запись Parquet и семейство функций variant_*. Это удобно для логов, где структура JSON-подобных записей меняется со временем. Триггеры — ещё одна давно просившаяся фича. В v2.0 есть BEFORE и AFTER, FOR EACH ROW и FOR EACH STATEMENT, transition tables через REFERENCING OLD/NEW TABLE — классический пример с аудиторской таблицей работает из коробки.
Из SQL-новинок: NEAREST joins для поиска по векторным эмбеддингам, DML внутри CTE, вложенные схемы, переменные вида $x вместо громоздкого getvariable(), функции json_set, json_insert, json_replace, json_remove, рекурсивные CTE с USING KEY — плюс стандартные FETCH FIRST, OVERLAY(), UNNEST в GROUP BY.
Асинхронный I/O переработан: слой ввода-вывода масштабируется независимо от обработки запросов. Parquet, CSV и родной формат DuckDB читаются асинхронно, появилась асинхронная запись Parquet и режимы MMAP и DIRECT_IO. Особенно заметно на сетевых хранилищах вроде S3. Оптимизатор стал умнее: частичные агрегаты проталкиваются под join, агрегации при нехватке памяти сбрасываются на диск, переписан движок рекурсивных CTE. На бенчмарке с обходом графа на миллион рёбер один и тот же запрос выполняется за 4.9 секунды в v1.5.4 и за 0.12 секунды в v2.0 — примерно в 40 раз быстрее. Row-group pruning теперь работает для структур, списков, decimal, UUID, IN-фильтров и даже функций вроде contains(message, 'ERROR'). Планировщик учитывает партиционирование lakehouse-форматов: DuckLake, Iceberg и Hive-партиции на S3.
Новый формат хранения использует buffer-managed ART-индексы — они больше не висят в памяти целиком, поэтому большие таблицы с индексами открываются мгновенно. Сжатие DICT_FSST включено по умолчанию, метаданные колонок грузятся лениво, а проверка корректности при чтении стала строже. Вместо парсера от PostgreSQL — собственный PEG-based парсер, в который расширения могут встраивать свой синтаксис. Есть режим совместимости с Spark. От библиотеки ICU отказались: расширение icu теперь само реализует timezone, календари и collation, а данные из IANA сжаты до ~45 кБ. Время конвертации 25 миллионов таймстампов ускорилось в 2.2 раза, фильтрация с немецкой collation — в 2.6 раза.
Стабильный C API стал по-настоящему стабильным: он генерируется из YAML-спецификации, вводится единое версионирование символов, кастомные аллокаторы и статическая линковка расширений. Расширение, написанное один раз через duckdb_extension.h, работает во всех будущих версиях. В v2.0 также появятся собственные доверенные репозитории расширений: можно создать репозиторий с RSA-ключом, подписывать расширения и ставить их через INSTALL, причём ключи проверяются по SHA-256 отпечатку.
Это только превью: детали могут поменяться до релиза. Помимо нового формата хранения, будут и другие ломающие изменения — например, завершение перехода на лямбда-синтаксис. Preview-сборки уже доступны, а подробности обещают в анонсе релиза.