Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

ネイティブプロトコル

ネイティブプロトコルは、ClickHouseのクライアントとサーバーがTCP上で使用する、バイナリの接続指向プロトコルです。このプロトコルでは、SQLクエリ、結果データ、INSERTのペイロード、実行テレメトリー、エラー信号がやり取りされます。コマンドラインクライアントや、C++および大半のサードパーティ製ネイティブドライバーを支えるプロトコルでもあります。

このページでは、プロトコルそのもの、つまりパケットのフレーミング、接続の状態遷移、バージョンネゴシエーション、そしてBlock以外のすべてのメッセージのボディを扱います。Data系パケット内のバイト列 (Block本体、そのカラム、型ごとのエンコーディング) については別のトピックであり、Native Format仕様で説明されています。

いくつかの性質は全体を通して共通しています。このプロトコルはバイナリであり、位置依存です。BlockInfo内を除いてフィールドタグは存在しないため、1バイトでもずれると、それ以降のすべてが同期しなくなります。また、これはステートフルなプロトコルであり、各TCP接続は一度に1つのクエリだけを処理します。多重化はありません。固定幅整数はリトルエンディアンです。

概要

プロパティ
転送方式 TCP (必要に応じて TLS でラップ可能)
バイト順 固定幅整数はリトルエンディアン
エンコーディング バイナリかつ位置ベース (BlockInfo を除きフィールドタグなし)
接続モデル Stateful、クエリは同時に 1 つのみ、マルチプレクシングなし
バージョン管理 ハンドシェイク時にネゴシエートされ、個々の機能はバージョンに応じて有効化される
データフォーマット すべての表形式データで Native Format を使用

wire 上のすべてのメッセージは VarUInt のパケットタイプコードで始まり、その後に、そのコードとネゴシエートされたプロトコルバージョンに応じて構造が決まるボディが続きます。

接続は 3 つのフェーズで進行します。最初に一回限りのハンドシェイクがあり、その後に任意回数の Ping または Query のやり取りが続き、最後に切断されます:

sequenceDiagram
    autonumber
    participant C as Client
    participant S as Server

    C->>S: TCP connect

    rect rgb(220, 235, 255)
        Note over C,S: Handshake
        C->>S: ClientHello (name, version, db, user, password)
        S->>C: ServerHello (server_name, version, [timezone, display_name, ...])
        Note over C,S: negotiated_version = min(client, server)
        opt negotiated_version ≥ 54458
            C->>S: Addendum (quota_key)
        end
    end

    rect rgb(220, 245, 225)
        Note over C,S: Query phase
        C->>S: Query packet (ClientInfo, settings, params, SQL)
        C->>S: External-table Data packets (0 or more)
        C->>S: Empty Data marker — the "go" signal
        loop until EndOfStream or Exception
            S->>C: Data / Progress / Log / ProfileInfo / Totals / ...
        end
        S->>C: EndOfStream
    end

ネイティブTCPプロトコルでは、SQL 内の FORMAT 句に関係なく、表形式のデータは常に Nativeフォーマットで運ばれます。RowBinaryCSVJSON などへの再フォーマットはクライアント側の役割であり、Nativeブロックをデコードした後に行われます。 (HTTPインターフェイスはこれとは別のコードパスで、FORMAT 句を実際に尊重します。HTTP はここでの対象外です。)

セキュリティ

トランスポートセキュリティ (TLS)

TLS はプロトコルの下位にあるトランスポート層で動作します。有効にすると TCP ストリーム全体が暗号化され、TLS を使用するかどうかにかかわらず、プロトコルメッセージ自体はバイト単位でまったく同一です。

認証

認証は、ハンドシェイク時の ClientHello メッセージで行われます。user フィールドと password フィールドは平文の文字列として送信されるため、転送中の認証情報を保護するのはトランスポート層の暗号化 (TLS) です。

空の user フィールドは、サーバーで設定されているデフォルトのセッションユーザー、つまり default_session_user サーバー設定 (デフォルト値は default) として解釈されます。この値は、protocols セクションでリスナーごとにオーバーライドされる場合があります。サーバーのデフォルトのセッションユーザーが空に設定されている場合、またはサーバーのバージョンが 26.8 より前の場合、空の user フィールドは例外として拒否されます。clickhouse-client は空のユーザー名を送信しないことに注意してください。クライアント側で default に置き換えます。

SSH チャレンジレスポンス認証は、プロトコルバージョン 54466 以降で利用できます。詳しくは、SSH チャレンジレスポンス認証 を参照してください。

サーバー間シークレット

分散クエリ実行では、サーバー同士は共有シークレットを知っていることを証明することで相互認証を行います。この際、シークレット自体が wire 上に送られることはありません。各 Query は、salt、nonce、設定されたシークレット、およびクエリから計算される 32 バイトの SHA-256 auth_hashQuery のフィールド 4 に含み、受信側のサーバーはこれを再計算して照合します。これは INTERSERVER_SECRET 機能 (v54441) によって有効化されます。外部クライアントは、ここには常に空文字列を送信します。サーバー間認証を参照してください。

バージョン管理とフィーチャーゲート

バージョン交渉

client と server はどちらも、ハンドシェイク時に対応する最大の プロトコルバージョン を通知します。交渉後のバージョン は、その 2 つのうち小さい方です。

negotiated_version = min(client_version, server_version)

その後の各メッセージでは、ネゴシエートされたバージョンに基づいて、wire 上にどのフィールドが含まれるかが決まります。

機能ゲート

機能は、それが導入されたプロトコルバージョンによって識別され、ネゴシエートされたバージョンがその番号以上の場合に有効になります。

機能一覧

