HN 日本語サマリー

← 一覧へ戻る
プログラミング

DuckDB 2.0.0 LTS "Cinnamon Teal" リリース

DuckDB 2.0.0 LTS "Cinnamon Teal" (duckdb.org)

438 pointsby nnx59 コメント

要約

DuckDB 2.0.0 LTS "Cinnamon Teal" がリリースされ、Quackプロトコルの改良、非同期I/O、VARIANT型、エージェントモード、トリガー、拡張可能なSQLパーサーなど、過去最大の機能追加が行われました。このバージョンは2027年10月までサポートされる長期サポート(LTS)リリースです。また、C++17コンパイラへの移行やICUライブラリへの依存解消など、内部構造にも変更が加えられています。

全文翻訳

DuckDB 2.0.0 LTS "Cinnamon Teal" の発表

ブログ リリース

TL;DR: DuckDBチームは、本日 "Cinnamon Teal"(Spatula cyanoptera)というコードネームのDuckDBバージョン2.0.0 LTSをリリースできることを嬉しく思います。これはこれまでの最大の機能リリースであり、Quackプロトコルの洗練されたバージョン、非同期I/O、ファーストクラスのVARIANT型、エージェントモード、トリガー、拡張可能なSQLパーサーなどを搭載し、DuckDB 1.5以降約20,000件のコミットによって構築されました。

私たちは、Spatula cyanoptera(アメリカ大陸西部で見られる、鮮やかな赤褐色のカモ)にちなんで名付けられた "Cinnamon Teal" というコードネームのDuckDB 2.0.0をリリースできることを誇りに思います。

DuckDB 1.0が安定性と後方互換性にすべてを捧げたのに対し、DuckDB 2.0は、何よりも機能リリースです。このブログ投稿では、ハイライトを要約し、破壊的な変更点と移行方法について説明します。 "A Preview of DuckDB v2.0" でこれらの新機能の多くを紹介しており、それぞれについてより深く掘り下げています。完全なリリースノートについては、GitHubのリリースページをご覧ください。

ヒント 新しいバージョンをインストールするには、インストールページをご覧ください。CLIやPythonなどの一部のクライアントはすでに利用可能です。その他のクライアントライブラリ(Go、R、Javaなど)や一部の拡張機能は、追加の変更とレビューラウンドが必要なため、リリースに数日かかる場合があります。

このリリースにより、現在3つのDuckDBラインが利用可能です。DuckDB 2.0は、少なくとも2027年10月までサポートされる長期サポート(LTS)リリースです。以前のLTSリリースであるバージョン1.4は、2026年11月下旬までサポートされています。スムーズな移行を確保し、テストに十分な時間を確保するため、DuckDB 1.5のサポート期間を2027年3月まで延長し、実質的にLTSリリースにもしました。詳細については、リリース カレンダーをご覧ください。

すべて新機能のドキュメントをナビゲートするために、 "What's New in the Docs for 2.0" ページも公開しました。これは、以下の各機能をそのドキュメントにリンクしています。

DuckDBをサーバーとして

DuckDBは、最初からインプロセスデータベースでした。5月にリリースされたDuckDB 1.5.3では、従来のクライアント・サーバー設定でDuckDBを実行できるQuackプロトコルと、DuckDBのネイティブプロトコルを実装するquack拡張機能を導入しました。これにより、任意のDuckDBプロセスがデータベースを提供できるようになります。

QuackとCONNECT

DuckDB 2.0.0は、Quackプロトコルの洗練されたバージョンを、大幅なパフォーマンスと安定性の向上とともに提供します。また、リモートデータベースへのアタッチと切り替えを単一のコマンドで実行できる新しいCONNECTステートメントを導入し、エルゴノミクスを向上させています。

DuckDBサーバー
CALL quack_serve(
    token = 'my_token'
);

CREATE TABLE events
    (date DATE, name VARCHAR);

INSERT INTO events VALUES
    ('2027-04-07',
     'DuckCon #8');

quack:

DuckDBクライアント
ATTACH 'quack:localhost'
    AS qk (TOKEN 'my_token');

CONNECT qk;
SELECT * FROM events;

┌────────────┬────────────┐
│    date    │    name    │
│    date    │  varchar   │
├────────────┼────────────┤
│ 2027-04-07 │ DuckCon #8 │
└────────────┴────────────┘

