INSERT, la télémétrie d’exécution et les signaux d’erreur. C’est le protocole sous-jacent au client en ligne de commande, au client C++ et à la plupart des drivers natifs tiers.
Cette page couvre le protocole lui-même : tramage des paquets, machine à états de la connexion, négociation de version et corps de chaque message autre que Block. Les octets à l’intérieur des paquets de la famille Data (le Block, ses colonnes et les encodages propres à chaque type) relèvent d’un sujet distinct, documenté dans la spécification Native Format.
Spécification complémentaireCette page est l’un des deux volets de cet ensemble et est publiée avec la spécification complémentaire Native Format. Les deux spécifications se répartissent clairement les rôles : cette page couvre la couche des paquets et du transport ; la spécification Native Format couvre les octets à l’intérieur des paquets de la famille
Data.BlockInfo, donc un seul octet mal placé désynchronise tout ce qui suit. Il est avec état, et chaque connexion TCP traite une requête à la fois — il n’y a pas de multiplexage. Les entiers à largeur fixe sont en little-endian.
Vue d’ensemble
Chaque message transmis commence par un code de type de paquet
VarUInt, suivi d’un corps dont la structure dépend de ce code et de la version du protocole négociée.
Une connexion se déroule en trois phases — un handshake initial unique, puis un nombre quelconque d’échanges Ping ou Query, puis la fermeture :
Le protocole TCP natif transporte toujours des données tabulaires au format Native, quelle que soit la clause FORMAT dans la requête SQL. Le reformatage en RowBinary, CSV, JSON, etc. relève du client et s’effectue après le décodage des blocs Native. (L’interface HTTP suit un chemin de code différent qui, lui, respecte bien la clause FORMAT ; HTTP n’est pas traité ici.)
Sécurité
Sécurité de transport (TLS)
Authentification
ClientHello. Les champs user et password sont transmis sous forme de chaînes en clair ; la protection des credentials en transit repose donc sur le chiffrement au niveau du transport (TLS).
L’authentification SSH par challenge-response est disponible à partir de la version 54466 du protocole — voir Authentification SSH par challenge-response.
Secret inter-serveurs
Query contient un auth_hash SHA-256 de 32 octets dans le champ 4 de Query, calculé à partir d’un salt, d’un nonce, du secret configuré et de la requête, puis recalculé et comparé par le serveur destinataire. Ce mécanisme est conditionné par la fonctionnalité INTERSERVER_SECRET (v54441). Les clients externes envoient toujours ici une chaîne vide. Voir Authentification inter-serveurs.
Gestion des versions et feature gates
Négociation de la version
Feature gates
Tableau des fonctionnalités
Enveloppe de paquet
VarUInt, et non un octet de largeur fixe. Pour les valeurs inférieures à 128, un VarUInt produit le même octet, mais les implémentations doivent utiliser l’encodage VarUInt afin de rester compatibles si de futurs types de paquets devaient atteindre 128 ou plus.
La référence des messages documente uniquement la charge utile de chaque paquet — les octets situés après le code du type de paquet. La numérotation des champs commence à 1 avec le premier champ de la charge utile.
Tramage par fragments (v54470+)
CHUNKED_PROTOCOL est négociée (voir le handshake), chaque paquet sur le wire est tramé par fragments. Ce tramage est propre à chaque sens : client→serveur et serveur→client sont négociés séparément et peuvent au final utiliser des modes différents (avec fragments ou sans encapsulation).
Format binaire par paquet :
VarUInt se trouve dans le flux découpé en fragments : c’est le premier octet de la charge utile du paquet (le premier octet du premier fragment), et non un octet distinct envoyé avant le tramage. La charge utile fragmentée de chaque paquet correspond à l’intégralité de [VarUInt packet_type_code][message body] de l’enveloppe de paquet. Un client qui laisse le type de paquet en dehors du flux découpé en fragments amène le pair à lire cet octet de type comme le premier octet de la taille de fragment u32, ce qui désynchronise la connexion.
Un même paquet peut être réparti sur plusieurs fragments si le tampon d’écriture se remplit en plein milieu du paquet ; la coupure peut se produire n’importe où, y compris au milieu du VarUInt du type de paquet. Le lecteur concatène les charges utiles des fragments et traite le zéro final sur 4 octets comme une limite de paquet transparente — il le consomme, mais ne le transmet pas à ce qui lit les corps de paquet.
Les paquets sans corps restent enveloppés : un paquet d’un seul octet comme Ping ou Pong devient [u32 size = 1][0x04][u32 0] une fois le découpage en fragments négocié. Toute mention ailleurs sur cette page d’un « octet unique sur le réseau » correspond à la forme antérieure au découpage en fragments.
Négociation. ServerHello et Addendum transportent chacun deux champs String, un par direction, avec des valeurs tirées de {"chunked", "notchunked", "chunked_optional", "notchunked_optional"} :
chunked/notchunkedsont stricts : ce côté exige exactement ce mode.- Les variantes
_optionalsont souples : elles acceptent le mode choisi par l’autre côté.
Côté client, la préférence d’ENVOI du client est négociée avec la préférence de RÉCEPTION du serveur, et inversement.
Temporalité. Les chaînes de négociation transitent sur le réseau sans tramage :
ClientHello → ServerHello (préférences du serveur) → Addendum (valeurs négociées du client). Le basculement vers le tramage s’applique à chaque octet envoyé après l’écriture de l’Addendum. L’Addendum lui-même, le ClientHello et le ServerHello sont toujours envoyés sans tramage.
Cycle de vie de la connexion
HANDSHAKE, READY, READING_RESPONSE, ou terminée. Comme le protocole ne prend pas en charge le multiplexage, un client qui envoie une nouvelle requête avant d’avoir entièrement lu la réponse précédente entrelace les octets dans le flux de données transmis et corrompt le flux.
États
HANDSHAKE → READY → READING_RESPONSE → READY — avec la boucle Ping/Pong sur elle-même, et toutes les transitions d’échec convergent vers l’unique sink Terminated.
Phase de handshake
-
Le client envoie
ClientHelloavec la version maximale du protocole qu’il prend en charge. -
Le client lit la réponse et la traite selon le type de paquet :
-
Si
negotiated_version ≥ 54458(la fonctionnalitéADDENDUM), le client envoie unAddendum. Cette décision repose sur la version négociée, et non sur la version déclarée du client.
READY ; en cas d’erreur, elle est interrompue.
Phase Ping
READY, le flux est :
-
Le client envoie
Ping. -
Le client lit la réponse :
Phase de requête
EndOfStream ou une Exception.
À partir de READY, le flux est :
En cas d’erreur à n’importe quelle étape, le serveur envoie une Exception au lieu de EndOfStream, ce qui met fin à la requête.
-
Le client envoie
Queryavec unquery_idunique (généralement un UUID). -
Le client envoie les tables externes éventuelles, puis le marqueur Data vide. Le paquet Data vide a
table_name = "",num_columns = 0,num_rows = 0. Le serveur ne commence pas à exécuter la requête tant qu’il n’a pas reçu ce marqueur. -
Le client passe à
READING_RESPONSEet vide son tampon d’écriture. -
Le client lit les paquets de réponse en boucle et les traite selon leur type :
EndOfStream ou d’une Exception gérée, la connexion revient à READY. Une violation du protocole ou une erreur d’E/S y met fin.
Le cas
num_rows == 0 piège souvent les nouvelles implémentations. Un bloc de zéro ligne est un marqueur de délimitation ou un en-tête de schéma, pas un signal de fin de flux. Seuls EndOfStream ou Exception mettent fin à la réponse.Phase INSERT
INSERT ; le serveur répond avec un bloc de schéma décrivant la table cible ; le client envoie des paquets Data contenant les lignes, puis le marqueur Data vide ; le serveur termine avec EndOfStream ou Exception.
À partir de READY, la requête SQL est un INSERT de la forme INSERT INTO <table> [(<cols>)] VALUES — sans littéral VALUES (...) intégré à la requête, puisque les données des lignes transitent via des paquets Data. Le flux :
- Le client envoie
Queryavecbodycontenant la requête SQL INSERT. - Le client envoie les éventuelles tables externes (cas rare pour INSERT). Contrairement à la phase de requête, il n’envoie pas ici de marqueur Data vide. Le paquet
QuerydeINSERTest envoyé avec des données en attente ; le bloc vide de fin de données est donc reporté à l’étape 5. L’envoyer avant le bloc de schéma amènerait le serveur à l’interpréter comme la fin du flux de lignes, à terminer l’INSERT sans aucune ligne, puis à analyser le premier vrai paquet de lignes comme un paquet de niveau supérieur parasite. - Le client consomme les paquets de métadonnées (TableColumns, Progress, ProfileInfo, Log, ProfileEvents) jusqu’à lire le paquet Data de schéma — un Block avec 0 ligne, mais la structure complète des colonnes (noms et types). Le bloc de schéma fait foi : les lignes envoyées ensuite par le client doivent correspondre à cette structure de colonnes.
- Le client envoie un ou plusieurs blocs de données. Pour chaque bloc, il écrit
VarUInt(ClientPacket::Data = 2), puisString("")pour le nom vide de la table externe, puis le Block. Les types de colonnes doivent correspondre, par position, à ceux des colonnes du bloc de schéma. - Le client envoie le terminateur de fin d’entrée : un paquet Data avec un Block vide (0 colonne, 0 ligne).
- Le client consomme le flux de réponse jusqu’à
EndOfStream(succès) ouException(échec).
async_insert = 1, le serveur place les lignes en file d’attente et les écrit dans le cadre d’un lot. À la version négociée ≥ 54484 (PROGRESS_IN_ASYNC_INSERT), une fois l’écriture terminée, le serveur émet un paquet Progress supplémentaire, immédiatement suivi des ProfileEvents de l’INSERT, puis de EndOfStream. En dessous de 54484, le serveur omet ce Progress final. Ce paquet est un Progress ordinaire ; comme le serveur réinitialise le pipeline de requête avant d’y intégrer les compteurs d’écriture, l’incrément ne contient en pratique que le temps écoulé, et les statistiques sur les lignes et octets écrits parviennent au client via les ProfileEvents associés. Un client qui consomme déjà les paquets Progress entrelacés à l’étape 6 n’a qu’à accepter un paquet de plus.
La connexion revient à l’état READY à la réception de EndOfStream ou d’une Exception gérée. Les violations du protocole et les erreurs d’E/S y mettent fin.
Référence des messages
Type utilise :
VarUInt— entier non signé à longueur variable (voir VarUInt).String— octets préfixés par un VarUInt (voir String).UInt8,Int32, etc. — entiers little-endian à largeur fixe.Bool— un seul octet,0x00ou0x01.
Role indique qui utilise chaque champ :
- client — défini par les clients externes.
- inter-serveur — n’a de sens que pour la communication de serveur à serveur ; les clients externes écrivent une valeur par défaut.
- universal — utilisé par les deux.
ClientHello (type de paquet 0)
ServerHello (type de paquet 0)
Rule — élément de
password_complexity_rules :
Cette liste reflète la configuration de politique de mot de passe définie par l’opérateur du serveur et reste purement indicative : le serveur n’applique pas ces règles pendant le handshake. Un client qui propose une fonctionnalité de changement ou de définition de mot de passe peut s’en servir pour signaler les erreurs avant d’envoyer au serveur un mot de passe non conforme.
Pour borner l’utilisation des ressources face à un serveur hostile ou mal configuré, plafonnez la valeur décodée de
count à 256 entrées et limitez chaque String pattern et message à 4096 octets. Une valeur count de 0 (aucune paire ensuite) est le cas le plus courant sur les serveurs sans politique de mot de passe configurée.Addendum (sans type de paquet)
ADDENDUM (v54458). Envoyé immédiatement après la fin du handshake. Il ne s’agit pas d’un type de paquet distinct — les champs sont transmis bruts sur le wire, sans octet de préfixe de type de paquet.
Le basculement vers le tramage par fragments s’applique après l’envoi de cet Addendum — l’Addendum lui-même n’est pas tramé.
Ping (type de paquet 4)
0x04 avant le tramage par fragments ; lorsque le découpage en fragments est négocié, l’octet devient la charge utile d’un fragment d’un octet (voir tramage par fragments).
Pong (type de paquet 4)
0x04 avant le tramage par fragments ; lorsque le découpage en blocs est négocié, cet octet devient la charge utile d’un octet d’un bloc (voir le tramage par fragments).
Exception (type de paquet 2)
Query (type de paquet 1)
ClientInfo (intégré à Query)
CLIENT_INFO (v54032). (Certains champs de ClientInfo sont conditionnés par des versions ultérieures, comme indiqué champ par champ ci-dessous.)
Disposition dépendante de l’interface (champs 7–12)Les champs 7 à 12 ci-dessus correspondent à la branche TCP. Lorsque
query_interface (champ 6) n’est pas TCP, ces champs sont remplacés par une autre disposition sur le wire — il ne s’agit pas simplement de champs omis de façon facultative ; un décodeur doit donc bifurquer selon la valeur du champ 6.query_interface = 2(HTTP) : les informations de requête HTTP relayée par le server sont écrites à la place —http_method(UInt8),http_user_agent(String), puisforwarded_for(String, conditionné parX_FORWARDED_FOR_IN_CLIENT_INFOv54443) ethttp_referer(String, conditionné parREFERER_IN_CLIENT_INFOv54447). Les champsos_user/client_hostname/client_name/version_*/protocol_versionne sont pas présents.- Toute autre interface : aucun des champs TCP (7–12) ni aucun des champs HTTP n’est écrit ; le stream continue directement avec
quota_key.
quota_key (champ 13) et distributed_depth (champ 14) suivent pour toutes les interfaces, puis version_patch (champ 15) n’est écrit que pour TCP.Cette bifurcation concerne surtout le trafic inter-serveurs, où le server initiateur relaie une query arrivée initialement via HTTP. Un décodeur qui lit systématiquement les champs TCP interprétera mal ces paquets — en traitant http_method ou http_user_agent comme quota_key.Authentification inter-serveur
auth_hash) n’est pas le secret partagé du cluster transmis sur le réseau. Envoyer le secret brut ferait à la fois échouer l’authentification et le divulguerait. À la place, un serveur agissant comme client inter-serveur prouve qu’il connaît le secret à l’aide d’un hachage SHA-256 salé :
- Entrer en mode inter-serveur. Le serveur qui se connecte l’indique dans
ClientHello: le champusercontient le marqueur inter-serveur etpasswordest vide. Il ajoute ensuite deux chaînes supplémentaires — le nom du cluster et unsaltde 32 octets nouvellement généré (encodeSHA256d’une valeur aléatoire) — immédiatement après les champsuser/password, dans le même paquetClientHello. Le serveur lit ces deux chaînes avant d’envoyerServerHello, donc un client doit les écrire d’emblée ; attendreServerHellod’abord provoque un interblocage, car le serveur reste bloqué à les lire. - Obtenir le nonce.
ServerHellocontient un nonceUInt64de 8 octets lorsqueINTERSERVER_SECRET_V2(v54462) est négocié. - Calculer le hachage. Pour chaque paquet Query autre que
InitialQuery, le client écritencodeSHA256(salt + nonce + cluster_secret + query + query_id + initial_user + external_roles)dans le champ 4 — un condensat de 32 octets. (nonceest sous forme de chaîne décimale et n’est présent que si une version ≥ v54462 a été négociée ;external_rolesn’est ajouté que lorsqueINTERSERVER_EXTERNALLY_GRANTED_ROLES(v54472) est négocié.) Pour unInitialQuery, ou lorsqu’aucun secret de cluster n’est configuré, le client écrit à la place une chaîne vide. - Vérifier. Le serveur lit le champ 4 avec une limite de 32 octets et recalcule la même concaténation à l’aide de sa propre copie du secret du cluster ; la connexion est rejetée si les condensats diffèrent.
auth_hash vide.
Paramètre
VarUInt 0, sans indicateurs ni valeur après. Seul l’encodage de chaque paramètre dépend de la version négociée, selon SETTINGS_SERIALIZED_AS_STRINGS (v54429).
v54429+ (STRINGS_WITH_FLAGS) — chaque paramètre est le triplet présenté ici :
Les champs 2 et 3 sont absents lorsque
key est vide.
Avant 54429 (BINARY) — chaque paramètre est [String key][type-specific binary value] : le champ flags n’est pas écrit, et la valeur est encodée dans la forme binaire native du paramètre (par exemple, un entier de largeur fixe ou une chaîne préfixée par sa longueur) plutôt que comme chaîne décimale/texte. La liste se termine toujours par un key vide. Un client ciblant une version négociée inférieure à 54429 doit lire et écrire cette forme binaire, et non le triplet ci-dessus. (Les paramètres personnalisés définis par l’utilisateur font exception : ils comportent toujours flags et une valeur de chaîne, dans les deux encodages.)
Le champ flags regroupe :
0x01— Important : le paramètre affecte les résultats de la requête et ne doit pas être ignoré silencieusement par des pairs plus anciens.0x02— Custom : un paramètre personnalisé défini par l’utilisateur.0x0c— un champ de tier sur 2 bits, et non un indicateur indépendant :0x00= Production,0x04= Obsolete,0x08= Experimental,0x0c= Beta. Lisez bien les 2 bits (flags & 0x0c) — un test naïfflags & 0x04classerait à tort Beta (0x0c) comme Obsolete.0x80— HotReload (rechargement de la config sans redémarrage ; défini dans l’enum des indicateurs, rencontré principalement pour les paramètres de coordination).
Paramètre
SELECT {x:UInt64}. Ils sont encodés de façon identique à un Setting avec l’indicateur Custom (0x02) activé, et se terminent de la même manière par une clé vide.
La valeur du paramètre est la représentation SQL de la valeur, et non un littéral brut. Les paramètres de type chaîne doivent être transmis déjà entre guillemets simples (par exemple, la valeur de
{name:String} est 'Alice', et non Alice) ; sinon, l’analyseur de valeurs du serveur les rejette.Data (type de paquet 1 serveur→client, type de paquet 2 client→serveur)
table_name est inclus avant le Block. Seul l’octet du type de paquet diffère.
Le marqueur de fin des données est un paquet dont le Block est vide —
0 colonnes et 0 lignes — quelle que soit la valeur de table_name. Le serveur traite un paquet client Data comme terminateur uniquement lorsque le bloc décodé est vide (block.empty()) ; un paquet avec table_name = "" et un bloc non vide est un paquet de lignes ordinaire, pas un terminateur. Ainsi, un flux de lignes INSERT est une séquence de blocs Data non vides suivie d’un bloc Data vide qui y met fin.
Les variantes de bloc et leur signification sont décrites dans Variantes de bloc.
Progress (type de paquet 3)
Progress précédent, et non des totaux cumulés. Avant l’envoi, le serveur lit ses compteurs et les réinitialise atomiquement à zéro, puis calcule elapsed_ns comme le temps écoulé depuis le dernier envoi. Un client doit donc accumuler localement les paquets successifs pour obtenir des totaux cumulés — traiter un paquet comme une valeur absolue fait reculer l’affichage de progression ou provoque un sous-comptage dès que plusieurs paquets arrivent.
ProfileInfo (type de paquet 6)
Totaux (type de paquet 7)
WITH TOTALS. Le format binaire est identique à Data : une chaîne table_name (toujours vide), suivie d’un bloc. Seul l’octet indiquant le type de paquet diffère.
Extremes (type de paquet 8)
extremes est activé. Le format binaire de transmission est identique à Data. Le bloc contient exactement 2 lignes : la ligne 0 contient le minimum de chaque colonne, la ligne 1 contient le maximum.
Log (type de paquet 10)
send_logs_level ; voir la transmission des logs en continu).
Même format d’enveloppe et de corps que Data. Le bloc a un num_columns = 8 fixe et un schéma prédéfini. Chaque ligne de log constitue une ligne sur les 8 colonnes, et un paquet Log peut contenir de nombreuses lignes.
ProfileEvents (type de paquet 14)
num_columns = 6 fixe et un schéma prédéfini. Chaque événement constitue une ligne.
Le type d’élément de la colonne
value n’est pas fixe d’un paquet à l’autre — les anciens serveurs émettent UInt64, les plus récents Int64. Lisez la chaîne de type de la colonne dans l’en-tête du bloc plutôt que de supposer une taille donnée.TableColumns (type de paquet 11)
COLUMN_DEFAULTS_METADATA (v54410). Le serveur l’envoie avant le bloc de schéma INSERT pour transmettre les métadonnées des valeurs par défaut des colonnes, mais uniquement lorsque la version négociée est ≥ 54410 et que le paramètre input_format_defaults_for_omitted_fields est activé. En dessous de 54410, le paquet n’est jamais envoyé ; un ancien client ne doit donc pas l’attendre — le bloc de schéma Data arrive directement. Un client v54410+ doit être prêt à l’un ou l’autre ordre : un TableColumns facultatif, puis le bloc de schéma.
Corps compressé à partir de v54481+À partir d’une version négociée ≥ 54481 (
COMPRESSED_LOGS_PROFILE_EVENTS_COLUMNS), le serveur écrit les deux champs via le même flux de sortie avec compression éventuelle ; lorsque la requête a compression = true, l’intégralité du corps TableColumns (external_table + columns_description) se trouve dans la trame de compression, et le client la lit via le flux décompressé correspondant. Lorsque la requête n’utilise pas de compression, le corps est transmis sans compression, exactement comme indiqué dans le tableau ci-dessus. C’est important pour les réponses de schéma INSERT : un client qui gère la compression pour Log et ProfileEvents, mais pas pour TableColumns, lira mal la réponse lorsque la compression de la requête est activée.TimezoneUpdate (type de paquet 17)
TIMEZONE_UPDATES (v54464). Envoyé à un seul endroit : l’initialiseur de la table function input (une query de la forme INSERT INTO <table> SELECT ... FROM input('<structure>'), qui transmet des lignes depuis le client). Juste après que le serveur a envoyé le bloc Data du schéma d’entrée (voir la phase INSERT), il émet TimezoneUpdate avec la valeur courante de session_timezone du contexte de la query, afin que le client interprète les lignes qu’il s’apprête à envoyer avec le même fuseau horaire. Le serveur n’émet pas ce paquet pour des changements arbitraires de SET session_timezone au milieu d’une query, ni pour indiquer au client comment formater les blocs de résultats envoyés ensuite.
Le paquet arrive une seule fois, immédiatement après le bloc du schéma d’entrée et avant que le client ne commence à envoyer des blocs de lignes. Un décodeur qui ignore
TimezoneUpdate DOIT quand même consommer le String final pour conserver l’alignement sur le wire.
Authentification SSH par défi-réponse (types de paquets 11, 12 et 18)
SSH_AUTHENTICATION (v54466) et disponible uniquement sur activation explicite. Une connexion entre dans le flux SSH lorsque ClientHello envoie user = " SSH KEY AUTHENTICATION " + <real_user> (avec les espaces au début et à la fin) et password = "". Le serveur lit le préfixe, le supprime pour retrouver l’utilisateur réel, puis bascule vers le mécanisme de défi-réponse.
Ce flux remplace l’authentification par mot de passe, et l’échange de défi-réponse a lieu avant
ServerHello — le serveur diffère sa réponse Hello jusqu’à ce que l’authentification réussisse :
-
Le client envoie
ClientHelloavec le préfixe marqueur SSH et un mot de passe vide. -
Le client envoie
SSHChallengeRequest(paquet 11). Le serveur n’a pas encore envoyéServerHello— il traite d’abord l’authentification et reste bloqué ici en attendant ce paquet. -
Le serveur répond avec
SSHChallenge, contenant des octets aléatoires (paquet 18). -
Le client construit la chaîne à signer et signe celle-ci, et non le défi brut, puis envoie
SSHChallengeResponse(paquet 12) avec la signature. Le message signé est la concaténation, octet par octet et sans séparateurs, de quatre parties dans cet ordre exact : -
Le serveur vérifie la signature à l’aide de la clé publique enregistrée de l’utilisateur, en reconstruisant la même chaîne
decimal(protocol_version) + default_database + user + challenge. En cas de succès, il envoieServerHello— la même réponse que dans le flux par mot de passe — et la négociation initiale se poursuit normalement (Addendum, etc.) ; en cas d’échec, il renvoie uneExceptionet met fin à la connexion. Un client qui signe uniquement les octets bruts du défi échouera à l’authentification.
C’est l’inverse de la négociation initiale par mot de passe, où
ServerHello suit immédiatement ClientHello. Avec l’authentification SSH, ServerHello n’est envoyé qu’après vérification de la signature, de sorte que le challenge-réponse SSH s’intercale dans la négociation initiale avant tout ServerHello.Référence des types de paquets
Client → serveur
Serveur → Client
Configuration
- Paramètres de la couche de transport — options de socket TCP et délais d’expiration, qui affectent le comportement de la connexion TCP elle-même.
- Paramètres de la couche applicative — paramètres réglables par requête transmis dans la liste des paramètres du paquet Query, qui influent sur ce que le serveur envoie sur le réseau ou sur la façon dont ces données sont encapsulées.
- Paramètres hors périmètre — paramètres souvent confondus avec les paramètres du protocole, mais qui contrôlent en réalité l’exécution SQL ou le stockage.
Paramètres de la couche transport
Options de socket
Délais d’attente
Les délais d’attente s’imbriquent ainsi :
Limites de connexion
Une connexion qui exécute régulièrement des requêtes peut rester active indéfiniment. Seules les connexions inactives sont fermées au bout d’une heure, et il n’existe pas de durée de vie maximale par défaut.
Paramètres de la couche applicative
Compression
L’indicateur
compression du paquet Query (champ 6) active ou désactive la compression ; ces paramètres sélectionnent le codec utilisé lorsqu’elle est activée.
Transmission des logs
Si
send_logs_level est défini sur une valeur autre que "none", le serveur émet des paquets Log pendant l’exécution de la requête.
Rapport de progression
Il s’agit d’un minimum visé, et non d’un maximum strict : le server peut envoyer des paquets Progress moins fréquemment si la query ne produit pas suffisamment de travail assez vite.
Enveloppe de résultat
INSERT asynchrone
Traçage distribué
Paramètres hors du périmètre
max_threads— parallélisme lors de l’exécution de la requête.max_memory_usage— limite de mémoire par requête.max_block_size,preferred_block_size_bytes— dimensionnement interne des blocs pendant le query processing ; les blocs transmis sur le réseau en sont indépendants.compile_expressions— compilation JIT ; CPU uniquement.async_insert_max_data_size— buffer de file d’attente côté serveur.- Tous les paramètres
input_format_*etoutput_format_*à l’exception de la familleinput_format_native_*/output_format_native_*— les paramètres non-nativesélectionnent ou ajustent d’autres formats (par exemple via HTTP) et ne modifient pas les blocsDatadu protocole natif.
*_native_* font exception : ils modifient les octets à l’intérieur des blocs Data en native TCP ; une implémentation du protocole doit donc en tenir compte. output_format_native_encode_types_in_binary_format fait passer le champ type de la colonne d’une chaîne textuelle à un encodage binaire du type, output_format_native_write_json_as_string émet les colonnes JSON comme une String, et output_format_native_use_flattened_dynamic_and_json_serialization sélectionne le layout FLATTENED Dynamic/JSON. Comme ces paramètres affectent le body du bloc plutôt que le packet envelope, ils sont décrits dans la spécification Native Format — voir column wire layout et types versionnés.
Glossaire
- Requête normale (
SELECT, etc.) : envoyé après le paquet Query et les éventuels paquets Data de table externe pour signaler « plus de données externes ». Le serveur commence alors l’exécution. INSERT: le client n’envoie pas de marqueur avant le schéma. Le serveur envoie d’abord le bloc de schéma, le client transmet ensuite ses blocs Data de lignes, puis envoie seulement après le paquet Data vide pour terminer le flux de lignes. Envoyer un marqueur vide avant le bloc de schéma serait interprété comme une fin immédiate des lignes et entraînerait la perte des données.
0 ou false).
Version négociée — min(client_version, server_version), calculé pendant la négociation initiale. Détermine quelles fonctionnalités sont actives pendant toute la durée de vie de la connexion.
Packet — un message sur le réseau : un code de type de paquet VarUInt suivi d’un body dont le format dépend du type. Voir packet envelope.
Code de type de paquet — le VarUInt initial d’un paquet qui identifie son format. Les valeurs 0–18 sont actuellement attribuées. Voir la référence des types de paquet.
Flux de réponse — la séquence de paquets émise par le serveur pendant une requête. Sa longueur est ouverte et il se termine par exactement un EndOfStream (succès) ou une Exception (échec). Voir la phase de requête.
bloc de schéma — le header block (un Block avec des colonnes mais 0 ligne) que le serveur envoie pendant la phase INSERT pour annoncer les formes de colonnes attendues avant que le client n’envoie les données.
Settings list — une séquence de tuples (key, flags, value) dans le body de Query, terminée par une key vide. Transporte une configuration de couche applicative propre à chaque requête. Voir Setting.
Stage — un champ VarUInt du paquet Query (champ 5) qui contrôle jusqu’où le serveur exécute la requête. Les clients externes envoient généralement 2 (Complete) ; les requêtes distribuées et les query plans sérialisés utilisent des valeurs plus élevées. Voir le champ 5 de Query pour l’ensemble complet des valeurs sur le réseau.
Terminator — un paquet qui termine un flux. La réponse à Query se termine par EndOfStream (succès) ou Exception (échec). Le flux d’entrée du client se termine par le marqueur Data vide.