機能 バージョン 影響範囲 ワイヤ形式への影響
BLOCK_INFO all Block すべての Block に BlockInfo プレフィックス (is_overflows, bucket_number) を追加します。
CLIENT_INFO 54032 Query Query のボディに ClientInfo ブロックを追加します。
TIMEZONE 54058 ServerHello ServerHello に timezone フィールドを追加します。
QUOTA_KEY_IN_CLIENT_INFO 54060 ClientInfo ClientInfo に quota_key フィールドを追加します。
DISPLAY_NAME 54372 ServerHello ServerHello に display_name フィールドを追加します。
VERSION_PATCH 54401 ServerHello, ClientInfo 両方に version_patch フィールドを追加します。
SERVER_LOGS 54406 Log send_logs_level が設定されている場合、サーバーは Log パケットを送信します。
COLUMN_DEFAULTS_METADATA 54410 TableColumns サーバーは、INSERT/入力スキーマ Block の前に、カラムのデフォルト値メタデータを含む TableColumns パケット (type 11) を送信することがあります。送信されるのは、ネゴシエートされたバージョンが 54410 以上 かつ input_format_defaults_for_omitted_fields が有効な場合のみです。このバージョン未満ではこのパケットは決して送信されないため、クライアントはこれを待機してはなりません。
WRITE_CLIENT_INFO 54420 Progress Progress に wrote_rowswrote_bytes を追加します。 (名前に反して、これは ClientInfo ブロックの有無を制御するもの ではありません — それは CLIENT_INFO (v54032) です。)
SETTINGS_SERIALIZED_AS_STRINGS 54429 Query (settings encoding) 常に存在する settings リストのエンコード方法を変更します。settings が送信されるかどうかを制御するものではありません。v54429+ では各 setting を (name, flags, value-as-string) として書き込みます。古い peer は flags なしで (name, type-specific-binary-value) を書き込みます。Setting を参照してください。
INTERSERVER_SECRET 54441 Query Query にサーバー間 auth_hash フィールドを追加します。これは生の secret ではなく、cluster secret に対する salt 付き SHA-256 です。外部クライアントは空文字列を送信します。Inter-server authentication を参照してください。
OPEN_TELEMETRY 54442 ClientInfo ClientInfo に OpenTelemetry の trace context を追加します。
DISTRIBUTED_DEPTH 54448 ClientInfo ClientInfo に distributed_depth フィールドを追加します。
INITIAL_QUERY_START_TIME 54449 ClientInfo initial_time フィールド (Int64、固定幅) を追加します。
PROFILE_EVENTS 54451 ProfileEvents サーバーはクエリ実行中に ProfileEvents パケットを送信します。
PARALLEL_REPLICAS 54453 ClientInfo ClientInfo に並列レプリカの協調フィールドを追加します。
CUSTOM_SERIALIZATION 54454 Block (Column) 各カラムの型文字列の後に has_custom_serialization バイトを追加します。
ADDENDUM 54458 Handshake クライアントは handshake 交換の後に addendum (quota_key) を送信します。
PARAMETERS 54459 Query Query のボディに parameters リストを追加します。
SERVER_QUERY_TIME_IN_PROGRESS 54460 Progress Progress に elapsed_ns フィールドを追加します。
PASSWORD_COMPLEXITY_RULES 54461 ServerHello ServerHello に、パスワードポリシーの regex pattern のリストと、人間が読めるメッセージを追加します。
INTERSERVER_SECRET_V2 54462 ServerHello ServerHello に 8 バイトの UInt64 nonce を追加します。これはサーバー間クエリ署名に使用され、外部クライアントはデコードして無視します。
TOTAL_BYTES_IN_PROGRESS 54463 Progress Progress に total_bytes_to_read (VarUInt) フィールドを total_rowswrote_rows の間に追加します。
TIMEZONE_UPDATES 54464 TimezoneUpdate TimezoneUpdate サーバーパケット (type 17) を追加します。ボディ: session timezone を保持する単一の String。これは input table function の initializer のみが、入力スキーマ Block の直後に送信するため、クライアントは送信する行をサーバーの session_timezone で解析します。TimezoneUpdate を参照してください。
SPARSE_SERIALIZATION 54465 Block (Column) サーバーは has_custom_serialization = 1 を設定し、スパースエンコードされたカラムを送信することがあります。ワイヤ形式: 1 バイトの kind (0x01 = SPARSE) 、続いて EOG で終端される VarUInt offset stream、その後に inner type で高密度にエンコードされた非デフォルト値です。kind_stack and sparse encoding を参照してください。
SSH_AUTHENTICATION 54466 Auth flow SSH challenge-response authentication を追加します。オプトイン方式: クライアントはこれをトリガーするために、空の password とともに " SSH KEY AUTHENTICATION " + <real_user> 形式の user を送信します。SSH challenge-response authentication を参照してください。
TABLE_READ_ONLY_CHECK 54467 TablesStatusResponse TablesStatusResponse 内の各 table の行に is_readonly フラグを追加します。TablesStatusRequest を発行しない外部クライアントではワイヤ形式の変更はありません。
SYSTEM_KEYWORDS_TABLE 54468 system tables サーバーは system.keywords を追加し、正規の clickhouse-client が keyword を自動補完できるようにします。native-protocol のワイヤ形式に変更はありません。
ROWS_BEFORE_AGGREGATION 54469 ProfileInfo ProfileInfo の末尾に、この順序で applied_aggregation (Bool) と rows_before_aggregation (VarUInt) を追加します。
CHUNKED_PROTOCOL 54470 Connection framing パケットごとの chunk フレーミングで、すべての packet body をラップします。Addendum でネゴシエートされます。ServerHello には各方向に対するサーバーの希望が含まれ、Addendum にはクライアントの最終選択が含まれます。chunked framing を参照してください。
VERSIONED_PARALLEL_REPLICAS_PROTOCOL 54471 ServerHello, Addendum 両側で、parallel-replicas 協調プロトコルのバージョンを表す VarUInt を交換します。ServerHello のフィールドは protocol_version の直後 (timezone の前) に配置されます。Addendum のフィールドは、chunked-protocol 文字列群の後ろに追加されます。現在の値: 8 (DBMS_PARALLEL_REPLICAS_PROTOCOL_VERSION)。バージョン 8 では MergeTreeAllRangesAnnouncementResponse (client パケット 14) が追加されます。ネゴシエートされた parallel-replicas バージョンが ≥ 8 の場合、initiator は Default モードではない各 follower announcement に対して、その stream の authoritative な parts 一覧を返信し、follower は read request を発行する前にそれを待機します。8 未満では announcement は fire-and-forget です。
INTERSERVER_EXTERNALLY_GRANTED_ROLES 54472 Query Query ボディに String external_roles フィールドが追加されます。位置は settings terminator と interserver-secret hash の間です。外部クライアントは空のロール一覧を送信します (1 バイトの 0x00、つまり String エンベロープ内の VarUInt 0) 。
V2_DYNAMIC_AND_JSON_SERIALIZATION 54473 Column body サーバーは Dynamic および JSON のカラム型に対して V2 シリアライゼーションを出力する場合があります。これにより、どの state_prefix バージョンを使用するかが制御されます。versioned types を参照してください。
SERVER_SETTINGS 54474 ServerHello サーバーは、デフォルト以外の settings を ServerHello の末尾、nonce の後ろに一覧として通知します。形式は、空の key で終端される (key, flags, value) の組です。Query パケットの settings 一覧と同じです。
QUERY_AND_LINE_NUMBERS 54475 ClientInfo ClientInfo の末尾に script_query_number (VarUInt) と script_line_number (VarUInt) を追加します。clickhouse-client が複数ステートメントのスクリプトでエラー箇所を特定するために使用します。外部クライアントは 0, 0 を送信します。
JWT_IN_INTERSERVER 54476 ClientInfo ClientInfo の末尾に、JWT の有無を示す UInt8 と、省略可能な String jwt を追加します。外部クライアント (JWT なし) はバイト 0x00 を送信します。 (C++ では DBMS_MIN_REVISON_WITH_JWT_IN_INTERSERVER と綴られています。定数名のスペルミスに注意してください。)
QUERY_PLAN_SERIALIZATION 54477 ServerHello, QueryPlan packet ServerHello は server settings の後ろに VarUInt query_plan_serialization_version を追加します。さらに、事前構築済みのクエリプランをサーバー間で配送するための ClientPacket::QueryPlan (コード 13) も導入されます。外部クライアントがこれを送信することはありません。
PARALLEL_BLOCK_MARSHALLING 54478 Block (Column) サーバーは、並列処理のためにカラムを ColumnBLOB (インライン圧縮) でラップする場合があります。これは、クエリで圧縮が有効かつ rows > 1 の場合にのみ適用されます。それ以外では通常のカラムのワイヤ形式が使われます。送信する Query パケットで圧縮を有効にしないクライアントでは、ワイヤ上の変更はありません。
VERSIONED_CLUSTER_FUNCTION_PROTOCOL 54479 ServerHello ServerHello の末尾に VarUInt cluster_function_protocol_version を追加します。これは *Cluster テーブル関数 (s3Cluster など) で使用されます。現在の値は 8 (DBMS_CLUSTER_PROCESSING_PROTOCOL_VERSION) です。バージョン 7 は private-repository の機能 (Iceberg compaction) 用に予約されており、8 ではサーバー間の cluster 読み取りタスクのペイロード (ReadTaskResponse ボディ。ここでは仕様を定義しません — 後述を参照) に、省略可能な read_source_index が追加されます。外部クライアントはデコードして無視します。
OUT_OF_ORDER_BUCKETS_IN_AGGREGATION 54480 BlockInfo BlockInfo のフィールドタグ付きストリームに field 3 (out_of_order_buckets: Vec<Int32>) を追加します。デコード形式は [VarUInt count][Int32]*count です。外部クライアントが自らこれを出力することはありません。デコーダーは、サーバーが送る空でない任意の一覧を読み取ります。
COMPRESSED_LOGS_PROFILE_EVENTS_COLUMNS 54481 Log, ProfileEvents, TableColumns サーバーは LogProfileEventsTableColumns の各パケットボディを compression frame でラップする場合があります。このバージョンでは、3 つすべてのボディが同じ任意圧縮の出力経路を通りますが、実際に compression frame になるのはクエリで compression = true の場合だけです。送信する Query パケットで圧縮を有効にしないクライアントでは、ワイヤ上の変更はありません。
REPLICATED_SERIALIZATION 54482 Block (Column) サーバーは、kind_stack 0x04 = REPLICATED を持つカラムを出力する場合があります。これは繰り返し値向けの Dictionary 風コンパクト形式です。kind_stack and sparse encoding を参照してください。このバージョン未満では、writer は送信前にそのようなカラムを展開していました。デコードは索引参照 (各行について elements[indexes[i]]) で行われます。leaf type と、Nullable/Array/Tuple/Map/Nested/LowCardinality の inner がサポートされます。
NULLABLE_SPARSE_SERIALIZATION 54483 Block (Column) スパースシリアライゼーションと Nullable(T) を組み合わせます。このバージョン未満では、writer は Nullable カラム向けの sparse を送信前に展開していました。v54483+ では、ワイヤデータは sparse-over-Nullable になります。kind_stack and sparse encoding を参照してください。
PROGRESS_IN_ASYNC_INSERT 54484 Progress (INSERT) 非同期 INSERT (async_insert = 1) では、insert がフラッシュされると、サーバーは EndOfStream の前に追加の Progress パケットを送信し、その後にその insert の ProfileEvents を送信します。これは、ネゴシエートされたバージョンが 54484 以上の場合に有効で、それ未満ではサーバーはこの末尾の Progress を省略します。Progress のワイヤ形式自体は変わらず、変わるのは送出される点だけです。実際には、この増分は経過時間を表します。書き込まれた行数カウンターは、付随する ProfileEvents を通じて報告されます。すでに Progress の割り込み受信を処理しているクライアントでは、形式の変更は不要で、追加の 1 パケットを許容するだけで済みます。
CLIENT_AGENT_IN_CLIENT_INFO 54485 ClientInfo ClientInfo の末尾に client_agent String を追加します。canonical client は環境から agent 識別子 (たとえば claude-codecursorgemini-cli、または AGENT 変数の値) を自動検出します。何も検出されなかった外部クライアントは空文字列を送信します。ネゴシエートされたバージョンが 54485 以上では必須です。これを省略すると Query パケットの残り部分との同期が崩れます。
INTERNAL_QUERY_FLAG 54486 ClientInfo ClientInfo の末尾に is_internal UInt8 を追加します。サーバー内部クエリ (ユーザーが発行したものではない) では 1 となり、リモートクエリにも伝播されるため、それらの system.query_log の行には internal ラベルが付きます。外部クライアントは 0 を送信します。ネゴシエートされたバージョンが 54486 以上では必須です。これを省略すると Query パケットの残り部分との同期が崩れます。
INTERSERVER_CURRENT_ROLES 54488 ClientInfo ClientInfo の末尾に current_roles の省略可能な String リストを追加します ([UInt8 present]、続いて 1 の場合は [VarUInt count][String]*count)。さらに、サーバー間クエリでは、シリアライズされたリストを含めるよう auth_hash を拡張します。initiator のアクティブ (有効化済み) なロール名を伝達するため、secondary node はユーザーのデフォルトロールにフォールバックせず、同じ方法で行ポリシーの範囲を設定します。サーバー間クエリでのみ設定され、かつ push_external_roles_in_interserver_queries = 1 の場合のみです。外部クライアントは 0 (不在) を送信します。ネゴシエートされたバージョンが 54488 以上では必須です。presence byte を省略すると Query パケットの残り部分との同期が崩れます。
CURRENT_AGGREGATION_VARIANT_SELECTION_METHOD 54489 集約 (二層バケット) 単一の String key に対する集約メソッドは enable_packed_string_keys_in_aggregation に従います。このリビジョン未満の peer は常に packed メソッドを使用し、この設定を認識しないため、それと二層バケットを交換すると同じ key が異なるバケットに配置されます。このような peer に対して initiator は安全側に失敗します。つまり、その peer の二層しきい値をゼロに設定し、単一層の Block を自ら再バケット化します。メソッドは 1 つのリリース内で変更される可能性があるため、バージョンではなく peer のリビジョンと比較されます。native-protocol のワイヤ形式に変更はありません。
HTTP_HANDLER_IN_CLIENT_INFO 54490 ClientInfo ClientInfo の HTTP 分岐で、http_referer の直後に http_handler_namehttp_request_url (いずれも String) を追加します。これらには、マッチした SQL 定義 HTTP ハンドラーの名前とリクエスト URL が格納されます。これにより、分散ハンドラークエリのリモートシャードでも currentHandler/currentRequestURL および http_handler_name/http_request_urlsystem.query_log カラムが設定されたままになります。query_interface = HTTP の場合にのみ書き込まれます (ハンドラー以外の HTTP クエリでは空文字列)。ネゴシエートされたバージョンが 54490 以上では必須です。これを省略すると Query パケットの残り部分との同期が崩れます。
QUANTILE_DETERMINISTIC_SKIP_DEGREE 54491 Block (Column) AggregateFunction(quantileDeterministic, ...) (およびその quantiles/median 表記) の state バージョンを 0 から 1 に引き上げます。これにより、シリアライズされた各 state の末尾に UInt8 skip_degree が追加されます。変更されるのはその 1 つの集約関数の state ペイロードのみです。カラムのフレーミングには変更がなく、他のすべての集約関数はそれぞれ独自のバージョンを維持します。このリビジョン未満では writer はバージョン 0 の state を出力するため、古い peer は影響を受けません。
STRING_WITH_SIZE_STREAM_SERIALIZATION 54492 Block (Column) String カラムは、値ごとの長さプレフィックス付きレイアウトから、累積バイトオフセットの別ストリーム (Array が使用するものと同じオフセットレイアウト) に切り替わり、そのまま送信されます: [UInt64 × num_rows offsets][data blob]。ネゴシエートされたリビジョンにより制御されます。54492 未満では、writer は値ごとの長さプレフィックスを使用します。ワイヤにはこれを示すカラムごとのマーカーはありません。両方の peer がリビジョンのみから判断します。String column layout を参照してください。

パケットのエンベロープ

送受信どちらの方向でも、通信上のすべてのメッセージは同じ外側の構造を持ちます:

[VarUInt: packet_type_code]    always encoded as VarUInt
[message body]                 format depends on packet_type_code

