メインコンテンツへスキップ
メインコンテンツへスキップ

Protobuf

入力出力エイリアス

説明

Protobuf フォーマットは Protocol Buffers フォーマットです。

このフォーマットでは外部のフォーマットスキーマが必要であり、そのスキーマはクエリ間でキャッシュされます。

ClickHouse は次をサポートしています:

  • proto2proto3 の両方の構文。
  • Repeated / optional / required フィールド。

テーブルのカラムと Protocol Buffers のメッセージ型のフィールドとの対応関係を決定するために、ClickHouse はそれらの名前を比較します。 この比較では大文字・小文字は区別されず、文字 _(アンダースコア)と .(ドット)は同一と見なされます。 カラムと Protocol Buffers のメッセージのフィールドの型が異なる場合は、必要な型変換が適用されます。

ネストされたメッセージもサポートされています。例えば、次のメッセージ型におけるフィールド z の場合:

message MessageType {
  message XType {
    message YType {
      int32 z;
    };
    repeated YType y;
  };
  XType x;
};

ClickHouse は x.y.z(または x_y_zX.y_Z など)という名前の列を探索します。

ネストされたメッセージは、ネストされたデータ構造 の入力または出力として適しています。

次に示すような protobuf スキーマで定義されたデフォルト値は適用されず、代わりに テーブルのデフォルト値 が使用されます。

syntax = "proto2";

message MessageType {
  optional int32 result_per_page = 3 [default = 10];
}

メッセージに oneof が含まれており、input_format_protobuf_oneof_presence が設定されている場合、ClickHouse は、どの oneof フィールドが見つかったかを示す列を設定します。

syntax = "proto3";

message StringOrString {
  oneof string_oneof {
    string string1 = 1;
    string string2 = 42;
  }
}
CREATE TABLE string_or_string ( string1 String, string2 String, string_oneof Enum('no'=0, 'hello' = 1, 'world' = 42))  Engine=MergeTree ORDER BY tuple();
INSERT INTO string_or_string from INFILE '$CURDIR/data_protobuf/String1' SETTINGS format_schema='$SCHEMADIR/string_or_string.proto:StringOrString' FORMAT ProtobufSingle;
SELECT * FROM string_or_string
   ┌─────────┬─────────┬──────────────┐
   │ string1 │ string2 │ string_oneof │
   ├─────────┼─────────┼──────────────┤
1. │         │ string2 │ world        │
   ├─────────┼─────────┼──────────────┤
2. │ string1 │         │ hello        │
   └─────────┴─────────┴──────────────┘

存在を示す列の名前は、oneof の名前と同一でなければなりません。入れ子になったメッセージもサポートされています(basic-examples を参照)。

許可される型は、Int8、UInt8、Int16、UInt16、Int32、UInt32、Int64、UInt64、Enum、Enum8 または Enum16 です。 Enum(および Enum8 または Enum16)は、oneof で取り得るすべてのタグに加えて、不在を示す 0 を含んでいる必要があります。文字列表現は任意です。

設定 input_format_protobuf_oneof_presence はデフォルトで無効になっています。

ClickHouse は、length-delimited 形式で protobuf メッセージを入力および出力します。 これは、各メッセージの前に、その長さを 可変長整数 (varint) として書き込む必要があることを意味します。

使用例

データの読み取りと書き込み

Example files

この例で使用するファイルは examples repository から入手できます。

この例では、ファイル protobuf_message.bin から ClickHouse テーブルにデータを読み込みます。その後、Protobuf フォーマットを使用して protobuf_message_from_clickhouse.bin というファイルに書き出します。

schemafile.proto というファイルを用意します。

syntax = "proto3";  
  
message MessageType {  
  string name = 1;  
  string surname = 2;  
  uint32 birthDate = 3;  
  repeated string phoneNumbers = 4;  
};
バイナリファイルの生成

すでに Protobuf 形式でデータをシリアライズおよびデシリアライズする方法をご存知の場合は、この手順はスキップできます。

ここでは Python を使用していくつかのデータを protobuf_message.bin にシリアライズし、それを ClickHouse に読み込みます。 別の言語を使用したい場合は、次も参照してください。「How to read/write length-delimited Protobuf messages in popular languages

次のコマンドを実行して、schemafile.proto と同じディレクトリに schemafile_pb2.py という名前の Python ファイルを生成します。 このファイルには、UserData Protobuf メッセージを表す Python クラスが含まれます。

protoc --python_out=. schemafile.proto

次に、schemafile_pb2.py と同じディレクトリに generate_protobuf_data.py という名前の新しい Python ファイルを作成します。 次のコードをそのファイルに貼り付けてください。