簡単のため、この例ではサーバーとクライアントを同じマシンで実行しています。デフォルトでは、サーバーはlocalhostのみをリッスンします。他のマシンからの接続を受け入れるには、サーバーをlocalhostのままにして、TLS終端リバースプロキシの後ろに配置します。allow_other_hostnameオプションは、サーバーを直接公開するもので、テスト目的のみです。

CONNECTステートメントはQuackに限定されません。これは、PostgreSQLやMySQLを含む、それをサポートする任意のリモートデータベースにセッションをポイントします。これらのデータベースでは、新しいリモートプッシュダウンオプティマイザが、テーブルをネットワーク経由でプルするのではなく、クエリを直接リモートサーバーに送信します。

CLIでのQuack

CLIには、Quackクライアント・サーバーペアをセットアップするためのショートカットもあります。新しい-serveおよび-connect引数は、コマンドラインから直接Quackサーバーを起動するか、それに接続します。

CLIサーバー
duckdb -serve
CREATE TABLE t AS
      SELECT 42 AS x;

quack:

CLIクライアント
duckdb -connect
FROM t;

┌───────┐
│   x   │
│ int32 │
├───────┤
│    42 │
└───────┘

パフォーマンス

非同期I/O

DuckDB 2.0は、ParquetおよびCSVファイルの読み込みのために非同期I/Oを導入しました。I/Oレイヤーはクエリ処理レイヤーから独立してスケーリングできるようになり、バックグラウンドで読み込みを発行し、複数のリクエストを進行させます。これにより、リモート読み込みの並列性が大幅に向上し、s3://のようなオブジェクトストレージでのクエリが劇的に高速化されます。

設計の詳細については、専用のブログ投稿で説明しました。その投稿では、非同期I/Oありとなしでのクエリ実行中のネットワークスループットを示しました。

[画像: Network throughput] [画像: Network throughput] ネットワークスループット。高い利用率と短い実行時間が望ましい。

VARIANT型がファーストクラスの市民に

VARIANT型は、JSONのような半構造化されたスキーマレスデータを、カラムナ実行のパフォーマンスを維持しながら格納します。そのため、JSONの強化版と考えることができます。VARIANTカラムは、各行に異なる形状のデータを格納できますが、JSONとは異なりテキスト形式ではありません。

DuckDB 1.5ではすでにVARIANTが導入されていましたが、DuckDB 2.0で完全に統合されました。DuckDB 2.0は、データ内の共通構造を検出し、それを「シュレッド」するため、圧縮率が高く、高速に実行できます。パイプラインは、ストレージからのシュレッドされた実行から、ParquetのシュレッドされたVARIANTの読み書きまで、エンドツーエンドで機能し、variant_*関数のファミリによってサポートされています。例:

CREATE TABLE events (payload VARIANT);
INSERT INTO events
VALUES
    ('{"user": {"id": 42, "tags": ["a", "b"]}}'::JSON::VARIANT),
    ('{"event": "click", "x": 10, "y": 20}'::JSON::VARIANT);

SELECT variant_type(payload), variant_keys(payload)
FROM events;
┌───────────────────────┬───────────────────────┐
│ variant_type(payload) │ variant_keys(payload) │
│        varchar        │       varchar[]       │
├───────────────────────┼───────────────────────┤
│ OBJECT                │ [user]                │
│ OBJECT                │ [event, x, y]         │
└───────────────────────┴───────────────────────┘
CLIでのエージェントモード

Claude Code、Codex、Cursorなどのコーディングエージェントは、ユーザーに代わってDuckDB CLIをますます駆動しています。2.0 CLIは、エージェントによって実行されているかどうかを検出し、エージェントモードに切り替わります。これは、パディングされたボックスではなく、コンパクトなMarkdownテーブルを出力し、結果が切り捨てられたことを明確に伝え、暴走するクエリを早期に停止し、エラーをJSONとして報告します。

長時間実行されるSQLクエリは、実行前に予想されるコストを報告するため、エージェントは待機するかどうかを決定できます。TPC-Hでの実験では、エージェントモードはモデルが読み取る必要のあるDuckDBの出力量を59%削減しました。手動テストのために-agentフラグを使用してオンにすることも、エージェントに-no-agentフラグを使用してオフにするように指示することもできます。

拡張性