完全なパケット型の一覧は、packet type referenceにあります。

パケット型は固定幅のバイトではなく、VarUInt です。値が128未満の場合、VarUInt でも同じ1バイトになりますが、将来パケット型が128以上になっても互換性を保てるよう、実装では VarUInt エンコーディングを使用する必要があります。

message referenceでは、各パケットのボディ、つまりパケット型コードの後に続くバイト列のみを記載しています。フィールド番号は、最初のボディフィールドを1として始まります。

チャンク化フレーミング (v54470+)

CHUNKED_PROTOCOL 機能の使用がネゴシエートされると (ハンドシェイクを参照) 、wire 上のすべてのパケットはチャンク化フレーミングで包まれます。この包み方は方向ごとです。つまり、client→server と server→client は個別にネゴシエートされるため、異なるモード (チャンク化または非フレーム) になる場合があります。

パケットごとの wire レイアウト:

<chunk>...   one or more chunks; their payloads concatenated form the whole packet
[u32 LE = 0] zero-size terminator marking end of packet

chunkごとのWireレイアウト:

[u32 LE: chunk_size]   chunk_size in [1, UINT32_MAX]
[chunk_size bytes]     packet bytes (see note below)

パケット型 VarUInt は chunk 化されたストリームの内側にあります。つまり、フレーミングの前に別個のバイトとして送られるのではなく、パケットペイロードの先頭バイト (最初の chunk の先頭バイト) です。各パケットの chunk ペイロードは、packet envelope の完全な [VarUInt packet_type_code][message body] 全体です。パケット型を chunk 化されたストリームの外側に置くクライアントは、その型バイトを u32 の chunk サイズの先頭バイトとして相手に読ませてしまい、接続の同期が崩れます。

書き込み側のバッファがパケット途中でいっぱいになると、1 つのパケットが複数の chunk に分割されることがあります。分割位置はどこでもあり得るため、パケット型の VarUInt の途中で分割されることもあります。読み取り側は chunk ペイロードを連結し、末尾の 4 バイトのゼロを透過的なパケット境界として扱います。つまり、それ自体は消費しますが、パケットボディを読む側には見せません。

ボディを持たないパケットも引き続きラップされます。PingPong のような 1 バイトのパケットは、chunk 化がネゴシエートされると [u32 size = 1][0x04][u32 0] になります。このページの他の箇所で「wire 上では単一バイト」と説明しているものは、いずれも chunk 化前の形式を指します。

ネゴシエーション。 ServerHello と Addendum はそれぞれ、方向ごとに 1 つずつ、合計 2 つの String フィールドを持ち、その値は {"chunked", "notchunked", "chunked_optional", "notchunked_optional"} から取られます。

  • chunked / notchunked は厳格です。その側はそのモードを厳密に要求します。
  • _optional バリアントは柔軟で、相手側が選んだどちらのモードも受け入れます。

各方向で合意される値は、ペアごとに次のように計算されます。

Server pref Client pref Agreed
*_optional anything CLIENT に従う (その starts_with("chunked"))
anything *_optional SERVER に従う
chunked strict chunked strict chunked
notchunked strict notchunked strict notchunked
strict mismatch strict mismatch プロトコルエラー — 接続は MUST 切断される必要があります

クライアント側では、クライアントの SEND 設定がサーバーの RECV 設定とネゴシエートされ、逆方向も同様です。

タイミング。 ネゴシエーション文字列はフレーム化されていない wire 上を流れます: ClientHello → ServerHello (server prefs) → Addendum (client のネゴシエート済みの値) 。フレーミングへの切り替えは、Addendum が flush されたに送信されるすべてのバイトに適用されます。Addendum 自体、ClientHello、ServerHello は常にフレーム化されません。

接続ライフサイクル

接続は常に、HANDSHAKEREADYREADING_RESPONSE、または終了済みの4つの状態のいずれか1つにあります。プロトコルは多重化を行わないため、前のレスポンスを読み切る前にクライアントが新しいリクエストを送信すると、転送中のバイト列が交錯してストリームが破損します。

状態

stateDiagram-v2
    [*] --> HANDSHAKE: TCP connect

    HANDSHAKE --> READY: handshake ok
    READY --> READING_RESPONSE: send Query
    READING_RESPONSE --> READY: EndOfStream / Exception
    READY --> READY: Ping / Pong

    HANDSHAKE --> Terminated: handshake error
    READING_RESPONSE --> Terminated: protocol violation / I/O error
    READY --> Terminated: close
    Terminated --> [*]

正常系の流れはそのまま下に進みます — HANDSHAKE → READY → READING_RESPONSE → READYPing/Pong の自己ループがあり、すべての失敗エッジは単一の Terminated シンクに集約されます。

State Description
HANDSHAKE TCP connection が開いた直後の初期状態です。ハンドシェイク メッセージのみ有効です。成功すると READY に遷移し、失敗すると終了します。
READY アイドル状態です。クライアントは Pingクエリ を送信するか、接続を閉じることができます。connection は READY のまま無期限に維持される場合があります (idle_connection_timeout の制約を受けます。詳細は connection limits を参照してください) 。
READING_RESPONSE クライアントがクエリを送信したときに入る状態です。クライアントは READY に戻る前に、サーバーのレスポンスストリームを最後まで完全に読み切る必要があります。ここでクライアント→サーバー間で許可されるパケットは Cancel のみです (このページでは説明していません) 。
Terminated 以後は使用できません。クライアントは新しい TCP connection を開き、ハンドシェイク をやり直す必要があります。

ハンドシェイクフェーズ

認証を行い、プロトコルバージョンをネゴシエートします。これは各接続で、他のどの処理よりも前に、必ず一度だけ発生します。

TCP接続は確立された直後で、まだメッセージは一切やり取りされていません。流れは次のとおりです。

sequenceDiagram
    autonumber
    participant C as Client
    participant S as Server

    C->>S: ClientHello
    alt success
        S->>C: ServerHello
    else error
        S->>C: Exception — connection terminates
    end
    Note over C,S: negotiated_version = min(client, server)
    opt negotiated_version ≥ 54458
        C->>S: Addendum
    end
  1. クライアントは、サポートする最大のプロトコルバージョンを指定して ClientHello を送信します。

  2. クライアントは応答を読み取り、パケットタイプに応じて次のように処理を振り分けます。

    Packet type Action
    Hello (0) ServerHello をデコードします。negotiated_version = min(client_ver, server_ver) を計算します。ステップ 3 に進みます。
    Exception (2) Exception をデコードします。エラーとして返し、接続を終了します。
    anything else プロトコル違反です。接続を終了します。
  3. negotiated_version ≥ 54458 (ADDENDUM 機能) の場合、クライアントは Addendum を送信します。この判断は、クライアントが宣言したバージョンではなく、ネゴシエートされたバージョンに基づきます。

成功すると接続は READY に移行し、何らかのエラーが発生した場合は終了します。

Ping フェーズ

TCP keepalive とは独立した、アプリケーションレベルの生存確認です。Ping/Pong の往復が成功すれば、TCP接続が双方向で生きており、サーバーが応答可能であることを確認できます。Ping はステートレスで、どのクエリとも相関付けられていないため、連続する複数の Ping は互いに独立しています。

READY からのフローは次のとおりです。

sequenceDiagram
    autonumber
    participant C as Client
    participant S as Server

    C->>S: Ping (0x04)
    alt responsive
        S->>C: Pong (0x04)
    else error
        S->>C: Exception
    end
  1. クライアントは Ping を送信します。

  2. クライアントはレスポンスを読み取ります。

    Packet type Action
    Pong (4) 応答可能であることを確認し、READY に戻ります。
    Exception (2) Exception をデコードし、エラーとして返します。
    anything else プロトコル違反。

クエリフェーズ

クライアントは SQL ステートメントを送信し、サーバーは結果ブロックと実行テレメトリーをストリームで返します。レスポンスは一連のパケットで構成され、最後は必ず 1 つの EndOfStream または Exception で終わります。

READY から始まるフローは次のとおりです。

sequenceDiagram
    autonumber
    participant C as Client
    participant S as Server

    C->>S: Query message
    C->>S: External-table Data packets (0 or more)
    C->>S: Empty Data marker — end-of-client-data (required)
    S->>C: Data — header block (N cols, 0 rows)
    loop until EndOfStream or Exception
        S->>C: Progress / Log (interleaved)
        S->>C: Data — result block (N cols, M rows)
        S->>C: Totals / Extremes (aggregation queries)
        S->>C: ProfileInfo / ProfileEvents (profiling)
    end
    S->>C: Data — empty block (boundary marker)
    S->>C: Progress — final updates
    S->>C: EndOfStream — authoritative end of query

いずれかの時点でエラーが発生した場合、サーバーは EndOfStream の代わりに Exception を送信し、それによってクエリは終了します。

  1. クライアントは一意の query_id (通常は UUID) を付けて Query を送信します。

  2. クライアントは任意の 外部テーブル を送信し、その後に空の Data マーカーを送信します。空の Data パケットは table_name = "", num_columns = 0, num_rows = 0 です。サーバーは、このマーカーを受信するまでクエリの実行を開始しません。

  3. クライアントは READING_RESPONSE に移行し、書き込みバッファを flush します。

  4. クライアントは応答パケットをループで読み取り、type ごとに処理を振り分けます。

    Packet type Action
    Data (1) block をデコードします。最初の Data はスキーマヘッダーで、それ以降は結果 block (蓄積対象) です。空の block は境界マーカーです。num_rows == 0 はクエリ終了ではありません
    Progress (3) 実行メトリクス。各パケットは前回からの増分なので、ローカルで累積します。
    EndOfStream (5) クエリ完了。ループを抜けて READY に戻ります。
    ProfileInfo (6) 実行後の profiling データ。
    Totals (7) aggregation の totals block (Data と同じワイヤ形式) 。
    Extremes (8) 最小値/最大値の block (Data と同じワイヤ形式) 。
    Log (10) server log の 1 行。
    TableColumns (11) カラムのデフォルト値に関するメタデータ。
    ProfileEvents (14) パフォーマンス counters。
    Exception (2) デコードして error として返します。ループを抜けて READY に戻ります。
    anything else クエリ phase 中に現れるのは想定外です。connection を終了します。

EndOfStream または処理済みの Exception を受け取ると、connection は READY に戻ります。プロトコル違反または I/O error が発生した場合は終了します。

INSERT フェーズ

INSERT フェーズは、クエリフェーズに 2 回の追加のやり取りを加えたものです。クライアントが INSERT ステートメントを送信すると、サーバーはターゲットテーブルを示す スキーマブロック を返します。続いてクライアントは行を含む Data packets をストリーミングし、その後に空の Data マーカーを送信します。最後に、サーバーは EndOfStream または Exception を返して終了します。

READY から開始する場合、SQL は INSERT INTO <table> [(<cols>)] VALUES 形式の INSERT です。行データは Data packets を通じて送られるため、インラインの VALUES (...) リテラルは含みません。フローは次のとおりです。

