INSERT, telemetria de execução e sinais de erro. É o protocolo usado pelo cliente de linha de comando, pelo driver nativo em C++ e pela maioria dos drivers nativos de terceiros.
Esta página aborda o protocolo em si: estruturação de pacotes, a máquina de estados da conexão, negociação de versão e o corpo de todas as mensagens que não sejam Block. Os bytes dentro dos pacotes da família Data (o Block, suas colunas e as codificações por tipo) são um assunto à parte, documentado na especificação de Formato Nativo.
Especificação complementarEsta página faz par com a especificação complementar de Formato Nativo e é publicada junto com ela. As duas especificações dividem o trabalho de forma clara: esta página cobre a camada de pacotes e transporte; a especificação de Formato Nativo cobre os bytes dentro dos pacotes da família
Data.BlockInfo, portanto um único byte fora do lugar dessincroniza tudo o que vem a seguir. Ele é com estado, e cada conexão TCP processa uma consulta por vez — não há multiplexação. Inteiros de largura fixa são codificados em little-endian.
Visão geral
Cada mensagem no wire começa com um código de tipo de pacote
VarUInt, seguido de um corpo cuja estrutura depende desse código e da versão negociada do protocolo.
Uma conexão passa por três fases — um handshake único, depois qualquer número de trocas Ping ou Query e, por fim, o encerramento:
O protocolo TCP nativo sempre transporta dados tabulares no formato Native, independentemente de qualquer cláusula FORMAT no SQL. Reformatar para RowBinary, CSV, JSON e assim por diante é tarefa do cliente, feita depois que ele decodifica os blocos Native. (A interface HTTP segue um caminho de código diferente e de fato respeita a cláusula FORMAT; HTTP está fora do escopo aqui.)
Segurança
Segurança de transporte (TLS)
Autenticação
ClientHello. Os campos user e password são transmitidos como strings em texto simples, portanto a criptografia na camada de transporte (TLS) é a responsável por proteger as credenciais em trânsito.
A autenticação SSH por challenge-response está disponível a partir da versão 54466 do protocolo — consulte autenticação SSH por challenge-response.
Segredo entre servidores
auth_hash SHA-256 de 32 bytes no campo 4 de Query, calculado a partir de um salt, um nonce, do segredo configurado e da consulta; o servidor receptor o recalcula e o compara. Isso é controlado pelo recurso INTERSERVER_SECRET (v54441). Clientes externos sempre enviam uma string vazia aqui. Consulte Autenticação entre servidores.
Versionamento e feature gates
Negociação de versão
Feature gates
Tabela de funcionalidades
Encapsulamento do pacote
VarUInt, não um byte de largura fixa. Para valores abaixo de 128, um VarUInt produz o mesmo único byte, mas as implementações devem usar a codificação VarUInt para permanecer compatíveis caso tipos de pacote futuros cheguem a 128 ou mais.
A referência de mensagens documenta apenas o corpo de cada pacote — os bytes após o código do tipo de pacote. A numeração dos campos começa em 1, com o primeiro campo do corpo.
Enquadramento em fragmentos (v54470+)
CHUNKED_PROTOCOL é negociado (consulte o handshake), cada pacote no wire é envolvido por um enquadramento em fragmentos. Esse encapsulamento é por direção: cliente→servidor e servidor→cliente são negociados separadamente e podem acabar em modos diferentes (em fragmentos versus sem enquadramento).
Layout no wire de cada pacote:
VarUInt está dentro do fluxo em fragmentos: ele é o primeiro byte do payload do pacote (o primeiro byte do primeiro fragmento), não um byte separado enviado antes do framing. O payload em fragmentos de cada pacote é o [VarUInt packet_type_code][message body] completo do envelope do pacote. Um cliente que deixa o tipo de pacote fora do fluxo em fragmentos faz com que a outra ponta leia esse byte de tipo como o primeiro byte do tamanho do fragmento u32, dessincronizando a conexão.
Um único pacote pode ser dividido em vários fragmentos se o buffer do gravador encher no meio do pacote; uma divisão pode ocorrer em qualquer ponto, inclusive dentro do VarUInt do tipo de pacote. O leitor concatena os payloads dos fragmentos e trata o zero final de 4 bytes como um limite de pacote transparente — ele o consome, mas não o expõe ao que estiver lendo os corpos dos pacotes.
Pacotes sem corpo ainda são encapsulados: um pacote de um único byte, como Ping ou Pong, torna-se [u32 size = 1][0x04][u32 0] assim que o envio em fragmentos é negociado. Qualquer descrição de “byte único no wire” em outro ponto desta página se refere à forma anterior ao envio em fragmentos.
Negociação. ServerHello e Addendum carregam, cada um, dois campos String, um por direção, com valores de {"chunked", "notchunked", "chunked_optional", "notchunked_optional"}:
chunked/notchunkedsão estritos: esse lado exige exatamente esse modo.- As variantes
_optionalsão flexíveis: aceitam qualquer modo que o outro lado escolher.
No lado do cliente, a preferência de ENVIO do cliente é negociada com a preferência de RECEBIMENTO do servidor, e vice-versa.
Temporização. As strings de negociação trafegam no wire sem framing: ClientHello → ServerHello (preferências do servidor) → Addendum (valores negociados do cliente). A mudança de framing se aplica a cada byte enviado depois que o Addendum é descarregado do buffer. O próprio Addendum, o ClientHello e o ServerHello nunca usam framing.
Ciclo de vida da conexão
HANDSHAKE, READY, READING_RESPONSE ou encerrada. Como o protocolo não oferece multiplexação, um cliente que envia uma nova solicitação antes de consumir totalmente a resposta anterior intercala bytes no wire e corrompe o fluxo.
Estados
HANDSHAKE → READY → READING_RESPONSE → READY — com o loop de Ping/Pong e todas as transições de falha convergindo para o único sink Terminated.
Fase de handshake
-
O cliente envia
ClientHellocom a versão máxima de protocolo compatível. -
O cliente lê a resposta e a encaminha de acordo com o tipo de pacote:
-
Se
negotiated_version ≥ 54458(o recursoADDENDUM), o cliente envia umAddendum. Essa decisão se baseia na versão negociada, não na versão declarada pelo cliente.
READY; em caso de erro, ela é encerrada.
Fase de Ping
READY, o fluxo é:
-
O cliente envia
Ping. -
O cliente lê a resposta:
Fase da consulta
EndOfStream ou Exception.
Partindo de READY, o fluxo é:
Em caso de erro em qualquer ponto, o servidor envia uma Exception em vez de EndOfStream, o que encerra a consulta.
-
O cliente envia
Querycom umquery_idexclusivo (normalmente um UUID). -
O cliente envia quaisquer tabelas externas e, em seguida, o marcador
Datavazio. O pacoteDatavazio temtable_name = "",num_columns = 0,num_rows = 0. O servidor não começa a executar a consulta até receber esse marcador. -
O cliente passa para
READING_RESPONSEe faz flush do buffer de escrita. -
O cliente lê os pacotes de resposta em loop, despachando por tipo:
EndOfStream ou em uma Exception tratada, a conexão retorna para READY. Uma violação de protocolo ou erro de E/S a encerra.
O caso
num_rows == 0 costuma confundir novas implementações. Um bloco com zero linhas é um marcador de limite ou um cabeçalho de esquema, não um sinal de fim de stream. Somente EndOfStream ou Exception encerra a resposta.Fase INSERT
INSERT; o servidor responde com um bloco de esquema que descreve a tabela de destino; o cliente transmite pacotes Data com as linhas e, em seguida, o marcador Data vazio; o servidor finaliza com EndOfStream ou Exception.
Partindo de READY, o SQL é um INSERT no formato INSERT INTO <table> [(<cols>)] VALUES — sem um literal VALUES (...) embutido, já que os dados das linhas fluem por meio de pacotes Data. O fluxo:
- O cliente envia
Querycombodydefinido como o SQL de INSERT. - O cliente envia quaisquer tabelas externas (raro em INSERT). Diferentemente da fase de consulta, ele não envia aqui um marcador Data vazio. O pacote
QuerydeINSERTé enviado com dados pendentes, portanto o bloco vazio de fim de dados é adiado para a etapa 5; enviá-lo antes do bloco de esquema faria o servidor interpretá-lo como o fim do stream de linhas, concluir o INSERT sem linhas e então analisar o primeiro pacote de linha real como um pacote avulso de nível superior. - O cliente drena os pacotes de metadados (TableColumns, Progress, ProfileInfo, Log, ProfileEvents) até ler o pacote Data de esquema — um Block com 0 linhas, mas com a estrutura completa das colunas (nomes e tipos). O bloco de esquema é o contrato: as linhas que o cliente envia em seguida devem corresponder a essas estruturas de coluna.
- O cliente envia bloco(s) de dados. Para cada bloco, ele grava
VarUInt(ClientPacket::Data = 2), depoisString("")para o nome vazio da tabela externa, e então o Block. Os tipos das colunas devem corresponder, por posição, às colunas do bloco de esquema. - O cliente envia o terminador de fim de entrada: um pacote Data com um Block vazio (0 colunas, 0 linhas).
- O cliente drena o stream de resposta até
EndOfStream(sucesso) ouException(falha).
async_insert = 1, o servidor enfileira as linhas e faz o flush delas como parte de um batch. Na versão negociada ≥ 54484 (PROGRESS_IN_ASYNC_INSERT), assim que o flush é concluído, o servidor emite um pacote extra de Progress, imediatamente seguido pelos ProfileEvents do insert e então por EndOfStream. Abaixo de 54484, o servidor omite esse Progress final. O pacote é um Progress comum; como o servidor redefine o pipeline da consulta antes de incorporar as contagens de escrita, na prática o incremento carrega apenas o tempo decorrido, e as estatísticas de linhas e bytes gravados chegam ao cliente por meio dos ProfileEvents correspondentes. Um cliente que já drena pacotes Progress intercalados na etapa 6 só precisa aceitar mais um pacote.
A conexão retorna para READY em EndOfStream ou em uma Exception tratada. Violações de protocolo e erros de E/S a encerram.
Referência de mensagens
Type usa:
VarUInt— inteiro sem sinal de comprimento variável (consulte VarUInt).String— bytes com prefixo VarUInt (consulte String).UInt8,Int32e assim por diante — inteiros little-endian de largura fixa.Bool— um único byte,0x00ou0x01.
Role indica quem usa cada campo:
- client — definido por clientes externos.
- inter-server — relevante apenas para a comunicação entre servidores; clientes externos gravam um valor padrão.
- universal — usado por ambos.
ClientHello (tipo de pacote 0)
ServerHello (tipo de pacote 0)
Rule — um elemento de
password_complexity_rules:
A lista reflete a configuração da política de senhas do operador do servidor e é puramente consultiva — o servidor não aplica essas regras durante o handshake. Um cliente que exponha funcionalidade de alteração/definição de senha pode usar as regras para sinalizar erros antes de fazer o round-trip de uma senha incompatível até o servidor.
Para limitar o uso de recursos diante de um servidor hostil ou mal configurado, limite o
count decodificado a 256 entradas e cada String pattern e message a 4096 bytes. Um count de 0 (sem pares subsequentes) é o caso comum para servidores sem política de senhas configurada.Adendo (sem tipo de pacote)
ADDENDUM (v54458). Enviado imediatamente após a conclusão da troca de handshake. Não é um tipo de pacote distinto — os campos vão pelo wire em bruto, sem prefixo de byte de tipo de pacote.
A mudança para o enquadramento chunked se aplica depois que este Adendo é enviado — o próprio Adendo não tem enquadramento.
Ping (tipo de pacote 4)
0x04 antes do enquadramento por fragmentos; quando o uso de fragmentos é negociado, o byte passa a ser o payload de um byte de um fragmento (consulte enquadramento por fragmentos).
Pong (tipo de pacote 4)
0x04 antes do enquadramento por fragmentos; quando a fragmentação é negociada, o byte se torna o payload de um byte de um fragmento (consulte enquadramento por fragmentos).
Exception (tipo de pacote 2)
Consulta (tipo de pacote 1)
ClientInfo (embutido em consulta)
CLIENT_INFO (v54032). (Alguns campos dentro de ClientInfo são condicionados a versões posteriores, como indicado abaixo em cada campo.)
Layout dependente da interface (campos 7–12)Os campos 7–12 acima correspondem ao ramo TCP. Quando
query_interface (campo 6) não é TCP, esses campos são substituídos por um layout de wire diferente — não se trata apenas de omissões opcionais, portanto um decodificador deve seguir o ramo com base no campo 6.query_interface = 2(HTTP): nesse caso, são gravadas as informações da requisição HTTP encaminhada pelo servidor —http_method(UInt8),http_user_agent(String), depoisforwarded_for(String, condicionado aX_FORWARDED_FOR_IN_CLIENT_INFOv54443) ehttp_referer(String, condicionado aREFERER_IN_CLIENT_INFOv54447). Os camposos_user/client_hostname/client_name/version_*/protocol_versionnão estão presentes.- Qualquer outra interface: nenhum dos campos TCP (7–12) nem dos campos HTTP é gravado; o fluxo segue diretamente para
quota_key.
quota_key (campo 13) e distributed_depth (campo 14) vêm em seguida para todas as interfaces, e version_patch (campo 15) é gravado apenas para TCP.Esse ramo importa principalmente para o tráfego entre servidores, quando o servidor de origem encaminha uma consulta que chegou originalmente por HTTP. Um decodificador que sempre ler os campos TCP interpretará esses pacotes de forma incorreta — tratando http_method ou http_user_agent como quota_key.Autenticação entre servidores
auth_hash) não é o segredo compartilhado do cluster no wire. Enviar o segredo puro faria a autenticação falhar e ainda o exporia. Em vez disso, um servidor atuando como cliente interservidor prova que conhece o segredo com um hash SHA-256 com salt:
- Entre no modo interservidor. O servidor que está se conectando sinaliza isso em
ClientHello: o campouseré o marcador interservidor epasswordfica vazio. Em seguida, ele acrescenta mais duas strings — o nome do cluster e umsaltde 32 bytes recém-gerado (encodeSHA256de um valor aleatório) — imediatamente após os camposuser/password, como parte do mesmo pacoteClientHello. O servidor lê essas duas strings antes de enviarServerHello, então o cliente precisa escrevê-las logo de saída; esperar primeiro porServerHellocausa deadlock, porque o servidor fica bloqueado tentando lê-las. - Obtenha o nonce.
ServerHellotraz um nonceUInt64de 8 bytes quandoINTERSERVER_SECRET_V2(v54462) é negociado. - Calcule o hash. Para cada pacote de consulta que não seja
InitialQuery, o cliente escreveencodeSHA256(salt + nonce + cluster_secret + query + query_id + initial_user + external_roles)no campo 4 — um digest de 32 bytes. (nonceusa sua forma de string decimal, presente somente quando negociado ≥ v54462;external_rolesé acrescentado somente quandoINTERSERVER_EXTERNALLY_GRANTED_ROLES(v54472) é negociado.) Para umInitialQuery, ou quando nenhum segredo de cluster está configurado, o cliente escreve uma string vazia. - Verifique. O servidor lê o campo 4 com um limite de 32 bytes e recompõe a mesma concatenação usando sua própria cópia do segredo do cluster; a conexão é rejeitada se os digests forem diferentes.
auth_hash vazio.
SETTING
key vazia — um único VarUInt 0, sem flags nem value em seguida. Apenas a codificação de cada configuração depende da versão negociada, condicionada por SETTINGS_SERIALIZED_AS_STRINGS (v54429).
v54429+ (STRINGS_WITH_FLAGS) — cada configuração é a tripla mostrada aqui:
Os campos 2 e 3 ficam ausentes quando
key está vazia.
Pré-54429 (BINARY) — cada configuração é [String key][type-specific binary value]: o campo flags não é gravado, e o valor é codificado na forma binária nativa da configuração (por exemplo, um inteiro de largura fixa ou uma string com prefixo de comprimento), em vez de como uma string decimal/textual. A lista continua sendo terminada por uma key vazia. Um cliente que tenha como alvo uma versão negociada inferior a 54429 deve ler e gravar essa forma binária, não a tripla acima. (As configurações personalizadas definidas pelo usuário são a exceção: elas sempre incluem flags e um valor em string, em ambas as codificações.)
O campo flags agrupa:
0x01— Important: a configuração afeta os resultados da consulta e não deve ser ignorada silenciosamente por pares mais antigos.0x02— Custom: uma custom setting definida pelo usuário.0x0c— um campo de tier de 2 bits, não uma flag independente:0x00= Production,0x04= Obsolete,0x08= Experimental,0x0c= Beta. Leia os 2 bits completos (flags & 0x0c) — um teste ingênuo comflags & 0x04classificaria Beta (0x0c) incorretamente como Obsolete.0x80— HotReload (recarregamento de config sem reinicialização; definido no enum de flags, encontrado principalmente em configurações de coordination).
Parâmetro
SELECT {x:UInt64}. Codificados exatamente como uma SETTING, com o sinalizador Custom (0x02) ativado, e encerrados da mesma forma, por uma chave vazia.
O valor do parâmetro é a representação SQL do valor, não um literal bruto. Parâmetros do tipo string devem ser passados já entre aspas simples (por exemplo, o valor de
{name:String} é 'Alice', não Alice); caso contrário, o analisador de valores do servidor os rejeitará.Data (tipo de pacote 1 servidor→cliente, tipo de pacote 2 cliente→servidor)
table_name antes do Block. Apenas o byte do tipo de pacote difere.
O marcador de fim de dados é um pacote cujo Block está vazio —
0 colunas e 0 linhas — independentemente de table_name. O servidor trata um pacote Data do cliente como terminador apenas quando o bloco decodificado está vazio (block.empty()); um pacote com table_name = "" e um bloco não vazio é um pacote comum de linhas, não um terminador. Portanto, um fluxo de linhas de INSERT é uma sequência de blocos Data não vazios, seguida por um bloco Data vazio que o encerra.
As variantes de bloco e seus significados estão documentados em Block variants.
Progress (tipo de pacote 3)
Progress anterior, não totais acumulados. Antes de enviar, o servidor lê seus contadores, reinicia-os atomicamente para zero e calcula elapsed_ns como o delta de tempo desde o último envio. Portanto, um cliente deve acumular os pacotes sucessivos localmente para obter totais correntes — tratar um pacote como um valor absoluto faz a exibição do progresso retroceder ou subestimar a contagem quando mais de um pacote chega.
ProfileInfo (tipo de pacote 6)
Totais (tipo de pacote 7)
WITH TOTALS. O formato wire é idêntico a Data: uma string table_name (sempre vazia), seguida por um Block. Apenas o byte do tipo de pacote difere.
Extremes (tipo de pacote 8)
extremes está ativada. O formato wire é idêntico a Data. O bloco tem exatamente 2 linhas: a linha 0 contém o mínimo de cada coluna, e a linha 1 contém o máximo.
Log (tipo de pacote 10)
send_logs_level; consulte streaming de logs).
Mesmo formato de envelope e corpo que Data. O bloco tem num_columns = 8 fixo e um esquema predefinido. Cada linha de log corresponde a uma linha nas 8 colunas, e um único pacote Log pode carregar muitas linhas.
ProfileEvents (tipo de pacote 14)
num_columns = 6 fixo e um esquema predefinido. Cada evento corresponde a uma linha.
O tipo de elemento da coluna
value não é fixo de um pacote para outro — servidores mais antigos emitem UInt64, e os mais novos, Int64. Leia a string de tipo da coluna no cabeçalho do bloco, em vez de presumir um tamanho fixo.TableColumns (tipo de pacote 11)
COLUMN_DEFAULTS_METADATA (v54410). O servidor o envia antes do bloco de esquema do INSERT para carregar os metadados de valores padrão das colunas, mas apenas quando a versão negociada é ≥ 54410 e a configuração input_format_defaults_for_omitted_fields está habilitada. Abaixo de 54410, o pacote nunca é enviado, portanto um cliente mais antigo não deve esperar por ele — o bloco de esquema Data vem diretamente. Um cliente v54410+ deve estar preparado para qualquer uma das ordens: um TableColumns opcional, seguido pelo bloco de esquema.
Corpo comprimido a partir da v54481Na versão negociada ≥ 54481 (
COMPRESSED_LOGS_PROFILE_EVENTS_COLUMNS), o servidor grava ambos os campos pelo mesmo caminho de saída opcionalmente comprimido; portanto, quando a consulta tem compression = true, todo o corpo de TableColumns (external_table + columns_description) fica dentro do frame de compressão, e o cliente o lê pelo fluxo descomprimido correspondente. Quando a consulta não tem compressão, o corpo vai pela wire sem compressão, exatamente como a tabela acima mostra. Isso é importante para respostas de esquema de INSERT: um cliente que altere o tratamento de compressão para Log e ProfileEvents, mas não para TableColumns, lerá a resposta incorretamente quando a compressão da consulta estiver habilitada.TimezoneUpdate (tipo de pacote 17)
TIMEZONE_UPDATES (v54464). Enviado em exatamente um lugar: no inicializador da table function input (uma consulta no formato INSERT INTO <table> SELECT ... FROM input('<structure>'), que transmite linhas do cliente). Logo após o servidor enviar o bloco Data do esquema de entrada (veja a fase de INSERT), ele emite TimezoneUpdate com o session_timezone atual do contexto da consulta, para que o cliente analise as linhas que está prestes a enviar com o mesmo fuso horário. O servidor não emite esse pacote para alterações arbitrárias de SET session_timezone no meio da consulta, nem para informar ao cliente como formatar blocos de resultado posteriores.
O pacote chega uma vez, imediatamente após o bloco de esquema de entrada e antes de o cliente começar a enviar blocos de linhas. Um decodificador que ignora
TimezoneUpdate AINDA DEVE consumir a String final para manter o wire alinhado.
Autenticação SSH por desafio-resposta (tipos de pacote 11, 12, 18)
SSH_AUTHENTICATION (v54466) e habilitada apenas por ativação explícita. Uma conexão entra no fluxo SSH quando ClientHello envia user = " SSH KEY AUTHENTICATION " + <real_user> (com os espaços iniciais e finais) e password = "". O servidor lê o prefixo, remove-o para recuperar o usuário real e passa para o modo desafio-resposta.
O fluxo é executado no lugar da autenticação por senha, e a troca de desafio-resposta acontece antes de
ServerHello — o servidor adia sua resposta Hello até que a autenticação seja concluída com sucesso:
-
O cliente envia
ClientHellocom o prefixo marcador SSH e uma senha vazia. -
O cliente envia
SSHChallengeRequest(pacote 11). O servidor ainda não enviouServerHello— ele primeiro processa a autenticação e fica aguardando esse pacote. -
O servidor responde com
SSHChallengetrazendo bytes aleatórios (pacote 18). -
O cliente monta a string a ser assinada e assina essa string, não o desafio bruto, depois envia
SSHChallengeResponse(pacote 12) com a assinatura. A mensagem assinada é a concatenação byte a byte, sem separadores, de quatro partes nesta ordem exata: -
O servidor verifica a assinatura usando a chave pública registrada do usuário, reconstruindo a mesma string
decimal(protocol_version) + default_database + user + challenge. Em caso de sucesso, ele enviaServerHello— a mesma resposta do fluxo por senha — e o handshake continua normalmente (Addendum etc.); em caso de falha, retorna umaExceptione encerra a conexão. Um cliente que assinar apenas os bytes brutos dechallengefalhará na autenticação.
Isso é o inverso do handshake de senha, em que
ServerHello vem imediatamente após ClientHello. Na autenticação SSH, ServerHello fica retido até que a assinatura seja verificada, de modo que o mecanismo de desafio-resposta do SSH é intercalado ao handshake antes que qualquer ServerHello apareça.MergeTreeAllRangesAnnouncementResponse (tipo de pacote 14)
parallel_replicas_protocol_version ≥ 8 (consulte VERSIONED_PARALLEL_REPLICAS_PROTOCOL). Clientes externos nunca enviam este pacote.
Quando a versão negociada de réplicas paralelas é ≥ 8, o ciclo de solicitação/resposta do iniciador para um MergeTreeAllRangesAnnouncement de um seguidor (tipo de pacote 15, direção servidor→cliente) muda:
- Um seguidor abre seu pipeline de leitura e envia
MergeTreeAllRangesAnnouncementao iniciador. - Somente quando o
modedo anúncio não éDefault(WithOrder = 1ouReverseOrder = 2, ambos usados para leituras paralelas em ordem), o iniciador responde comMergeTreeAllRangesAnnouncementResponse. Paramode = Default = 0, o iniciador permanece em silêncio e o seguidor não espera — o modoDefaultdistribui intervalos a cadaMergeTreeReadTaskRequeste nunca precisa da lista prévia de partes. - O seguidor bloqueia aguardando a resposta (quando ela é esperada) antes de emitir sua primeira
MergeTreeReadTaskRequest(pacote do servidor16— enviado do seguidor para o iniciador; o iniciador responde comMergeTreeReadTaskResponse, pacote do cliente10), usando a lista de partes retornada para filtrar a construção das origens exatamente para as partes que pertencem ao seu stream#split_i.
8, o anúncio é fire-and-forget independentemente do modo, e o seguidor constrói origens sobre todas as partes conhecidas localmente (o comportamento legado).
Corpo
Corpo de RangesInDataPartsDescription
corpo de RangesInDataPartDescription
Corpo de MergeTreePartInfo
Corpo de MarkRanges
Referência de tipos de pacote
Cliente → Servidor
Servidor → Cliente
Configuração
- Configurações da camada de transporte — opções de socket TCP e timeouts, que afetam o comportamento da própria conexão TCP.
- Configurações da camada de aplicação — ajustes por consulta incluídos na lista de configurações do pacote consulta, que afetam o que o servidor envia no wire ou como isso é estruturado.
- Configurações fora do escopo — configurações frequentemente confundidas com configurações de protocolo, mas que na verdade controlam a execução de SQL ou o armazenamento.
Configurações da camada de transporte
Opções de socket
Timeouts
Os timeouts se encadeiam assim:
Limites de conexão
Uma conexão que executa consultas regularmente pode permanecer ativa indefinidamente. Apenas conexões ociosas são encerradas após uma hora, e não há um ciclo de vida máximo padrão.
Configurações da camada de aplicação
Compressão
O sinalizador
compression no pacote consulta (campo 6) ativa e desativa a compressão; essas configurações definem qual codec é usado quando ele está ativado.
Streaming de logs
Configurar
send_logs_level com qualquer valor diferente de "none" faz com que o servidor emita pacotes de Log durante a execução da consulta.
Relatório de Progress
Este é um mínimo esperado, não um máximo rígido: o servidor pode enviar pacotes Progress com menos frequência quando a consulta não estiver gerando trabalho com rapidez suficiente.
Envelope do resultado
INSERT assíncrono
Rastreamento distribuído
Configurações fora do escopo
max_threads— paralelismo na execução da consulta.max_memory_usage— limite de memória por consulta.max_block_size,preferred_block_size_bytes— dimensionamento interno de blocos durante o processamento da consulta; os blocos wire são independentes dessas configurações.compile_expressions— compilação JIT; afeta apenas a CPU.async_insert_max_data_size— buffer da fila no servidor.- Todas as configurações
input_format_*eoutput_format_*, exceto a famíliainput_format_native_*/output_format_native_*— as não-nativeselecionam ou ajustam outros formatos (por exemplo, via HTTP) e não alteram os blocosDatado protocolo nativo.
*_native_* são a exceção: elas alteram os bytes dentro dos blocos Data do native TCP, portanto uma implementação de protocolo deve levá-las em conta. output_format_native_encode_types_in_binary_format muda o campo type da coluna de uma string textual para uma codificação binária de tipo, output_format_native_write_json_as_string emite colunas JSON como String, e output_format_native_use_flattened_dynamic_and_json_serialization seleciona o layout FLATTENED de Dynamic/JSON. Como elas afetam o body do bloco, e não o envelope do pacote, elas são especificadas na especificação Native Format — consulte layout wire da coluna e tipos versionados.
Glossário
- Consulta normal (
SELECT, etc.): enviado após o pacote consulta e quaisquer pacotes Data de tabelas externas para sinalizar “não há mais dados externos”. O servidor então inicia a execução. INSERT: o cliente não envia um marcador antes do esquema. O servidor envia primeiro o bloco de esquema, o cliente transmite seus blocos Data de linhas e só então envia o pacote Data vazio para encerrar o fluxo de linhas. Enviar um marcador vazio antes do bloco de esquema seria interpretado como um fim imediato das linhas, e os dados seriam perdidos.
min(client_version, server_version), calculada durante o handshake. Determina quais recursos ficam ativos durante o ciclo de vida da conexão.
Pacote — uma mensagem wire: um código de tipo de pacote VarUInt seguido por um body cujo formato depende do tipo. Veja envelope do pacote.
Código de tipo de pacote — o VarUInt inicial de um pacote que identifica seu formato. Os valores de 0 a 18 estão atribuídos no momento. Veja a referência de tipos de pacote.
Fluxo de resposta — a sequência de pacotes que o servidor emite durante uma consulta. Seu comprimento é indefinido e ele é encerrado por exatamente um EndOfStream (sucesso) ou Exception (falha). Veja a fase da consulta.
Bloco de esquema — o bloco de cabeçalho (um Block com colunas, mas 0 linhas) que o servidor envia durante a fase de INSERT para informar os formatos de coluna esperados antes de o cliente enviar os dados.
Lista de configurações — uma sequência de tuplas (key, flags, value) no body de consulta, terminada por uma chave vazia. Carrega a configuração por consulta na camada de aplicação. Veja SETTING.
Estágio — um campo VarUInt no pacote consulta (campo 5) que controla até onde o servidor executa a consulta. Clientes externos normalmente enviam 2 (Complete); consultas distribuídas e planos de consulta serializados usam os valores mais altos. Veja o campo 5 de consulta para o conjunto completo de valores wire.
Terminador — um pacote que encerra um fluxo. A resposta de consulta termina em EndOfStream (sucesso) ou Exception (falha). O fluxo de entrada do cliente termina no marcador Data vazio.