全く新しいSQLパーサー

DuckDBは常にPostgreSQLから派生したパーサーを使用してきました。2.0では、DuckDB 1.5でオプトイン機能としてデビューした、独自のモダンで拡張可能なPEGベースのパーサーがデフォルトになりました。拡張機能は、文法自体にフックできるようになりました(これはランタイム拡張可能パーサーに関する研究で最初に検討されたアイデアです)。そのため、まったく新しいSQL構文を公開する拡張機能が期待されます。エラーメッセージには、正確なソース位置が含まれるようになりました。詳細については、PEGパーサーのドキュメントをご覧ください。新しいパーサーは古いパーサーと互換性があるように設計されているため、切り替えに気づかないはずです。もし気づいた場合は、問題を報告してください。それまでの間、legacy_parser拡張機能を使用してSET enable_legacy_parser = true;を実行することで、古いパーサーに戻すことができます。

古いパーサーは、2018年の最初のコミット以来DuckDBを支えてきました。その文法はYACC/Bisonで生成され、LALR(1)パーサーを生成するため、DuckSQLが成長するにつれて、小さな追加でも既存のルールと衝突し、shift/reduceまたはreduce/reduceの競合を引き起こす可能性がありました。PEG文法は、記述された順序で代替案を試すことでこれを回避するため、構文の追加はもはや無関係なルールを壊すリスクを伴いません。Python自体も、同じ柔軟性のためにバージョン3.9でPEGベースのパーサーに移行しました。

拡張機能を一度だけ書き、自分でホストする

DuckDB 2.0は、改良されたC API v2を提供します。これは前任者よりも大幅に強力で、ファイルシステムやテーブル関数などのコンポーネントを拡張機能に公開します。安定版とマークする前に、フィールドでテストしたいと考えているため、ぜひ試してフィードバックをお寄せください!

また、独自の信頼できる拡張機能リポジトリを登録してホストすることもできます。これにより、組織は独自の拡張機能をホストおよび署名し、組み込みのものと同様にインストールおよびロードできるようになります。

SET allow_extension_repositories = 'allowed';

CREATE EXTENSION REPOSITORY my_repo
    WITH PREFIX 'https://extensions.example.org';

INSTALL AND LOAD my_ext FROM my_repo;

リポジトリは、名前、URLプレフィックス、およびそこから提供される拡張機能に署名するために信頼される1つ以上のRSA公開鍵です。プレフィックスは、DuckDBが読み取れるもの(ローカルパス、https、s3など)を指すことができます。CREATE時に、DuckDBはリポジトリの公開鍵を取得し、リポジトリ定義にピン留めします。各鍵のSHA-256フィンガープリントを出力するため、帯域外で公開されたものと比較できます。

この機能はまだ開発途上であり、将来のリリースで構文が変更される可能性があります。詳細については、拡張機能リポジトリのページをご覧ください。

コア拡張機能の更新

DuckDBのコア拡張機能も、いくつかの新機能、修正、およびパフォーマンス最適化を提供しています。これらはすべてをここにリストするには多すぎるため、いくつかのみを強調し、今後数週間の専用ブログ投稿でカバーします。

  • DuckLake 1.1をリリースしました。これには新しい仕様と参照実装が含まれています。
  • Iceberg拡張機能は、機能的に完全な実装になることを目指しています。この目的のために、RESTカタログATTACHオプションを追加しました。これらは、テーブルの場所を名前空間から派生させるか、カタログから返されたmetadata-locationファイルからテーブルメタデータをロードします。さらにオプションでURLプレフィックスと名前空間セパレーターを制御します。
  • Delta拡張機能は、CREATE TABLE(パーティショニングとDeltaテーブルプロパティを含む)でDeltaテーブルを作成でき、タイムトラベルをサポートし、VARIANTサポートを改善します。

SQL機能

トリガー

DuckDB 2.0はトリガーを提供します。CREATE TRIGGERステートメントは、BEFOREおよびAFTERトリガー、FOR EACH ROWおよびFOR EACH STATEMENT、REFERENCING OLD/NEW TABLEを介したトランジションテーブル、イベントごとの複数のトリガー、トリガーされたテーブルでのRETURNING、およびDROP TRIGGERを備えています。古典的なユースケースは監査テーブルであり、トリガーはテーブル変更に応答して何が変更されたかを記録します。一部の組み合わせ(例: BEFORE ... FOR EACH ROWおよびUPDATE ... FOR EACH ROWトリガー)はまだサポートされていません。詳細は制限事項を参照してください。