sequenceDiagram
    autonumber
    participant C as Client
    participant S as Server

    C->>S: Query packet (INSERT body)
    C->>S: External-table Data packets (0 or more)
    Note over C,S: No empty Data marker here —<br/>it would end the row stream before it starts
    opt metadata before schema
        S->>C: TableColumns / Progress / ...
    end
    S->>C: Data packet — schema block (columns, 0 rows)
    Note over C,S: Schema block is the contract:<br/>rows sent next must match these column shapes
    loop one or more blocks
        C->>S: Data packet (rows N)
    end
    C->>S: Data packet — empty block (0 rows), end-of-input terminator
    loop until EndOfStream or Exception
        S->>C: Progress / ProfileInfo / Log / ProfileEvents
    end
    opt async_insert = 1 and negotiated_version ≥ 54484
        S->>C: trailing Progress, then insert ProfileEvents
    end
    S->>C: EndOfStream
  1. クライアントは、body を INSERT SQL に設定した Query を送信します。
  2. クライアントは、外部テーブル (INSERT ではまれ) もあれば送信します。クエリフェーズ とは異なり、ここでは空の Data マーカーは送信しませんINSERTQuery パケットは後続のデータを伴って送信されるため、データ終端を示す空の block は手順 5 まで送られません。これをスキーマ block より前に送ると、server はそれを行ストリームの終端として読み取り、行が 1 つもないまま INSERT を完了し、その後に到着する最初の実データの行パケットを、場違いなトップレベルパケットとして parse してしまいます。
  3. クライアントは、スキーマ Data パケットを読み取るまで、メタデータ パケット (TableColumns、Progress、ProfileInfo、Log、ProfileEvents) を順に受信します。これは 0 行ですが、完全なカラム構造 (名前と型) を持つ Block です。スキーマ block は契約です。次にクライアントが送信する行は、これらのカラムの shape に一致している必要があります。
  4. クライアントは data block を送信します。各 block について、VarUInt(ClientPacket::Data = 2)、次に空の external-table 名として String("")、その後に Block を書き込みます。カラム型は、スキーマ block のカラムと位置で一致している必要があります。
  5. クライアントは入力終端を送信します。これは空の Block (0 カラム、0 行) を持つ Data パケットです。
  6. クライアントは、EndOfStream (成功) または Exception (失敗) に達するまでレスポンスストリームを順に受信します。

非同期 INSERT (v54484+). クエリに async_insert = 1 が含まれている場合、server は行をキューに入れ、バッチの一部として flush します。ネゴシエートされた バージョン が 54484 以上 (PROGRESS_IN_ASYNC_INSERT) では、flush が完了すると server は追加の Progress パケットを送信し、その直後にその insert の ProfileEvents、続いて EndOfStream を送信します。54484 未満では、server はこの末尾の Progress を送信しません。このパケットは通常の Progress です。server は書き込み件数を反映する前にクエリ パイプラインをリセットするため、実際にはこの増分には経過時間しか含まれず、書き込まれた行数とバイト数の統計は付随する ProfileEvents を通じてクライアントに渡されます。手順 6 ですでに Progress の割り込み受信に対応しているクライアントであれば、追加の 1 パケットを受け取れるようにするだけで十分です。

接続は、EndOfStream または処理済みの Exception を受け取ると READY に戻ります。プロトコル違反や I/O エラーが発生した場合、接続は終了します。

メッセージリファレンス

フィールドは wire 順で記載しています。Type カラムでは次を使用します。

  • VarUInt — 可変長の符号なし整数 (VarUIntを参照) 。
  • String — VarUInt プレフィックス付きのバイト列 (Stringを参照) 。
  • UInt8Int32 など — 固定幅の little-endian 整数。
  • Bool — 1 バイトで、0x00 または 0x01 です。

Role カラムは、各フィールドを誰が使用するかを示します。

  • client — 外部クライアントが設定します。
  • inter-server — サーバー間通信でのみ意味があります。外部クライアントはデフォルト値を書き込みます。
  • universal — 両方で使用されます。

これらの表で記載しているのは、各パケットのパケット種別コードに続くボディのみです。

ClientHello (パケットタイプ 0)

クライアント → サーバー。TCP接続の確立後に送信される最初のメッセージです。

# フィールド Type ロール 説明
1 client_name String universal クライアント識別子 (例: "clickhouse-client")
2 version_major VarUInt universal クライアントのメジャーバージョン
3 version_minor VarUInt universal クライアントのマイナーバージョン
4 protocol_version VarUInt universal クライアントがサポートする最大のプロトコルバージョン
5 database String universal デフォルトのデータベース名
6 user String universal 認証用のユーザー名。空の場合はサーバーのデフォルトセッションユーザー (default_session_user サーバー設定。26.8より前のサーバーでは拒否されます) を意味します。認証を参照してください。
7 password String universal パスワード (平文)

ServerHello (パケットタイプ 0)

Server → Client。認証に成功した場合の ClientHello への応答です。

# Field Type Role Condition Description
1 server_name String universal always サーバー識別子
2 version_major VarUInt universal always サーバーのメジャーバージョン
3 version_minor VarUInt universal always サーバーのマイナーバージョン
4 protocol_version VarUInt universal always サーバーのプロトコルバージョン
4a parallel_replicas_protocol_version VarUInt universal VERSIONED_PARALLEL_REPLICAS_PROTOCOL (v54471) サーバーの parallel-replicas 協調プロトコルバージョン。Wire 上の位置: protocol_version の直後timezone の前。現在値: 8
5 timezone String universal TIMEZONE (v54058) サーバータイムゾーン (例: "UTC")
6 display_name String universal DISPLAY_NAME (v54372) 人間が読めるサーバー名
7 version_patch VarUInt universal VERSION_PATCH (v54401) サーバーのパッチバージョン
8 proto_send_chunked_srv String universal CHUNKED_PROTOCOL (v54470) サーバーが優先する送信方向のチャンク化。"chunked""notchunked""chunked_optional""notchunked_optional" のいずれかです。チャンク化フレーミング を参照してください。バージョンゲートの値はより高いにもかかわらず、wire 上では password_complexity_rules より前に置かれます。
9 proto_recv_chunked_srv String universal CHUNKED_PROTOCOL (v54470) サーバーが優先する受信方向のチャンク化。値のセットはフィールド 8 と同じです。
10 password_complexity_rules Rule[] universal PASSWORD_COMPLEXITY_RULES (v54461) サーバーのパスワードポリシーです。VarUInt count の後に count × Rule が続きます。以下を参照してください。
11 nonce UInt64 inter-server INTERSERVER_SECRET_V2 (v54462) 8 バイト LE のランダム nonce。サーバーの inter-server クエリ署名スキームで使用されます。外部クライアントはこれをデコードし (ストリームの整合を保つため) 、値自体は無視することが推奨されます。
12 server_settings Setting[] universal SERVER_SETTINGS (v54474) サーバーの非デフォルト設定の通知です。フォーマットは、0 個以上の (String key, VarUInt flags, String value) の組で、空の key で終端します。Query packet の settings list と同じです。
13 query_plan_serialization_version VarUInt universal QUERY_PLAN_SERIALIZATION (v54477) サーバーがサポートする query-plan シリアル化バージョン。外部クライアントはデコードして無視します。
14 cluster_function_protocol_version VarUInt universal VERSIONED_CLUSTER_FUNCTION_PROTOCOL (v54479) サーバーの *Cluster テーブル関数プロトコルバージョン。現在値: 8。この値は inter-server のクラスター read-task payload (それ以外は未規定の ReadTaskResponse ボディ) に追加されるフィールドを制御します。バージョン 7 は private-repository の機能 (Iceberg compaction) 用に予約されており、8 では任意の read_source_index が追加されます。外部クライアントはクラスター読み取りには関与しないため、このフィールドはデコードして無視します。

Rulepassword_complexity_rules の要素:

# Field Type Description
1 pattern String 適合するパスワードが一致しなければならない正規表現パターン。
2 message String パスワードがこのルールに違反したときに表示される、人間が読める説明。

このリストはサーバー運用者のパスワードポリシー設定を反映したもので、あくまで参考情報です。サーバーはハンドシェイク中にこれらのルールを強制しません。パスワードの変更または設定機能を提供するクライアントは、準拠していないパスワードをサーバーに送信する前に、これらのルールを使ってエラーを示すことができます。

追補 (パケットタイプなし)

クライアント → サーバー。ADDENDUM (v54458) によって有効化され、ハンドシェイクのやり取りが完了した直後に送信されます。これは独立したパケットタイプではなく、各フィールドはパケットタイプを示すバイト接頭辞なしで、生のまま wire に載せられます。

# Field Type Role Condition Description
1 quota_key String universal always サーバー側のキー付きクォータに使用するリソースクォータキー。キー付きクォータを使用しないクライアントは空文字列を送信します。
2 proto_send_chunked String universal CHUNKED_PROTOCOL (v54470) クライアントがネゴシエートした Outbound chunking: "chunked" または "notchunked"。ServerHello の proto_recv_chunked_srv を基に算出されます。
3 proto_recv_chunked String universal CHUNKED_PROTOCOL (v54470) クライアントがネゴシエートした Inbound chunking。proto_send_chunked_srv を基に算出されます。
4 parallel_replicas_protocol_version VarUInt universal VERSIONED_PARALLEL_REPLICAS_PROTOCOL (v54471) クライアントがサポートする parallel-replicas 協調プロトコルのバージョン。分散クエリに参加しない外部クライアントであっても、サーバーの互換性チェックを通すために、有効なバージョン (現在は 8) を送信することが SHOULD です。

チャンク化フレーミング への切り替えは、この Addendum が flush された後 に適用されます。つまり、Addendum 自体はフレーム化されません。

Ping (パケットタイプ 4)

クライアント → サーバー。ボディはありません。パケットは、チャンク化フレーミング の前では 1 バイトの 0x04 のみです。chunking がネゴシエートされると、このバイトは chunk の 1 バイトのペイロードになります (チャンク化フレーミング を参照) 。

Pong (packet type 4)

サーバー → クライアント。ボディはありません。パケットは、チャンク化フレーミングの前は 1 バイトの 0x04 のみです。チャンク化がネゴシエートされると、このバイトは chunk の 1 バイトのペイロードになります (chunked framingを参照) 。

Exception (packet type 2)

サーバー → クライアント。いずれかのフェーズでサーバーがエラーに遭遇した場合に送信されます。

# フィールド ロール 説明
1 code Int32 universal エラーコード
2 name String universal Exception クラス (例: "DB::Exception")
3 message String universal 人間が読めるエラーメッセージ
4 stack_trace String universal サーバー側のスタックトレース
5 has_nested (obsolete) Bool universal 廃止された互換性用バイト。サーバーは常に false を書き込みます

クエリ (パケットタイプ 1)

クライアント → サーバー。