import schemafile_pb2  # Module generated by 'protoc'
from google.protobuf import text_format
from google.protobuf.internal.encoder import _VarintBytes # Import the internal varint encoder

def create_user_data_message(name, surname, birthDate, phoneNumbers):
    """
    Creates and populates a UserData Protobuf message.
    """
    message = schemafile_pb2.MessageType()
    message.name = name
    message.surname = surname
    message.birthDate = birthDate
    message.phoneNumbers.extend(phoneNumbers)
    return message

# The data for our example users
data_to_serialize = [
    {"name": "Aisha", "surname": "Khan", "birthDate": 19920815, "phoneNumbers": ["(555) 247-8903", "(555) 612-3457"]},
    {"name": "Javier", "surname": "Rodriguez", "birthDate": 20001015, "phoneNumbers": ["(555) 891-2046", "(555) 738-5129"]},
    {"name": "Mei", "surname": "Ling", "birthDate": 19980616, "phoneNumbers": ["(555) 956-1834", "(555) 403-7682"]},
]

output_filename = "protobuf_messages.bin"

# バイナリファイルを書き込みモード ('wb') で開く
with open(output_filename, "wb") as f:
    for item in data_to_serialize:
        # 現在のユーザー用の Protobuf メッセージインスタンスを作成する
        message = create_user_data_message(
            item["name"],
            item["surname"],
            item["birthDate"],
            item["phoneNumbers"]
        )

        # メッセージをシリアライズする
        serialized_data = message.SerializeToString()

        # シリアライズされたデータの長さを取得する
        message_length = len(serialized_data)

        # Protobuf ライブラリの内部 _VarintBytes を使用して長さをエンコードする
        length_prefix = _VarintBytes(message_length)

        # 長さのプレフィックスを書き込む
        f.write(length_prefix)
        # シリアライズされたメッセージデータを書き込む
        f.write(serialized_data)

print(f"Protobuf messages (length-delimited) written to {output_filename}")

# --- オプション: 検証 (読み戻して表示する) ---
# 読み戻しには、varint 用の内部 Protobuf デコーダも使用します。
from google.protobuf.internal.decoder import _DecodeVarint32

print("\n--- Verifying by reading back ---")
with open(output_filename, "rb") as f:
    buf = f.read() # varint のデコードを簡単にするため、ファイル全体をバッファに読み込む
    n = 0
    while n < len(buf):
        # varint の長さプレフィックスをデコードする
        msg_len, new_pos = _DecodeVarint32(buf, n)
        n = new_pos
        
        # メッセージデータを取り出す
        message_data = buf[n:n+msg_len]
        n += msg_len

        # メッセージをパースする
        decoded_message = schemafile_pb2.MessageType()
        decoded_message.ParseFromString(message_data)
        print(text_format.MessageToString(decoded_message, as_utf8=True))

ここで、コマンドラインからスクリプトを実行します。 例えば uv を使って、Python の仮想環境から実行することを推奨します。

uv venv proto-venv
source proto-venv/bin/activate

次の Python ライブラリをインストールする必要があります。

uv pip install --upgrade protobuf

バイナリファイルを生成するために、スクリプトを実行します。

python generate_protobuf_data.py

スキーマに一致する ClickHouse テーブルを作成します。

CREATE DATABASE IF NOT EXISTS test;
CREATE TABLE IF NOT EXISTS test.protobuf_messages (
  name String,
  surname String,
  birthDate UInt32,
  phoneNumbers Array(String)
)
ENGINE = MergeTree()
ORDER BY tuple()

コマンドラインからテーブルにデータを挿入します:

cat protobuf_messages.bin | clickhouse-client --query "INSERT INTO test.protobuf_messages SETTINGS format_schema='schemafile:MessageType' FORMAT Protobuf"

Protobuf 形式を使用して、データをバイナリファイルに書き出すこともできます。

SELECT * FROM test.protobuf_messages INTO OUTFILE 'protobuf_message_from_clickhouse.bin' FORMAT Protobuf SETTINGS format_schema = 'schemafile:MessageType'

Protobuf スキーマを使用して、ClickHouse からファイル protobuf_message_from_clickhouse.bin に書き出されたデータをデシリアライズできます。

ClickHouse Cloud を使用したデータの読み取りと書き込み

ClickHouse Cloud では、Protobuf スキーマファイルをアップロードすることはできません。ただし、クエリ内でスキーマを指定するために format_protobuf_schema 設定を使用できます。この例では、ローカルマシン上のシリアル化されたデータを読み取り、ClickHouse Cloud のテーブルに挿入する方法を示します。

