> ## 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.

> Документация по интерфейсу Apache Arrow Flight в ClickHouse, который позволяет клиентам Flight SQL подключаться к ClickHouse

# Интерфейс Arrow Flight

<h2 id="overview">
  Обзор
</h2>

ClickHouse поддерживает протокол [Apache Arrow Flight](https://arrow.apache.org/docs/format/Flight.html) — высокопроизводительный RPC-фреймворк для эффективной передачи столбцовых данных в формате [Arrow IPC](https://arrow.apache.org/docs/format/Columnar.html#serialization-and-interprocess-communication-ipc) поверх [gRPC](https://grpc.io/).

Реализация также поддерживает [Arrow Flight SQL](https://arrow.apache.org/docs/format/FlightSql.html), что позволяет BI-инструментам и приложениям, работающим по протоколу Flight SQL, отправлять запросы к ClickHouse напрямую.

Основные возможности:

* Выполнение SQL-запросов и получение результатов в формате Apache Arrow.
* Вставка данных в таблицы с использованием формата Arrow.
* Запрос метаданных (каталогов, схем, таблиц, первичных ключей) через команды Flight SQL.
* Создание, связывание, выполнение и закрытие серверных подготовленных операторов через Flight SQL.
* Управление сеансами и настройками через действия Flight SQL.
* Шифрование TLS и аутентификация по имени пользователя и паролю.
* Поэтапное получение результатов через `PollFlightInfo`.
* Отмена запросов через `CancelFlightInfo`.

<h2 id="enabling-server">
  Включение сервера Arrow Flight
</h2>

Чтобы включить сервер Arrow Flight, добавьте настройку `arrowflight_port` в конфигурацию сервера ClickHouse:

```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>

Чтобы включить TLS для интерфейса Arrow Flight, настройте следующие параметры:

```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+tls://` вместо `grpc://`.

<h2 id="authentication">
  Аутентификация
</h2>

Интерфейс Arrow Flight поддерживает два метода аутентификации:

<h3 id="basic-auth">
  Базовая аутентификация
</h3>

Клиенты проходят аутентификацию по имени пользователя и паролю через стандартный HTTP-заголовок `Authorization: Basic`. При успешной аутентификации server возвращает Bearer-токен в заголовке ответа.

<h3 id="bearer-auth">
  Аутентификация с Bearer-токеном
</h3>

В последующих запросах можно использовать Bearer-токен, полученный при базовой аутентификации, передавая его в заголовке `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")

# Базовая аутентификация возвращает bearer-токен для последующих запросов
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 поддерживает сеансы ClickHouse с помощью пользовательских заголовков метаданных gRPC:

| Заголовок | Описание |
| - | - |
| `x-clickhouse-session-id` | Идентификатор сеанса. Если указан, несколько запросов используют одно и то же состояние сеанса (временные таблицы, настройки). |
| `x-clickhouse-session-timeout` | Тайм-аут сеанса в секундах. Не должен превышать `max_session_timeout`. |
| `x-clickhouse-session-check` | Установите значение `1`, чтобы проверить, существует ли сеанс, не создавая его. |
| `x-clickhouse-session-close` | Установите значение `1`, чтобы закрыть сеанс после завершения запроса. Для этого в конфигурации сервера параметр `enable_arrow_close_session` должен быть установлен в `true`. |

<Note>
  Поскольку Arrow Flight использует gRPC поверх HTTP/2, имена заголовков метаданных учитывают регистр и должны быть указаны строчными буквами точно так, как показано (например, `x-clickhouse-session-id`, а не `X-ClickHouse-Session-Id`). Это требование [RFC 9113, раздел 8.2](https://www.rfc-editor.org/rfc/rfc9113#section-8.2), согласно которому имена полей HTTP/2 должны содержать только строчные символы. В отличие от этого, в HTTP/1.1 имена заголовков регистронезависимы.
</Note>

Сеансы позволяют задавать сохраняемые настройки ClickHouse через действие `SetSessionOptions` (см. [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 private key. Обязателен, если TLS включен. |
| `arrowflight.tickets_lifetime_seconds` | `600` | Время в секундах до истечения срока действия тикетов Flight и их удаления. Установите `0`, чтобы отключить автоматическое истечение срока действия тикетов. |
| `arrowflight.cancel_ticket_after_do_get` | `false` | Если `true`, тикеты отменяются сразу после получения через `DoGet`, что освобождает память. |
| `arrowflight.poll_descriptors_lifetime_seconds` | `600` | Время в секундах до истечения срока действия дескрипторов опроса. Установите `0`, чтобы отключить автоматическое истечение срока действия. |
| `arrowflight.cancel_flight_descriptor_after_poll_flight_info` | `false` | Если `true`, дескрипторы опроса отменяются после использования в `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`. |
| `default_session_timeout` | `60` | Тайм-аут сеанса по умолчанию в секундах. Также определяет срок действия Bearer-токена. |
| `max_session_timeout` | `3600` | Максимально допустимый тайм-аут сеанса в секундах. |

<h2 id="rpc-methods">
  Поддерживаемые методы RPC
</h2>

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

Выполняет запрос и возвращает `FlightInfo`, содержащий схему результата, конечные точки с тикетами для получения данных, число строк и число байтов.

Принимает `FlightDescriptor`, который может быть одним из следующих:

* **дескриптор PATH**: Путь с одним компонентом, интерпретируемый как имя таблицы. Генерирует `SELECT * FROM <table>`.
* **дескриптор CMD**: Либо исходную строку SQL-запроса, либо сериализованную protobuf-команду Flight SQL (см. [Команды Flight SQL](#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>

Извлекает данные по указанному тикету. Принимает одно из следующего:

* Тикет, возвращённый `GetFlightInfo` или `PollFlightInfo`.
* Необработанную строку SQL-запроса в качестве значения тикета.

```python theme={null}
# Использование ticket из GetFlightInfo
reader = client.do_get(endpoint.ticket, options)
table = reader.read_all()

# Использование SQL-запроса в качестве ticket
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.

**Вставка по имени таблицы** (дескриптор 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()
```

**Выполнение DDL/DML через Flight SQL `CommandStatementUpdate`:**

Клиенты Flight SQL используют `CommandStatementUpdate` для выполнения DDL/DML-команд (CREATE, INSERT, ALTER и т. д.). В ответе возвращается количество затронутых строк.

**Пакетный приём через 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`. Идентификатор запроса извлекается из поля `app_metadata` объекта `FlightInfo`. Также отменяет все дескрипторы опроса, связанные с этим запросом.

```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`.

Поддерживаемые типы значений: string, boolean, integer, double и списки строк.

Если имя настройки неизвестно, возвращается ошибка `INVALID_NAME`. Если значение не удаётся разобрать, возвращается ошибка `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 она может отличаться от фактически отдаваемой по двум причинам:

* При определении схемы каждый `?` заменяется на `NULL`, поэтому плейсхолдер, определяющий столбец результата, получает тип от этого `NULL`, а не от значения, которое вы привяжете позже. `SELECT ? AS x` определяет столбец типа `Nothing`, но при привязке `5` отдаётся `UInt8`. У плейсхолдера, используемого только в предикате, как в `SELECT id, name FROM t WHERE id = ?`, такой проблемы нет, поскольку типы результата берутся из таблицы.
* Столбец, не имеющий эквивалента в Arrow, получает свой тип Arrow из [`output_format_arrow_unsupported_types`](/ru/reference/settings/formats/output-format), которое каждый вызов разрешает из сеанса, из которого он выполняется. Поскольку дескриптор принадлежит пользователю, а не одному сеансу, последующий вызов может разрешить его иначе и отдать `binary` там, где был заявлен `utf8`, или наоборот. Установка режима внутри самого подготовленного запроса фиксирует его для обоих случаев.

Подготовленные операторы принадлежат аутентифицированному пользователю, а не отдельному сеансу. Если вы открываете несколько сеансов от имени одного и того же пользователя, вы можете выполнять, повторно привязывать и закрывать один и тот же дескриптор оператора из любого из этих сеансов.

Другие пользователи не могут выполнять, привязывать или закрывать дескриптор оператора, который они не создавали.

`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`, закрываются все подготовленные операторы аутентифицированного пользователя в этом сеансе.
* Если идентификатор сеанса не указан, закрываются только подготовленные операторы без сеанса для аутентифицированного пользователя.

Если подготовленный оператор создан в сеансе (через `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>

| Команда | Описание |
| - | - |
| `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` с дескриптором оператора. Принимается только один набор параметров (одна строка), и количество привязанных значений должно точно соответствовать количеству плейсхолдеров `?`. |
| `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])

# Вставка данных с помощью DoPut с дескриптором PATH
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 всегда использует каноническое отображение 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`, а также настройки индексов словаря здесь не действуют. Поэтому один и тот же запрос может давать через Arrow Flight иную схему, чем через `FORMAT Arrow`, и это сделано намеренно по двум причинам:

* 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`. Если бы настройка сеанса превращала эти столбцы `utf8` в `binary`, ClickHouse перестал бы соответствовать спецификации для любого драйвера Flight SQL, а также изменилась бы схема отдельных таблиц, которую ClickHouse объявляет внутри `table_schema`.
* Клиент Flight получает схему и данные разными вызовами (`GetFlightInfo` или `GetSchema`, затем `DoGet`). Любая настройка, способная изменить схему, открывает возможность рассогласования между объявленной схемой и переданным потоком, если в промежутке сеанс изменится.

Единственное исключение — тип, у которого вообще нет эквивалента в Arrow, например `JSON`, `Dynamic`, `QBit` или `AggregateFunction`. Канонического отображения, на которое можно опереться, нет, поэтому ClickHouse должен выбрать представление, а настройка [`output_format_arrow_unsupported_types`](/ru/reference/settings/formats/output-format) позволяет указать, какое именно:

| Value | Поведение |
| - | - |
| `throw` | Запрос отклоняется. |
| `text` | По одному значению на строку в текстовой форме, как столбец Arrow `Utf8` — то, что возвращает `CAST(col AS String)`. |
| `binary` (по умолчанию) | По одному значению на строку в двоичной форме, как столбец Arrow `Binary` — кодирование, которое использует `RowBinary`. |

Столбец `AggregateFunction` — единственный тип, который остаётся столбцом Arrow `Binary` даже в режиме `text`: его текстовая форма — это сырое агрегатное состояние, не являющееся корректным UTF-8, а столбец Arrow `Utf8` обязан содержать корректный UTF-8. Если нужно читаемое значение, используйте `finalizeAggregation`.

По той же причине ClickHouse заменяет каждую некорректную последовательность UTF-8 в значении `text` на U+FFFD (`�`), прежде чем записать его в столбец `Utf8`. `Dynamic`, содержащий `String`, сериализует эти байты дословно, а они могут быть произвольными, поэтому без такой замены столбец нарушал бы спецификацию Arrow и мог бы быть отклонён строгим клиентом. Изменяются только значения, которые и так не являются корректным текстом. Если байты должны сохраняться в точности, используйте режим `binary`.

Настройка `output_format_arrow_string_as_string` никогда не применяется к таким столбцам, в том числе и в `FORMAT Arrow` — она управляет только настоящими столбцами `String` и `FixedString`. Поэтому тип Arrow у столбца `clickhouse.opaque` всегда указывает, какое кодирование он содержит: `Utf8` для текстовой формы, `Binary` для двоичной.

Именно поэтому агрегатное состояние, содержащееся в `Dynamic`, теряет информацию в режиме `text`, хотя столбец `AggregateFunction` — нет. Столбец типизируется из `Dynamic`, который ничего не говорит о содержимом своих строк, а схема фиксируется до того, как будет просмотрено хоть одно значение, поэтому состоянию нельзя выделить собственный столбец `Binary`. Чтобы сохранить его, используйте режим `binary`. `Variant` перечисляет свои альтернативы, поэтому `AggregateFunction` среди них получает собственный дочерний столбец `Binary` и остаётся незатронутым.

В остальном такой столбец неотличим от настоящего `Utf8`/`Binary`, поэтому он объявляется расширенным типом Arrow: метаданные поля содержат `ARROW:extension:name` = `clickhouse.opaque`, а исходное имя типа ClickHouse — в `ARROW:extension:metadata`. Клиент, не распознающий имя расширения, видит обычный тип хранения, как предписывает спецификация Arrow. Вложенные столбцы помечаются в собственном поле, поэтому метку несёт дочерний элемент `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 также может выступать клиентом Flight и читать данные с внешних серверов Arrow Flight. См.:

* [Движок таблицы ArrowFlight](/ru/reference/engines/table-engines/integrations/arrowflight)
* [Табличная функция arrowFlight](/ru/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)
* [Формат Arrow в ClickHouse](/ru/reference/formats/Arrow/Arrow)
