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

> Documentation du format Protobuf

# Protobuf

| Entrée | Sortie | Alias |
| - | - | - |
| ✔ | ✔ | |

## Description

Le format `Protobuf` est le format [Protocol Buffers](https://protobuf.dev/).

Ce format nécessite un schéma de format externe, qui est mis en cache d’une requête à l’autre.

ClickHouse prend en charge :

* les syntaxes `proto2` et `proto3`.
* les champs `Repeated`/`optional`/`required`.

Pour établir la correspondance entre les colonnes de la table et les champs du type de message de Protocol Buffers, ClickHouse compare leurs noms.
Cette comparaison est insensible à la casse, et les caractères `_` (underscore) et `.` (point) sont considérés comme équivalents.
Si les types d’une colonne et d’un champ du message Protocol Buffers sont différents, la conversion nécessaire est appliquée.

Les messages imbriqués sont pris en charge. Par exemple, pour le champ `z` dans le type de message suivant :

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

ClickHouse essaie de trouver une colonne nommée `x.y.z` (ou `x_y_z` ou `X.y_Z`, etc.).

Les messages imbriqués conviennent en entrée ou en sortie d’une [structure de données imbriquée](https://clickhouse.com/docs/fr/reference/data-types/nested-data-structures/index.md).

Pour les champs **mappés** absents dans le format binaire :

* Les colonnes mappées ordinaires **non nullables** utilisent la valeur par défaut du champ du schéma protobuf (`proto2` `[default = …]`, sinon la valeur par défaut du type) lors de l’analyse, et non l’expression `DEFAULT` de la table.
* Les colonnes mappées `Nullable(...)` prennent la valeur `NULL` lorsque le champ est absent (elles n’utilisent pas la valeur par défaut du champ ou du type protobuf).
* Lorsque [`input_format_protobuf_flatten_google_wrappers`](https://clickhouse.com/docs/fr/reference/settings/formats/input-format#input_format_protobuf_flatten_google_wrappers) est activé pour les wrappers `google.protobuf.*Value` :
  * un wrapper **absent** sur une colonne `Nullable(...)` est traité comme un champ externe manquant et devient `NULL` ;
  * un wrapper **présent mais vide** (`str {}`) conserve la valeur par défaut du scalaire imbriqué (`''` / `0`) ;
  * une colonne non nullable mappée à un wrapper absent reçoit la valeur par défaut du scalaire imbriqué plutôt que `NULL`.

Les expressions `DEFAULT` de la table (et les expressions par défaut) s’appliquent aux colonnes de table qui n’ont **aucun** champ correspondant dans le type de message lorsque [`input_format_defaults_for_omitted_fields`](https://clickhouse.com/docs/fr/reference/settings/formats/input-format#input_format_defaults_for_omitted_fields) est activé (par défaut). Si ce paramètre est défini sur `0`, les colonnes non mappées conservent la valeur par défaut du type de données insérée lors de l’analyse, au lieu de l’expression `DEFAULT` de la table.

Exemple de valeur par défaut d’un champ de schéma proto2 (utilisée pour un champ mappé absent du message) :

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

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

Si un message contient [oneof](https://protobuf.dev/programming-guides/proto3/#oneof) et que `input_format_protobuf_oneof_presence` est défini, ClickHouse remplit la colonne indiquant quel champ du oneof a été trouvé.

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

Le nom de la colonne qui indique la présence doit être identique au nom de oneof.
Les messages imbriqués sont pris en charge (voir  [basic-examples](#basic-examples)). Les messages vides sont également pris en charge.
Les types autorisés sont Int8, UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Enum, Enum8 ou Enum16.
Enum (ainsi que Enum8 ou Enum16) doit contenir 0 pour indiquer l’absence ainsi que le tag de chaque cas de oneof ayant une colonne correspondante dans la table cible ; les représentations sous forme de chaîne n’ont pas d’importance.
Pour les membres de message oneof sans colonne de table correspondante, les tags Enum manquants sont également autorisés. Si une telle branche est présente dans l’entrée, ClickHouse considère la présence du oneof comme omise et écrit 0 dans la colonne de présence.

Le paramètre [`input_format_protobuf_oneof_presence`](https://clickhouse.com/docs/fr/reference/settings/formats/input-format#input_format_protobuf_oneof_presence) est désactivé par défaut

ClickHouse lit et écrit des messages protobuf au format `length-delimited`.
Cela signifie qu’avant chaque message, sa longueur doit être encodée sous la forme d’un [entier de longueur variable (varint)](https://developers.google.com/protocol-buffers/docs/encoding#varints).

## Exemple d'utilisation

### Lecture et écriture de données

> [!NOTE]
>
> Les fichiers utilisés dans cet exemple sont disponibles dans le [repository examples](https://github.com/ClickHouse/formats/ProtoBuf)

Dans cet exemple, nous allons lire des données à partir du fichier `protobuf_message.bin` dans une table ClickHouse. Nous les écrirons ensuite
dans un fichier nommé `protobuf_message_from_clickhouse.bin` au format `Protobuf`.

Étant donné le fichier `schemafile.proto` :

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

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

> **Génération du fichier binaire**
>
> Si vous savez déjà comment sérialiser et désérialiser des données au format `Protobuf`, vous pouvez ignorer cette étape.
>
> Nous utiliserons Python pour sérialiser des données dans `protobuf_message.bin` et les lire dans ClickHouse.
> Si vous souhaitez utiliser un autre langage, voir aussi : ["How to read/write length-delimited Protobuf messages in popular languages"](https://cwiki.apache.org/confluence/display/GEODE/Delimiting+Protobuf+Messages).
>
> Exécutez la commande suivante pour générer un fichier Python nommé `schemafile_pb2.py` dans
> le même répertoire que `schemafile.proto`. Ce fichier contient les classes Python
> qui représentent votre message Protobuf `UserData` :
>
> ```bash theme={null}
> protoc --python_out=. schemafile.proto
> ```
>
> Créez maintenant un nouveau fichier Python nommé `generate_protobuf_data.py`, dans le même
> répertoire que `schemafile_pb2.py`. Collez-y le code suivant :
>
> ```python theme={null}
> import schemafile_pb2  # Module generated by 'protoc'
> from google.protobuf import text_format
> from google.protobuf.internal.encoder import _VarintBytes # Importer l'encodeur varint interne
>
> 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
>
> # Les données de nos utilisateurs d'exemple
> 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"
>
> # Ouvrir le fichier binaire en mode écriture binaire ('wb')
> with open(output_filename, "wb") as f:
>     for item in data_to_serialize:
>         # Créer une instance de message Protobuf pour l'utilisateur en cours
>         message = create_user_data_message(
>             item["name"],
>             item["surname"],
>             item["birthDate"],
>             item["phoneNumbers"]
>         )
>
>         # Sérialiser le message
>         serialized_data = message.SerializeToString()
>
>         # Obtenir la longueur des données sérialisées
>         message_length = len(serialized_data)
>
>         # Utiliser le _VarintBytes interne de la bibliothèque Protobuf pour encoder la longueur
>         length_prefix = _VarintBytes(message_length)
>
>         # Écrire le préfixe de longueur
>         f.write(length_prefix)
>         # Écrire les données du message sérialisé
>         f.write(serialized_data)
>
> print(f"Protobuf messages (length-delimited) written to {output_filename}")
>
> # --- Facultatif : vérification (relecture et affichage) ---
> # Pour la relecture, nous utiliserons également le décodeur Protobuf interne pour les varints.
> from google.protobuf.internal.decoder import _DecodeVarint32
>
> print("\n--- Verifying by reading back ---")
> with open(output_filename, "rb") as f:
>     buf = f.read() # Lire le fichier entier dans un buffer pour faciliter le décodage varint
>     n = 0
>     while n < len(buf):
>         # Décoder le préfixe de longueur varint
>         msg_len, new_pos = _DecodeVarint32(buf, n)
>         n = new_pos
>
>         # Extraire les données du message
>         message_data = buf[n:n+msg_len]
>         n += msg_len
>
>         # Analyser le message
>         decoded_message = schemafile_pb2.MessageType()
>         decoded_message.ParseFromString(message_data)
>         print(text_format.MessageToString(decoded_message, as_utf8=True))
> ```
>
> Exécutez maintenant le script en ligne de commande. Il est recommandé de l’exécuter depuis un
> environnement virtuel Python, par exemple avec `uv` :
>
> ```bash theme={null}
> uv venv proto-venv
> source proto-venv/bin/activate
> ```
>
> Vous devrez installer la bibliothèque Python suivante :
>
> ```bash theme={null}
> uv pip install --upgrade protobuf
> ```
>
> Exécutez le script pour générer le fichier binaire :
>
> ```bash theme={null}
> python generate_protobuf_data.py
> ```

Créez une table ClickHouse correspondant au schéma :

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

Insérez les données dans la table en ligne de commande :

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

Vous pouvez également écrire à nouveau les données dans un fichier binaire au format `Protobuf` :

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

Grâce à votre schéma Protobuf, vous pouvez désormais désérialiser les données que ClickHouse a écrites dans le fichier `protobuf_message_from_clickhouse.bin`.

### Lecture et écriture de données avec ClickHouse Cloud

Avec ClickHouse Cloud, vous ne pouvez pas importer de fichier de schéma Protobuf. Vous pouvez toutefois utiliser le paramètre `format_schema_source`
pour spécifier le schéma dans la requête. Dans cet exemple, nous vous montrons comment lire des données sérialisées depuis votre machine locale
et les insérer dans une table dans ClickHouse Cloud.

Comme dans l’exemple précédent, créez la table d’après le schéma Protobuf dans 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()
```

Le paramètre `format_schema_source` définit la source du paramètre `format_schema`

Valeurs possibles :

* 'file' (par défaut) : non pris en charge dans Cloud
* 'string' : `format_schema` correspond au contenu littéral du schéma.
* 'query' : `format_schema` est une requête permettant de récupérer le schéma.

### `format_schema_source='string'`

Pour insérer les données dans ClickHouse Cloud en spécifiant le schéma sous forme de chaîne, exécutez :

```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"
```

Sélectionnez les données insérées dans 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'`

Vous pouvez également stocker votre schéma Protobuf dans une table.

Créez dans ClickHouse Cloud une table dans laquelle insérer des données :

```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 vous avez exécuté l'exemple précédent, commencez par vider la table afin d'obtenir le même résultat que ci-dessous :

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

Insérez les données dans ClickHouse Cloud en spécifiant le schéma sous la forme d'une requête à exécuter.
Envoyez cette insertion via l'[interface HTTP](https://clickhouse.com/docs/fr/concepts/features/interfaces/http.md#insert-protobuf) : le serveur y analyse les données et exécute la requête de schéma, alors que `clickhouse client` analyse les données d'entrée côté client, où la requête de schéma échoue.
L'option `--url-query` de curl (curl 7.87 et versions ultérieures) encode chaque paramètre au format URL ; la requête et la requête de schéma peuvent donc être écrites telles quelles :

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

Sélectionnez les données insérées dans 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']
```

### Utilisation d’un schéma autogénéré

Si vous n’avez pas de schéma Protobuf externe pour vos données, vous pouvez tout de même écrire/lire des données au format Protobuf
en utilisant un schéma autogénéré. Pour cela, utilisez le paramètre `format_protobuf_use_autogenerated_schema`.

Par exemple :

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

Dans ce cas, ClickHouse générera automatiquement le schéma Protobuf à partir de la structure de la table à l’aide de la fonction
[`structureToProtobufSchema`](https://clickhouse.com/docs/fr/reference/functions/regular-functions/other-functions#structureToProtobufSchema). Il utilisera ensuite ce schéma pour sérialiser les données au format Protobuf.

Vous pouvez également lire un fichier Protobuf avec le schéma autogénéré. Dans ce cas, il est nécessaire que le fichier soit créé à partir du même schéma :

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

Le paramètre [`format_protobuf_use_autogenerated_schema`](https://clickhouse.com/docs/fr/reference/settings/formats/format#format_protobuf_use_autogenerated_schema) est activé par défaut et s'applique si [`format_schema`](https://clickhouse.com/docs/fr/reference/settings/formats/format-schema#format_schema) n'est pas défini.

Vous pouvez également enregistrer le schéma autogénéré dans le fichier lors des opérations d'entrée/sortie à l'aide du paramètre [`output_format_schema`](https://clickhouse.com/docs/fr/reference/settings/formats/output-format#output_format_schema). Par exemple :

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

Dans ce cas, le schéma Protobuf autogénéré sera enregistré dans le fichier `path/to/schema/schema.capnp`.

### Vider le cache Protobuf

Pour recharger le schéma Protobuf chargé à partir de [`format_schema_path`](https://clickhouse.com/docs/fr/reference/settings/server-settings/settings/format#format_schema_path), utilisez l’instruction [`SYSTEM DROP ... FORMAT CACHE`](https://clickhouse.com/docs/fr/reference/statements/system#system-drop-schema-format).

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