> ## 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.

> Documentación del formato Protobuf

# Protobuf

| Entrada | Salida | Alias |
| - | - | - |
| ✔ | ✔ | |

## Descripción

El formato `Protobuf` es el formato de [Protocol Buffers](https://protobuf.dev/).

Este formato requiere un esquema de formato externo, que se mantiene en caché entre consultas.

ClickHouse admite:

* tanto la sintaxis `proto2` como la `proto3`.
* campos `Repeated`/`optional`/`required`.

Para encontrar la correspondencia entre las columnas de la tabla y los campos del tipo de mensaje de Protocol Buffers, ClickHouse compara sus nombres.
Esta comparación no distingue entre mayúsculas y minúsculas, y los caracteres `_` (guion bajo) y `.` (punto) se consideran equivalentes.
Si los tipos de una columna y de un campo del mensaje de Protocol Buffers son diferentes, se aplica la conversión necesaria.

Se admiten mensajes anidados. Por ejemplo, para el campo `z` en el siguiente tipo de mensaje:

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

ClickHouse intenta encontrar una columna llamada `x.y.z` (o `x_y_z`, `X.y_Z`, etc.).

Los mensajes anidados son adecuados para la entrada o salida de [estructuras de datos anidadas](https://clickhouse.com/docs/es/reference/data-types/nested-data-structures/index.md).

Para los campos **asignados** que no están presentes en los datos transmitidos:

* Las columnas asignadas ordinarias **no anulables** usan el valor predeterminado del campo del esquema protobuf (`proto2` `[default = …]`; de lo contrario, el valor predeterminado del tipo) durante el análisis, no la expresión `DEFAULT` de la tabla.
* Las columnas asignadas `Nullable(...)` se resuelven como `NULL` cuando el campo está ausente (no toman el valor predeterminado del campo o tipo protobuf).
* Con [`input_format_protobuf_flatten_google_wrappers`](https://clickhouse.com/docs/es/reference/settings/formats/input-format#input_format_protobuf_flatten_google_wrappers) habilitado para los envoltorios `google.protobuf.*Value`:
  * un envoltorio **ausente** en una columna `Nullable(...)` se trata como un campo externo ausente y se convierte en `NULL`;
  * un envoltorio **presente pero vacío** (`str {}`) conserva el valor predeterminado del escalar anidado (`''` / `0`);
  * una columna no anulable asignada a un envoltorio ausente obtiene el valor predeterminado del escalar anidado en lugar de `NULL`.

El `DEFAULT` de la tabla (y las expresiones predeterminadas) se aplican a las columnas de la tabla que no tienen **ningún** campo coincidente en el tipo de mensaje cuando [`input_format_defaults_for_omitted_fields`](https://clickhouse.com/docs/es/reference/settings/formats/input-format#input_format_defaults_for_omitted_fields) está habilitado (de forma predeterminada). Si ese SETTING es `0`, las columnas no asignadas conservan el valor predeterminado del tipo de datos insertado durante el análisis en lugar de la expresión `DEFAULT` de la tabla.

Ejemplo de un valor predeterminado de campo de un esquema proto2 (usado para un campo asignado ausente del mensaje):

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

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

Si un mensaje contiene [oneof](https://protobuf.dev/programming-guides/proto3/#oneof) y se establece `input_format_protobuf_oneof_presence`, ClickHouse completa la columna que indica qué campo de oneof se encontró.

```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        │
   └─────────┴─────────┴──────────────┘
```

El nombre de la columna que indica la presencia debe ser el mismo que el de `oneof`.
Se admiten mensajes anidados (consulte [ejemplos básicos](#basic-examples)). Los mensajes vacíos también se admiten.
Los tipos permitidos son Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Enum, Enum8 o Enum16.
Enum (así como Enum8 o Enum16) debe contener 0 para indicar ausencia y la etiqueta de cada caso de `oneof` que tenga una columna coincidente en la tabla de destino; las representaciones textuales no importan.
Para los miembros de mensajes `oneof` sin columnas coincidentes en la tabla, también se permiten etiquetas Enum faltantes. Si dicha rama está presente en la entrada, ClickHouse trata la presencia de `oneof` como omitida y escribe 0 en la columna de presencia.

El SETTING [`input_format_protobuf_oneof_presence`](https://clickhouse.com/docs/es/reference/settings/formats/input-format#input_format_protobuf_oneof_presence) está deshabilitado de forma predeterminada

ClickHouse recibe y envía mensajes protobuf en formato `length-delimited`.
Esto significa que, antes de cada mensaje, su longitud debe escribirse como un [entero de longitud variable (varint)](https://developers.google.com/protocol-buffers/docs/encoding#varints).

## Ejemplo de uso

### Lectura y escritura de datos

> [!NOTE]
>
> Los archivos usados en este ejemplo están disponibles en el [repositorio de ejemplos](https://github.com/ClickHouse/formats/ProtoBuf)

En este ejemplo, leeremos algunos datos de un archivo `protobuf_message.bin` a una tabla de ClickHouse. Luego los volveremos a escribir
en un archivo llamado `protobuf_message_from_clickhouse.bin` usando el formato `Protobuf`.

Dado el archivo `schemafile.proto`:

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

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

> **Generar el archivo binario**
>
> Si ya sabes cómo serializar y deserializar datos en el formato `Protobuf`, puedes omitir este paso.
>
> Usaremos Python para serializar algunos datos en `protobuf_message.bin` y leerlos en ClickHouse.
> Si quieres usar otro lenguaje, consulta también: ["How to read/write length-delimited Protobuf messages in popular languages"](https://cwiki.apache.org/confluence/display/GEODE/Delimiting+Protobuf+Messages).
>
> Ejecuta el siguiente comando para generar un archivo Python llamado `schemafile_pb2.py` en
> el mismo directorio que `schemafile.proto`. Este archivo contiene las clases de Python
> que representan tu mensaje `UserData` de Protobuf:
>
> ```bash theme={null}
> protoc --python_out=. schemafile.proto
> ```
>
> Ahora, crea un nuevo archivo Python llamado `generate_protobuf_data.py` en el mismo
> directorio que `schemafile_pb2.py`. Pega el siguiente código en él:
>
> ```python theme={null}
> import schemafile_pb2  # Módulo generado por 'protoc'
> from google.protobuf import text_format
> from google.protobuf.internal.encoder import _VarintBytes # Importa el codificador varint interno
>
> def create_user_data_message(name, surname, birthDate, phoneNumbers):
>     """
>     Crea y completa un mensaje UserData de Protobuf.
>     """
>     message = schemafile_pb2.MessageType()
>     message.name = name
>     message.surname = surname
>     message.birthDate = birthDate
>     message.phoneNumbers.extend(phoneNumbers)
>     return message
>
> # Los datos de nuestros usuarios de ejemplo
> 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"
>
> # Abre el archivo binario en modo de escritura binaria ('wb')
> with open(output_filename, "wb") as f:
>     for item in data_to_serialize:
>         # Crea una instancia de mensaje Protobuf para el usuario actual
>         message = create_user_data_message(
>             item["name"],
>             item["surname"],
>             item["birthDate"],
>             item["phoneNumbers"]
>         )
>
>         # Serializa el mensaje
>         serialized_data = message.SerializeToString()
>
>         # Obtén la longitud de los datos serializados
>         message_length = len(serialized_data)
>
>         # Usa _VarintBytes de la biblioteca Protobuf para codificar la longitud
>         length_prefix = _VarintBytes(message_length)
>
>         # Escribe el prefijo de longitud
>         f.write(length_prefix)
>         # Escribe los datos del mensaje serializado
>         f.write(serialized_data)
>
> print(f"Protobuf messages (length-delimited) written to {output_filename}")
>
> # --- Opcional: Verificación (volver a leer e imprimir) ---
> # Para volver a leer, también usaremos el decodificador interno de varints de Protobuf.
> from google.protobuf.internal.decoder import _DecodeVarint32
>
> print("\n--- Verifying by reading back ---")
> with open(output_filename, "rb") as f:
>     buf = f.read() # Lee todo el archivo en un búfer para facilitar la decodificación varint
>     n = 0
>     while n < len(buf):
>         # Decodifica el prefijo de longitud varint
>         msg_len, new_pos = _DecodeVarint32(buf, n)
>         n = new_pos
>
>         # Extrae los datos del mensaje
>         message_data = buf[n:n+msg_len]
>         n += msg_len
>
>         # Analiza el mensaje
>         decoded_message = schemafile_pb2.MessageType()
>         decoded_message.ParseFromString(message_data)
>         print(text_format.MessageToString(decoded_message, as_utf8=True))
> ```
>
> Ahora ejecuta el script desde la línea de comandos. Se recomienda ejecutarlo desde un
> entorno virtual de Python, por ejemplo con `uv`:
>
> ```bash theme={null}
> uv venv proto-venv
> source proto-venv/bin/activate
> ```
>
> Tendrás que instalar las siguientes bibliotecas de Python:
>
> ```bash theme={null}
> uv pip install --upgrade protobuf
> ```
>
> Ejecuta el script para generar el archivo binario:
>
> ```bash theme={null}
> python generate_protobuf_data.py
> ```

Crea una tabla de ClickHouse que coincida con el esquema:

```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()
```

Inserte los datos en la tabla desde la línea de comandos:

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

También puede volver a escribir los datos en un archivo binario con el formato `Protobuf`:

```sql theme={null}
SELECT * FROM test.protobuf_messages INTO OUTFILE 'protobuf_message_from_clickhouse.bin' FORMAT Protobuf SETTINGS format_schema = 'schemafile:MessageType'
```

Con el esquema Protobuf, ahora puede deserializar los datos que se escribieron desde ClickHouse en el archivo `protobuf_message_from_clickhouse.bin`.

### Lectura y escritura de datos con ClickHouse Cloud

Con ClickHouse Cloud no es posible cargar un archivo de esquema Protobuf. Sin embargo, puede usar la configuración `format_schema_source`
para especificar el esquema en la consulta. En este ejemplo, le mostramos cómo leer datos serializados desde su máquina local
e insertarlos en una table de ClickHouse Cloud.

Como en el ejemplo anterior, cree la table según el esquema de su archivo Protobuf en ClickHouse Cloud:

```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()
```

La configuración `format_schema_source` define la fuente de la configuración `format_schema`

Valores posibles:

* 'file' (predeterminado): no es compatible con Cloud
* 'string': `format_schema` es el contenido literal del esquema.
* 'query': `format_schema` es una consulta para obtener el esquema.

### `format_schema_source='string'`

Inserte los datos en ClickHouse Cloud, especificando el esquema como una cadena; ejecute:

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

Seleccione los datos insertados en la table:

```bash theme={null}
clickhouse client --host <hostname> --secure --password <password> --query "SELECT * FROM test.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']
```

### `format_schema_source='query'`

También puede almacenar su esquema Protobuf en una table.

Cree en ClickHouse Cloud una table en la que insertar los datos:

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

```sql theme={null}
INSERT INTO test.protobuf_schema VALUES ('syntax = "proto3";message MessageType {  string name = 1;  string surname = 2;  uint32 birthDate = 3;  repeated string phoneNumbers = 4;};');
```

Si ejecutó el ejemplo anterior, vacíe primero la table para que el resultado que se muestra a continuación coincida:

```sql theme={null}
TRUNCATE TABLE test.protobuf_messages;
```

Inserte los datos en ClickHouse Cloud y especifique el esquema como una consulta que se ejecutará.
Envíe esta inserción a través de la [interfaz HTTP](https://clickhouse.com/docs/es/concepts/features/interfaces/http.md#insert-protobuf), donde el servidor analiza los datos y ejecuta la consulta del esquema: `clickhouse client` analiza los datos de entrada en el lado del cliente, donde la consulta del esquema falla.
La opción `--url-query` de curl (curl 7.87 y versiones posteriores) codifica cada parámetro para URL, por lo que la consulta y la consulta del esquema pueden escribirse tal cual:

```bash theme={null}
curl 'https://<hostname>:8443/' --user 'default:<password>' \
    --url-query 'query=INSERT INTO test.protobuf_messages FORMAT Protobuf' \
    --url-query 'format_schema_source=query' \
    --url-query 'format_schema=SELECT schema FROM test.protobuf_schema' \
    --url-query 'format_schema_message_name=MessageType' \
    --data-binary @protobuf_messages.bin
```

Seleccione los datos insertados en la table:

```bash theme={null}
clickhouse client --host <hostname> --secure --password <password> --query "SELECT * FROM test.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']
```

### Uso de un esquema autogenerado

Si no dispone de un esquema Protobuf externo para sus datos, aún puede exportar/importar datos en formato Protobuf
mediante un esquema autogenerado. Para ello, utilice la configuración `format_protobuf_use_autogenerated_schema`.

Por ejemplo:

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

En este caso, ClickHouse generará automáticamente el esquema de Protobuf según la estructura de la tabla mediante la función
[`structureToProtobufSchema`](https://clickhouse.com/docs/es/reference/functions/regular-functions/other-functions#structureToProtobufSchema). A continuación, usará este esquema para serializar los datos en formato Protobuf.

También puede leer un archivo Protobuf con el esquema autogenerado. En este caso, es necesario que el archivo se haya creado con el mismo esquema:

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

La configuración [`format_protobuf_use_autogenerated_schema`](https://clickhouse.com/docs/es/reference/settings/formats/format#format_protobuf_use_autogenerated_schema) está habilitada de forma predeterminada y se aplica si no se ha establecido [`format_schema`](https://clickhouse.com/docs/es/reference/settings/formats/format-schema#format_schema).

También puede guardar el esquema autogenerado en el archivo durante la entrada/salida mediante la configuración [`output_format_schema`](https://clickhouse.com/docs/es/reference/settings/formats/output-format#output_format_schema). Por ejemplo:

```sql theme={null}
SELECT * FROM test.hits format Protobuf SETTINGS format_protobuf_use_autogenerated_schema=1, output_format_schema='path/to/schema/schema.proto'
```

En este caso, el esquema de Protobuf autogenerado se guardará en el archivo `path/to/schema/schema.capnp`.

### Eliminar la caché de Protobuf

Para recargar el esquema Protobuf cargado desde [`format_schema_path`](https://clickhouse.com/docs/es/reference/settings/server-settings/settings/format#format_schema_path), use la sentencia [`SYSTEM DROP ... FORMAT CACHE`](https://clickhouse.com/docs/es/reference/statements/system#system-drop-schema-format).

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