コンテンツにスキップ
FDL
Esc
↑↓移動↵開く⌘Jプレビュー
このページの内容

バージョン

バージョン ID、version.json の項目、固めたカタログ、dataset.json、ほかの DuckLake 形式のカタログ。

バージョンは versions/<version ID>/ の下のファイルです。一度書いたら変えてはいけません。例外は次のとおりです。

  • 保持期間を過ぎたとき、expired.json を足し、カタログを消す(データファイル)
  • 取り下げのとき、"reason": "withdrawn" の expired.json を足してカタログを消し、version.json 自体に取り下げるデータがあればそれも消す(データファイル)
  • catalogs/<format>.duckdb はあとから足してよい。既にあるファイルは変えない
  • version.json.sig は、バージョンを作ったあとに署名する書き手が足してよい

バージョン ID

  • 小文字・ハイフン区切りの UUIDv7。たとえば 019a3c4e-7b21-7f3a-9c55-2f0d8e1a4b6c。作った順に並び、書き手どうしが調整しなくても重ならない
  • 中身のハッシュではない。同じカタログでも別のバイト列で書かれることがあり、ハッシュは安定しない。中身は catalog.sha256 で確かめる
  • 人に見せる番号は seq。ブランチをまたぐと重なるので ID にはしない

version.json

{
  "fdl": "0.1",
  "id": "019a3c4e-7b21-7f3a-9c55-2f0d8e1a4b6c",
  "seq": 12,
  "parents": ["019a2f10-0c4d-7e88-a1b2-55c0d9e3f7aa"],
  "created_at": "2026-10-30T09:00:00Z",
  "author": { "name": "example" },
  "message": "Add the final 2025 figures",
  "catalog": {
    "path": "ducklake.duckdb",
    "storage": "duckdb-v1.5.0",
    "bytes": 4194304,
    "sha256": "9f2c…"
  },
  "dataset": {
    "path": "dataset.json",
    "bytes": 2048,
    "sha256": "4c1d…"
  },
  "ducklake": {
    "format": "1.0",
    "snapshot_id": 345,
    "snapshots": [301, 345]
  },
  "files": {
    "path": "files.json",
    "bytes": 1536,
    "sha256": "7a0e…"
  },
  "changes": [
    { "table": "main.population", "kind": "add_column", "column": "note", "breaking": false }
  ],
  "inputs": [
    {
      "source_id": "example:population-2025",
      "sha256": "1b7e…",
      "retrieved_at": "2026-10-29T03:12:00Z",
      "url": "https://example.org/population-2025.csv"
    }
  ],
  "annotations": {}
}
項目 必須 意味
fdl must 書いたときの仕様のバージョン
id must ディレクトリと同じ ID。違っていたら読み手は読んではいけない
seq must 人に見せる番号。最初は 1、そのあとは最初の親の seq に 1 を足す
parents must 親のバージョン ID。最初のバージョンでは空、そのあとは 1 つ
created_at must UTC、ISO 8601
author must 作った人。name は必須、email などは任意
message must 何を変えたか
catalog must カタログのファイル名、大きさ、sha256、保存形式(storage)
dataset must カタログの隣の dataset.json のファイル名、大きさ、sha256。必須になる前に書かれたバージョンには無い
ducklake must DuckLake の形式、固めたスナップショット、親から足したスナップショットの範囲(最初と最後)
files may カタログの隣の files.json を書いたときの、そのファイル名、大きさ、sha256(files.json)
changes may 親との列の違い。breaking は、親に向けて書いた SQL が失敗しうるか(列の削除・改名・作り直し)
inputs may 使った元のファイル。source_id の付け方は書き手が決める
annotations may 形式が解釈しない情報。読む側(ホストやツール)の名前をキーにする

読み手は、知らない項目を無視しなければなりません。

FDL 0.1 では、書き手が書く親は多くても 1 つです。parents をリストにしているのは、仕様の将来のバージョンで、項目を変えずにマージを表せるようにするためです。読み手は、どんな長さのリストも受け付け、履歴をたどるときは最初の要素を使わなければなりません。

スナップショットごとの説明と SQL は、DuckLake の commit_message と commit_extra_info としてカタログの中に残し、version.json には写しません。

形式番号

項目 指すもの 変わるとき
fdl この仕様 この仕様が変わったとき
ducklake.format DuckLake の管理用の表の形 DuckLake の仕様が変わったとき
catalog.storage カタログファイルの保存形式 DuckDB の保存形式が変わったとき

読み手は、version.json だけで、そのバージョンを開けるか判断できます。

files.json

files.json は、ducklake.snapshot_id の時点の表ごとのデータファイルの索引です。カタログを開けない読み手のためのものです。書き手はこれをカタログから作り、カタログと食い違ったときはカタログが正です。置くかどうかは任意で、読み手はこれが無くても動かなければなりません。Git の到達可能性ビットマップ(pack の隣の .bitmap)に当たります。あるコミットからたどれるオブジェクトの一覧をオブジェクトから作ったもので、読むのを速くし、無くても読めます。

{
  "tables": {
    "main.population": {
      "files": [
        {
          "path": "main/population/ducklake-019a3c4e-….parquet",
          "rows": 1741,
          "bytes": 52311,
          "footer_bytes": 2048,
          "deletes": { "path": "main/population/ducklake-019a3c4f-…-delete.parquet", "rows": 3, "bytes": 812 }
        }
      ]
    }
  }
}
  • tables には、そのスナップショットにあるすべての表を schema.table をキーにして入れます。行の無い表の files は空です
  • path はリポジトリの data_dir からの相対パスです。ファイルはカタログの順に並べます
  • rows と bytes は、カタログが記録している行数と大きさです。footer_bytes は Parquet のフッターの大きさで、記録があるときだけ書きます
  • deletes は、そのデータファイルから行を消したときの削除ファイルです
  • renamed_columns は、その表の列の名前を変えたことがあるときに true にします。名前を変える前に書いたデータファイルは、古い名前のままです