# フィールド ロール 条件 説明
1 query_id String universal always 一意のクエリ識別子 (UUID)
2 client_info ClientInfo universal CLIENT_INFO (v54032) ClientInfo を参照
3 settings Setting[] universal always Setting を参照。常に存在します (空のキーで終端) 。バージョンによる制限があるのは設定ごとのエンコーディングのみです。詳しくは Setting のエンコーディングに関する注記を参照してください。ネゴシエートされたバージョンが 54429 未満の場合、クライアントはこのフィールドを省略してはなりません。
3a external_roles String universal INTERSERVER_EXTERNALLY_GRANTED_ROLES (v54472) 外部から付与されたロール名をシリアライズした一覧。空の一覧 = String エンベロープに包まれたバイト 0x00 (VarUInt 0) (ワイヤ形式では [VarUInt 1][0x00]) 。外部クライアントは常に空を送信します。
4 auth_hash String inter-server INTERSERVER_SECRET (v54441) サーバー間認証ハッシュ。クラスターの生のシークレット ではありません。下記の Inter-server authentication を参照してください。外部クライアント (および任意の InitialQuery) は空文字列を送信します。
5 stage VarUInt universal always クエリ処理ステージ。0 = FetchColumns、1 = WithMergeableState、2 = Complete、3 = WithMergeableStateAfterAggregation、4 = WithMergeableStateAfterAggregationAndLimit、7 = QueryPlan。値 3/4 は分散クエリで現れ、7 はシリアライズされたクエリプランを伴います。外部クライアントは通常 2 を送信します。
6 compression VarUInt universal always 0 = 無効、1 = 有効
7 query_body String universal always SQL テキスト
8 parameters Parameter[] client PARAMETERS (v54459) Parameter を参照。空のキーで終端します。

ClientInfo (Query に埋め込み)

クライアント → サーバー。Query のボディ (フィールド 2) に埋め込まれます。CLIENT_INFO (v54032) で制御されます。 (ClientInfo 内の一部のフィールドは、以下の各フィールドの注記にあるとおり、以降のバージョンで制御されます。)

# フィールド Type ロール 条件 説明
1 query_kind UInt8 universal 常に 0 = NoQuery、1 = InitialQuery、2 = SecondaryQuery。外部クライアントは 1 を送信します。
2 initial_user String universal 常に クエリを開始したユーザー
3 initial_query_id String universal 常に 元のクエリ ID
4 initial_address String universal 常に 送信元クライアントのソケットアドレス。サーバーがこの値を名前解決することは決してありません (ホスト名やサービス名のルックアップは行いません) 。SECONDARY_QUERY の場合 (この値は保持され、たとえば system.query_log や inter-server 認証で使用されます) 、受け入れられる形式は IPv4 の a.b.c.d:port または角括弧付き IPv6 の [addr]:port です。このとき、host は IP リテラル、port は 0..65535 の 10 進数である必要があります。その他の形式 (たとえば localhost:9000host:http:9000、または /tmp/ch.sock のような UNIX ソケットパス) は INCORRECT_DATA で拒否されます。INITIAL_QUERY の場合、サーバーはこのフィールドを実際の peer アドレスで上書きするため、任意の値を受け付けます (ip:port の単純な形式ではない値は、デフォルトの 0.0.0.0:0 に置き換えられます) 。外部クライアントは自身の ip:port を送信する必要があります。
5 initial_time Int64 client INITIAL_QUERY_START_TIME (v54449) クエリの開始時刻 (マイクロ秒) 。固定長 8 バイトで、VarUInt ではありません
6 query_interface UInt8 universal 常に 1 = TCP、2 = HTTP
7 os_user String client interface = TCP の場合 OS のユーザー名
8 client_hostname String client interface = TCP の場合 クライアントマシンのホスト名
9 client_name String client interface = TCP の場合 クライアントアプリケーション名
10 version_major VarUInt universal interface = TCP の場合 Client major version
11 version_minor VarUInt universal interface = TCP の場合 Client minor version
12 protocol_version VarUInt universal interface = TCP の場合 送信元クライアント自身の TCP プロトコルバージョン (DBMS_TCP_PROTOCOL_VERSION) であり、ネゴシエートされたバージョンではありません。peer revision はどのフィールドが存在するかを決めるだけで、この値はイニシエーターにコンパイル時に組み込まれたバージョンです。そのため、新しいクライアントが古いサーバーと通信する場合、ネゴシエートされたサーバー revision より大きくなることがあります。
13 quota_key String universal QUOTA_KEY_IN_CLIENT_INFO (v54060) サーバー側のキー付きクォータで使用するリソースクォータキー。キー付きクォータを使用しないクライアントは空文字列を送信します。
14 distributed_depth VarUInt inter-server DISTRIBUTED_DEPTH (v54448) Distributed クエリのネスト深度。外部クライアントは 0 を送信します。
15 version_patch VarUInt universal VERSION_PATCH (v54401), TCP のみ Client パッチバージョン
16 open_telemetry (below) client OPEN_TELEMETRY (v54442) トレースコンテキスト。トレーシングを使用しないクライアントは 0 を送信します。
17 collaborate_with_initiator VarUInt inter-server PARALLEL_REPLICAS (v54453) Bool を VarUInt として表したものです。外部クライアントは 0 を送信します。
18 count_participating_replicas VarUInt inter-server PARALLEL_REPLICAS (v54453) 外部クライアントは 0 を送信します。
19 number_of_current_replica VarUInt inter-server PARALLEL_REPLICAS (v54453) 外部クライアントは 0 を送信します。
20 script_query_number VarUInt client QUERY_AND_LINE_NUMBERS (v54475) 複数ステートメントのスクリプト内における、1 始まりのステートメント位置。外部クライアントは 0 を送信します。
21 script_line_number VarUInt client QUERY_AND_LINE_NUMBERS (v54475) ソーススクリプト内における、1 始まりの行番号。外部クライアントは 0 を送信します。
22 jwt_present UInt8 inter-server JWT_IN_INTERSERVER (v54476) 0 = JWT なし、1 = この後に JWT が続きます。JWT 認証を使用しない外部クライアントは 0 を送信します。
23 jwt String inter-server JWT_IN_INTERSERVER (v54476), if jwt_present=1 JWT Bearer token。フィールド 22 が 1 の場合にのみ存在します。
24 client_agent String client CLIENT_AGENT_IN_CLIENT_INFO (v54485) 末尾のフィールド。環境から自動検出されたクライアントツール/エージェントの識別子 (例: claude-codecursorgemini-cli、または AGENT 環境変数) 。検出されたエージェントがない外部クライアントは空文字列を送信します。通常の Query パスでは、ネゴシエートされたバージョンが 54485 以上になると存在します (TCP のみでなく、すべてのインターフェイスで送信されます) 。
25 is_internal UInt8 client INTERNAL_QUERY_FLAG (v54486) 末尾のフィールド。サーバー内部のクエリ (ユーザーが発行したものではない) の場合は 1 で、リモートクエリにも伝播され、system.query_log で内部クエリとしてラベル付けされます。query_kind (フィールド 1) とは独立しています。外部クライアントは 0 を送信します。ネゴシエートされたバージョンが 54486 以上になると存在します (TCP のみでなく、すべてのインターフェイスで送信されます) 。
26 current_roles UInt8 [+ String リスト] inter-server INTERSERVER_CURRENT_ROLES (v54488) 末尾のフィールド。[UInt8 present] で、present = 1 の場合はイニシエーターのアクティブ (有効) なロール名の [VarUInt count][String]*count リストが続きます。セカンダリノードは、ユーザーのデフォルトロールではなくイニシエーターのロールに基づいて行ポリシーの適用範囲を設定できます。inter-server クエリで、かつ push_external_roles_in_interserver_queries = 1 の場合にのみ設定されます。それ以外の場合、および外部クライアントでは、present = 0 です。ネゴシエートされたバージョンが 54488 以上では、この存在バイトが必要です (TCP のみでなく、すべてのインターフェイスで送信されます) 。inter-server クエリでは、シリアル化されたリストも auth_hash に組み込まれます (Inter-server authentication を参照) 。

OpenTelemetry エンコーディング (フィールド 16) :

[UInt8: has_trace]              0 = no trace data follows, 1 = trace data follows
If has_trace == 1:
  [16 bytes: trace_id]          byte-swapped per-8-bytes
  [8 bytes:  span_id]           byte-swapped
  [String:   trace_state]       W3C trace state
  [UInt8:    trace_flags]       W3C trace flags

Inter-server authentication

Query のフィールド 4 (auth_hash) は、通信上でやり取りされる共有クラスターシークレット ではありません。生のシークレットを送信すると、認証に失敗するだけでなく、シークレット自体も漏えいしてしまいます。代わりに、inter-server クライアントとして動作するサーバーは、ソルト付き SHA-256 ハッシュを使ってシークレットを知っていることを証明します。

  1. inter-server モードに入ります。 接続するサーバーは、ClientHello 内でそのことを通知します。user フィールドには inter-server マーカーが入り、password は空です。続いて、同じ ClientHello パケットの一部として、user/password フィールドの直後に 2 つの文字列、つまりクラスター名と新たに生成した 32 バイトの salt (ランダムな値の encodeSHA256) を追加します。サーバーは ServerHello を送信する前にこの 2 つの文字列を読み取るため、クライアントはそれらを先に書き込む必要があります。先に ServerHello を待つとデッドロックになります。サーバーがそれらの読み取りで待機しているためです。
  2. nonce を取得します。 INTERSERVER_SECRET_V2 (v54462) がネゴシエートされると、ServerHello には 8 バイトの UInt64 nonce が含まれます。
  3. ハッシュを計算します。 InitialQuery ではないすべての Query パケットについて、クライアントはフィールド 4 に encodeSHA256(salt + nonce + cluster_secret + query + query_id + initial_user + external_roles + current_roles) を書き込みます。これは 32 バイトのダイジェストです。 (nonce は 10 進文字列形式で、v54462 以上がネゴシエートされた場合にのみ含まれます。external_rolesINTERSERVER_EXTERNALLY_GRANTED_ROLES (v54472) がネゴシエートされた場合にのみ追加されます。current_roles は、プレゼンスバイトを含まない [VarUInt count][String]*count のシリアル化されたロール名リストであり、INTERSERVER_CURRENT_ROLES (v54488) がネゴシエートされ、リストが存在する場合にのみ追加されます。) InitialQuery の場合、またはクラスターシークレットが設定されていない場合、クライアントは代わりに空文字列を書き込みます。
  4. 検証します。 サーバーはフィールド 4 を最大 32 バイトで読み取り、自身が保持するクラスターシークレットのコピーを使って同じ連結内容を再計算します。ダイジェストが一致しない場合、接続は拒否されます。

外部 (inter-server ではない) クライアントはこのモードに入ることはなく、常に空の auth_hash を送信します。

設定

Query ボディの設定リスト (Query パケットのフィールド 3) にインラインでエンコードされます。このリストは、ネゴシエーションされたバージョンに関係なく常に存在し、空の key を持つ Setting、つまり後続に flags も value も続かない単一の VarUInt 0 で終端されます。setting ごとのエンコードだけが、SETTINGS_SERIALIZED_AS_STRINGS (v54429) を境に、ネゴシエーション済みバージョンに応じて変わります。

v54429+ (STRINGS_WITH_FLAGS) — 各 setting は、ここに示す 3 つ組です。

