> ## Documentation Index
> Fetch the complete documentation index at: https://clickhouse.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

> Форматы фрейминга мультиплексируют данные, итоги, экстремумы, прогресс, события профилирования и серверные журналы в одном потоке ответа по HTTP

# Форматы фрейминга

Формат фрейминга мультиплексирует различные части ответа на запрос в одном потоке: фрагменты данных, итоги и экстремумы, пакеты прогресса, события профилирования (метрики) и серверные журналы — всё, что поддерживает собственный протокол. Это обеспечивает расширенный обмен данными по HTTP-протоколу.

Форматы фрейминга не зависят от [форматов вывода](/docs/ru/reference/formats): они инкапсулируют байты, создаваемые любым форматом вывода, разделяя и при необходимости кодируя эти фрагменты байтов. Конкатенация полезных нагрузок всех пакетов `data`, `totals` и `extremes` в точности соответствует тому, что сформировал бы формат вывода без фрейминга. Вспомогательные пакеты (прогресс, журналы, события профилирования, исключения) представляются в формате JSON.

Фрейминг также может расширять возможности формата вывода — это единственное намеренное исключение из указанного выше правила. Семейство форматов `JSONCompactEachRow` не включает итоги и экстремумы в обычный вывод, поскольку их строки невозможно отличить от обычных строк данных. При использовании формата фрейминга тип пакета позволяет их различать, поэтому эти форматы помещают строки итогов и экстремумов (в обычном синтаксисе строк) в пакеты `totals` и `extremes`. Для этих форматов конкатенация полезных нагрузок только пакетов `data` в точности соответствует тому, что формат вывода сформировал бы без фрейминга, а пакеты `totals` и `extremes` содержат дополнительные строки, отсутствующие в выводе без фрейминга. Поэтому клиент, восстанавливающий вывод без фрейминга из такого потока, должен объединять только полезные нагрузки `data`.

Формат фрейминга выбирается настройкой уровня запроса `framing_output_format`. В настоящее время он применяется к HTTP-протоколу и игнорируется другими интерфейсами.

Серверные журналы включаются в поток в виде пакетов, если задана настройка `send_logs_level`. События профилирования включаются, если включена настройка `send_profile_events` (по умолчанию). Пакеты прогресса и событий профилирования отправляются не чаще одного раза в `interactive_delay` микросекунд.

Успешный поток завершается финальным пакетом `progress`, содержащим итоговые счётчики (`result_rows`, `result_bytes`, `memory_usage`), подобно финальному пакету прогресса собственного протокола. Эти счётчики становятся известны только после завершения запроса, поэтому ни один предыдущий пакет `progress` их не содержит. Финальный пакет `progress` записывается после завершающих пакетов `log` и `profile_events`, сформированных при журналировании завершения запроса (например, записи в журнале «пиковое потребление памяти»), поэтому он действительно является последним пакетом потока. При сбое последним пакетом становится пакет `exception`, а пакет `progress` с итоговыми счётчиками вообще не записывается — он служит маркером успешного завершения потока — даже если сбой произошёл после завершения самого запроса и итоговые счётчики уже были известны (например, при сбое записи в журнал запросов).

Поскольку этот завершающий участок потока записывается после регистрации записи `QueryFinish` в `system.query_log`, события профилирования сетевой отправки запроса (`NetworkSendBytes`, `NetworkSendElapsedMicroseconds`) не включают отправку завершающих пакетов и закрытие ответа, а также, если ответ буферизуется (`http_response_buffer_size` или `wait_end_of_query`), отправку буферизованного тела ответа, которое передаётся только после завершения запроса. Это соответствует собственному протоколу, который также отправляет завершающие журналы и события профилирования после записи в журнале запросов.

Всё, что запрос включает только через собственное предложение `SETTINGS` — формат фрейминга, `send_logs_level` или `send_profile_events`, — неизвестно до разбора запроса, поэтому соответствующие журналы и события профилирования фиксируются только с момента выполнения запроса. Журналы и события профилирования этапов разбора, планирования и анализа фиксируются, только если настройка задана в сеансе или URL. В частности, запрос, завершающийся сбоем на этапе анализа (до выполнения конвейера) — например, из-за ссылки на неизвестную таблицу, — и включающий `send_logs_level` только в своём предложении `SETTINGS`, передаёт только пакет `exception`, но не журналы этапа анализа. Чтобы фиксировать их, задайте `send_logs_level` в сеансе или URL.