DuckLake を使わずに Parquet ファイルを読む読み手は、deletes を持つファイルがある表や、renamed_columns が true の表を読んではいけません。DuckLake と同じように削除と改名を当てはめる場合は読んでかまいません。当てはめずに読むと、消した行が返ったり、名前を変えた列が欠けたりします。

保持期間から外すとき、files.json はカタログと一緒に消します。

固めたカタログ

ducklake.duckdb は、DuckDB のファイル形式の DuckLake カタログです。

  • 管理用の行を ducklake.snapshot_id まで持ち、それより後は持たない
  • 行をカタログの中に持たない。データはすべてデータファイルにある
  • 参照するデータファイルはすべて data_dir の下にあり、カタログのデータパスからの相対で記録されている。書き手はこれを確かめなければならず、ホストも確かめてよい
  • data_path は、書き手が公開したデータフォルダの絶対 URL(ルートと data_dir)。読み手はこれを上書きする(読むを参照)
  • DuckLake がスナップショットにタグを付けられるようになるまで、書き手は作業カタログで、バージョンを固めたスナップショットを消すような ducklake_expire_snapshots を流してはいけない

dataset.json

バージョンの中身を言葉で説明するファイルです。データセット、その表、表の列を説明します。バージョンの一部としてバージョンと一緒に一度だけ書くので、読み手はカタログを開かなくても、何がどんな条件で入っているかが分かり、古いバージョンも公開したときの説明を持ち続けます。

書き手が説明を書き、書き手のツールがそれを固めたカタログと照らし合わせ、カタログから分かることを書き足します。

{
  "name": "population",
  "title": "Population by town",
  "description": "Census population of each town, by age and sex",
  "keywords": ["population", "census"],
  "language": "en",
  "homepage": "https://example.org/population",
  "issued": "2026-10-30",
  "update_frequency": "yearly",
  "licenses": [{ "id": "CC-BY-4.0", "spdx": "CC-BY-4.0", "url": "https://creativecommons.org/licenses/by/4.0/" }],
  "sources": [{ "title": "2025 census", "path": "https://example.org/census-2025" }],
  "contributors": [{ "title": "Example City", "roles": ["publisher"] }],
  "temporal_coverage": { "start": "2025", "end": "2025" },
  "spatial_coverage": ["JP"],
  "schemas": [{ "name": "main", "title": "Tables" }],
  "tables": [
    {
      "schema": "main",
      "name": "population",
      "title": "Population",
      "description": "One row per town, age group and sex",
      "primary_key": ["town_code", "age_group", "sex"],
      "depends_on": [],
      "sql": "SELECT …",
      "rows": 120000,
      "fields": [
        { "name": "town_code", "title": "Town code", "description": "…", "type": "varchar", "nullable": false }
      ]
    }
  ],
  "annotations": {}
}
  • 必須なのは name と title だけで、ほかは省いてよい
  • 書き手は説明を YAML(dataset.yml)で持ってもよく、テーブルごとに別のファイル(<name>.table.yml、tables の 1 項目)に分けてもよい。バージョンには常に説明全体が 1 つの JSON で入る
  • licenses の各要素は SPDX の識別子か、id に書き手が足したいもの(spdx・url・title)を添えたオブジェクト
  • tables には、カタログの最後のスナップショットにある表をすべて並べる。書き手が説明しなかった表は、書き手のツールがカタログから分かることで足す。カタログに無い表や列を説明していたら断る
  • カタログのビューは表のあとに "kind": "view" を付けて並べ、sql はカタログのものを入れる。カタログはビューの列を記録しないので、ビューの fields は書き手が書いたままで、type・nullable・rows を持たない。表には kind を付けない
  • カタログから取るもの: 表ごとの rows、列ごとの type(DuckLake の型名)と nullable。書き手が description を書かなかったところには、カタログのコメント(COMMENT ON)を使う
  • この仕様が決めていないキーは断る。形式が解釈しない情報は、最上位・表・列それぞれの annotations に、読む側の名前をキーにして置く。書き手は書かれたまま残す
  • annotations という名前になる前のバージョンは、同じものを extensions に持つ。読み手は extensions を annotations として読む
  • ライセンスや出典に書いたことは書き手の主張で、ホストは公開する前に確かめてよい

ほかの形式のカタログ

1 つのバージョンが DuckLake の形式ごとにカタログを持ってよく、読み手は DuckDB を替えずに、開けるファイルを選べます。

versions/<ID>/
  ducklake.duckdb            作ったときのカタログ(version.json が説明するもの)
  catalogs/1.0.duckdb        形式 1.0 で書いたもの
  catalogs/1.1.duckdb        形式 1.1 で書いたもの
  • catalogs/<format>.duckdb はすべて、同じデータファイルを参照し、同じ行を返さなければならない
  • 読み手は、開ける形式を新しい順に試し、どれも無ければ ducklake.duckdb を開く
  • 宣言した形式をあるバージョンで作れない書き手は、そのファイルを置かない。どのファイルも開けない読み手は、そのバージョンを読めない
  • 古い形式で表せないものを使ったバージョンには、その形式のカタログを置かない
  • fdl.json の until を過ぎた形式は、新しいバージョンでは書かない。過去のバージョンはそのまま残してよい
  • データファイルは、fdl.json で宣言した一番古い形式が読めるように書かなければならない
  • catalog.storage は、宣言した読み手が開ける一番古い DuckDB の保存形式にするのがよい

DuckDB 2.0 で古い形式を書く方法は DuckLake の形式にあります。

このページは役に立ちましたか?