# フィールド Type ロール 説明
1 key String universal Setting 名。空ならリスト終端。
2 flags VarUInt universal メタデータのビットフラグ。以下を参照。
3 value String universal 文字列として表した Setting の値

key が空の場合、フィールド 2 と 3 は存在しません。

Pre-54429 (BINARY) — 各 setting は [String key][type-specific binary value] です。flags フィールドは書き込まれず、値は 10 進数やテキスト文字列ではなく、setting 固有のネイティブなバイナリ形式 (たとえば固定幅整数や長さプレフィックス付き文字列) でエンコードされます。リストは引き続き空の key で終端されます。ネゴシエーションされたバージョンが 54429 未満のクライアントは、上記の 3 つ組ではなく、このバイナリ形式を読み書きしなければなりません。 (ただし、ユーザー定義の custom setting は例外で、どちらのエンコーディングでも常に flags と文字列値を持ちます。)

flags フィールドには次が格納されます。

  • 0x01Important: この setting はクエリ結果に影響するため、古い peer によって黙って無視されてはなりません。
  • 0x02Custom: ユーザー定義の custom setting。
  • 0x0c — 独立したフラグではなく 2-bit tier フィールドです: 0x00 = Production、0x04 = 廃止された、0x08 = Experimental、0x0c = ベータ。2 ビットすべて (flags & 0x0c) を読んでください。単純に flags & 0x04 で判定すると、ベータ (0x0c) を廃止されたものと誤分類してしまいます。
  • 0x80HotReload (再起動なしでの config 再読み込み。flags enum で定義されており、主に 協調 settings で見られます) 。

パラメータ

SELECT {x:UInt64} のようなパラメータ化クエリで使用するクエリパラメータです。Custom フラグ (0x02) が設定された Setting と同じ形式でエンコードされ、同様に空の key で終端されます。

# フィールド 役割 説明
1 key String クライアント パラメータ名。空 = リストの終端。
2 flags VarUInt クライアント 常に 0x02 (Custom)
3 value String クライアント 文字列としてのパラメータ値。クォートについては以下の注記を参照してください。

Data (パケットタイプ 1 server→client、パケットタイプ 2 client→server)

双方向で使用されます。結果ブロック、INSERT データ、外部テーブル、データ終端マーカーを運びます。

ワイヤ形式は対称で、どちらの方向でも Block の前に table_name プレフィックスが含まれます。異なるのは パケットタイプ byte のみです。

[VarUInt: packet_type]     1 (server→client) or 2 (client→server)
[String:  table_name]      External table name; empty in most cases
[Block]                    See the Native Format spec for the Block layout
フィールド Type 役割 説明
table_name String 共通 外部テーブル名。空 ("") が一般的なケースで、メインテーブル、クエリ結果、INSERT の行ストリームがこれに該当します。table_name が空 ("") であること自体はデータ終端マーカーを意味しません (通常の INSERT 行パケットにも "" が含まれます) 。
Block body Block とカラムの構造 を参照してください。

データ終端マーカーは、table_name に関係なく、Block が空、つまり 0 カラムかつ 0 行のパケットです。サーバーは、デコードされたブロックが空 (block.empty()) の場合にのみ、クライアントの Data パケットを終端として扱います。table_name = "" でブロックが空でないパケットは、終端ではなく通常の行パケットです。したがって、INSERT の行ストリームは、空でない Data ブロックが連続し、その最後にストリームを終了する 1 つの空の Data ブロックが続く形になります。

ブロックの各種バリアントとその意味については、Block variants を参照してください。

Progress (パケットタイプ 3)

サーバー → クライアント。クエリの実行中に定期的に送信されます。すべてのフィールドは VarUInt で、各パケットに含まれるのは直前の Progress パケット以降の増分であり、累計値ではありません。送信前に、サーバーは counters を読み取ってアトミックに 0 にリセットし、elapsed_ns は前回送信からの経過時間の差分として計算します。したがってクライアントは、連続して届くパケットをローカルで必ず加算していく必要があります。パケットを絶対値として扱うと、複数のパケットが到着した時点で進捗表示が後戻りしたり、過少計上されたりします。

# フィールド ロール 条件 説明
1 rows VarUInt 共通 常時 前回のパケット以降に読み取られた行数 (実行中の合計に加算)
2 bytes VarUInt 共通 常時 前回のパケット以降に読み取られたバイト数 (実行中の合計に加算)
3 total_rows VarUInt 共通 常時 読み取る推定総行数への増分。累積すること (特定のパケットでは 0 の場合あり)
4 total_bytes VarUInt 共通 TOTAL_BYTES_IN_PROGRESS (v54463) 読み取る推定総バイト数への増分。累積します。wire 上では total_rowswrote_rows の間に置かれます。
5 wrote_rows VarUInt 共通 WRITE_CLIENT_INFO (v54420) 前回のパケット以降に書き込まれた行数 (INSERT 用) 。累積します
6 wrote_bytes VarUInt 共通 WRITE_CLIENT_INFO (v54420) 前回のパケット以降に書き込まれたバイト数 (INSERT 用) 。累積します
7 elapsed_ns VarUInt 共通 SERVER_QUERY_TIME_IN_PROGRESS (v54460) 前回のパケット以降に経過したナノ秒数 (クエリ全体の経過時間ではなく差分) 。累積します

ProfileInfo (パケットタイプ 6)

Server → Client。各クエリにつき1回、実行の終盤に送信されます。

# フィールド ロール 条件 説明
1 rows VarUInt universal always 処理された総行数
2 blocks VarUInt universal always 処理された総ブロック数
3 bytes VarUInt universal always 処理された総バイト数
4 applied_limit Bool universal always LIMIT 句が適用されたかどうか
5 rows_before_limit VarUInt universal always LIMIT 適用前の行数
6 obsolete Bool universal always 廃止された互換性バイト。サーバーはここに常に true を書き込み、クライアントは読み取り時にこれを破棄します。これは **「rows_before_limit が計算された」**ことを示すフラグではありません。意味のある limit の状態は、フィールド 4 (applied_limit) とフィールド 5 を組み合わせたものです。読み取って無視してください。
7 applied_aggregation Bool universal ROWS_BEFORE_AGGREGATION (v54469) GROUP BY が適用されたかどうか
8 rows_before_aggregation VarUInt universal ROWS_BEFORE_AGGREGATION (v54469) 集約前の行数

Totals (パケットタイプ 7)

サーバー → クライアント。WITH TOTALS を指定したクエリに対して送信されます。ワイヤ形式は Data と同一で、table_name 文字列 (常に空) の後に Block が続きます。異なるのはパケットタイプのバイトだけです。

[VarUInt: 7]                packet type
[String:  table_name]       always empty
[Block]                     see the Native Format spec

Extremes (パケットタイプ 8)

サーバー → クライアント。extremes 設定が有効な場合に送信されます。ワイヤ形式は Data と同一です。ブロックにはちょうど 2 行が含まれます。行 0 には各カラムの最小値が、行 1 には最大値が入ります。

[VarUInt: 8]                packet type
[String:  table_name]       always empty
[Block]                     num_rows = 2

Log (パケットタイプ 10)

サーバー → クライアント。クエリにアクティブなログキューがある場合に送信されます (send_logs_level 設定。詳細はログストリーミングを参照) 。

Data と同じエンベロープおよびボディのフォーマットです。この block は固定の num_columns = 8 と、あらかじめ定義されたスキーマを持ちます。各ログ行は 8 つのカラムすべてにまたがる 1 行で、1 つの Log パケットに複数の行が含まれる場合があります。

[VarUInt: 10]               packet type
[String:  table_name]       always empty
[Block]                     num_columns = 8, num_rows = number of log lines

以下の 8 つのカラム (この順序どおり) :

# Name Type Description
1 event_time DateTime イベントのタイムスタンプ (epoch からの秒数)
2 event_time_microseconds UInt32 マイクロ秒部分
3 host_name String ログを出力したサーバーのホスト名
4 query_id String このログが属するクエリ ID
5 thread_id UInt64 OS スレッド ID
6 priority Int8 ログレベル (Poco の priority: 1 = Fatal, … 8 = Trace, 9 = Test)
7 source String ロガー名
8 text String ログメッセージ本文

ProfileEvents (パケットタイプ 14)

サーバー → クライアント。クエリごとのパフォーマンスカウンターを格納します。

エンベロープとボディのフォーマットは Data と同じです。ブロックは固定の num_columns = 6 を持ち、スキーマは事前定義されています。各イベントは 1 行です。

[VarUInt: 14]               packet type
[String:  table_name]       always empty
[Block]                     num_columns = 6, num_rows = number of events

6つのカラム:

# 名前 説明
1 host_name String サーバーのホスト名
2 current_time DateTime イベントのタイムスタンプ
3 thread_id UInt64 スレッド ID
4 type Enum8 イベント種別: 1 = Increment (カウンター) 、2 = Gauge。基盤となるストレージでは符号付き 1 バイトです。
5 name String イベント名 (例: "Query""NetworkReceiveBytes")
6 value Int64 カウンター値または Gauge の値

TableColumns (パケットタイプ 11)

Server → Client。COLUMN_DEFAULTS_METADATA (v54410) によって制御されます。サーバーは、カラムのデフォルト値に関するメタデータを渡すために、INSERT のスキーマブロックの前にこれを送信します。ただし、ネゴシエートされたバージョンが 54410 以上 かつ input_format_defaults_for_omitted_fields 設定が有効な場合に限られます。54410 未満ではこのパケットは一切送信されないため、古い client はこれを待ってはなりません — スキーマ Data block が直接続きます。v54410+ の client は、どちらの順序にも対応できるようにしておく必要があります。つまり、オプションの TableColumns があり、その後にスキーマブロックが続く場合です。

# フィールド Type ロール 説明
1 external_table String universal 外部 table 名。空の場合はメイン table。
2 columns_description String universal テキスト形式のカラム定義。例: "id Int32, name String DEFAULT ''"。自由形式のテキストなので、文字列として parse してください。

TimezoneUpdate (パケット型 17)

Server → Client。TIMEZONE_UPDATES (v54464) で有効になります。送信されるのは厳密に 1 か所だけで、input table function の初期化時です (INSERT INTO <table> SELECT ... FROM input('<structure>') 形式のクエリで、client から行をストリーミングします) 。server は入力スキーマの Data block (INSERT phase を参照) を送信した直後に、クエリコンテキストにおける現在の session_timezone を含む TimezoneUpdate を送出します。これにより、client はこれから送信する行を同じ timezone で parse できます。server は、クエリの途中で任意に実行された SET session_timezone の変更に対してこのパケットを送出することはなく、後続の結果 block をどのような timezone でフォーマットすべきかを client に伝えるために送出することもありません。

# フィールド ロール 説明
1 timezone String 共通 新しい session のデフォルト timezone (例: "UTC""Europe/Berlin") 。

このパケットは 1 回だけ到着し、入力スキーマ block の直後、client が行 block の送信を開始する前に届きます。TimezoneUpdate を無視するデコーダーでも、wire の整合性を保つために末尾の String は必ず消費しなければなりません。

