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

> Документация по формату Protobuf

# Protobuf

| Ввод | Вывод | Псевдоним |
| ---- | ----- | --------- |
| ✔    | ✔     |           |

<div id="description">
  ## Описание
</div>

Формат `Protobuf` — это формат [Protocol Buffers](https://protobuf.dev/).

Для этого формата требуется внешняя схема, которая кэшируется между запросами.

ClickHouse поддерживает:

* оба синтаксиса: `proto2` и `proto3`.
* поля `Repeated`/`optional`/`required`.

Чтобы установить соответствие между столбцами таблицы и полями типа сообщения Protocol Buffers, ClickHouse сравнивает их имена.
Это сравнение регистронезависимо, а символы `_` (подчёркивание) и `.` (точка) считаются эквивалентными.
Если типы столбца и поля сообщения Protocol Buffers различаются, применяется необходимое преобразование.

Поддерживаются вложенные сообщения. Например, для поля `z` в следующем типе сообщения:

```capnp theme={null}
message MessageType {
  message XType {
    message YType {
      int32 z;
    };
    repeated YType y;
  };
  XType x;
};
```

ClickHouse пытается найти столбец с именем `x.y.z` (или `x_y_z`, `X.y_Z` и так далее).

Вложенные сообщения подходят для ввода и вывода [вложенных структур данных](/docs/ru/reference/data-types/nested-data-structures/index).

Значения по умолчанию, определённые в схеме protobuf, подобной приведённой ниже, не применяются; вместо них используются [значения по умолчанию таблицы](/docs/ru/reference/statements/create/table#default_values):

```capnp theme={null}
syntax = "proto2";

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

Если сообщение содержит [oneof](https://protobuf.dev/programming-guides/proto3/#oneof) и задан SETTING `input_format_protobuf_oneof_presence`, ClickHouse заполняет столбец, указывающий, какое поле oneof присутствует в сообщении.

```capnp theme={null}
syntax = "proto3";

message StringOrString {
  oneof string_oneof {
    string string1 = 1;
    string string2 = 42;
  }
}
```

```sql theme={null}
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
```

```text theme={null}
   ┌─────────┬─────────┬──────────────┐
   │ string1 │ string2 │ string_oneof │
   ├─────────┼─────────┼──────────────┤
1. │         │ string2 │ world        │
   ├─────────┼─────────┼──────────────┤
2. │ string1 │         │ hello        │
   └─────────┴─────────┴──────────────┘
```

Имя столбца, указывающего на наличие, должно совпадать с именем oneof.
Вложенные сообщения поддерживаются (см. [basic-examples](#basic-examples)). Пустые сообщения тоже поддерживаются.
Допустимые типы: Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Enum, Enum8 или Enum16.
Enum (а также Enum8 или Enum16) должен содержать все возможные теги oneof, а также 0 для обозначения отсутствия; строковые представления значения не имеют.

По умолчанию SETTING [`input_format_protobuf_oneof_presence`](/docs/ru/reference/settings/formats#input_format_protobuf_oneof_presence) отключена

ClickHouse принимает и выводит protobuf-сообщения в формате `с префиксом длины`.
Это означает, что перед каждым сообщением его длина должна быть записана как [целое число переменной длины (varint)](https://developers.google.com/protocol-buffers/docs/encoding#varints).

<div id="example-usage">
  ## Пример использования
</div>

<div id="basic-examples">
  ### Чтение и запись данных
</div>

<Info>
  **Файлы примера**

  Файлы, используемые в этом примере, доступны в [репозитории с примерами](https://github.com/ClickHouse/formats/ProtoBuf)
</Info>

В этом примере мы прочитаем данные из файла `protobuf_message.bin` в таблицу ClickHouse. Затем запишем их
обратно в файл `protobuf_message_from_clickhouse.bin` в формате `Protobuf`.

Дан файл `schemafile.proto`:

```capnp theme={null}
syntax = "proto3";

message MessageType {
  string name = 1;
  string surname = 2;
  uint32 birthDate = 3;
  repeated string phoneNumbers = 4;
};
```

<Accordion title="Создание двоичного файла">
  Если вы уже знаете, как сериализовать и десериализовать данные в формате `Protobuf`, можете пропустить этот шаг.

  Мы будем использовать Python, чтобы сериализовать данные в `protobuf_message.bin` и загрузить их в ClickHouse.
  Если вы хотите использовать другой язык, см. также: ["Как читать и записывать Protobuf-сообщения с префиксом длины на популярных языках"](https://cwiki.apache.org/confluence/display/GEODE/Delimiting+Protobuf+Messages).

  Выполните следующую команду, чтобы создать Python-файл с именем `schemafile_pb2.py` в
  том же каталоге, что и `schemafile.proto`. Этот файл содержит Python-классы,
  представляющие ваше Protobuf-сообщение `UserData`:

  ```bash theme={null}
  protoc --python_out=. schemafile.proto
  ```

  Теперь создайте новый Python-файл с именем `generate_protobuf_data.py` в том же
  каталоге, что и `schemafile_pb2.py`. Вставьте в него следующий код:

  ```python theme={null}
  import schemafile_pb2  # Модуль, созданный с помощью 'protoc'
  from google.protobuf import text_format
  from google.protobuf.internal.encoder import _VarintBytes # Импортируем внутренний кодировщик varint

  def create_user_data_message(name, surname, birthDate, phoneNumbers):
      """
      Создает и заполняет Protobuf-сообщение UserData.
      """
      message = schemafile_pb2.MessageType()
      message.name = name
      message.surname = surname
      message.birthDate = birthDate
      message.phoneNumbers.extend(phoneNumbers)
      return message

  # Данные пользователей для этого примера
  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)

          # Используем внутреннюю функцию _VarintBytes из библиотеки Protobuf для кодирования длины
          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() # Считываем весь файл в buffer для более простого декодирования 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))
  ```

  Теперь запустите скрипт из командной строки. Рекомендуется запускать его в
  виртуальном окружении Python, например с помощью `uv`:

  ```bash theme={null}
  uv venv proto-venv
  source proto-venv/bin/activate
  ```

  Вам потребуется установить следующие Python-библиотеки:

  ```bash theme={null}
  uv pip install --upgrade protobuf
  ```

  Запустите скрипт, чтобы создать двоичный файл:

  ```bash theme={null}
  python generate_protobuf_data.py
  ```
</Accordion>

Создайте таблицу ClickHouse, соответствующую схеме:

```sql theme={null}
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()
```

Вставьте данные в таблицу с помощью командной строки:

```bash theme={null}
cat protobuf_messages.bin | clickhouse-client --query "INSERT INTO test.protobuf_messages SETTINGS format_schema='schemafile:MessageType' FORMAT Protobuf"
```

Вы также можете записать данные обратно в двоичный файл в формате `Protobuf`:

```sql theme={null}
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`.

<div id="basic-examples-cloud">
  ### Чтение и запись данных с помощью ClickHouse Cloud
</div>

В ClickHouse Cloud нельзя загрузить файл схемы Protobuf. Однако можно использовать SETTING `format_protobuf_schema`,
чтобы указать схему в запросе. В этом примере мы покажем, как считать сериализованные данные с локальной
машины и вставить их в таблицу в ClickHouse Cloud.

Как и в предыдущем примере, создайте таблицу в ClickHouse Cloud в соответствии со схемой Protobuf:

```sql theme={null}
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()
```

SETTING `format_schema_source` задаёт источник для SETTING `format_schema`

Возможные значения:

* 'file' (по умолчанию): не поддерживается в Cloud
* 'string': `format_schema` содержит буквальное содержимое схемы.
* 'query': `format_schema` — это запрос для получения схемы.

<div id="format-schema-source-string">
  ### `format_schema_source='string'`
</div>

Чтобы выполнить вставку данных в ClickHouse Cloud, указав схему строкой, выполните:

```bash theme={null}
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"
```

Выберите вставленные в таблицу данные:

```bash theme={null}
clickhouse client --host <hostname> --secure --password <password> --query "SELECT * FROM testing.protobuf_messages"
```

```response theme={null}
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']
```

<div id="format-schema-source-query">
  ### `format_schema_source='query'`
</div>

Вы также можете хранить схему Protobuf в таблице.

Создайте в ClickHouse Cloud таблицу, в которую будут вставляться данные:

```sql theme={null}
CREATE TABLE testing.protobuf_schema (
  schema String
)
ENGINE = MergeTree()
ORDER BY tuple();
```

```sql theme={null}
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, указав схему в запросе, который нужно выполнить:

```bash theme={null}
cat protobuf_messages.bin | clickhouse client --host <hostname> --secure --password <password> --query "INSERT INTO testing.protobuf_messages SETTINGS format_schema_source='SELECT schema FROM testing.protobuf_schema', format_schema='schemafile:MessageType' FORMAT Protobuf"
```

Выберите данные, которые были вставлены в таблицу:

```bash theme={null}
clickhouse client --host <hostname> --secure --password <password> --query "SELECT * FROM testing.protobuf_messages"
```

```response theme={null}
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']
```

<div id="using-autogenerated-protobuf-schema">
  ### Использование автоматически созданной схемы
</div>

Если у вас нет внешней схемы Protobuf для ваших данных, вы всё равно можете выводить и читать данные в формате Protobuf,
используя автоматически сгенерированную схему. Для этого используйте SETTING `format_protobuf_use_autogenerated_schema`.

Например:

```sql theme={null}
SELECT * FROM test.hits format Protobuf SETTINGS format_protobuf_use_autogenerated_schema=1
```

В этом случае ClickHouse автоматически сгенерирует схему Protobuf в соответствии со структурой таблицы с помощью функции
[`structureToProtobufSchema`](/docs/ru/reference/functions/regular-functions/other-functions#structureToProtobufSchema). Затем эта схема будет использоваться для сериализации данных в формате Protobuf.

Вы также можете читать файл Protobuf с автоматически сгенерированной схемой. В этом случае файл должен быть создан с использованием той же схемы:

```bash theme={null}
$ cat hits.bin | clickhouse-client --query "INSERT INTO test.hits SETTINGS format_protobuf_use_autogenerated_schema=1 FORMAT Protobuf"
```

SETTING [`format_protobuf_use_autogenerated_schema`](/docs/ru/reference/settings/formats#format_protobuf_use_autogenerated_schema) включен по умолчанию и применяется, если [`format_schema`](/docs/ru/reference/settings/formats#format_schema) не задан.

Вы также можете сохранять автоматически сгенерированную схему в файл при вводе и выводе данных, используя SETTING [`output_format_schema`](/docs/ru/reference/settings/formats#output_format_schema). Например:

```sql theme={null}
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`.

<div id="drop-protobuf-cache">
  ### Очистка кэша protobuf
</div>

Чтобы перезагрузить схему Protobuf, загруженную из [`format_schema_path`](/docs/ru/reference/settings/server-settings/settings#format_schema_path), используйте оператор [`SYSTEM DROP ... FORMAT CACHE`](/docs/ru/reference/statements/system#system-drop-schema-format).

```sql theme={null}
SYSTEM DROP FORMAT SCHEMA CACHE FOR Protobuf
```
