INSERT, telemetría de ejecución y señales de error. Es el protocolo en el que se basan el Client de línea de comandos, el driver nativo de C++ y la mayoría de los drivers nativos de terceros.
Esta página describe el protocolo en sí: el encapsulado de paquetes, la máquina de estados de la conexión, la negociación de versiones y el cuerpo de todos los mensajes que no son Block. Los bytes dentro de los paquetes de la familia Data (el Block, sus columnas y las codificaciones de cada tipo) son un aspecto aparte, documentado en la especificación de Formato nativo.
Especificación complementariaEsta página es una de las dos partes de un conjunto y se publica junto con la especificación complementaria de Formato nativo. Ambas especificaciones reparten claramente el trabajo: esta página cubre la capa de paquetes y transporte; la especificación de Formato nativo cubre los bytes dentro de los paquetes de la familia
Data.BlockInfo, así que un solo byte fuera de lugar desincroniza todo lo que viene después. Tiene estado, y cada conexión TCP procesa una consulta cada vez; no hay multiplexación. Los enteros de ancho fijo están en formato little-endian.
Descripción general
Cada mensaje en el wire comienza con un código de tipo de paquete
VarUInt, seguido de un cuerpo cuya estructura depende de ese código y de la versión del protocolo negociada.
Una conexión pasa por tres fases: un handshake único, luego cualquier cantidad de intercambios Ping o Query, y por último el cierre:
El protocolo TCP nativo siempre transporta datos tabulares en el formato Native, independientemente de cualquier cláusula FORMAT en SQL. Volver a formatear en RowBinary, CSV, JSON, etc. es tarea del Client, y se hace después de decodificar los bloques Native. (La interfaz HTTP sigue una ruta de código distinta que sí respeta la cláusula FORMAT; HTTP queda fuera del alcance de esta explicación.)
Seguridad
Seguridad de transporte (TLS)
Autenticación
ClientHello. Los campos user y password se transmiten como cadenas de texto en claro, por lo que el cifrado a nivel de transporte (TLS) es lo que protege las credenciales durante el tránsito.
La autenticación SSH de desafío-respuesta está disponible a partir de la versión 54466 del protocolo; consulta Autenticación SSH de desafío-respuesta.
Secreto entre servidores
auth_hash SHA-256 de 32 bytes en el campo 4 de Query, calculado a partir de una sal, un nonce, el secreto configurado y la consulta; el servidor receptor lo vuelve a calcular y lo compara. Esto está controlado por la funcionalidad INTERSERVER_SECRET (v54441). Los Clients externos siempre envían aquí una cadena vacía. Consulte Autenticación entre servidores.
Versionado y control de funcionalidad
Negociación de versiones
Controles de funcionalidad
Tabla de funcionalidades
Encapsulado del paquete
VarUInt, no un byte de ancho fijo. Para valores inferiores a 128, un VarUInt produce el mismo byte, pero las implementaciones deben usar la codificación VarUInt para seguir siendo compatibles si en el futuro los tipos de paquetes llegan a 128 o más.
La referencia de mensajes documenta solo el cuerpo de cada paquete: los bytes que vienen después del código de tipo de paquete. La numeración de los campos comienza en 1 con el primer campo del cuerpo.
Enmarcado por fragmentos (v54470+)
CHUNKED_PROTOCOL (ver el handshake), cada paquete en el wire se encapsula con enmarcado por fragmentos. Este encapsulado es por dirección: Client→servidor y servidor→Client se negocian por separado y pueden quedar en modos distintos (por fragmentos o sin enmarcar).
Formato en el wire de cada paquete:
VarUInt está dentro del flujo fragmentado: es el primer byte de la carga útil del paquete (el primer byte del primer fragmento), no un byte separado enviado por delante del enmarcado. La carga útil fragmentada de cada paquete es el [VarUInt packet_type_code][message body] completo de la envoltura del paquete. Un Client que deja el tipo de paquete fuera del flujo fragmentado hace que el par lea ese byte de tipo como el primer byte del tamaño del fragmento u32, desincronizando la conexión.
Un solo paquete puede dividirse en varios fragmentos si el búfer del escritor se llena a mitad del paquete; la división puede caer en cualquier punto, incluso dentro del VarUInt del tipo de paquete. El lector concatena las cargas útiles de los fragmentos y trata el cero final de 4 bytes como un límite de paquete transparente: lo consume, pero no se lo expone a quien esté leyendo los cuerpos de los paquetes.
Los paquetes sin cuerpo siguen yendo encapsulados: un paquete de un solo byte como Ping o Pong se convierte en [u32 size = 1][0x04][u32 0] una vez que se negocia la fragmentación. Cualquier descripción de “un solo byte en el wire” en otra parte de esta página corresponde a la forma previa a la fragmentación.
Negociación. ServerHello y Addendum llevan cada uno dos campos String, uno por dirección, con valores tomados de {"chunked", "notchunked", "chunked_optional", "notchunked_optional"}:
chunked/notchunkedson estrictos: ese lado requiere exactamente ese modo.- Las variantes
_optionalson flexibles: aceptan el modo que elija el otro lado.
En el lado del Client, la preferencia de ENVÍO del Client se negocia frente a la preferencia de RECEPCIÓN del servidor, y viceversa.
Temporización. Las cadenas de negociación viajan por el wire sin enmarcado: ClientHello → ServerHello (preferencias del servidor) → Addendum (valores negociados del Client). El cambio al enmarcado se aplica a cada byte enviado después de que se vacíe el Addendum. El propio Addendum, ClientHello y ServerHello siempre van sin enmarcado.
Ciclo de vida de la conexión
HANDSHAKE, READY, READING_RESPONSE o terminada. Como el protocolo no multiplexa, un Client que envía una nueva solicitud antes de terminar de leer la respuesta anterior intercala bytes en tránsito y corrompe el flujo.
Estados
HANDSHAKE → READY → READING_RESPONSE → READY — con el bucle de Ping/Pong y todas las transiciones de error convergiendo en el único estado terminal Terminated.
Fase de handshake
-
El Client envía
ClientHellocon la versión máxima del protocolo que admite. -
El Client lee la respuesta y actúa según el tipo de paquete:
-
Si
negotiated_version ≥ 54458(la funcionalidadADDENDUM), el Client envía unAddendum. Esta decisión se basa en la versión negociada, no en la versión declarada del Client.
READY; ante cualquier error, se termina.
Fase de Ping
READY, el flujo es:
-
El Client envía
Ping. -
El Client lee la respuesta:
Fase de consulta
EndOfStream o una Exception.
Partiendo de READY, el flujo es:
Ante cualquier error, el servidor envía una Exception en lugar de EndOfStream, lo que termina la consulta.
-
El Client envía
Querycon unquery_idúnico (normalmente, un UUID). -
El Client envía cualquier tabla externa y luego el marcador
Datavacío. El paqueteDatavacío tienetable_name = "",num_columns = 0,num_rows = 0. El servidor no empieza a ejecutar la consulta hasta que recibe este marcador. -
El Client pasa a
READING_RESPONSEy vacía su búfer de escritura. -
El Client lee los paquetes de respuesta en un bucle y los procesa según su tipo:
EndOfStream o una Exception controlada, la conexión vuelve a READY. Una infracción del protocolo o un error de I/O la termina.
El caso
num_rows == 0 suele confundir a las implementaciones nuevas. Un bloque de cero filas es un marcador de límite o una cabecera de esquema, no una señal de fin de stream. Solo EndOfStream o Exception pone fin a la respuesta.Fase INSERT
INSERT; el servidor responde con un bloque de esquema que describe la tabla de destino; el Client transmite paquetes Data con las filas y, después, el marcador Data vacío; el servidor finaliza con EndOfStream o Exception.
Partiendo de READY, el SQL es un INSERT con la forma INSERT INTO <table> [(<cols>)] VALUES — sin ningún literal VALUES (...) en línea, ya que los datos de las filas fluyen a través de paquetes Data. El flujo:
- El Client envía
Queryconbodyestablecido en el SQL de INSERT. - El Client envía cualquier tabla externa (algo poco habitual en INSERT). A diferencia de la fase consulta, no envía aquí un marcador Data vacío. El paquete
consultadeINSERTse envía con datos pendientes, por lo que el bloque vacío de fin de datos se difiere hasta el paso 5; enviarlo antes del bloque de esquema haría que el servidor lo interpretara como el final del flujo de filas, completara el INSERT sin filas y luego analizara el primer paquete de fila real como un paquete suelto de nivel superior. - El Client consume los paquetes de metadatos (TableColumns, Progress, ProfileInfo, Log, ProfileEvents) hasta leer el paquete Data del esquema: un Block con 0 filas, pero con la estructura completa de columnas (nombres y tipos). El bloque de esquema es el contrato: las filas que el Client envíe a continuación deben coincidir con estas estructuras de columna.
- El Client envía uno o varios bloques de datos. Para cada bloque, escribe
VarUInt(ClientPacket::Data = 2), luegoString("")para el nombre vacío de la tabla externa y, después, el Block. Los tipos de columna deben alinearse por posición con las columnas del bloque de esquema. - El Client envía el terminador de fin de entrada: un paquete Data con un Block vacío (0 columnas, 0 filas).
- El Client consume el flujo de respuesta hasta
EndOfStream(éxito) oException(fallo).
async_insert = 1, el servidor pone las filas en cola y las vacía como parte de un lote. Con una versión negociada ≥ 54484 (PROGRESS_IN_ASYNC_INSERT), una vez completado el vaciado, el servidor emite un paquete adicional de Progress, seguido inmediatamente por los ProfileEvents del insert y luego EndOfStream. Por debajo de 54484, el servidor omite ese Progress final. El paquete es un Progress normal; como el servidor restablece el query pipeline antes de incorporar los recuentos de escritura, en la práctica el incremento solo contiene el tiempo transcurrido, y las estadísticas de filas y bytes escritos llegan al Client mediante los ProfileEvents correspondientes. Un Client que ya consume paquetes Progress entrelazados en el paso 6 solo necesita aceptar uno más.
La conexión vuelve a READY en EndOfStream o tras una Exception controlada. Las infracciones del protocolo y los errores de I/O la terminan.
Referencia de mensajes
Type usa:
VarUInt— entero sin signo de longitud variable (consulta VarUInt).String— bytes con prefijoVarUInt(consulta String).UInt8,Int32, etcétera — enteros little-endian de ancho fijo.Bool— un único byte,0x00o0x01.
Role indica quién usa cada campo:
- Client — lo establecen los clientes externos.
- inter-server — solo tiene sentido para la comunicación entre servidores; los clientes externos escriben un valor predeterminado.
- universal — lo usan ambos.
ClientHello (tipo de paquete 0)
ServerHello (tipo de paquete 0)
Rule — un elemento de
password_complexity_rules:
La lista refleja la configuración de la política de contraseñas del operador del servidor y es puramente informativa: el servidor no aplica estas reglas durante el handshake. Un Client que exponga funcionalidad para cambiar o establecer contraseñas puede usar las reglas para señalar errores antes de enviar al servidor una contraseña no válida.
Para limitar el uso de recursos frente a un servidor hostil o mal configurado, limite el
count decodificado a 256 entradas y cada String pattern y message a 4096 bytes. Un count de 0 (sin pares posteriores) es el caso habitual en servidores sin ninguna política de contraseñas configurada.Apéndice (sin tipo de paquete)
ADDENDUM (v54458). Se envía inmediatamente después de que se completa el intercambio de handshake. No es un tipo de paquete distinto: los campos van por el wire en bruto, sin prefijo de byte de tipo de paquete.
El cambio al enmarcado por fragmentos se aplica después de que este Apéndice se haya enviado por completo; el propio Apéndice no lleva enmarcado.
Ping (tipo de paquete 4)
0x04 antes del enmarcado por fragmentos; cuando se negocia la fragmentación, el byte pasa a ser el payload de un byte de un fragmento (consulte enmarcado por fragmentos).
Pong (tipo de paquete 4)
0x04 antes del enmarcado por fragmentos; cuando se negocia la fragmentación, el byte pasa a ser el payload de un byte de un fragmento (consulta el enmarcado por fragmentos).
Exception (tipo de paquete 2)
Consulta (tipo de paquete 1)
ClientInfo (incluido en consulta)
CLIENT_INFO (v54032). (Algunos campos dentro de ClientInfo están condicionados por versiones posteriores, como se indica más abajo en cada campo.)
Layout dependiente de la interfaz (campos 7–12)Los campos 7–12 anteriores corresponden a la rama TCP. Cuando
query_interface (campo 6) no es TCP, estos campos se sustituyen por un layout wire diferente; no son simples omisiones opcionales, por lo que un decodificador debe bifurcarse en función del campo 6.query_interface = 2(HTTP): en su lugar, se escribe la información de la solicitud HTTP reenviada por el servidor:http_method(UInt8),http_user_agent(String), y despuésforwarded_for(String, condicionado porX_FORWARDED_FOR_IN_CLIENT_INFOv54443) yhttp_referer(String, condicionado porREFERER_IN_CLIENT_INFOv54447). No están presentes los camposos_user/client_hostname/client_name/version_*/protocol_version.- Cualquier otra interfaz: no se escribe ninguno de los campos TCP (7–12) ni ninguno de los campos HTTP; el flujo continúa directamente con
quota_key.
quota_key (campo 13) y distributed_depth (campo 14) aparecen en todas las interfaces, y luego version_patch (campo 15) se escribe solo para TCP.Esta rama importa principalmente para el tráfico entre servidores, donde el servidor que inicia reenvía una consulta que originalmente llegó por HTTP. Un decodificador que siempre lea los campos TCP interpretará mal esos paquetes, tratando http_method o http_user_agent como quota_key.Autenticación entre servidores
auth_hash) no es el secreto compartido del clúster en la representación wire. Enviar el secreto en bruto haría que la autenticación fallara y además lo expondría. En su lugar, un servidor que actúa como Client entre servidores demuestra que conoce el secreto con un hash SHA-256 con salt:
- Entrar en modo entre servidores. El servidor que se conecta lo indica dentro de
ClientHello: el campouseres el marcador entre servidores ypasswordestá vacío. A continuación, añade dos strings más —el nombre del clúster y unasaltde 32 bytes recién generada (encodeSHA256de un valor aleatorio)— inmediatamente después de los camposuser/password, como parte del mismo paqueteClientHello. El servidor lee estas dos strings antes de enviarServerHello, por lo que un Client debe escribirlas de antemano; esperar primero aServerHelloprovoca interbloqueos, porque el servidor queda bloqueado leyéndolas. - Obtener el nonce.
ServerHelloincluye un nonceUInt64de 8 bytes cuando se negociaINTERSERVER_SECRET_V2(v54462). - Calcular el hash. Para cada paquete consulta que no sea
InitialQuery, el Client escribeencodeSHA256(salt + nonce + cluster_secret + query + query_id + initial_user + external_roles)en el campo 4: un digest de 32 bytes. (nonceva en su forma de string decimal, presente solo cuando se negocia ≥ v54462;external_rolesse añade solo cuando se negociaINTERSERVER_EXTERNALLY_GRANTED_ROLES(v54472).) Para unInitialQuery, o cuando no hay ningún secreto de clúster configurado, el Client escribe en su lugar un string vacío. - Verificar. El servidor lee el campo 4 con un límite de 32 bytes y vuelve a calcular la misma concatenación usando su propia copia del secreto del clúster; la conexión se rechaza si los digests difieren.
auth_hash vacío.
Configuración
key vacía: un único VarUInt 0, sin indicadores ni valor a continuación. Solo la codificación de cada configuración depende de la versión negociada, controlada por SETTINGS_SERIALIZED_AS_STRINGS (v54429).
v54429+ (STRINGS_WITH_FLAGS) — cada configuración es el triplete que se muestra aquí:
Los campos 2 y 3 no están presentes cuando
key está vacía.
Pre-54429 (BINARY) — cada configuración es [String key][type-specific binary value]: el campo flags no se escribe, y el valor se codifica en la forma binaria nativa de la configuración (por ejemplo, un entero de ancho fijo o una cadena con prefijo de longitud) en lugar de como una cadena decimal o de texto. La lista sigue terminando con una key vacía. Un client que apunte a una versión negociada inferior a 54429 debe leer y escribir esta forma binaria, no el triplete anterior. (Las configuraciones personalizadas definidas por el usuario son la excepción: siempre llevan flags y un valor de cadena, en ambas codificaciones).
El campo flags contiene:
0x01— Importante: la configuración afecta a los resultados de la consulta y no debe ser ignorada silenciosamente por pares más antiguos.0x02— Personalizada: una configuración personalizada definida por el usuario.0x0c— un campo tier de 2 bits, no un indicador independiente:0x00= Production,0x04= Obsolete,0x08= Experimental,0x0c= Beta. Lea los 2 bits completos (flags & 0x0c) — una comprobación ingenua deflags & 0x04clasificaría erróneamente Beta (0x0c) como Obsolete.0x80— HotReload (recarga de configuración sin reinicio; definido en el enum de indicadores, presente principalmente en configuraciones de coordinación).
Parámetro
SELECT {x:UInt64}. Se codifican de forma idéntica a un SETTING con el indicador Custom (0x02) activado, y terminan con una clave vacía de la misma forma.
El valor del parámetro es la representación SQL del valor, no un literal sin procesar. Los parámetros de tipo cadena deben pasarse ya entre comillas simples (por ejemplo, el valor de
{name:String} es 'Alice', no Alice); de lo contrario, el analizador de valores del servidor los rechazará.Data (tipo de paquete 1 servidor→client, tipo de paquete 2 client→servidor)
table_name antes del Block. Solo cambia el byte del tipo de paquete.
El marcador de fin de datos es un paquete cuyo Block está vacío:
0 columnas y 0 filas, independientemente de table_name. El servidor trata un paquete Data del client como terminador solo cuando el bloque decodificado está vacío (block.empty()); un paquete con table_name = "" y un bloque no vacío es un paquete de filas normal, no un terminador. Por tanto, un flujo de filas de INSERT es una secuencia de bloques Data no vacíos seguida de un bloque Data vacío que lo finaliza.
Las variantes de bloque y su significado se documentan en Block variants.
Progress (tipo de paquete 3)
Progress anterior, no totales acumulados. Antes de enviarlo, el server lee sus contadores, los restablece atómicamente a cero y calcula elapsed_ns como la diferencia de tiempo desde el último envío. Por lo tanto, un client debe acumular localmente los paquetes sucesivos para obtener totales acumulados; tratar un paquete como un valor absoluto hace que la visualización del progreso retroceda o cuente de menos cuando llega más de un paquete.
ProfileInfo (tipo de paquete 6)
Totales (tipo de paquete 7)
WITH TOTALS. El formato de transmisión es idéntico a Data: una cadena table_name (siempre vacía) seguida de un Block. Solo cambia el byte del tipo de paquete.
Extremes (tipo de paquete 8)
extremes está habilitada. El formato de transmisión es idéntico a Data. El bloque tiene exactamente 2 filas: la fila 0 contiene el mínimo de cada columna y la fila 1, el máximo.
Log (packet type 10)
send_logs_level; consulte streaming de logs).
El formato del sobre y del cuerpo es el mismo que el de Data. El bloque tiene un num_columns = 8 fijo y un esquema predefinido. Cada línea de log ocupa una fila en las 8 columnas, y un único paquete Log puede contener muchas filas.
ProfileEvents (tipo de paquete 14)
num_columns = 6 fijo y un esquema predefinido. Cada evento es una fila.
El tipo de elemento de la columna
value no es fijo entre paquetes: los servidores antiguos emiten UInt64 y los más nuevos, Int64. Lee la cadena de tipo de la columna desde la cabecera del bloque en lugar de asumir un tamaño fijo.TableColumns (tipo de paquete 11)
COLUMN_DEFAULTS_METADATA (v54410). El server lo envía antes del bloque de esquema de INSERT para transportar metadatos de valores predeterminados de las columnas, pero solo cuando la versión negociada es ≥ 54410 y la configuración input_format_defaults_for_omitted_fields está habilitada. Por debajo de 54410, el paquete no se envía nunca, por lo que un client más antiguo no debe esperarlo: el bloque de esquema Data llega directamente. Un client v54410+ debe estar preparado para cualquiera de estas dos secuencias: un TableColumns opcional y luego el bloque de esquema.
Cuerpo comprimido a partir de v54481Con una versión negociada ≥ 54481 (
COMPRESSED_LOGS_PROFILE_EVENTS_COLUMNS), el server escribe ambos campos a través de la misma ruta de salida con compresión opcional, de modo que, cuando la consulta tiene compression = true, todo el cuerpo de TableColumns (external_table + columns_description) va dentro del frame de compresión; el client lo lee a través del flujo descomprimido correspondiente. Cuando la consulta no tiene compresión, el cuerpo se transmite sin comprimir, exactamente como muestra la tabla anterior. Esto es importante para las respuestas de esquema de INSERT: un client que cambie el manejo de la compresión para Log y ProfileEvents, pero no para TableColumns, interpretará mal la respuesta cuando la compresión de la consulta esté habilitada.TimezoneUpdate (tipo de paquete 17)
TIMEZONE_UPDATES (v54464). Se envía exactamente en un lugar: el inicializador de la table function input (una consulta de la forma INSERT INTO <table> SELECT ... FROM input('<structure>'), que transmite filas desde el client). Justo después de que el servidor envía el bloque Data del esquema de entrada (consulta la fase INSERT), emite TimezoneUpdate con la session_timezone actual del contexto de la consulta para que el client procese las filas que está a punto de enviar con la misma zona horaria. El servidor no emite este paquete para cambios arbitrarios de SET session_timezone a mitad de consulta, ni para indicarle al client cómo debe dar formato a bloques de resultados posteriores.
El paquete llega una sola vez, inmediatamente después del bloque del esquema de entrada y antes de que el client empiece a enviar bloques de filas. Un decodificador que ignore
TimezoneUpdate DEBE igualmente consumir el String final para mantener alineado el wire.
Autenticación SSH de desafío-respuesta (tipos de paquete 11, 12, 18)
SSH_AUTHENTICATION (v54466) y disponible solo mediante activación explícita. Una conexión entra en el flujo SSH cuando ClientHello envía user = " SSH KEY AUTHENTICATION " + <real_user> (con los espacios inicial y final) y password = "". El servidor lee el prefijo, lo elimina para recuperar el usuario real y cambia al modo de desafío-respuesta.
Este flujo se ejecuta en lugar de la autenticación por contraseña, y el intercambio de desafío-respuesta ocurre antes de ServerHello: el servidor aplaza su respuesta Hello hasta que la autenticación se complete correctamente:
- El cliente envía ClientHello con el prefijo marcador SSH y una contraseña vacía.
-
El cliente envía
SSHChallengeRequest(paquete 11). El servidor todavía no ha enviado ServerHello: primero procesa la autenticación y queda bloqueado aquí, esperando este paquete. -
El servidor responde con
SSHChallenge, que contiene bytes aleatorios (paquete 18). -
El cliente construye la cadena que debe firmar y firma esa, no el desafío en bruto; luego envía
SSHChallengeResponse(paquete 12) con la firma. El mensaje firmado es la concatenación byte a byte, sin separadores, de cuatro partes en este orden exacto: -
El servidor verifica la firma con la public key registrada del usuario, reconstruyendo la misma cadena
decimal(protocol_version) + default_database + user + challenge. Si la validación tiene éxito, envíaServerHello—la misma respuesta que en el flujo con contraseña— y el handshake continúa con normalidad (Addendum, etc.); si falla, devuelve unaExceptiony termina la connection. Un client que firme solo los bytes del desafío en bruto no superará la autenticación.
Esto es lo contrario del handshake de autenticación por contraseña, en el que ServerHello sigue inmediatamente a ClientHello. Con la autenticación SSH, ServerHello se retiene hasta que se verifica la firma, por lo que el challenge-response de SSH se intercala en el handshake antes de que aparezca cualquier ServerHello.
Referencia de tipos de paquete
Cliente → Servidor
Servidor → Cliente
Configuración
- ajuste de la capa de transporte — opciones de socket TCP y tiempos de espera, que afectan al comportamiento de la propia conexión TCP.
- ajuste de la capa de aplicación — parámetros ajustables por consulta incluidos en la lista de settings del paquete Query, que afectan a lo que el servidor envía por la red o a cómo se estructura.
- ajuste fuera del alcance — settings que a menudo se confunden con la configuración del protocolo, pero que en realidad controlan la ejecución de SQL o el almacenamiento.
Ajustes de la capa de transporte
Opciones de socket
Tiempos de espera
Los tiempos de espera se anidan así:
Límites de conexión
Una conexión que ejecuta consultas con regularidad puede permanecer activa indefinidamente. Solo las conexiones inactivas se cierran al cabo de una hora, y no existe un tiempo de vida máximo predeterminado.
Ajustes de la capa de aplicación
Compresión
El campo
compression del paquete Query (campo 6) activa o desactiva la compresión; estos ajustes seleccionan qué códec se usa cuando está activada.
Streaming de logs
Establecer
send_logs_level en cualquier valor distinto de "none" hace que el servidor emita paquetes Log durante la ejecución de la consulta.
Reporte de Progress
Este es un mínimo objetivo, no un máximo estricto: el servidor puede enviar paquetes Progress con menor frecuencia cuando la consulta no genera trabajo con suficiente rapidez.
Envolvente del resultado
INSERT asíncrono
Trazabilidad distribuida
Ajustes fuera del alcance
max_threads— paralelismo dentro de la ejecución de la consulta.max_memory_usage— límite de memoria por consulta.max_block_size,preferred_block_size_bytes— dimensionamiento interno de bloques durante el procesamiento de consultas; los bloques en el wire son independientes de estos.compile_expressions— compilación JIT; solo CPU.async_insert_max_data_size— búfer de cola del lado del servidor.- Todos los ajustes
input_format_*youtput_format_*excepto la familiainput_format_native_*/output_format_native_*— los que no sonnativeseleccionan o ajustan otros formatos (por ejemplo, sobre HTTP) y no cambian los bloquesDatadel protocolo nativo.
*_native_* son la excepción: cambian los bytes dentro de los bloques Data del TCP nativo, por lo que una implementación del protocolo debe tenerlos en cuenta. output_format_native_encode_types_in_binary_format cambia el campo type de la columna de una cadena de texto a una codificación binaria de tipos, output_format_native_write_json_as_string emite las columnas JSON como String, y output_format_native_use_flattened_dynamic_and_json_serialization selecciona el layout FLATTENED de Dynamic/JSON. Como afectan al cuerpo del bloque y no a la envoltura del paquete, se especifican en la especificación Native Format; consulta column wire layout y versioned types.
Glosario
- Consulta normal (
SELECT, etc.): se envía después del paquete Query y de cualquier paquete Data de tablas externas para indicar que “no hay más datos externos”. El server comienza entonces la ejecución. INSERT: el client no envía un marcador previo al esquema. El server envía primero el bloque de esquema, el client transmite sus blocks Data de filas y solo después envía el paquete Data vacío para terminar el flujo de filas. Si se enviara un marcador vacío antes del bloque de esquema, se interpretaría como un final inmediato de las filas y los datos se perderían.
min(client_version, server_version), calculada durante el handshake. Determina qué features están activas durante el lifetime de la connection.
Packet — un mensaje wire: un código de tipo de paquete VarUInt seguido de un body cuyo formato depende del tipo. Consulta envoltura de paquete.
Código de tipo de paquete — el VarUInt inicial de un paquete que identifica su formato. Actualmente están asignados los valores 0–18. Consulta la referencia de tipos de paquetes.
Flujo de respuesta — la secuencia de paquetes que el server emite durante una consulta. Su longitud es abierta y termina con exactamente un EndOfStream (éxito) o Exception (error). Consulta la fase de consulta.
Schema block — el header block (un Block con columnas pero 0 filas) que el server envía durante la fase de INSERT para indicar las shapes de columna esperadas antes de que el client envíe datos.
Lista de Settings — una secuencia de tuples (key, flags, value) en el body de Query, terminada por una key vacía. Lleva la configuración por consulta de la capa de aplicación. Consulta Setting.
Stage — un field VarUInt del paquete Query (field 5) que controla hasta dónde ejecuta la consulta el server. Los clients externos suelen enviar 2 (Complete); las distributed queries y los planes de consulta serializados usan los valores más altos. Consulta el field 5 de Query para ver el conjunto completo de valores wire.
Terminator — un paquete que pone fin a un flujo. La respuesta de Query termina con EndOfStream (éxito) o Exception (error). El flujo de entrada del client termina con el marcador Data vacío.