SSH チャレンジレスポンス認証 (パケットタイプ 11、12、18)

SSH_AUTHENTICATION (v54466) で有効になり、明示的に有効化した場合にのみ使用されます。ClientHello が user = " SSH KEY AUTHENTICATION " + <real_user> (先頭と末尾のスペースを含む) および password = "" を送信すると、接続は SSH フローに入ります。サーバーはこのプレフィックスを読み取り、取り除いて実際のユーザー名を復元し、チャレンジレスポンス方式に切り替えます。

Packet Code Direction Body
SSHChallengeRequest 11 Client → Server (ボディなし)
SSHChallenge 18 Server → Client String challenge — ランダムなバイト列。署名対象となる文字列を構成する要素の 1 つ (以下を参照)
SSHChallengeResponse 12 Client → Server String signature — 生の challenge に対するものではなく、以下で定義する連結結果に対する SSH 署名

このフローはパスワード認証の代わりに実行され、チャレンジレスポンスのやり取りは ServerHello より前に行われます。サーバーは認証が成功するまで Hello 応答を保留します。

  1. Client は、SSH マーカープレフィックスと空の password を含む ClientHello を送信します。

  2. Client は SSHChallengeRequest (パケット 11) を送信します。サーバーはまだ ServerHello を送信しておらず、先に認証を処理するため、このパケットを待ってここでブロックします。

  3. サーバーは、ランダムなバイト列を含む SSHChallenge (パケット 18) を返します。

  4. Client は署名対象の文字列を構築し、生の challenge ではなくその文字列に署名してから、署名を含む SSHChallengeResponse (パケット 12) を送信します。署名対象のメッセージは、次の 4 つの部分をこの厳密な順序で、区切り文字なしにバイト単位で連結したものです。

    to_sign = decimal(protocol_version) + default_database + user + challenge
    Part Source
    decimal(protocol_version) Client のプロトコルバージョンを 10 進 ASCII 文字列 で表したもの (例: "54466") — バージョン番号を文字列として表したものであり、VarUInt や固定幅整数ではありません。サーバーは ClientHello で受信したものと同じプロトコルバージョンを使って検証します。
    default_database ClientHellodatabase フィールド (ない場合は空文字列) 。
    user " SSH KEY AUTHENTICATION " のマーカープレフィックスを取り除いた実際のユーザー名 — サーバーがプレフィックスを除去して復元する名前と同じです。
    challenge SSHChallenge パケット内の生の challenge バイト列。
  5. サーバーは、ユーザーに登録された公開鍵に対して署名を検証し、同じ decimal(protocol_version) + default_database + user + challenge 文字列を再構築します。成功すると ServerHello を送信します。これはパスワードフローと同じ応答で、その後ハンドシェイクは通常どおり継続します (Addendum など) 。失敗した場合は Exception を返して接続を終了します。生の challenge バイト列だけに署名した Client は認証に失敗します。

sequenceDiagram
    autonumber
    participant C as Client
    participant S as Server

    C->>S: ClientHello (SSH marker user, empty password)
    C->>S: SSHChallengeRequest (11)
    Note over C,S: Server has NOT sent ServerHello yet —<br/>it authenticates first and blocks here
    S->>C: SSHChallenge (18) — random bytes to sign
    C->>S: SSHChallengeResponse (12) — signature over version+db+user+challenge
    alt signature verifies
        S->>C: ServerHello — handshake continues normally
    else verification fails
        S->>C: Exception — connection terminates
    end

SSH 認証を使用しない外部 client では、パケット 11、12、18 が現れることはありません。ユーザーが username のプレフィックスで明示的に有効化しない限り、これらが wire 上に出ることはありません。

MergeTreeAllRangesAnnouncementResponse (パケットタイプ 14)

Client → Server、サーバー間専用。parallel_replicas_protocol_version ≥ 8 の場合に有効です (VERSIONED_PARALLEL_REPLICAS_PROTOCOL を参照) 。外部クライアントがこのパケットを送信することはありません。

ネゴシエートされた parallel-replicas のバージョンが ≥ 8 の場合、フォロワーの MergeTreeAllRangesAnnouncement (パケットタイプ 15、server→client 方向) に対するイニシエーターのリクエスト/レスポンスサイクルは次のように変わります。

  1. フォロワーは自身の read pipeline を開き、MergeTreeAllRangesAnnouncement をイニシエーターに送信します。
  2. announcement の modeDefault 以外の場合のみ (WithOrder = 1 または ReverseOrder = 2。いずれも順序付き並列読み取り に使用) 、イニシエーターは MergeTreeAllRangesAnnouncementResponse を返します。mode = Default = 0 の場合、イニシエーターは何も返さず、フォロワーも待機しません。Default モードでは、各 MergeTreeReadTaskRequest ごとに範囲が割り当てられるため、事前のパーツ一覧は不要です。
  3. フォロワーは、最初の MergeTreeReadTaskRequest (server パケット 16 — follower→initiator に送信され、イニシエーターは MergeTreeReadTaskResponse (client パケット 10) で応答) を発行する前に、必要な場合はレスポンスを待機します。返されたパーツ一覧を使って、#split_i stream が所有するパーツだけを対象に source の構築を正確に絞り込みます。

バージョン 8 未満では、mode に関係なく announcement は fire-and-forget であり、フォロワーはローカルで認識しているすべてのパーツに対して source を構築します (従来の動作) 。

ボディ