前の例と同様に、ClickHouse Cloud 上で Protobuf のスキーマに従ってテーブルを作成します。

CREATE DATABASE IF NOT EXISTS test;
CREATE TABLE IF NOT EXISTS test.protobuf_messages (
  name String,
  surname String,
  birthDate UInt32,
  phoneNumbers Array(String)
)
ENGINE = MergeTree()
ORDER BY tuple()

設定 format_schema_source は、設定 format_schema のソースを定義します。

指定可能な値:

  • 'file' (デフォルト): Cloud ではサポートされていません
  • 'string': format_schema はスキーマ内容そのもの(リテラル)です。
  • 'query': format_schema はスキーマを取得するためのクエリです。

format_schema_source='string'

スキーマを文字列として指定して ClickHouse Cloud にデータを挿入するには、次のコマンドを実行します:

cat protobuf_messages.bin | clickhouse client --host <hostname> --secure --password <password> --query "INSERT INTO testing.protobuf_messages SETTINGS format_schema_source='syntax = "proto3";message MessageType {  string name = 1;  string surname = 2;  uint32 birthDate = 3;  repeated string phoneNumbers = 4;};', format_schema='schemafile:MessageType' FORMAT Protobuf"

テーブルに挿入されたデータを取得します:

clickhouse client --host <ホスト名> --secure --password <パスワード> --query "SELECT * FROM testing.protobuf_messages"
Aisha Khan 19920815 ['(555) 247-8903','(555) 612-3457']
Javier Rodriguez 20001015 ['(555) 891-2046','(555) 738-5129']
Mei Ling 19980616 ['(555) 956-1834','(555) 403-7682']

format_schema_source='query'

Protobuf スキーマをテーブルに保存することもできます。

データを挿入するためのテーブルを ClickHouse Cloud 上で作成します。

CREATE TABLE testing.protobuf_schema (
  schema String
)
ENGINE = MergeTree()
ORDER BY tuple();
INSERT INTO testing.protobuf_schema VALUES ('syntax = "proto3";message MessageType {  string name = 1;  string surname = 2;  uint32 birthDate = 3;  repeated string phoneNumbers = 4;};');

実行するクエリ内でスキーマを指定して、データを ClickHouse Cloud に挿入します:

cat protobuf_messages.bin | clickhouse client --host <ホスト名> --secure --password <パスワード> --query "INSERT INTO testing.protobuf_messages SETTINGS format_schema_source='SELECT schema FROM testing.protobuf_schema', format_schema='schemafile:MessageType' FORMAT Protobuf"

テーブルに挿入したデータを選択します:

clickhouse client --host <ホスト名> --secure --password <パスワード> --query "SELECT * FROM testing.protobuf_messages"
Aisha Khan 19920815 ['(555) 247-8903','(555) 612-3457']
Javier Rodriguez 20001015 ['(555) 891-2046','(555) 738-5129']
Mei Ling 19980616 ['(555) 956-1834','(555) 403-7682']

自動生成スキーマの利用

データ用の外部 Protobuf スキーマがない場合でも、自動生成されたスキーマを利用することで、Protobuf 形式でデータを出力/入力できます。 この場合は format_protobuf_use_autogenerated_schema 設定を使用します。

例:

SELECT * FROM test.hits format Protobuf SETTINGS format_protobuf_use_autogenerated_schema=1

この場合、ClickHouse はテーブル構造に従って関数 structureToProtobufSchema を使用し、Protobuf スキーマを自動生成します。その後、このスキーマを使ってデータを Protobuf 形式でシリアライズします。

自動生成されたスキーマを使って Protobuf ファイルを読み込むこともできます。この場合、そのファイルは同じスキーマを使用して作成されている必要があります。

$ cat hits.bin | clickhouse-client --query "INSERT INTO test.hits SETTINGS format_protobuf_use_autogenerated_schema=1 FORMAT Protobuf"

設定 format_protobuf_use_autogenerated_schema はデフォルトで有効になっており、format_schema が設定されていない場合に適用されます。

また、output_format_schema 設定を使用して、入出力時に自動生成されたスキーマをファイルに保存することもできます。たとえば、次のようにします。

SELECT * FROM test.hits format Protobuf SETTINGS format_protobuf_use_autogenerated_schema=1, output_format_schema='path/to/schema/schema.proto'

この場合、自動生成された Protobuf スキーマは path/to/schema/schema.capnp というファイルに保存されます。

Protobuf キャッシュの削除

format_schema_path から読み込まれた Protobuf スキーマを再読み込むには、SYSTEM DROP ... FORMAT CACHE ステートメントを使用します。

SYSTEM DROP FORMAT SCHEMA CACHE FOR Protobuf