CREATE TRIGGER trg_audit
    AFTER UPDATE ON target
    REFERENCING
        OLD TABLE AS o
        NEW TABLE AS n
    FOR EACH STATEMENT
        INSERT INTO audit
        SELECT n.id, o.val, n.val
        FROM o JOIN n ON o.id = n.id;

トリガーは、マテリアライズドビューを増分的に維持することもできます!例えば、age_statisticsテーブルの国籍ごとの平均年齢を追跡するには、挿入と削除の操作に対して2つのトリガーを使用し、平均値とともに合計値と人数を格納できます。

詳細については、こちらをクリックしてください。

CREATE TABLE people (name VARCHAR, nationality VARCHAR, age INTEGER);
CREATE TABLE age_statistics (
    nationality VARCHAR PRIMARY KEY,
    sum_age INTEGER,
    num_people INTEGER,
    average_age DOUBLE
);

挿入時に、新しい行を国籍ごとに集計し、age_statisticsにマージし、新しい国籍ごとに新しい行を追加します。

CREATE TRIGGER people_insert
    AFTER INSERT ON people
    REFERENCING NEW TABLE AS n
    FOR EACH STATEMENT
        MERGE INTO age_statistics s
        USING (
            SELECT nationality, sum(age) AS sum_age, count(*) AS num_people
            FROM n
            GROUP BY nationality
        ) d
        ON s.nationality = d.nationality
        WHEN MATCHED THEN UPDATE SET
            sum_age = s.sum_age + d.sum_age,
            num_people = s.num_people + d.num_people,
            average_age = (s.sum_age + d.sum_age) / (s.num_people + d.num_people)
        WHEN NOT MATCHED THEN INSERT
            VALUES (d.nationality, d.sum_age, d.num_people, d.sum_age / d.num_people);

削除時に、削除された行を減算し、最後の人がいなくなったら国籍を削除します。

CREATE TRIGGER people_delete
    AFTER DELETE ON people
    REFERENCING OLD TABLE AS o
    FOR EACH STATEMENT
        MERGE INTO age_statistics s
        USING (
            SELECT nationality, sum(age) AS sum_age, count(*) AS num_people
            FROM o
            GROUP BY nationality
        ) d
        ON s.nationality = d.nationality
        WHEN MATCHED AND s.num_people = d.num_people THEN DELETE
        WHEN MATCHED THEN UPDATE SET
            sum_age = s.sum_age - d.sum_age,
            num_people = s.num_people - d.num_people,
            average_age = (s.sum_age - d.sum_age) / (s.num_people - d.num_people);

人々が出入りするのを見てみましょう。

INSERT INTO people VALUES
    ('Alice', 'US', 34), ('Bram', 'NL', 27), ('Csilla', 'HU', 45),
    ('Dave', 'US', 58), ('Eva', 'NL', 19), ('Femke', 'NL', 41);
FROM age_statistics ORDER BY nationality;
┌─────────────┬─────────┬────────────┬─────────────┐
│ nationality │ sum_age │ num_people │ average_age │
│   varchar   │  int32  │   int32    │   double    │
├─────────────┼─────────┼────────────┼─────────────┤
│ HU          │      45 │          1 │        45.0 │
│ NL          │      87 │          3 │        29.0 │
│ US          │      92 │          2 │        46.0 │
└─────────────┴─────────┴────────────┴─────────────┘
DELETE FROM people WHERE name = 'Alice';
DELETE FROM people WHERE nationality = 'HU';
FROM age_statistics ORDER BY nationality;
┌─────────────┬─────────┬────────────┬─────────────┐
│ nationality │ sum_age │ num_people │ average_age │
│   varchar   │  int32  │   int32    │   double    │
├─────────────┼─────────┼────────────┼─────────────┤
│ NL          │      87 │          3 │        29.0 │
│ US          │      58 │          1 │        58.0 │
└─────────────┴─────────┴────────────┴─────────────┘

トリガーは、「将来の作業」セクションのロードマップにある長年の項目であるマテリアライズドビューに、私たちをさらに一歩近づけます。

SQL方言の追加