# フィールド 説明
1 version Int64 (リトルエンディアン) 送信側の parallel-replicas プロトコルバージョンです。受信側の TCP リビジョンが DBMS_MIN_REVISION_WITH_VERSIONED_PARALLEL_REPLICAS_PROTOCOL (54471) 以上の場合、この値は DBMS_PARALLEL_REPLICAS_PROTOCOL_VERSION (現在は 8) と等しくなり、それ以外の場合は DBMS_MIN_SUPPORTED_PARALLEL_REPLICAS_PROTOCOL_VERSION (3) にフォールバックします。receiver は DBMS_MIN_SUPPORTED_PARALLEL_REPLICAS_PROTOCOL_VERSION 未満の値を拒否します。
2 parts RangesInDataPartsDescription マージコーディネーターがそのアナウンスのストリームに対して登録した、正とされるパーツの集合です。空のリストは、そのストリームがマージコーディネーター上に存在しないことを意味します (たとえば、follower がイニシエーターの作成数を超える分割を過剰にアナウンスした場合) 。この場合、そのストリームに対応する follower 側のプールは即座に完了済みとしてマークされます。
3 stream_id String このレスポンスが応答するアナウンスの stream_id をそのまま返します (分割トポロジが使われている場合は、テーブル名に #split_i 接尾辞を付けたもの) 。

RangesInDataPartsDescription ボディ

# フィールド 説明
1 count VarUInt この後に続くパーツディスクリプタの数です。デコーダは、100'000'000'000 を超える値を不正なものとして拒否します。
2 parts RangesInDataPartDescription repeated count times ディスクリプタです。マージコーディネーターへの登録順に並びます。

RangesInDataPartDescription ボディ

# フィールド Gate 説明
1 info MergeTreePartInfo universal パートの識別情報 (パーティション、ブロック範囲、レベル、ミューテーション) 。
2 ranges MarkRanges universal この stream が処理できる info 内のマーク範囲。空のリストは、そのパートは登録済みだが、現在は作業が割り当てられていないことを意味します。
3 rows VarUInt universal ranges がカバーする合計行数。
4 projection_name String DBMS_PARALLEL_REPLICAS_MIN_VERSION_WITH_PROJECTION (PR v5) プライマリパートの行では空で、それ以外の場合はプロジェクション名です。
5 min_marks_per_task VarUInt DBMS_PARALLEL_REPLICAS_MIN_VERSION_WITH_MIN_MARKS_PER_TASK (PR v6) フォロワーのプールがこのパートに対して 1 つの読み取りタスクにまとめるべきマーク数の下限。

MergeTreePartInfo ボディ

# フィールド 説明
1 version Int64 (little-endian) 常に DBMS_MERGE_TREE_PART_INFO_VERSION (1) です。デコーダはそれ以外の値を受け付けません。
2 partition_id String パーティション識別子 (例: パーティション化されていないテーブルでは "all"、またはパーティションキーのタプル式を文字列化した値) 。
3 min_block Int64 (little-endian) このパートのブロック範囲における最初のブロック番号。
4 max_block Int64 (little-endian) このパートのブロック範囲における最後のブロック番号 (この値を含む) 。
5 level UInt32 (little-endian) マージレベル。
6 mutation Int64 (little-endian) このパートを生成した mutation のバージョン (mutation が適用されていない場合は 0) 。
7 use_legacy_max_level Bool (text) 単一の ASCII バイト ('1' または '0') としてエンコードされます。パート名フォーマットとの後方互換性を保つための履歴上のフラグです。

MarkRanges ボディ

# フィールド 説明
1 size UInt64 (little-endian) 後続する mark-range ペアの数。注: little-endian の固定幅で、VarUInt ではありません
2 ranges (UInt64 begin, UInt64 end)size 回繰り返したもの (各 little-endian) 半開区間 [begin, end) の mark インターバル。

パケットタイプ リファレンス

Client → Server

Code Name ボディ形式 説明
0 Hello ClientHello ハンドシェイクの開始
1 Query Query クエリ実行リクエスト
2 Data Data データブロック (INSERT データ、外部テーブル、データ終了マーカー)
3 Cancel (ボディなし) 実行中のクエリをキャンセル
4 Ping Ping 疎通確認
5 TablesStatusRequest 未指定 テーブル状態の確認
6 KeepAlive 未指定 接続のキープアライブ
7 Scalar 未指定 スカラーデータブロック
8 IgnoredPartUUIDs 未指定 クエリから除外するパーツ
9 ReadTaskResponse 未指定 S3 クラスターの読み取りレスポンス
10 MergeTreeReadTaskResponse 未指定 並列読み取りタスクのレスポンス
11 SSHChallengeRequest SSH auth SSH 認証チャレンジリクエスト
12 SSHChallengeResponse SSH auth SSH 認証チャレンジレスポンス
13 QueryPlan 未指定 クエリプラン
14 MergeTreeAllRangesAnnouncementResponse MergeTreeAllRangesAnnouncementResponse フォロワーのMergeTreeAllRangesAnnouncementに対するイニシエーターの応答 (parallel_replicas_protocol_version ≥ 8 の場合に限る — VERSIONED_PARALLEL_REPLICAS_PROTOCOLを参照) 。サーバー間のみ — 外部クライアントが送信することはありません。

サーバー → クライアント

コード 名前 ボディ形式 説明
0 Hello ServerHello ハンドシェイク応答
1 Data Data 結果データブロック
2 Exception Exception エラー
3 Progress Progress クエリ実行の進行状況
4 Pong Pong 生存確認応答
5 EndOfStream (ボディなし) クエリ完了
6 ProfileInfo ProfileInfo 実行後のプロファイリングデータ
7 Totals Totals GROUP BY WITH TOTALS 行
8 Extremes Extremes 最小値/最大値 (2行ブロック)
9 TablesStatusResponse 未指定 テーブルステータス応答
10 Log Log クエリ実行時のログ行
11 TableColumns TableColumns デフォルト値用のカラム定義
12 PartUUIDs 未指定 一意のパートID
13 ReadTaskRequest 未指定 クラスター読み取りタスクのリクエスト
14 ProfileEvents ProfileEvents パフォーマンスカウンター
15 MergeTreeAllRangesAnnouncement 未指定 並列読み取りの初期化
16 MergeTreeReadTaskRequest 未指定 並列読み取りタスクの割り当て
17 TimezoneUpdate TimezoneUpdate サーバータイムゾーンの更新
18 SSHChallenge SSH auth SSH認証チャレンジ

設定

このセクションでは、ネイティブプロトコル接続の挙動を左右する調整項目について説明します。

以下のデフォルト値は最近のサーバーリリースに基づいていますが、バージョンやデプロイ環境によって異なる場合があります。

トランスポート層の設定

ソケットオプション

オプション デフォルト 適用側 説明
TCP_NODELAY on 両側 Nagle アルゴリズムを無効化します。小さなパケットは即座に送信されます。
SO_KEEPALIVE on (クライアント) 、OS のデフォルト (サーバー) 非対称 カーネルレベルの TCP keepalive プローブです。tcp_keep_alive_timeout > 0 の場合、クライアントはこれを明示的に有効にします。サーバーは OS のデフォルト設定を引き継ぎます。
SO_RCVBUF / SO_SNDBUF OS のデフォルト ソケットバッファのサイズです。プロトコル側では調整されません。

タイムアウト

Setting Default Unit Side Description
connect_timeout 10 クライアント 初回のTCP接続を確立するまでのタイムアウト。
handshake_timeout_ms 10000 ミリ秒 クライアント ハンドシェイク中にServerHelloを受信するまでのタイムアウト。
send_timeout 300 両方 この時間内に1バイトも書き込めない場合、接続は例外を送出します。
receive_timeout 300 両方 この時間内に1バイトも読み取れない場合、接続は例外を送出します。
tcp_keep_alive_timeout 290 クライアント OSが最初のTCPキープアライブプローブを送信するまでのアイドル時間。
receive_data_timeout_ms 2000 ミリ秒 クライアント レプリカから最初のDataパケットを受信するまでのタイムアウト。
connect_timeout_with_failover_ms 1000 ミリ秒 クライアント レプリカを順番に試行する際の、試行ごとの接続タイムアウト。
connect_timeout_with_failover_secure_ms 1000 ミリ秒 クライアント TLS経由でレプリカを順番に試行する際の、試行ごとの接続タイムアウト。
hedged_connection_timeout_ms 50 ミリ秒 クライアント ヘッジドリクエストにおける、試行ごとの接続タイムアウト。
poll_interval 10 サーバー サーバーのアイドル接続およびシャットダウンの確認ループの粒度。

タイムアウトは次のように入れ子になります:

tcp_keep_alive_timeout (290s)
      < receive_timeout (300s)
      < idle_connection_timeout (3600s)
      < tcp_close_connection_after_queries_seconds (0 = unlimited by default)

OS の keepalive が最初に働き、カーネルレベルで切断されたピアを暗黙のうちに検出できる場合があります。次の防御策は、アプリケーションの受信タイムアウトです。最後の手段として、アイドルタイムアウトが長時間使われていない接続を回収します。

接続の上限

設定 デフォルト 単位 対象 説明
max_connections 4096 件数 サーバー 最大同時 TCP 接続数。
idle_connection_timeout 3600 サーバー アイドル接続を開いたままにできる最大時間。
tcp_close_connection_after_queries_num 0 (無制限) 件数 サーバー 強制的に切断されるまでの、接続あたりの最大クエリ数。
tcp_close_connection_after_queries_seconds 0 (無制限) サーバー アクティビティの有無にかかわらない、接続の最大総寿命。

定期的にクエリを発行する接続は、無期限に維持できます。1 時間後に回収されるのはアイドル接続のみで、デフォルトでは接続の最大寿命は設定されていません。

アプリケーション層の設定

これらの設定は、クエリごとに Query パケットの設定リスト に含まれて送信されます。これにより、サーバーがワイヤ形式で送信する内容や、そのフレーミング方法が変わります。

圧縮

Setting Default Unit Description
network_compression_method "LZ4" string Query パケットの compression フラグが設定されている場合に使用される圧縮コーデック。値: "LZ4", "LZ4HC", "ZSTD", "NONE"
network_zstd_compression_level 1 1–15 network_compression_method == "ZSTD" の場合の ZSTD レベル。

Query パケット (フィールド 6) の compression フラグは、圧縮のオン/オフを切り替えます。これらの設定では、オンのときに使用するコーデックを選択します。

ログストリーミング

設定 デフォルト 単位 説明
send_logs_level "fatal" 文字列 最小ログレベル。値: "none", "fatal", "error", "warning", "information", "debug", "trace", "test"
send_logs_source_regexp "" 文字列 ロガーのソースに対する正規表現フィルター。空の場合はすべてのソースに一致します。

send_logs_level"none" 以外に設定すると、サーバーはクエリ実行中に Log パケットを出力します。

Progress レポート

設定 デフォルト 単位 説明
interactive_delay 100000 マイクロ秒 連続する Progress パケット間の目標最小間隔。

これは目標となる最小値であり、厳密な最大値ではありません。クエリの処理の進みが十分に速くない場合、サーバーは Progress パケットの送信頻度をさらに下げることがあります。

結果エンベロープ

Setting Default Unit Description
extremes false bool true の場合、サーバーは各カラムの最小値/最大値を含む Extremes パケットを送信します。
max_result_rows 0 (無制限) 件数 送信される行数の上限です。動作は result_overflow_mode によって制御されます。
max_result_bytes 0 (無制限) 非圧縮バイト数 非圧縮バイト数の上限です。動作は result_overflow_mode によって制御されます。
result_overflow_mode "throw" string "throw" は Exception でストリームを終了します。"break" は部分的な結果を送信した後、EndOfStream を送信します。

非同期 INSERT

設定 デフォルト 単位 説明
async_insert true bool true の場合、INSERT データはサーバー側でキューに追加され、バッチ処理されます。
wait_for_async_insert true bool true の場合 (async_insert が有効なとき) 、サーバーはキュー内のデータがフラッシュされるまで応答を保留します。
wait_for_async_insert_timeout 120 seconds サーバーがフラッシュを待機してから応答を返すまでの最大時間です。

分散トレーシング

設定 デフォルト 単位 説明
opentelemetry_start_trace_probability 0.0 0~1 の確率 サーバー側で応答テレメトリーに OpenTelemetry コンテキストを付加する確率。

対象外の設定

これらの設定はプロトコルレベルの設定と誤解されることがありますが、ワイヤ上の挙動ではなく、SQL の実行、ストレージ、または CPU 使用量を制御するものです。プロトコル実装側でこれらを特別に扱う必要はありません。

  • max_threads — クエリ実行時の並列度。
  • max_memory_usage — クエリごとのメモリ上限。
  • max_block_size, preferred_block_size_bytes — クエリ処理中の内部的な block サイズを指定します。ワイヤ上の block はこれらとは独立しています。
  • compile_expressions — JIT コンパイル。CPU にのみ影響します。
  • async_insert_max_data_size — server-side の queue バッファ。
  • input_format_*output_format_* のすべての設定 (input_format_native_* / output_format_native_* ファミリーは除く) — native 以外のものは、別のフォーマット (たとえば HTTP 経由) を選択または調整するためのものであり、ネイティブプロトコルの Data block は変更しません。

例外は *_native_* 設定です。これらはネイティブ TCP の Data block 内のバイト列を変更するため、プロトコル実装ではそれを考慮する必要があります。output_format_native_encode_types_in_binary_format は column の type フィールドをテキスト文字列からバイナリの型エンコーディングに切り替え、output_format_native_write_json_as_stringJSON カラムを String として出力し、output_format_native_use_flattened_dynamic_and_json_serialization は FLATTENED Dynamic/JSON レイアウトを選択します。これらはパケットのエンベロープではなく block のボディに影響するため、Native Format の仕様で規定されています。詳しくは、column wire layoutversioned types を参照してください。

用語集

キャンセル — 実行中のクエリを中止する、クライアント起点のパケット (type 3) です。このページでは詳しく規定していません。

クライアントデータ終端マーカー — 入力ストリームを閉じるためにクライアントが送信する空の Data パケット (0 カラム、0 行) です。その位置はクエリの種類によって異なります。

  • 通常のクエリ (SELECT など) : Query パケットと外部テーブル用の Data パケット群の後に送信し、「外部データはこれ以上ない」ことを示します。その後、サーバーが実行を開始します。
  • INSERT: クライアントはスキーマ前のマーカーを送信しません。最初にサーバーがスキーマブロックを送信し、次にクライアントが行の Data ブロックをストリーミングし、最後に行ストリームを終了するための空の Data パケットを送信します。スキーマブロックの前に空マーカーを送ると、行がそこで終了したものとして即座に解釈され、データが失われます。

Feature — 特定のプロトコルバージョンで導入されたワイヤ形式の変更です。交渉済みバージョンがその feature のバージョン以上であれば有効になります。versioning and feature gates を参照してください。

Inter-server — サーバー間の distributed queries でのみ意味を持つフィールド用のロールラベルです。外部クライアントはデフォルト値 (通常は空文字列、0、または false) を書き込みます。

交渉済みバージョンmin(client_version, server_version) です。handshake 中に計算されます。これにより、connection の存続期間中にどの features が有効かが決まります。

Packet — ワイヤメッセージです。先頭に VarUInt のパケット type code があり、その後に type に応じたフォーマットのボディが続きます。packet envelope を参照してください。

Packet type code — パケット先頭の VarUInt で、そのフォーマットを識別します。現在は 0–18 の値が割り当てられています。packet type reference を参照してください。

レスポンスストリーム — クエリ実行中にサーバーが出力するパケット列です。長さは未定で、終端は EndOfStream (成功) または Exception (失敗) のどちらか 1 つだけです。query phase を参照してください。

スキーマブロックINSERT phase 中にサーバーが送信する header block (カラムはあるが 0 行の Block) で、クライアントがデータを送信する前に、想定されるカラム shape を通知するものです。

設定リストQuery ボディ内の (key, flags, value) tuple の並びで、空の key で終端します。クエリごとのアプリケーション層 configuration を保持します。Setting を参照してください。

StageQuery パケット内の VarUInt フィールド (field 5) で、サーバーがクエリをどこまで実行するかを制御します。外部クライアントは通常 2 (Complete) を送信し、distributed queries やシリアライズ済みのクエリプランではより大きい値を使用します。wire 値の完全な一覧は Query の field 5 を参照してください。

Terminator — ストリームを終了するパケットです。Query のレスポンスは EndOfStream (成功) または Exception (失敗) で終わります。クライアントの入力ストリームは空の Data マーカーで終わります。

Navigation