То же ограничение, связанное с поздним обнаружением, действует и для `send_logs_source_regexp`: очередь журналов фильтрует записи по источнику в момент захвата каждой из них, поэтому регулярное выражение, заданное только в предложении `SETTINGS` самого запроса, начинает действовать лишь с момента выполнения запроса. Пакеты `log` этапов разбора, планирования и анализа фильтруются значением настройки, заданным для сеанса или в URL; если там оно не задано, фильтрация не выполняется. Поэтому они могут содержать источники, не соответствующие регулярному выражению на уровне запроса. И наоборот, записи, отброшенные более узким регулярным выражением на уровне сеанса или URL, безвозвратно теряются и не восстанавливаются более широким выражением на уровне запроса. Задайте `send_logs_source_regexp` для сеанса или в URL, чтобы фильтровать весь жизненный цикл запроса.

Если во время выполнения запроса возникает исключение, оно отправляется в виде пакета `exception` (последнего пакета потока) независимо от настройки `http_write_exception_in_output_format`, поэтому клиент всегда может разобрать ответ как поток пакетов. После регистрации исключения формат вывода больше не добавляет байты полезной нагрузки: запрос, завершающийся сбоем до формирования какого-либо вывода, вообще не передаёт пакет `data` (даже пустой каркас документа формата), а запрос, завершающийся сбоем в середине потока, оставляет объединённую полезную нагрузку усечённой в точке сбоя, без суффикса формата — полезная нагрузка завершившегося сбоем запроса не должна выглядеть как полноценный документ.

Есть одно исключение: если запись самого пакета завершается сбоем после частичной записи (например, соединение разрывается после того, как некоторые байты пакета уже достигли клиента), кадрирование аварийно завершается, а поток прекращается без финального пакета `exception`. Частично записанный пакет никогда не отправляется повторно, поскольку это добавило бы дубликат после усечённых байтов и повредило поток. В этой ситуации клиент получает усечённый ответ и разорванное HTTP-соединение вместо корректно сформированного завершающего пакета. То же правило применяется при сбое во время закрытия самого потока ответа (сброса буферизованных результатов, завершения HTTP-сжатия, закрытия сокета): к этому моменту часть или весь поток успешного ответа уже переданы по сети, поэтому к нему ничего не добавляется — ни пакет `exception`, ни общий блок HTTP-ошибки, — и клиент получает усечённый ответ и разорванное соединение. Оно также применяется, когда сбой происходит при доставке самого исключения: если запись завершающего пакета `exception` завершается сбоем (например, при выгрузке завершающих журналов) после того, как была сформирована какая-либо часть потока пакетов — независимо от того, была ли она уже передана или всё ещё находится в серверных буферах ответа (`http_response_buffer_size`), — поток аналогично завершается без каких-либо добавлений, поэтому обычное тело HTTP-ошибки никогда не смешивается с частичным потоком пакетов. Сбой при записи строковых полей вспомогательных пакетов `log`, `profile_events` и `exception` также считается частично записанным пакетом, включая сбой при записи последних байтов такой строки: поток тогда заканчивается этим усечённым пакетом и вообще не содержит терминатора — ни пакета `exception`, ни финального пакета `progress` со счётчиками, — поэтому клиент, которому требуется терминатор, обнаружит сбой, даже если сам запрос выполнен успешно.

Формат фрейминга также применяется к запросам, не создающим поток результатов: успешной команде `INSERT`, DDL-запросу или любому другому запросу без вывода. Такой ответ не содержит пакетов `data`, но всё равно задаёт для ответа `Content-Type` формата фрейминга и передаёт пакеты `progress`, `log` и `profile_events`, как в собственном протоколе. Поток заканчивается финальным пакетом `progress`, содержащим итоговые счётчики (например, `result_rows` и `result_bytes` с числом записанных строк для `INSERT`). Поскольку полезная нагрузка не форматируется, формат вывода для таких запросов не имеет значения и не влияет на кадрированный поток.

<div id="available-framing-formats">
  ## Доступные форматы фрейминга
</div>