いつものように、DuckDBのフレンドリーなSQL方言は成長し続けています。このリリースサイクルのお気に入りのいくつかです。

他にもあります。DuckDB 2.0.0は、SQL標準のFETCH FIRSTをサポートし、UNNESTをGROUP BY句で使用することを許可します。また、複数の行が一致する場合のMERGEおよびUPDATE ... FROMのセマンティクスも明確に定義します。

2.0への移行

DuckDB 2.0には、少数の破壊的な変更が含まれています。ほとんどのユーザーにとってアップグレードは簡単ですが、多くのDuckDBプロセスを実行する複雑なデプロイメントでは、より注意が必要です。ストレージ形式が問題の最も可能性の高い原因です。DuckDB 2.0より前のバージョンは、DuckDB 2.0によって作成されたデータベースファイルを読み取ることができません。DuckDB 2.0は、既存のデータベースファイルを読み書きできます。これらのファイルは古いバージョンでも読み取り可能です。本番環境に切り替える前に、シャドウシステムで2.0.0をテストすることをお勧めします。また、ワークロードがミッションクリティカルな場合は、11月にリリースされる最初のパッチリリースを待つことを検討してください。

詳細については、移行ガイドをご覧ください。

内部と非推奨

コンパイラ要件

DuckDBのC++11での長い期間を経て、DuckDB 2.0はC++17コンパイラを要求するようになりました。

リリースビルドのデフォルトコンパイラとしてClangに切り替えました。LinuxおよびmacOSでは、DuckDBとそのコア拡張機能はClangでビルドされます。Windowsでは、CLIはClangでビルドされますが、ほとんどのコア拡張機能は引き続きMSVCでビルドされます。将来的には、WindowsビルドをさらにClangに移行する可能性がありますが、これにはさらなる変更が必要です。

ICUなしのタイムゾーン、カレンダー、照合順序

タイムゾーン対応タイムスタンプ、カレンダー、照合順序は、常にICUライブラリによってサポートされてきました。2.0では、ICUライブラリは完全に削除されました。icu拡張機能は、タイムゾーン、カレンダー、照合順序を独自に実装するようになりました。タイムゾーンデータはIANAデータベースから直接構築され、約45KBに圧縮されています。

すべて以前と同じように機能し続け、ネイティブ実装はより高速です。問題が見つかった場合は、お知らせください!

非推奨
ラムダ構文

古いシングルアローラムダ構文(x -> x + 1)はデフォルトで無効になりました。代わりにPythonスタイルの構文を使用してください。

SELECT list_transform([1, 2, 3], lambda x: x + 1);

SET lambda_syntax = 'enable_single_arrow';を使用して、古い構文を一時的に再度有効にできます。フラグとシングルアロー構文の両方は、v2.1で完全に削除されます。

アマルガメーションビルド

DuckDBのソースコード全体を単一のヘッダーファイルと単一のソースファイルにバンドルするアマルガメーションビルドは、非推奨になりました。アマルガメーションは、ほとんどの場合、現実的に有用であるには大きくなりすぎました。

macOS x86_64の段階的廃止

プラットフォームのリストからosx_amd64を段階的に廃止しています。現時点では、DuckDBおよびコア拡張機能は引き続きビルドおよびサポートされていますが、コミュニティ拡張機能はこのプラットフォーム向けにはビルドされなくなりました。DuckDB 2.1ではosx_amd64のサポートを終了します。

概要

これらはいくつかのハイライトでしたが、このリリースにはさらに多くの機能と改善が含まれています。DuckDB 1.5以降、多くの貢献者によって約20,000件のコミットが行われ、小規模な機能からパフォーマンスやユーザビリティの向上まで、多くのものが提供されました。完全なリリースノートはGitHubで確認できます。

詳細な問題レポートとフィードバックを提供してくれたコミュニティ、そして特に貢献者の皆様に感謝いたします。

最近の投稿

[画像: Benchmarking DuckDB v2.0-dev on NVIDIA Vera]

NVIDIA VeraでのDuckDB v2.0-devのベンチマーク

[画像]

Gábor Szárnyas

[画像: From JSON Strings to Typed Columns]

JSON文字列から型付きカラムへ

The DuckDB team

[画像: What Is Inside an Iceberg Table?]

Icebergテーブルの中身は?

The DuckDB team