> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-parallel-read-in-order-multi-part.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Flight SQL クライアントを ClickHouse に接続できるようにする、ClickHouse の Apache Arrow Flight インターフェイスに関するドキュメント

# Arrow Flight インターフェイス

<h2 id="overview">
  概要
</h2>

ClickHouse は [Apache Arrow Flight](https://arrow.apache.org/docs/format/Flight.html) プロトコルをサポートしています。これは、[gRPC](https://grpc.io/) 上で [Arrow IPC](https://arrow.apache.org/docs/format/Columnar.html#serialization-and-interprocess-communication-ipc) フォーマットを使用し、効率的な列指向データ転送を実現する高性能な RPC フレームワークです。

この実装には [Arrow Flight SQL](https://arrow.apache.org/docs/format/FlightSql.html) のサポートも含まれており、Flight SQL プロトコルに対応した BI ツールやアプリケーションから ClickHouse に直接クエリできます。

主な機能:

* SQL クエリを実行し、結果を Apache Arrow フォーマットで取得できます。
* Arrow フォーマットを使用してテーブルにデータを挿入できます。
* Flight SQL コマンドを使ってメタデータ (カタログ、スキーマ、テーブル、主キー) をクエリできます。
* Flight SQL を介してサーバー側のプリペアドステートメントを作成、バインド、実行、クローズできます。
* Flight SQL アクションを介してセッションと設定を管理できます。
* TLS 暗号化とユーザー名/パスワード認証。
* `PollFlightInfo` による段階的な結果取得。
* `CancelFlightInfo` によるクエリのキャンセル。

<h2 id="enabling-server">
  Arrow Flight Server を有効にする
</h2>

Arrow Flight Server を有効にするには、ClickHouse サーバー設定に `arrowflight_port` 設定を追加します。

```xml theme={null}
<clickhouse>
    <arrowflight_port>9090</arrowflight_port>
</clickhouse>
```

起動時に、インターフェイスが有効になっていることを示すログメッセージが表示されます。

```text theme={null}
{} <Information> Application: Arrow Flight compatibility protocol: 0.0.0.0:9090
```

<h2 id="tls-configuration">
  TLS 設定
</h2>

Arrow Flight インターフェイスで TLS を有効にするには、以下の設定を行います。

```xml theme={null}
<clickhouse>
    <arrowflight_port>9090</arrowflight_port>
    <arrowflight>
        <enable_ssl>true</enable_ssl>
        <ssl_cert_file>/path/to/server-cert.pem</ssl_cert_file>
        <ssl_key_file>/path/to/server-key.pem</ssl_key_file>
    </arrowflight>
</clickhouse>
```

TLS が有効な場合、クライアントは `grpc://` ではなく `grpc+tls://` 認証スキームを使用して接続する必要があります。

<h2 id="authentication">
  認証
</h2>

Arrow Flight インターフェイスでは、2 つの認証方法がサポートされています。

<h3 id="basic-auth">
  基本認証
</h3>

クライアントは、標準の HTTP `Authorization: Basic` ヘッダーを介して、ユーザー名とパスワードで認証します。認証に成功すると、サーバーはレスポンスヘッダーで Bearer トークンを返します。

<h3 id="bearer-auth">
  ベアラートークン認証
</h3>

後続のリクエストでは、基本認証で返されたベアラートークンを `Authorization: Bearer <token>` ヘッダーで使用できます。トークンは使用するたびに自動的に更新され、有効期限は `default_session_timeout` サーバー設定 (既定値: 60 秒) に従います。

<h3 id="auth-python-example">
  Python の例
</h3>

```python theme={null}
import pyarrow.flight as flight

client = flight.FlightClient("grpc://localhost:9090")

# 基本認証は後続の呼び出しに使用するベアラートークンを返す
token_pair = client.authenticate_basic_token("default", "")
options = flight.FlightCallOptions(headers=[token_pair])
```

TLS の場合:

```python theme={null}
import pyarrow.flight as flight

with open("ca-cert.pem", "rb") as f:
    tls_root_certs = f.read()

client = flight.FlightClient(
    "grpc+tls://localhost:9090",
    tls_root_certs=tls_root_certs,
)

token_pair = client.authenticate_basic_token("default", "password")
options = flight.FlightCallOptions(headers=[token_pair])
```

<h2 id="session-management">
  セッション管理
</h2>

Arrow Flight インターフェイスは、カスタム gRPC メタデータヘッダーを介して ClickHouse のセッションをサポートします。

| ヘッダー | 説明 |
| - | - |
| `x-clickhouse-session-id` | セッション識別子。指定すると、複数のリクエストで同じセッション状態 (一時テーブル、設定) が共有されます。 |
| `x-clickhouse-session-timeout` | 秒単位のセッションタイムアウトです。`max_session_timeout` を超えてはなりません。 |
| `x-clickhouse-session-check` | セッションを作成せずに存在を確認するには、`1` に設定します。 |
| `x-clickhouse-session-close` | リクエスト完了後にセッションを閉じるには、`1` に設定します。server config で `enable_arrow_close_session` を `true` に設定しておく必要があります。 |

<Note>
  Arrow Flight は HTTP/2 上で gRPC を使用するため、メタデータヘッダー名では大文字と小文字が区別され、ここに示すとおり、正確に小文字で指定する必要があります (例: `x-clickhouse-session-id`。`X-ClickHouse-Session-Id` ではありません) 。これは、HTTP/2 のフィールド名には小文字のみを含めることを義務付ける [RFC 9113, Section 8.2](https://www.rfc-editor.org/rfc/rfc9113#section-8.2) で規定されています。これは、ヘッダー名で大文字と小文字が区別されない HTTP/1.1 とは異なります。
</Note>

セッションを使用すると、`SetSessionOptions` アクションで永続的な ClickHouse 設定を指定できます ([DoAction](#doaction) を参照) 。

<h2 id="configuration-reference">
  サーバー設定リファレンス
</h2>

| 設定 | デフォルト | 説明 |
| - | - | - |
| `arrowflight_port` | — | Arrow Flight サーバー用のポートです。この設定が指定されている場合にのみサーバーが起動します。 |
| `arrowflight.enable_ssl` | `false` | TLS 暗号化を有効にします。 |
| `arrowflight.ssl_cert_file` | — | TLS 証明書ファイルへのパスです。TLS を有効にする場合は必須です。 |
| `arrowflight.ssl_key_file` | — | TLS 秘密鍵ファイルへのパスです。TLS を有効にする場合は必須です。 |
| `arrowflight.tickets_lifetime_seconds` | `600` | Flight ticket の有効期限が切れてクリーンアップされるまでの時間 (秒) です。ticket の自動期限切れを無効にするには `0` に設定します。 |
| `arrowflight.cancel_ticket_after_do_get` | `false` | `true` の場合、ticket は `DoGet` で消費された直後にキャンセルされ、メモリが解放されます。 |
| `arrowflight.poll_descriptors_lifetime_seconds` | `600` | poll ディスクリプタの有効期限が切れるまでの時間 (秒) です。自動期限切れを無効にするには `0` に設定します。 |
| `arrowflight.cancel_flight_descriptor_after_poll_flight_info` | `false` | `true` の場合、poll ディスクリプタは `PollFlightInfo` で消費された後にキャンセルされます。 |
| `arrowflight.max_prepared_statements_per_user` | `100` | ユーザーごとに開いておけるプリペアドステートメントの最大数です。制限を無効にするには `0` に設定します。 |
| `arrowflight.prepared_statements_lifetime_seconds` | `-1` | プリペアドステートメントの有効期間モードです。`> 0`: この値を有効期間として使用し、セッションに紐づくステートメントとセッションレスのステートメントの両方で、リクエストごとに有効期限を更新します。`0`: 自動期限切れを無効にします。`-1`: セッションに紐づくステートメントでは、セッションタイムアウトを有効期間として使用し、リクエストごとに更新します。セッションレスのステートメントは自動的には期限切れになりません。 |
| `enable_arrow_close_session` | `true` | クライアントが `x-clickhouse-session-close` header を介してセッションを閉じられるようにします。 |
| `default_session_timeout` | `60` | デフォルトのセッションタイムアウト (秒) です。Bearer トークンの有効期限も制御します。 |
| `max_session_timeout` | `3600` | 許可されるセッションタイムアウトの最大値 (秒) です。 |

<h2 id="rpc-methods">
  サポート対象のRPCメソッド
</h2>

<h3 id="getflightinfo">
  GetFlightInfo
</h3>

クエリを実行し、結果のスキーマ、データ取得用チケット付きのエンドポイント、行数、バイト数を含む `FlightInfo` を返します。

受け取る `FlightDescriptor` には、次のいずれかを指定できます。

* **PATH descriptor**: テーブル名として解釈される単一要素の path です。`SELECT * FROM <table>` を生成します。
* **CMD descriptor**: 生の SQL クエリ文字列、またはシリアライズされた Flight SQL protobuf コマンドです ([Flight SQL Commands](#flight-sql-commands) を参照) 。

クエリは最後まで実行され、結果はサーバー側のチケットに保存されます。データの各 block ごとに個別のエンドポイント/チケットが生成されるため、クライアントはデータを並列に取得できます。

```python theme={null}
# テーブル名でクエリ
descriptor = flight.FlightDescriptor.for_path("my_table")
info = client.get_flight_info(descriptor, options)

# SQLでクエリ
descriptor = flight.FlightDescriptor.for_command(
    "SELECT * FROM my_table WHERE id > 100"
)
info = client.get_flight_info(descriptor, options)

# 結果を取得
for endpoint in info.endpoints:
    reader = client.do_get(endpoint.ticket, options)
    table = reader.read_all()
    print(table.to_pandas())
```

<h3 id="pollflightinfo">
  PollFlightInfo
</h3>

長時間実行されるクエリの結果を、段階的に取得できるようにします。`GetFlightInfo` のようにクエリ全体の完了を待つのではなく、`PollFlightInfo` は結果をブロック単位で返します。

最初の呼び出しでクエリの実行が開始され、レスポンスには次の内容が含まれます。

* その時点で利用可能なデータブロックに対応するエンドポイントを含む `FlightInfo`
* 次回のポーリングに使用する `FlightDescriptor` (さらに結果が返される見込みがある場合)

返されたディスクリプタを使った後続の呼び出しでは、追加のブロックを取得できます。これ以上利用可能なデータがない場合、レスポンスには次のディスクリプタは含まれません。

<Note>
  現在の実装では、データブロックが利用可能になるまで待機し、データがない場合に即座に返すことはありません。
</Note>

<h3 id="getschema">
  GetSchema
</h3>

クエリ全体を実行せずに、クエリ結果の Arrow スキーマを返します。`GetFlightInfo` と同じ種類のディスクリプタを受け付けます。

```python theme={null}
descriptor = flight.FlightDescriptor.for_command(
    "SELECT 1 AS x, 'hello' AS y"
)
schema_result = client.get_schema(descriptor, options)
schema = schema_result.schema
print(schema)  # x: int32, y: string
```

<h3 id="doget">
  DoGet
</h3>

指定された ticket に対応するデータを取得します。次のいずれかを受け付けます。

* `GetFlightInfo` または `PollFlightInfo` が返す ticket。
* ticket の値として指定する、生の SQL query 文字列。

```python theme={null}
# GetFlightInfoから取得したticketを使用する場合
reader = client.do_get(endpoint.ticket, options)
table = reader.read_all()

# ticketとして生のSQLクエリを使用する場合
ticket = flight.Ticket("SELECT number FROM system.numbers LIMIT 10")
reader = client.do_get(ticket, options)
table = reader.read_all()
```

<h3 id="doput">
  DoPut
</h3>

ClickHouse にデータを送信します。`FlightDescriptor` と Arrow レコードバッチのストリームを受け取ります。

**テーブル名による insert** (PATH ディスクリプタ) :

```python theme={null}
schema = pa.schema([("id", pa.int64()), ("name", pa.string())])
batch = pa.record_batch(
    [pa.array([1, 2, 3]), pa.array(["Alice", "Bob", "Charlie"])],
    schema=schema,
)

descriptor = flight.FlightDescriptor.for_path("my_table")
writer, _ = client.do_put(descriptor, schema, options)
writer.write_batch(batch)
writer.close()
```

**SQLによる挿入** (CMDディスクリプタ) :

```python theme={null}
descriptor = flight.FlightDescriptor.for_command(
    "INSERT INTO my_table FORMAT Arrow"
)
writer, _ = client.do_put(descriptor, schema, options)
writer.write_batch(batch)
writer.close()
```

**Flight SQL `CommandStatementUpdate` による DDL/DML の実行:**

Flight SQL クライアントは、DDL/DML ステートメント (CREATE、INSERT、ALTER など) の実行に `CommandStatementUpdate` を使用します。レスポンスには、影響を受けた行数が含まれます。

**Flight SQL `CommandStatementIngest` による一括取り込み:**

サポートされているのは、既存のテーブルへの追記のみです (`TABLE_NOT_EXIST_OPTION_FAIL` + `TABLE_EXISTS_OPTION_APPEND`) 。このコマンドでは、カタログと一時テーブルはサポートされていません。

`transaction_id` は `CommandStatementUpdate` と `CommandStatementIngest` ではサポートされていません。指定した場合、ClickHouse は `NotImplemented` エラーを返します。

<Note>
  データ転送に使用できるのは `Arrow` フォーマットのみです。SQL で他のフォーマット (例: `FORMAT JSON`) を指定すると、エラーになります。
</Note>

<h3 id="doaction">
  DoAction
</h3>

名前付きアクションを実行します。サポートされているアクションは次のとおりです。

<h4 id="cancelflightinfo">
  CancelFlightInfo
</h4>

`FlightInfo` に関連付けられた実行中のクエリをキャンセルします。クエリ ID は `FlightInfo` の `app_metadata` フィールドから抽出されます。また、そのクエリに関連付けられたすべての poll ディスクリプタもキャンセルします。

```python theme={null}
# PollFlightInfo経由で長時間実行クエリを開始し、キャンセルする
cancel_request = flight.CancelFlightInfoRequest(info)
result = client.cancel_flight_info(cancel_request, options)
# 成功した場合、result.status は CancelStatus.CANCELLED になる
```

<h4 id="setsessionoptions">
  SetSessionOptions
</h4>

現在のセッションに対する ClickHouse のサーバー設定を行います。`x-clickhouse-session-id` ヘッダーでセッション ID が設定されている必要があります。

サポートされる値の型: string、boolean、integer、double、および string list。

設定名が不明な場合は、error `INVALID_NAME` が返されます。値をパースできない場合は、error `INVALID_VALUE` が返されます。

<h4 id="getsessionoptions">
  GetSessionOptions
</h4>

現在のセッションの ClickHouse 設定とその値をすべて返します。設定名から文字列値へのマップを返します (内部的には `system.settings` にクエリします) 。

<h4 id="createpreparedstatement">
  CreatePreparedStatement
</h4>

サーバー側のプリペアドステートメントを作成し、ステートメントハンドルを返します。リクエストには、`?` プレースホルダーを含む SQL クエリテキストが含まれます。

このアクションでは `transaction_id` はサポートされていません。指定した場合、ClickHouse は `NotImplemented` エラーを返します。

クエリステートメントの場合、レスポンスには次が含まれることがあります。

* `dataset_schema`: 結果セットのスキーマ。
* `parameter_schema`: ステートメントパラメータのスキーマ。

有効なクエリでスキーマ推論に失敗した場合でも (たとえば、そのクエリではプレースホルダーを `NULL` に置き換えることが有効でない場合) 、ClickHouse はプリペアドステートメントを作成し、`dataset_schema` を含めずにハンドルを返します。

`dataset_schema` は、Flight SQL 仕様が意図しているとおり、あくまで最善の推測です。仕様では、結果スキーマはパラメータに依存する可能性があり、サーバーは最善の推測を返すべきであり、クライアントはそのスキーマが正確であると想定してはならないとされています。これに依存しないでください。データを表すスキーマを得るには、ステートメントを実行してください。ClickHouse では、次の 2 つの理由により、実際に返されるものと異なる場合があります。

* 推論では各 `?` を `NULL` に置き換えるため、結果カラムを決定するプレースホルダーは、後でバインドする値ではなくその `NULL` から型付けされます。`SELECT ? AS x` では `Nothing` 型のカラムが推論されますが、`5` をバインドすると `UInt8` が返されます。`SELECT id, name FROM t WHERE id = ?` のように、predicate の中でのみ使用されるプレースホルダーでは、結果の型はテーブルから決まるため、この問題は発生しません。
* Arrow に同等の型が存在しないカラムは、[`output_format_arrow_unsupported_types`](/ja/reference/settings/formats/output-format) から Arrow の型が決まりますが、これは呼び出しごとに、その呼び出しを行うセッションから解決されます。ハンドルは単一のセッションではなくユーザーに属するため、後の呼び出しでは異なる解決結果となり、`utf8` と通知されていたところで `binary` が返される、またはその逆が起こることがあります。プリペアドクエリ自体の中でモードを設定すれば、両方について固定できます。

プリペアドステートメントは、単一のセッションではなく、認証されたユーザーに属します。同じユーザーとして複数のセッションを開いている場合、それらのどのセッションからでも同じステートメントハンドルを実行、再バインド、クローズできます。

他のユーザーは、自分で作成していないステートメントハンドルを実行、バインド、またはクローズできません。

`arrowflight.prepared_statements_lifetime_seconds` は有効期限の動作を制御します。

* `> 0`: 設定された値をステートメントの有効期間として使用します。セッションに紐づくステートメントとセッションレスステートメントの両方で、リクエストのたびに有効期限が更新されます。
* `0`: プリペアドステートメントは自動的に期限切れになりません。
* `-1` (デフォルト): ステートメントがセッション内で作成された場合、その有効期間はそのセッションのタイムアウトに従い、そのセッション内のリクエストごとに更新されます。セッションなしでステートメントが作成された場合、自動的に期限切れにはなりません。

期限切れになったステートメントは削除され、`arrowflight.max_prepared_statements_per_user` のカウント対象にも含まれなくなります。

<h4 id="closepreparedstatement">
  ClosePreparedStatement
</h4>

リクエストに空でないステートメントハンドルが含まれている場合、プリペアドステートメントを閉じ、関連するサーバー側リソースを解放します。

ClickHouse は、ハンドルが空の場合、`ClosePreparedStatement` による一括クローズもサポートしています。

* `x-clickhouse-session-id` が存在する場合、そのセッション内で認証済みユーザーのすべてのプリペアドステートメントを閉じます。
* セッション ID が存在しない場合、認証済みユーザーのセッションに属さないプリペアドステートメントのみを閉じます。

プリペアドステートメントがセッション内で (`x-clickhouse-session-id` を介して) 作成された場合、そのセッションが閉じられると、そのステートメントも自動的に閉じられます。

<h2 id="flight-sql-commands">
  Flight SQL コマンド
</h2>

`CMD` ディスクリプタにシリアライズされた [Flight SQL protobuf](https://arrow.apache.org/docs/format/FlightSql.html) メッセージが含まれている場合、ClickHouse は以下のコマンドを処理します。

<h3 id="flightsql-getflightinfo">
  GetFlightInfo / GetSchema でサポート
</h3>

| Command | Description |
| - | - |
| `CommandStatementQuery` | 任意の SQL クエリを実行します。`transaction_id` には対応していません。 |
| `CommandGetSqlInfo` | サーバーのメタデータ (名前、バージョン、Arrow のバージョン、機能) を取得します。 |
| `CommandGetCatalogs` | カタログを一覧表示します。空の結果を返します (ClickHouse はカタログを使用しません) 。 |
| `CommandGetDbSchemas` | データベースを一覧表示します。オプションの `db_schema_filter_pattern` (SQL の `LIKE` パターン) に対応しています。 |
| `CommandGetTables` | テーブルを一覧表示します。スキーマ、テーブル名、テーブルタイプによるフィルターと、オプションでのスキーマの含有に対応しています。 |
| `CommandGetTableTypes` | テーブルエンジンのタイプ (`system.table_engines` から) を一覧表示します。 |
| `CommandGetPrimaryKeys` | 指定したテーブルの主キーを構成するキーカラムを取得します。 |
| `CommandPreparedStatementQuery` | ハンドルで指定した、プリペアドステートメントの `SELECT` 形式のステートメントを実行します。 |

<h3 id="flightsql-doput">
  DoPut 経由でサポートされるもの
</h3>

| Command | Description |
| - | - |
| `CommandStatementUpdate` | DDL/DML ステートメント (CREATE、INSERT、ALTER など) を実行します。影響を受けた行数を返します。`transaction_id` はサポートされていません。 |
| `CommandStatementIngest` | Arrow データを既存のテーブルに一括で取り込みます。サポートされるのは追加モードのみです。`transaction_id` はサポートされていません。 |
| `CommandPreparedStatementQuery` | `DoPut` 経由で送信する際にプリペアドステートメントのパラメータ値をバインドし、その後、ステートメントハンドルを含む `DoPutPreparedStatementResult` を返します。受け付けられるパラメータセットは 1 つ (1 行) のみで、バインドする値の数は `?` プレースホルダーの数と完全に一致している必要があります。 |
| `CommandPreparedStatementUpdate` | ハンドルを使用してプリペアド DDL/DML ステートメントを実行し、影響を受けた行数を返します。 |

<h3 id="flightsql-not-implemented">
  ClickHouse でサポートされていないもの
</h3>

これらのコマンドは ClickHouse が提供していない機能に対応しているため、Arrow Flight SQL インターフェイスではサポートされていません。

| コマンド | 理由 |
| - | - |
| `CommandGetCrossReference` | ClickHouse はリレーショナルデータベースではなく、外部キー制約を実装していないため、クロスリファレンスのメタデータは利用できません。 |
| `CommandGetExportedKeys` | ClickHouse はリレーショナルデータベースではなく、外部キー制約を実装していないため、エクスポートされたキーのメタデータは利用できません。 |
| `CommandGetImportedKeys` | ClickHouse はリレーショナルデータベースではなく、外部キー制約を実装していないため、インポートされたキーのメタデータは利用できません。 |
| `CommandStatementSubstraitPlan` | ClickHouse は Substrait プランをサポートしていません。 |

<h2 id="complete-example">
  完全な使用例
</h2>

```python title="Query" theme={null}
import pyarrow as pa
import pyarrow.flight as flight

# 接続と認証
client = flight.FlightClient("grpc://localhost:9090")
token = client.authenticate_basic_token("default", "")
options = flight.FlightCallOptions(headers=[token])

# PATH ディスクリプタを使用した DoPut によるデータ挿入
schema = pa.schema([("id", pa.uint32()), ("value", pa.string())])
batch = pa.record_batch(
    [pa.array([1, 2, 3], type=pa.uint32()), pa.array(["a", "b", "c"])],
    schema=schema,
)
descriptor = flight.FlightDescriptor.for_path("test")
writer, _ = client.do_put(descriptor, schema, options)
writer.write_batch(batch)
writer.close()

# GetFlightInfo + DoGet を使用したデータのクエリ
descriptor = flight.FlightDescriptor.for_command(
    "SELECT * FROM test ORDER BY id"
)
info = client.get_flight_info(descriptor, options)
for endpoint in info.endpoints:
    reader = client.do_get(endpoint.ticket, options)
    table = reader.read_all()
    print(table.to_pandas())
```

```text title="Response" theme={null}
   id value
0   1     a
1   2     b
2   3     c
```

<h2 id="data-format">
  データフォーマット
</h2>

すべてのデータは Apache Arrow IPC フォーマットで転送されます。サポートされるのは `Arrow` フォーマットのみで、他の ClickHouse フォーマット (例: `FORMAT JSON`、`FORMAT CSV`) を指定するとエラーになります。

ClickHouse データ型は、シリアライゼーション時に Arrow の型へマッピングされます。Arrow Flight は常に canonical な Arrow マッピングを使用し、`Arrow` および `ArrowStream` 出力フォーマットとは異なり、型の表現方法を変更する `output_format_arrow_*` 系の設定には従い**ません**。`output_format_arrow_string_as_string`、`output_format_arrow_low_cardinality_as_dictionary`、`output_format_arrow_date_as_uint16`、`output_format_arrow_fixed_string_as_fixed_byte_array`、および dictionary インデックス関連の設定は、ここでは効果がありません。したがって、同一のクエリであっても Arrow Flight 経由では `FORMAT Arrow` 経由と異なるスキーマになることがありますが、これは次の 2 つの理由による意図的な設計です。

* Flight SQL はメタデータ応答のスキーマを固定しています。たとえば `CommandGetTables` は `catalog_name: utf8, db_schema_name: utf8, table_name: utf8 not null, table_type: utf8 not null, table_schema: bytes not null` を返す必要があります。session setting によってこれらの `utf8` カラムが `binary` に変わってしまうと、ClickHouse はあらゆる Flight SQL ドライバーに対して非準拠となり、さらに ClickHouse が `table_schema` 内で公開するテーブルごとのスキーマも変わってしまいます。
* Flight クライアントは、スキーマとデータを別々の呼び出し (`GetFlightInfo` または `GetSchema`、続いて `DoGet`) で取得します。スキーマを変更し得る設定が存在すると、その間に session が変化した場合に、公開されたスキーマと実際に配信されるストリームが食い違う余地が生まれます。

唯一の例外は、`JSON`、`Dynamic`、`QBit`、`AggregateFunction` のように Arrow に対応する型がまったく存在しないケースです。基準とすべき canonical なマッピングがないため、ClickHouse は何らかの表現を選択せざるを得ず、[`output_format_arrow_unsupported_types`](/ja/reference/settings/formats/output-format) でどの表現を使うかを指定できます。

| 値 | 動作 |
| - | - |
| `throw` | クエリが拒否されます。 |
| `text` | 各行の値を text form で Arrow の `Utf8` カラムとして出力します — `CAST(col AS String)` が返すものと同じです。 |
| `binary` (デフォルト) | 各行の値をバイナリ形式で Arrow の `Binary` カラムとして出力します — `RowBinary` が用いる encoding です。 |

`AggregateFunction` カラムは、`text` モードであっても Arrow の `Binary` カラムのままとなる唯一の型です。その text form は生の aggregate state であり valid UTF-8 ではない一方、Arrow の `Utf8` カラムは valid UTF-8 を保持しなければならないためです。読みやすい値が必要な場合は `finalizeAggregation` を使用してください。

同じ理由から、ClickHouse は `text` の値に含まれるすべての invalid UTF-8 sequence を、`Utf8` カラムへ書き込む前に U+FFFD (`�`) に置き換えます。`String` を保持する `Dynamic` はそのバイト列をそのままシリアライズし、その内容は任意のバイトになり得るため、この処理がなければカラムが Arrow 仕様に違反し、厳格なクライアントに拒否される可能性があります。変化するのは、もともと valid text ではない値だけです。バイト列を厳密に保持する必要がある場合は `binary` モードを使用してください。

`output_format_arrow_string_as_string` は、`FORMAT Arrow` の場合も含めてこれらのカラムには適用されません。この設定が制御するのは実際の `String` および `FixedString` カラムのみです。したがって、`clickhouse.opaque` カラムの Arrow 型は、保持している encoding を常に示します。text form であれば `Utf8`、バイナリ形式であれば `Binary` です。

これが、`AggregateFunction` カラムでは問題にならないにもかかわらず、`Dynamic` に保持された aggregate state が `text` モードでは情報を失う理由です。カラムは `Dynamic` として型付けされるため各行が何を保持しているかは分からず、しかもスキーマは値を 1 つも見ない段階で確定するため、その state に専用の `Binary` カラムを割り当てることはできません。保持したい場合は `binary` モードを使用してください。`Variant` は取り得る型を列挙するため、その中に含まれる `AggregateFunction` は専用の `Binary` の子を持ち、影響を受けません。

こうしたカラムは、それ以外の点では本物の `Utf8`/`Binary` カラムと区別できないため、Arrow の extension 型として宣言されます。フィールドのメタデータには `ARROW:extension:name` = `clickhouse.opaque` が保持され、元の ClickHouse 型名は `ARROW:extension:metadata` に格納されます。extension 名を認識しないクライアントは、Arrow 仕様が定めるとおり、素の storage 型として認識します。nested カラムはそれぞれ自身のフィールドにタグ付けされるため、`Array(JSON)` ではその子がタグを持ち、`Map(JSON, ...)` ではそのキーがタグを持ちます。コンテナ自体ではありません。

従来のブール値設定 `output_format_arrow_unsupported_types_as_binary` も引き続き使用でき、`0` の場合は `throw`、`1` の場合は `binary` と同等です。この設定は、`output_format_arrow_unsupported_types` がデフォルト値のままの場合にのみ参照されます。

<h2 id="compatibility">
  互換性
</h2>

Arrow Flight インターフェイスは、Arrow Flight または Arrow Flight SQL プロトコルをサポートするあらゆるクライアントやツールと互換性があります。具体的には、次のようなものが含まれます。

* Python (`pyarrow`)
* Java (`org.apache.arrow.flight`)
* C++ (`arrow::flight`)
* Go (`apache/arrow/go`)
* ADBC (Arrow Database Connectivity) ドライバー
* DBeaver など、Flight SQL をサポートするその他のツール

お使いのツール向けにネイティブの ClickHouse コネクタ (例: JDBC、ODBC、ネイティブプロトコル) が利用できる場合は、パフォーマンス上の理由やフォーマット互換性のために Arrow Flight が特に必要でない限り、そちらを優先して使用してください。

<h2 id="client-side">
  クライアント側のArrowFlight機能
</h2>

ClickHouseは、外部のArrow Flightサーバーからデータを読み取るFlightクライアントとしても動作します。詳しくは、以下を参照してください。

* [ArrowFlightテーブルエンジン](/ja/reference/engines/table-engines/integrations/arrowflight)
* [arrowFlightテーブル関数](/ja/reference/functions/table-functions/arrowflight)

<h2 id="see-also">
  関連項目
</h2>

* [Apache Arrow Flight 仕様](https://arrow.apache.org/docs/format/Flight.html)
* [Apache Arrow Flight SQL 仕様](https://arrow.apache.org/docs/format/FlightSql.html)
* [ClickHouse の Arrow フォーマット](/ja/reference/formats/Arrow/Arrow)