| Имя                                                      | Описание                                                                      |
| -------------------------------------------------------- | ----------------------------------------------------------------------------- |
| [`None`](#framing-format-none)                           | Без фрейминга: всё работает в обычном режиме.                                 |
| [`EventStream`](#framing-format-eventstream)             | События, отправляемые сервером по HTTP (`text/event-stream`).                 |
| [`JSONEachPacketBase64`](#framing-format-jsoneachpacket) | Один объект JSON на пакет; отформатированные данные закодированы в Base64.    |
| [`JSONEachPacketString`](#framing-format-jsoneachpacket) | Один объект JSON на пакет; отформатированные данные помещаются в JSON-строку. |

<div id="framing-format-none">
  ## None
</div>

Используется по умолчанию. Прозрачно передаёт всё применимое (данные, итоги, экстремальные значения, прогресс) в выходной формат и игнорирует всё неприменимое (метрики, журналы). Поэтому всё работает так же, как по умолчанию, включая форматы, которые сами поддерживают отображение прогресса, например `JSONEachRowWithProgress`.

<div id="framing-format-eventstream">
  ## EventStream
</div>

Упаковывает пакеты в [события, отправляемые сервером](https://html.spec.whatwg.org/multipage/server-sent-events.html), и задаёт для `Content-Type` ответа значение `text/event-stream; charset=UTF-8; payload=base64`. Каждый пакет отправляется как событие с именем, соответствующим типу пакета: `data`, `totals`, `extremes`, `progress`, `log`, `profile_events`, `exception`. Пакеты прогресса и другие вспомогательные пакеты отправляются в формате JSON.

События, отправляемые сервером, — это текстовый протокол, в котором переводы строк (включая возврат каретки, `\r`) используются как разделители полей. Поэтому байты, сформированные форматом вывода, не встраиваются дословно: блок форматированных данных кодируется в Base64 в одном поле `data:`, которое при декодировании содержит полностью отформатированную полезную нагрузку со всеми переводами строк. Именно это означает параметр `payload=base64` в `Content-Type`. Конкатенация декодированных полезных нагрузок пакетов `data`, `totals` и `extremes` в точности совпадает с тем, что формат вывода сформировал бы без кадрирования, байт в байт, для любого формата вывода: текстового, бинарного (`Native`, `RowBinary`) или необработанной сквозной передачи (`RawBLOB`, `TSVRaw`).

Вспомогательные JSON-пакеты (`progress`, `log`, `profile_events`, `exception`) никогда не кодируются: они записываются как одно поле `data:` с JSON без переводов строк.

Форматы вывода `*WithProgress` (`JSONEachRowWithProgress`, `JSONCompactEachRowWithProgress`) записывают прогресс как встроенные строки, являющиеся частью собственного вывода. Формат фрейминга вместо этого передаёт прогресс отдельными пакетами `progress`, поэтому он несовместим с этими форматами вывода и отклоняет их. Используйте базовый формат вывода (например, `JSONEachRow`) с кадрированием либо кадрирование `None` с форматом `*WithProgress`.

```bash theme={null}
curl "http://localhost:8123/?framing_output_format=EventStream" -d "SELECT number FROM numbers(3) FORMAT JSONEachRow"
```

```text theme={null}
event: data
data: eyJudW1iZXIiOiIwIn0KeyJudW1iZXIiOiIxIn0KeyJudW1iZXIiOiIyIn0K

event: profile_events
data: [{"host_name":"localhost","current_time":"2026-07-11 00:00:00","thread_id":"0","type":"increment","name":"SelectedRows","value":"3"},{"host_name":"localhost","current_time":"2026-07-11 00:00:00","thread_id":"0","type":"increment","name":"SelectedBytes","value":"24"}]

event: progress
data: {"read_rows":"3","read_bytes":"24","total_rows_to_read":"3","result_rows":"3","result_bytes":"24","elapsed_ns":"1174415"}

```

`EventStream` использует HTTP-протокол и генерирует исключение, если он неприменим.

<div id="framing-format-jsoneachpacket">
  ## JSONEachPacketBase64 и JSONEachPacketString
</div>

Каждый пакет представляет собой объект JSON в отдельной строке (JSON, разделённый символами новой строки, `application/x-ndjson`) и содержит информацию о пакете. Байты, сформированные форматом вывода, помещаются в поле `data`: в кодировке base64 в `JSONEachPacketBase64` (подходит для двоичных форматов вывода) или как JSON-строка в `JSONEachPacketString`.

Эти два варианта по-разному кодируют поле `data`, поэтому различаются по `Content-Type` ответа, как и `EventStream`: `JSONEachPacketBase64` устанавливает `application/x-ndjson; charset=UTF-8; payload=base64`, а `JSONEachPacketString` — `application/x-ndjson; payload=string`. Таким образом, клиент может определить только по метаданным ответа, нужно ли декодировать поле `data` из base64. `charset=UTF-8` гарантируется только для `JSONEachPacketBase64`, поскольку лишь кодирование base64 обеспечивает корректность UTF-8 для всего потока независимо от байтов полезной нагрузки — см. ниже.

Поскольку `JSONEachPacketString` помещает байты полезной нагрузки в JSON-строку, он предназначен для форматов вывода, формирующих корректный текст UTF-8. Столбцы `String` и `FixedString` могут содержать произвольные байты, поэтому текстовые форматы вывода, такие как `JSONEachRow`, `TSV` или `CSV`, могут выводить для таких значений некорректный UTF-8 — так же, как собственный формат `JSONEachRow` ClickHouse при значении по умолчанию `output_format_json_validate_utf8 = 0`. В этом случае итоговая JSON-строка, а следовательно, и весь поток NDJSON, не обязательно будут корректным UTF-8. `JSONEachPacketString` не проверяет и не перекодирует полезную нагрузку; для точной побайтовой передачи произвольных байтов используйте `JSONEachPacketBase64`.

Форматы вывода, которые заведомо создают байты не в UTF-8, `JSONEachPacketString` сразу отклоняет с ошибкой, до выполнения запроса: двоичные форматы (`Native`, `RowBinary`), сквозные форматы без обработки (`RawBLOB`, `TSVRaw`), форматы, записывающие в вывод из заголовка запроса имя столбца, имя типа данных или имя элемента `Tuple`, не закодированные в UTF-8, а также конфигурации, в которых литералы, задаваемые настройками, сериализации записывают дословно и которые не являются корректным UTF-8: настройки `format_csv_delimiter`, `format_tsv_null_representation` / `format_csv_null_representation` и `bool_true_representation` / `bool_false_representation`.

```bash theme={null}
curl "http://localhost:8123/?framing_output_format=JSONEachPacketString" -d "SELECT number FROM numbers(3) FORMAT JSONEachRow"
```

```text theme={null}
{"packet":"data","data":"{\"number\":\"0\"}\n{\"number\":\"1\"}\n{\"number\":\"2\"}\n"}
{"packet":"profile_events","profile_events":[{"host_name":"localhost","current_time":"2026-07-11 00:00:00","thread_id":"0","type":"increment","name":"SelectedRows","value":"3"}]}
{"packet":"progress","progress":{"read_rows":"3","read_bytes":"24","total_rows_to_read":"3","result_rows":"3","result_bytes":"24","elapsed_ns":"1265958"}}
```

При использовании `JSONEachPacketBase64` тот же пакет данных выглядит так:

```text theme={null}
{"packet":"data","data":"eyJudW1iZXIiOiIwIn0KeyJudW1iZXIiOiIxIn0KeyJudW1iZXIiOiIyIn0K"}
```

<div id="framing-format-packet-kinds">
  ## Типы пакетов
</div>

| Пакет            | Содержимое                                                                                                                                                                                        |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `data`           | Байты, сформированные форматом вывода для основного результата (включая префикс и суффикс формата).                                                                                               |
| `totals`         | Байты, сформированные форматом вывода для строки итогов (`WITH TOTALS`).                                                                                                                          |
| `extremes`       | Байты, сформированные форматом вывода для экстремальных значений (настройка `extremes`).                                                                                                          |
| `progress`       | Прогресс выполнения запроса в формате JSON: `read_rows`, `read_bytes`, `total_rows_to_read`, `result_rows`, `result_bytes`, `elapsed_ns`, `memory_usage` (поля с нулевыми значениями опускаются). |
| `log`            | Запись в журнале сервера в формате JSON: `event_time`, `host_name`, `query_id`, `thread_id`, `priority`, `source`, `text`.                                                                        |
| `profile_events` | Массив событий профилирования в формате JSON: `host_name`, `current_time`, `thread_id`, `type` (`increment` или `gauge`), `name`, `value`.                                                        |
| `exception`      | Сообщение об исключении в формате JSON.                                                                                                                                                           |

В отличие от полезных нагрузок `data`, `totals` и `extremes` (см. приведённые выше примечания о точном соответствии байтов), для строковых полей вспомогательных пакетов (`query_id`, `text` и `source` в `log`, `name` в `profile_events` и сообщения `exception`) не предусмотрено экранирование с помощью base64. Некоторые из них (например, `query_id`, получаемый из запроса) могут содержать произвольные байты. Эти поля всегда преобразуются в допустимый UTF-8: недопустимые последовательности заменяются символом замены (`U+FFFD`), поэтому вспомогательные пакеты всегда представляют собой допустимый JSON.

Одновременная обработка нескольких запросов пока не реализована, но дизайн это допускает: каждый пакет можно дополнить информацией об индексе запроса среди нескольких запросов.
