INSERT, телеметрию выполнения и сообщения об ошибках. Именно этот протокол используется клиентом командной строки, C++ и большинством сторонних нативных драйверов.
На этой странице рассматривается сам протокол: кадрирование пакетов, машина состояний соединения, согласование версий и тело каждого сообщения, кроме Block. Байты внутри пакетов семейства Data (то есть Block, его столбцы и кодировки отдельных типов) — отдельная тема, описанная в спецификации Native Format.
Сопутствующая спецификацияЭта страница — одна из двух частей пары и публикуется вместе с сопутствующей спецификацией Native Format. Эти две спецификации чётко разделяют задачи: эта страница описывает пакетный и транспортный уровни, а спецификация Native Format — байты внутри пакетов семейства
Data.BlockInfo, поэтому один смещённый байт нарушает синхронизацию всего, что идёт дальше. Он работает с сохранением состояния, и каждое TCP-соединение обрабатывает только один запрос за раз — мультиплексирования нет. Для целых чисел фиксированной ширины используется порядок байтов little-endian.
Обзор
Каждое передаваемое по сети сообщение начинается с кода типа пакета
VarUInt, за которым следует тело, структура которого зависит от этого кода и согласованной версии протокола.
Соединение проходит через три фазы — однократное рукопожатие, затем любое количество обменов Ping или Query, после чего соединение закрывается:
Протокол нативного TCP всегда передаёт табличные данные в формате Native, независимо от наличия предложения FORMAT в SQL. Повторное форматирование в RowBinary, CSV, JSON и так далее — задача клиента; оно выполняется после декодирования блоков Native. (HTTP-интерфейс использует другой кодовый путь и действительно учитывает предложение FORMAT; HTTP здесь не рассматривается.)
Безопасность
Защита транспортного уровня (TLS)
Аутентификация
ClientHello. Поля user и password передаются как строки в открытом виде, поэтому учетные данные при передаче защищаются шифрованием на транспортном уровне (TLS).
Аутентификация SSH по схеме challenge-response доступна начиная с версии протокола 54466 — см. Аутентификация SSH по схеме challenge-response.
Межсерверный секрет
auth_hash в поле 4 Query, вычисленный на основе salt, nonce, настроенного секрета и запроса; принимающий сервер вычисляет его заново и сравнивает. Это контролируется возможностью INTERSERVER_SECRET (v54441). Внешние клиенты всегда отправляют здесь пустую строку. См. Межсерверная аутентификация.
Версионирование и флаги возможностей
Согласование версии
Флаги возможностей
Таблица возможностей
Оболочка пакета
VarUInt, а не байт фиксированной длины. Для значений меньше 128 VarUInt даёт тот же однобайтный результат, но реализации должны использовать кодирование VarUInt, чтобы сохранить совместимость, если в будущем появятся типы пакетов со значением 128 и выше.
В справочнике по сообщениям описывается только тело каждого пакета — байты после кода типа пакета. Нумерация полей начинается с 1, где первое поле — это первое поле тела.
Фрагментное кадрирование (v54470+)
CHUNKED_PROTOCOL согласована (см. этап рукопожатия), каждый пакет при передаче оборачивается во фрагментное кадрирование. Такое оборачивание выполняется отдельно для каждого направления: client→server и server→client согласуются независимо и в итоге могут работать в разных режимах (с фрагментным кадрированием или без него).
Структура данных в канале для каждого пакета:
VarUInt находится внутри потока, разбитого на фрагменты: это первый байт полезной нагрузки пакета (первый байт первого фрагмента), а не отдельный байт, отправляемый перед кадрированием. Полезная нагрузка фрагментов каждого пакета представляет собой полный [VarUInt packet_type_code][message body] из конверта пакета. Если клиент оставляет тип пакета вне потока, разбитого на фрагменты, другая сторона считывает этот байт типа как первый байт размера фрагмента u32, что приводит к рассинхронизации соединения.
Один пакет может быть разделен на несколько фрагментов, если буфер пишущей стороны заполняется посреди пакета; разбиение может произойти в любом месте, в том числе внутри VarUInt типа пакета. Читатель объединяет полезные нагрузки фрагментов и рассматривает завершающий 4-байтовый ноль как прозрачную границу пакета — он считывает его, но не передает тому, что читает тела пакетов.
Пакеты без тела все равно оборачиваются: однобайтовый пакет, такой как Ping или Pong, после согласования разбиения на фрагменты становится [u32 size = 1][0x04][u32 0]. Любое описание «один байт в байтовом потоке» в других местах этой страницы относится к форме до разбиения на фрагменты.
Согласование. ServerHello и Addendum содержат по два поля String, по одному для каждого направления, со значениями из {"chunked", "notchunked", "chunked_optional", "notchunked_optional"}:
chunked/notchunked— строгие: эта сторона требует именно этот режим.- Варианты
_optional— гибкие: они принимают любой режим, который выберет другая сторона.
На стороне клиента предпочтение SEND клиента согласуется с предпочтением RECV сервера, и наоборот.
Время применения. Строки согласования передаются без кадрирования: ClientHello → ServerHello (предпочтения сервера) → Addendum (согласованные значения клиента). Переключение на кадрирование применяется ко всем байтам, отправленным после того, как
Addendum сброшен на диск. Сам Addendum, ClientHello и ServerHello всегда передаются без кадрирования.
Жизненный цикл соединения
HANDSHAKE, READY, READING_RESPONSE или завершено. Поскольку протокол не поддерживает мультиплексирование, клиент, который отправляет новый запрос, не дочитав предыдущий ответ до конца, перемешивает байты в потоке передачи данных и повреждает поток.
Состояния
HANDSHAKE → READY → READING_RESPONSE → READY — с самопетлёй Ping/Pong, а все ветви сбоев сходятся в единственное конечное состояние Terminated.
Фаза рукопожатия
-
Клиент отправляет
ClientHello, указав максимальную поддерживаемую версию протокола. -
Клиент читает ответ и обрабатывает его в зависимости от типа пакета:
-
Если
negotiated_version ≥ 54458(возможностьADDENDUM), клиент отправляетAddendum. Это решение принимается на основе согласованной версии, а не версии, объявленной клиентом.
READY; при любой ошибке оно завершается.
Фаза Ping
запросом, поэтому несколько последовательных Ping независимы.
Начиная с READY, последовательность такова:
-
Клиент отправляет
Ping. -
Клиент читает ответ:
Фаза запроса
EndOfStream или Exception.
Начиная с READY, последовательность выглядит так:
При ошибке на любом этапе сервер отправляет Exception вместо EndOfStream, что приводит к завершению запроса.
-
Клиент отправляет
Queryс уникальнымquery_id(обычно UUID). -
Клиент отправляет все внешние таблицы, затем пустой маркер
Data. У пустого пакетаData:table_name = "",num_columns = 0,num_rows = 0. Сервер не начинает выполнять запрос, пока не получит этот маркер. -
Клиент переходит в
READING_RESPONSEи сбрасывает буфер записи. -
Клиент в цикле читает пакеты ответа и обрабатывает их по типу:
EndOfStream или обработанном Exception connection возвращается в READY. Нарушение protocol или ошибка I/O приводит к её завершению.
Случай
num_rows == 0 часто вызывает путаницу в новых реализациях. Block с нулём строк — это маркер границы или заголовок схемы, а не признак конца потока. Ответ завершается только при EndOfStream или Exception.Фаза INSERT
INSERT; сервер отвечает блоком схемы, описывающим целевую таблицу; клиент потоково передаёт пакеты Data со строками, а затем пустой маркер Data; сервер завершает обмен сообщением EndOfStream или Exception.
Начиная с READY, SQL-запрос имеет вид INSERT INTO <table> [(<cols>)] VALUES — без встроенного литерала VALUES (...), поскольку данные строк передаются через пакеты Data. Поток:
- Клиент отправляет
Query, где вbodyуказан SQL-запросINSERT. - Клиент отправляет все внешние таблицы (для
INSERTэто редкость). В отличие от фазы запроса, здесь он не отправляет пустой маркерData. ПакетINSERTQueryотправляется вместе с ожидающими данными, поэтому пустой завершающий блок данных откладывается до шага 5; если отправить его до блока схемы, сервер воспримет его как конец потока строк, завершитINSERTбез строк, а затем разберёт первый реальный пакет строк как лишний пакет верхнего уровня. - Клиент считывает пакеты метаданных (TableColumns, Progress, ProfileInfo, Log, ProfileEvents), пока не получит пакет
Dataсо схемой —Blockс 0 строк, но с полной структурой столбцов (имена и типы). Блок схемы — это контракт: строки, которые клиент отправит дальше, должны соответствовать этим структурам столбцов. - Клиент отправляет блоки данных. Для каждого блока он записывает
VarUInt(ClientPacket::Data = 2), затемString("")для пустого имени внешней таблицы, а затем самBlock. Типы столбцов должны соответствовать столбцам блока схемы по позиции. - Клиент отправляет завершающий маркер конца ввода: пакет
Dataс пустымBlock(0 столбцов, 0 строк). - Клиент считывает поток ответа до
EndOfStream(успех) илиException(ошибка).
INSERT (v54484+). Когда запрос содержит async_insert = 1, сервер ставит строки в очередь и сбрасывает их в хранилище как часть батча. При согласованной версии ≥ 54484 (PROGRESS_IN_ASYNC_INSERT) после завершения сброса сервер отправляет дополнительный пакет Progress, сразу после которого идут ProfileEvents этой вставки, а затем EndOfStream. Ниже 54484 сервер пропускает этот завершающий Progress. Это обычный пакет Progress; поскольку сервер сбрасывает конвейер запроса перед добавлением счётчиков записи, на практике это приращение содержит только затраченное время, а статистика по записанным строкам и байтам поступает клиенту через сопутствующие ProfileEvents. Клиенту, который уже считывает чередующиеся пакеты Progress на шаге 6, достаточно просто принять ещё один пакет.
Соединение возвращается в состояние READY при EndOfStream или обработанном Exception. Нарушения протокола и ошибки ввода-вывода приводят к его завершению.
Справочник сообщений
Type используются:
VarUInt— беззнаковое целое число переменной длины (см. VarUInt).String— байты с префиксомVarUInt(см. String).UInt8,Int32и так далее — целые числа фиксированной длины в little-endian формате.Bool— один байт,0x00или0x01.
Role показывает, кто использует каждое поле:
- client — задаётся внешними клиентами.
- inter-server — имеет значение только при обмене между серверами; внешние клиенты записывают значение по умолчанию.
- universal — используется в обоих случаях.
ClientHello (тип пакета 0)
ServerHello (тип пакета 0)
Rule — элемент
password_complexity_rules:
Этот список отражает конфигурацию политики паролей, заданную оператором сервера, и носит исключительно рекомендательный характер — сервер не применяет эти правила во время рукопожатия. Клиент, предоставляющий возможность смены или установки пароля, может использовать эти правила, чтобы выявлять ошибки до отправки на сервер пароля, не соответствующего требованиям.
Чтобы ограничить использование ресурсов при работе с враждебным или неверно настроенным сервером, ограничьте декодированное значение
count 256 элементами, а каждую строку pattern и message — 4096 байтами. Значение count, равное 0 (без последующих пар), — обычный случай для серверов, у которых политика паролей не настроена.Дополнение (без типа пакета)
ADDENDUM (v54458). Отправляется сразу после завершения обмена рукопожатия. Это не отдельный тип пакета — поля передаются в wire в сыром виде, без байтового префикса типа пакета.
Переключение на фрагментированное кадрирование вступает в силу после сброса этого Дополнения на диск — само Дополнение передаётся без кадрирования.
Ping (тип пакета 4)
0x04; при согласованном разбиении на фрагменты этот байт становится однобайтовой полезной нагрузкой фрагмента (см. кадрирование с фрагментацией).
Pong (тип пакета 4)
0x04 до кадрирования с фрагментацией; если согласована фрагментация, этот байт становится однобайтной полезной нагрузкой фрагмента (см. кадрирование с фрагментацией).
Исключение (тип пакета 2)
Query (тип пакета 1)
ClientInfo (встроено в Query)
CLIENT_INFO (v54032). (Некоторые поля внутри ClientInfo поддерживаются только в более поздних версиях — это указано ниже для каждого поля.)
Структура, зависящая от интерфейса (поля 7–12)Поля 7–12 выше относятся к ветке TCP. Когда
query_interface (поле 6) не TCP, эти поля заменяются другой структурой в wire-формате — это не просто необязательные пропуски, поэтому декодер должен выполнять ветвление по полю 6.query_interface = 2(HTTP): вместо них записывается информация о HTTP-запросе, пересланном сервером, —http_method(UInt8),http_user_agent(String), затемforwarded_for(String, при наличииX_FORWARDED_FOR_IN_CLIENT_INFOv54443) иhttp_referer(String, при наличииREFERER_IN_CLIENT_INFOv54447). Поляos_user/client_hostname/client_name/version_*/protocol_versionотсутствуют.- Любой другой интерфейс: ни поля TCP (7–12), ни поля HTTP не записываются; поток сразу продолжается с
quota_key.
quota_key (поле 13) и distributed_depth (поле 14) следуют для всех интерфейсов, затем version_patch (поле 15) записывается только для TCP.Эта ветка важна главным образом для межсерверного трафика, когда инициирующий сервер пересылает запрос, который изначально пришел по HTTP. Декодер, который всегда читает поля TCP, будет неверно интерпретировать такие пакеты — принимая http_method или http_user_agent за quota_key.Межсерверная аутентификация
auth_hash) не является общим секретом кластера в передаваемом по сети виде. Отправка самого секрета и не пройдет аутентификацию, и раскроет его. Вместо этого сервер, выступающий как межсерверный клиент, доказывает знание секрета с помощью SHA-256-хеша с salt:
- Войдите в межсерверный режим. Подключающийся сервер сигнализирует об этом в
ClientHello: полеuserслужит маркером межсерверного режима, аpasswordпусто. Затем он добавляет еще две строки — имя кластера и заново сгенерированный 32-байтныйsalt(encodeSHA256от случайного значения) — сразу после полейuser/password, как часть того же пакетаClientHello. Сервер читает эти две строки до отправкиServerHello, поэтому клиент должен записать их сразу; если сначала ждатьServerHello, возникнет взаимная блокировка, потому что сервер будет заблокирован на их чтении. - Получите nonce.
ServerHelloсодержит 8-байтныйUInt64nonce, когда согласованINTERSERVER_SECRET_V2(v54462). - Вычислите hash. Для каждого пакета Query, кроме
InitialQuery, клиент записываетencodeSHA256(salt + nonce + cluster_secret + query + query_id + initial_user + external_roles)в поле 4 — 32-байтный дайджест. (nonce— это его десятичное строковое представление, присутствующее только при согласовании ≥ v54462;external_rolesдобавляется только при согласованииINTERSERVER_EXTERNALLY_GRANTED_ROLES(v54472).) ДляInitialQuery, а также если секрет кластера не настроен, клиент вместо этого записывает пустую строку. - Проверьте. Сервер читает поле 4 с ограничением в 32 байта и заново вычисляет ту же конкатенацию, используя свою собственную копию секрета кластера; соединение отклоняется, если дайджесты не совпадают.
auth_hash.
Параметр
key — одним VarUInt 0, без последующих flags или value. Только кодирование каждого отдельного параметра зависит от согласованной версии и определяется SETTINGS_SERIALIZED_AS_STRINGS (v54429).
v54429+ (STRINGS_WITH_FLAGS) — каждый параметр представляет собой тройку, показанную ниже:
Поля 2 и 3 отсутствуют, если
key пуст.
До 54429 (BINARY) — каждый параметр имеет вид [String key][type-specific binary value]: поле flags не записывается, а значение кодируется в собственном бинарном формате параметра (например, как целое фиксированной ширины или строка с префиксом длины), а не как десятичная или текстовая строка. Список по-прежнему завершается пустым key. Клиент, работающий с согласованной версией ниже 54429, должен читать и записывать именно эту бинарную форму, а не тройку выше. (Исключение — пользовательские custom settings: в обоих форматах кодирования они всегда содержат flags и строковое значение.)
Поле flags включает:
0x01— Important: параметр влияет на результаты запроса и не должен молча игнорироваться более старыми peer.0x02— Custom: пользовательская custom setting.0x0c— 2-битное поле уровня, а не отдельный флаг:0x00= продакшн,0x04= устаревший,0x08= экспериментальный,0x0c= бета. Считывайте все 2 бита (flags & 0x0c) — простая проверкаflags & 0x04ошибочно классифицирует Beta (0x0c) как Obsolete.0x80— HotReload (перезагрузка config без рестарта; определён в enum флагов, в основном встречается для настроек coordination).
Параметр
SELECT {x:UInt64}. Кодируются так же, как настройка с установленным флагом Custom (0x02), и таким же образом завершаются пустым ключом.
Значение параметра — это SQL-представление значения, а не сырой литерал. Параметры строкового типа должны передаваться уже заключёнными в одинарные кавычки (например, значение для
{name:String} — 'Alice', а не Alice); в противном случае парсер значений на стороне сервера их отклонит.Данные (пакет типа 1 server→client, пакет типа 2 client→server)
table_name. Отличается только байт типа пакета.
Маркер конца данных — это пакет, у которого Block пуст:
0 столбцов и 0 строк, независимо от table_name. Сервер считает клиентский пакет Data терминатором только тогда, когда декодированный блок пуст (block.empty()); пакет с table_name = "" и непустым блоком — это обычный пакет строк, а не терминатор. Таким образом, поток строк INSERT — это последовательность непустых блоков Data, за которой следует один пустой блок Data, завершающий поток.
Варианты блоков и их значение описаны в разделе Варианты блоков.
Прогресс (тип пакета 3)
Progress, а не накопленные итоги. Перед отправкой сервер считывает свои счётчики и атомарно сбрасывает их в ноль, а elapsed_ns вычисляет как разницу во времени с момента предыдущей отправки. Поэтому клиент должен накапливать последовательные пакеты локально, чтобы получить накопленные итоги — если трактовать пакет как абсолютное значение, отображение прогресса начнёт откатываться назад или занижать значения, как только придёт более одного пакета.
ProfileInfo (тип пакета 6)
Итоги (тип пакета 7)
WITH TOTALS. Формат передачи данных идентичен данным: строка table_name (всегда пустая), за которой следует блок. Отличается только байт типа пакета.
Extremes (тип пакета 8)
extremes. Формат передачи данных идентичен данным. Блок содержит ровно 2 строки: строка 0 содержит минимум по каждому столбцу, строка 1 — максимум.
Log (тип пакета 10)
send_logs_level; см. потоковую передачу логов).
Формат обёртки и тела такой же, как у данных. Блок имеет фиксированное значение num_columns = 8 и предопределённую схему. Каждая строка лога соответствует одной строке по всем 8 столбцам, и один пакет Log может содержать много строк.
ProfileEvents (тип пакета 14)
num_columns = 6 и предопределённую схему. Каждое событие соответствует одной строке.
Тип элемента столбца
value различается между пакетами: старые серверы выдают UInt64, новые — Int64. Считывайте строку типа столбца из заголовка блока, а не предполагая фиксированную разрядность.TableColumns (тип пакета 11)
COLUMN_DEFAULTS_METADATA (v54410). Сервер отправляет этот пакет перед блоком схемы INSERT, чтобы передать метаданные значений по умолчанию для столбцов, но только если согласованная версия ≥ 54410 и включена настройка input_format_defaults_for_omitted_fields. Для версий ниже 54410 пакет не отправляется никогда, поэтому более старый клиент не должен его ожидать — блок схемы Data приходит сразу. Клиент v54410+ должен быть готов к одному из двух вариантов: либо необязательный TableColumns, затем блок схемы, либо сразу блок схемы.
Сжатое тело в v54481+При согласованной версии ≥ 54481 (
COMPRESSED_LOGS_PROFILE_EVENTS_COLUMNS) сервер записывает оба поля через один и тот же выходной поток с необязательным сжатием, поэтому, если у запроса compression = true, всё тело TableColumns (external_table + columns_description) находится внутри фрейма сжатия; клиент считывает его через соответствующий распакованный поток. Если для запроса сжатие не используется, тело передаётся несжатым ровно в том виде, как показано в таблице выше. Это важно для ответов со схемой INSERT: клиент, который переключает обработку сжатия для Log и ProfileEvents, но не для TableColumns, будет неверно читать ответ, если для запроса включено сжатие.TimezoneUpdate (пакет типа 17)
TIMEZONE_UPDATES (v54464). Отправляется ровно в одном месте: при инициализации табличной функции input (запрос вида INSERT INTO <table> SELECT ... FROM input('<structure>'), который потоково передаёт строки от клиента). Сразу после того, как сервер отправляет Data-блок входной схемы (см. фазу INSERT), он отправляет TimezoneUpdate с текущим значением session_timezone из контекста запроса, чтобы клиент разбирал строки, которые собирается отправить, в том же часовом поясе. Сервер не отправляет этот пакет при произвольных изменениях SET session_timezone посреди запроса и не использует его, чтобы сообщить клиенту, как форматировать последующие блоки результатов.
Пакет приходит один раз: сразу после блока входной схемы и до того, как клиент начнёт отправлять блоки строк. Декодер, который игнорирует
TimezoneUpdate, ВСЁ РАВНО ДОЛЖЕН прочитать завершающий String, чтобы сохранить выравнивание в потоке.
Аутентификация SSH по схеме челлендж-ответ (типы пакетов 11, 12, 18)
SSH_AUTHENTICATION (v54466) и включается только явным образом. Соединение переходит в SSH-сценарий, когда ClientHello отправляет user = " SSH KEY AUTHENTICATION " + <real_user> (с пробелами в начале и конце) и password = "". Сервер считывает префикс, удаляет его, чтобы восстановить реальное имя пользователя, и переключается на схему челлендж-ответ.
Этот сценарий используется вместо аутентификации по паролю, а обмен по схеме челлендж-ответ происходит до ServerHello — сервер откладывает ответ Hello, пока аутентификация не завершится успешно:
- Клиент отправляет ClientHello с SSH-префиксом-маркером и пустым паролем.
-
Клиент отправляет
SSHChallengeRequest(пакет 11). Сервер ещё не отправил ServerHello — он сначала обрабатывает аутентификацию и останавливается здесь в ожидании этого пакета. -
Сервер отвечает пакетом
SSHChallengeсо случайными байтами (пакет 18). -
Клиент формирует строку для подписи и подписывает её, а не сырой челлендж, затем отправляет
SSHChallengeResponse(пакет 12) с подписью. Подписываемое сообщение — это побайтовая конкатенация без разделителей четырёх частей в следующем точном порядке: -
Сервер проверяет подпись по зарегистрированному открытому ключу пользователя, восстанавливая ту же строку
decimal(protocol_version) + default_database + user + challenge. В случае успеха он отправляетServerHello— тот же ответ, что и при аутентификации по паролю, — и рукопожатие продолжается в обычном режиме (Addendum и т. д.); в случае неудачи сервер возвращаетИсключениеи завершает соединение. Клиент, который подписывает только сырые байты челленджа, не пройдёт аутентификацию.
Это обратный вариант рукопожатия с паролем, при котором сразу после ClientHello следует ServerHello. При SSH-аутентификации ServerHello задерживается до проверки подписи, поэтому механизм SSH challenge-response встраивается в рукопожатие до появления какого-либо ServerHello.
Справочник по типам пакетов
Клиент → Сервер
Сервер → Клиент
Конфигурация
- Настройки транспортного уровня — параметры TCP-сокета и тайм-ауты, влияющие на поведение самого TCP-соединения.
- Настройки прикладного уровня — параметры на уровне запроса, передаваемые в списке настроек пакета Query, которые влияют на то, что сервер отправляет по сети и как эти данные структурируются.
- Настройки вне области рассмотрения — настройки, которые часто путают с настройками протокола, но которые на самом деле управляют выполнением SQL или хранилищем.
Настройки транспортного уровня
Параметры сокета
Тайм-ауты
Эти тайм-ауты вложены следующим образом:
Ограничения соединений
Соединение, которое регулярно выполняет запросы, может существовать неограниченно долго. Через час закрываются только бездействующие соединения, а максимальное время жизни по умолчанию не ограничено.
Настройки прикладного уровня
Сжатие
Флаг
compression в пакете Query (поле 6) включает и отключает сжатие; эти настройки определяют, какой кодек используется, когда сжатие включено.
Потоковая передача журналов
Если задать для
send_logs_level любое значение, кроме "none", сервер будет отправлять пакеты Log во время выполнения запроса.
Отчёт о прогрессе
Это целевой минимум, а не жёсткий максимум: сервер может отправлять пакеты Прогресс реже, если запрос выполняется недостаточно быстро.
Оболочка результата
Асинхронная вставка
Распределённая трассировка
Настройки вне области рассмотрения
max_threads— параллелизм при выполнении запроса.max_memory_usage— ограничение памяти на запрос.max_block_size,preferred_block_size_bytes— внутренний сайзинг блоков при обработке запроса; блоки на уровне передачи от них не зависят.compile_expressions— JIT-компиляция; влияет только на CPU.async_insert_max_data_size— буфер очереди на стороне сервера.- Все настройки
input_format_*иoutput_format_*, кроме семействаinput_format_native_*/output_format_native_*, — варианты, отличные отnative, выбирают или настраивают другие форматы (например, по HTTP) и не изменяют блокиDataсобственного протокола.
*_native_* — исключение: они меняют байты внутри блоков Data нативного TCP, поэтому реализация протокола должна это учитывать. output_format_native_encode_types_in_binary_format переключает поле type столбца с текстовой строки на двоичное кодирование типа, output_format_native_write_json_as_string выводит столбцы JSON как String, а output_format_native_use_flattened_dynamic_and_json_serialization выбирает FLATTENED-структуру Dynamic/JSON. Поскольку они влияют на тело блока, а не на оболочку пакета, они описаны в спецификации Native Format — см. структуру столбца в передаваемых данных и версируемые типы.
Глоссарий
- Обычный запрос (
SELECTи т. п.): отправляется после пакета Query и всех пакетов данных внешних таблиц, чтобы сообщить: «внешних данных больше нет». После этого server начинает выполнение. INSERT: клиент не отправляет маркер до схемы. Сначала server отправляет блок схемы, затем клиент передаёт свои блоки данных со строками и только после этого отправляет пустой пакет данных, чтобы завершить поток строк. Если отправить пустой маркер до блока схемы, он будет прочитан как немедленное завершение строк, и данные будут потеряны.
min(client_version, server_version), вычисляется во время рукопожатия. Определяет, какие возможности активны на всём протяжении lifetime connection.
Пакет — wire-сообщение: код типа пакета VarUInt, за которым следует body, формат которого зависит от типа. См. packet envelope.
Код типа пакета — начальный VarUInt пакета, который определяет его формат. В настоящее время назначены значения 0–18. См. packet type reference.
Поток ответов — последовательность пакетов, которые server отправляет во время выполнения запроса. Имеет произвольную длину и завершается ровно одним EndOfStream (успех) или Исключение (ошибка). См. query phase.
Блок схемы — заголовочный блок (Block со столбцами, но 0 строк), который server отправляет во время фазы INSERT, чтобы объявить ожидаемую структуру столбцов до того, как клиент отправит данные.
Список Settings — последовательность Tuple (key, flags, value) в body Query, завершающаяся пустым key. Содержит конфигурацию прикладного уровня для конкретного запроса. См. Setting.
Stage — поле VarUInt в пакете Query (field 5), управляющее тем, насколько далеко server выполняет запрос. Внешние клиенты обычно отправляют 2 (Complete); distributed queries и сериализованные query plan используют более высокие значения. Полный набор значений wire см. в field 5 Query.
Терминатор — пакет, который завершает поток. Ответ на Query завершается пакетом EndOfStream (успех) или Исключение (ошибка). Входной поток клиента завершается пустым маркером данных.