> ## Documentation Index
> Fetch the complete documentation index at: https://private-7c7dfe99-fix-nav-issues.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Client de C de solo cabecera para el protocolo nativo de ClickHouse, diseñado para integrarse.

# Client de C de ClickHouse

`clickhouse-c` es un client de C de solo cabecera para el [protocolo nativo](/es/interfaces/tcp) de ClickHouse.
El código fuente y la referencia de cada cabecera están en el [repositorio de GitHub](https://github.com/ClickHouse/clickhouse-c).

A diferencia de los clients de más alto nivel, deliberadamente hace muy poco por ti. La cabecera principal decodifica y
codifica bloques en formato [Native](/es/interfaces/formats/Native) mediante una callback de E/S que tú proporcionas. Tú te encargas
del socket, el contexto TLS, el asignador de memoria, los reintentos y el pooling de conexiones. Eso hace que sea lo bastante pequeño como para
integrarse: incluir solo `clickhouse.h` no añade dependencias de enlazado aparte de libc.

<Note>
  Esta biblioteca está en desarrollo activo. La v1 decodifica los tipos básicos de ClickHouse.
  Informa sobre limitaciones o funcionalidades faltantes mediante el [rastreador de issues](https://github.com/ClickHouse/clickhouse-c/issues).
  Ten en cuenta, sin embargo, que a esta biblioteca le falta funcionalidad por diseño.
</Note>

<div id="non-goals">
  ## Lo que la biblioteca no hace
</div>

Estos son objetivos deliberadamente fuera de alcance. Gestiónelos en su aplicación o con una biblioteca complementaria:

* Protocolo HTTP. Envuelva libcurl directamente para la [interfaz HTTP](/es/interfaces/http).
* Resolución DNS, failover de endpoints, pooling de conexiones, reintentos y backoff.
* Ciclo de vida del contexto TLS. El backend de OpenSSL usa un `SSL` que ya debe estar conectado.
* Hilos. Cada `chc_client` es monohilo por diseño.
* E/S asíncrona dentro de la biblioteca. El client bloqueante llama a `chc_io.read` de forma síncrona. Para un
  client de bucle de eventos que no realiza ninguna E/S por sí mismo, use el [client sin E/S](#async-client).

<div id="headers">
  ## Cómo se organiza la biblioteca
</div>

`clickhouse-c` se distribuye como un conjunto simple de archivos de cabecera. Cada cabecera incluye tanto las declaraciones como la implementación,
protegidas por una macro centinela. Selecciona las cabeceras que necesite tu compilación.

| Cabecera                                                                                                         | Propósito                                                                                                       | Flags de enlace  |
| ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ---------------- |
| [`clickhouse.h`](https://github.com/ClickHouse/clickhouse-c/blob/main/doc/clickhouse.md)                         | Núcleo: tipos, errores, asignador, vtable de E/S, parser de nombres de tipos, lector y escritor de bloques      | —                |
| [`clickhouse-client.h`](https://github.com/ClickHouse/clickhouse-c/blob/main/doc/clickhouse-client.md)           | Bucle de paquetes TCP: Hello, Query, Data, EndOfStream, Exception, Progress, Pong                               | —                |
| [`clickhouse-async.h`](https://github.com/ClickHouse/clickhouse-c/blob/main/doc/clickhouse-async.md)             | client sin E/S: el mismo bucle de paquetes, controlado por el envío de bytes por parte del llamador, sin socket | —                |
| [`clickhouse-compression.h`](https://github.com/ClickHouse/clickhouse-c/blob/main/doc/clickhouse-compression.md) | Estructura de tramas comprimidas, CityHash128, despacho de codecs y adaptadores `LZ4`/`ZSTD`                    | `-llz4 -lzstd`   |
| [`clickhouse-posix-io.h`](https://github.com/ClickHouse/clickhouse-c/blob/main/doc/clickhouse-posix-io.md)       | Backend de E/S sobre `read(2)`/`write(2)` en modo bloqueante                                                    | —                |
| [`clickhouse-openssl.h`](https://github.com/ClickHouse/clickhouse-c/blob/main/doc/clickhouse-openssl.md)         | Backend de E/S sobre `SSL_read`/`SSL_write`                                                                     | `-lssl -lcrypto` |

<div id="server-setting">
  ## Configuración del servidor requerida
</div>

El decodificador lee del flujo transmitido nombres de tipo imprimibles, por lo que deben codificarse como texto. ClickHouse
los escribe como texto de forma predeterminada, pero fija esta configuración en tus consultas para evitar que un perfil de servidor o de sesión
que la establezca en binario rompa la decodificación:

```plaintext theme={null}
output_format_native_encode_types_in_binary_format = 0
```

<div id="adding-to-project">
  ## Añadirlo a tu proyecto
</div>

No hay ningún paquete que instalar, así que debes incorporar las cabeceras a tu árbol mediante un submódulo de Git o una copia.
Exactamente una unidad de traducción define `CHC_IMPLEMENTATION` e incluye la implementación;
todas las demás unidades incluyen las mismas cabeceras solo para las declaraciones.

```c theme={null}
/* clickhouse_impl.c */
#define CHC_IMPLEMENTATION
#include "clickhouse.h"
#include "clickhouse-posix-io.h"
#include "clickhouse-client.h"
#include "clickhouse-compression.h"
```

```c theme={null}
/* every other TU */
#include "clickhouse.h"
#include "clickhouse-client.h"
```

Define `CHC_PROVIDE_STDLIB_ALLOC` antes de incluir `clickhouse.h` para usar `chc_alloc_stdlib`.
Define `CHC_NO_LZ4` o `CHC_NO_ZSTD` para `clickhouse-compression.h` para prescindir de la dependencia de lz4/zstd.

<div id="connecting-over-tcp">
  ## Conexión por TCP
</div>

Para comunicarse con un servidor ClickHouse, debe configurar usted mismo el socket, encapsularlo en un `chc_io` y pasárselo a `chc_client_init`, que ejecuta el handshake Hello de forma síncrona. La biblioteca no se encarga de DNS, failover, reconexión ni pooling; eso queda a cargo del llamador.

```c theme={null}
int fd = socket(AF_INET, SOCK_STREAM, 0);
int one = 1;
setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof one);

struct sockaddr_in sa = {};
sa.sin_family      = AF_INET;
sa.sin_port        = htons(9000);
sa.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
connect(fd, (struct sockaddr *) &sa, sizeof sa);

chc_alloc al = chc_alloc_stdlib();
chc_posix_io state;
chc_io io;
chc_posix_io_init(&state, &io, fd, NULL, NULL);

chc_client *client = NULL;
chc_client_opts opts = {
    .user     = "default",
    .password = "",
    .database = "default",
};
chc_err err = {};
if (chc_client_init(&client, &opts, &al, &io, &err) != CHC_OK) {
    fprintf(stderr, "connect: %s\n", err.msg);
    chc_client_close(client);   /* safe to call on the NULL-on-failure handle */
    return 1;
}

const chc_server_info *info = chc_client_server_info(client);
printf("connected to %s %llu.%llu.%llu\n", info->display_name,
       (unsigned long long) info->version_major,
       (unsigned long long) info->version_minor,
       (unsigned long long) info->version_patch);
```

Cada `chc_client` es monohilo y encapsula una conexión. La biblioteca invoca los
callbacks de `chc_io` de forma síncrona; lo que hagan internamente esos callbacks (`epoll`, `io_uring`,
`WaitLatchOrSocket`) depende de usted.

<div id="running-a-query">
  ## Ejecutar una consulta
</div>

Envíe la consulta y, a continuación, siga leyendo paquetes hasta `CHC_PKT_END_OF_STREAM`. Use `chc_client_send_query_ex` para
incluir la [configuración de servidor requerida](#server-setting); `chc_client_send_query`, sin más, envía una
lista de configuraciones vacía y hereda los valores predeterminados del servidor.

```c theme={null}
chc_query_setting settings[] = {
    { .name = "output_format_native_encode_types_in_binary_format", .value = "0" },
};
chc_query_opts qopts = { .settings = settings, .n_settings = 1 };

const char *sql = "SELECT number, toString(number * number) FROM numbers(5)";
if (chc_client_send_query_ex(client, sql, strlen(sql), &qopts, &err) != CHC_OK) {
    fprintf(stderr, "query: %s\n", err.msg);
    return 1;
}

for (;;) {
    chc_packet pkt = {};
    if (chc_client_recv_packet(client, &pkt, &err) != CHC_OK) {
        fprintf(stderr, "recv: %s\n", err.msg);
        break;
    }

    if (pkt.kind == CHC_PKT_DATA) {
        for (size_t r = 0; r < chc_block_n_rows(pkt.block); r++)
            for (size_t c = 0; c < chc_block_n_columns(pkt.block); c++)
                print_value(chc_block_column_type(pkt.block, c),
                            chc_block_column(pkt.block, c), r);
    } else if (pkt.kind == CHC_PKT_EXCEPTION) {
        fprintf(stderr, "server: %s\n", pkt.exception->display_text);
    }

    bool done = pkt.kind == CHC_PKT_END_OF_STREAM;
    chc_packet_clear(client, &pkt);
    if (done) break;
}
```

Las excepciones del servidor llegan como paquetes `CHC_PKT_EXCEPTION`, no como un valor distinto de OK devuelto por
`chc_client_recv_packet`. Solo los fallos de nivel de transporte devuelven un valor no OK. El primer paquete `CHC_PKT_DATA`
de un resultado es un bloque de encabezado que describe el esquema con cero filas; después vienen los bloques de datos.
`chc_packet_clear` libera el bloque o la excepción del paquete; primero establezca esos campos del paquete en NULL para
quedarse con la propiedad en su lugar.

<div id="reading-column-data">
  ## Lectura de datos de columnas
</div>

Los bloques están orientados a columna. Cada columna tiene una disposición física, devuelta por `chc_column_layout`, y el despacho se hace en función de ella; su tipo declarado proviene de `chc_block_column_type`. Las disposiciones compuestas se anidan, por lo que leer un `Nullable(Array(String))` implica desenvolver el `Nullable`, recorrer los desplazamientos del array y, después, segmentar los datos de texto.

| Disposición               | Accesores                                                                                                                                                             |
| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CHC_COL_FIXED`           | `chc_column_fixed_data(c, &elem_size)` — `n_rows * elem_size` bytes en orden little-endian                                                                            |
| `CHC_COL_STRING`          | `chc_column_string_data(c)`, `chc_column_string_offsets(c)` — `offsets[i]` es el final exclusivo de la fila `i` en el orden de bytes del host; la fila 0 empieza en 0 |
| `CHC_COL_NULLABLE`        | `chc_column_null_map(c)` (un byte por fila, 1 = `NULL`), `chc_column_nullable_inner(c)`                                                                               |
| `CHC_COL_ARRAY`           | `chc_column_array_offsets(c)` (finales acumulados), `chc_column_array_values(c)`; `Map` se decodifica como `Array(Tuple(K, V))`                                       |
| `CHC_COL_TUPLE`           | `chc_column_tuple_arity(c)`, `chc_column_tuple_child(c, i)` — cada hijo tiene el mismo número de filas                                                                |
| `CHC_COL_LOW_CARDINALITY` | `chc_column_lc_key_size(c)` (1/2/4/8), `chc_column_lc_keys(c)`, `chc_column_lc_dict(c)`; la ranura 0 del diccionario es el valor predeterminado                       |

Un lector para columnas numéricas simples, de texto y Nullable:

```c theme={null}
void print_value(const chc_type *t, const chc_column *c, size_t row)
{
    if (chc_column_layout(c) == CHC_COL_NULLABLE) {
        if (chc_column_null_map(c)[row]) { fputs("\\N", stdout); return; }
        print_value(chc_type_child(t, 0), chc_column_nullable_inner(c), row);
        return;
    }

    switch (chc_column_layout(c)) {
    case CHC_COL_FIXED: {
        /* fixed_data is a raw little-endian byte slab. memcpy into a typed
           local to avoid unaligned loads and strict-aliasing UB, then
           byte-swap on big-endian hosts. */
        size_t es;
        const uint8_t *p = chc_column_fixed_data(c, &es) + row * es;
        switch (chc_type_kind(t)) {
        case CHC_UINT64: { uint64_t v; memcpy(&v, p, sizeof v); printf("%" PRIu64, v); break; }
        case CHC_INT32:  { int32_t  v; memcpy(&v, p, sizeof v); printf("%" PRId32, v); break; }
        case CHC_FLOAT64: { double  v; memcpy(&v, p, sizeof v); printf("%g", v); break; }
        /* ... remaining numeric kinds ... */
        default: break;
        }
        break;
    }
    case CHC_COL_STRING: {
        const uint8_t  *bytes   = chc_column_string_data(c);
        const uint64_t *offsets = chc_column_string_offsets(c);
        uint64_t start = row == 0 ? 0 : offsets[row - 1];
        fwrite(bytes + start, 1, (size_t) (offsets[row] - start), stdout);
        break;
    }
    default: break;
    }
}
```

Los datos `CHC_COL_FIXED` son little-endian en la transmisión; en hosts big-endian debes intercambiar manualmente el orden de bytes de los
enteros de varios bytes. Los desplazamientos y las claves de LowCardinality ya se convierten al
orden del host durante la decodificación. Los `UUIDs` constan de dos mitades `UInt64` little-endian, IPv4 es un entero little-endian de 4 bytes, e IPv6 está en
orden de bytes de red. Los ticks de `DateTime64` son UTC; la zona horaria del tipo es solo metadatos.

Al ingestar desde un peer no confiable, llama a `chc_column_validate` en cada columna antes de recorrerla.
`chc_block_read` no valida invariantes entre campos, como los desplazamientos de los arrays y las
claves de LowCardinality, por lo que un bloque falsificado podría leer fuera de los límites de las columnas internas.

<div id="inserting-data">
  ## Inserción de datos
</div>

Cree un bloque con `chc_block_builder` y luego páselo a `chc_client_send_data`. El constructor registra
punteros en lugar de copiar los datos, por lo que los bloques de memoria de las columnas deben seguir siendo válidos hasta después del envío. Un INSERT envía la consulta,
espera el bloque de cabecera del servidor, envía uno o más bloques de datos y luego envía un bloque vacío para
terminar el flujo.

```c theme={null}
const char *sql = "INSERT INTO greetings (id, message) VALUES";
chc_client_send_query(client, sql, strlen(sql), "", 0, &err);

/* Wait for the server's header block (schema, 0 rows). */
bool got_header = false;
while (!got_header) {
    chc_packet pkt = {};
    if (chc_client_recv_packet(client, &pkt, &err) != CHC_OK) {
        fprintf(stderr, "recv: %s\n", err.msg);
        return 1;
    }
    chc_packet_kind kind = pkt.kind;
    if (kind == CHC_PKT_DATA) got_header = true;
    else if (kind == CHC_PKT_EXCEPTION && pkt.exception)
        fprintf(stderr, "server: %s\n", pkt.exception->display_text);
    chc_packet_clear(client, &pkt);
    if (kind == CHC_PKT_EXCEPTION || kind == CHC_PKT_END_OF_STREAM) return 1;  /* no header coming */
}

chc_block_builder *bb = NULL;
chc_block_builder_init(&bb, &al, &err);

uint64_t ids[3] = { 1, 2, 3 };
chc_type *u64 = NULL;
chc_type_parse("UInt64", 6, &al, &u64, &err);
chc_block_builder_append_fixed(bb, "id", 2, u64, ids, 3, &err);

/* String columns: cumulative exclusive end offsets + a packed byte slab. */
uint64_t offsets[3] = { 5, 11, 20 };   /* "hello", "buenas", "goedendag" */
const uint8_t bytes[] = "hellobuenasgoedendag";
chc_block_builder_append_string(bb, "message", 7, offsets, bytes, 3, &err);

chc_client_send_data(client, bb, &err);   /* the populated block */
chc_client_send_data(client, NULL, &err); /* empty block ends the INSERT */

/* Drain to EndOfStream. */
for (;;) {
    chc_packet pkt = {};
    chc_client_recv_packet(client, &pkt, &err);
    bool done = pkt.kind == CHC_PKT_END_OF_STREAM;
    chc_packet_clear(client, &pkt);
    if (done) break;
}

chc_block_builder_destroy(bb);
chc_type_destroy(u64, &al);
```

`chc_block_builder_append_fixed` recibe `n_rows * elem_size` bytes en formato little-endian;
`chc_block_builder_append_string` recibe desplazamientos finales exclusivos acumulativos en el orden de bytes del host sobre un
slab packed. Enviar el builder a través de `chc_client_send_data` en lugar de la función de nivel inferior
`chc_block_write` permite que el client establezca las opciones del block a partir de la revisión negociada y aplique
compresión.

<div id="compression">
  ## Compresión
</div>

Pase un modo de compresión y un codec completo en `chc_client_opts`. El client descomprime los
paquetes Data entrantes y comprime los salientes. El encabezado de compresión incluye adaptadores `LZ4` y `ZSTD`;
cada inicialización solo completa sus propios campos, así que llame a ambas para admitir cualquiera de los dos.

```c theme={null}
#include "clickhouse-compression.h"

chc_codec codec = {};
chc_lz4_codec_init(&codec);
chc_zstd_codec_init(&codec);

chc_client_opts opts = {
    .user        = "default",
    .compression = CHC_COMP_LZ4,   /* or CHC_COMP_ZSTD */
    .codec       = &codec,
};
```

Para usar una biblioteca de compresión para la que el proyecto no proporciona un binding, implemente usted mismo un `chc_codec`;
la vtable está declarada en `clickhouse-compression.h`.

<div id="tls">
  ## TLS
</div>

`clickhouse-openssl.h` proporciona un backend `chc_io` basado en `SSL_read`/`SSL_write`. OpenSSL se maneja externamente:
la biblioteca nunca crea un `SSL_CTX`, verifica certificados, configura SNI ni llama a `SSL_connect` /
`SSL_shutdown`. Para cuando se invoque `chc_io.read`, el handshake debe haberse completado.

```c theme={null}
#include "clickhouse-openssl.h"

SSL *ssl = /* connected, handshake complete */;
chc_openssl_io state;
chc_io io;
chc_openssl_io_init(&state, &io, ssl, NULL, NULL);
/* hand &io to chc_client_init, same as the POSIX backend */
```

[ClickHouse Cloud](/es/cloud/overview) y otros despliegues con TLS habilitado usan el protocolo nativo en
el puerto 9440. Ambos backends aceptan un callback opcional `check_cancel`, que se consulta entre lecturas, y un
tiempo límite de lectura mediante `chc_openssl_io_set_deadline` / `chc_posix_io_set_deadline`.

<div id="async-client">
  ## Client ioless (asíncrono)
</div>

`clickhouse-async.h` es una variante ioless del client TCP para bucles de eventos. Nunca toca un
`socket`: tú le entregas los bytes que has recibido y extraes los bytes que quiere enviar, gestionando `epoll`,
`io_uring` o `WaitLatchOrSocket` por tu cuenta. Las opciones, los tipos de paquetes y el constructor de bloques son los
mismos que en el client con bloqueo.

`chc_async_client_init` no realiza ninguna E/S y no puede bloquearse. El handshake se ejecuta después como una
máquina de estados reanudable, al igual que cada envío y recepción. Cuando el parser supera los bytes que has proporcionado, la
llamada devuelve `CHC_WOULD_BLOCK` en lugar de bloquearse: proporciona más bytes de entrada y vuelve a llamar, y el
parser se reanuda a mitad del bloque.

```c theme={null}
#include "clickhouse-async.h"

chc_async_client *c = NULL;
chc_client_opts opts = { .user = "default" };
chc_async_client_init(&c, &opts, &al, &err);

for (;;) {
    int rc = chc_async_handshake(c, &err);
    if (rc == CHC_OK) break;
    if (rc != CHC_WOULD_BLOCK) break;   /* hard error */
    pump(c);   /* drain pending_out to the socket; feed received bytes to chc_async_submit */
}

chc_async_send_query(c, sql, strlen(sql), "", 0, &err);

for (;;) {
    chc_packet pkt = {};
    int rc = chc_async_recv_packet(c, &pkt, &err);
    if (rc == CHC_WOULD_BLOCK) { pump(c); continue; }
    if (rc != CHC_OK) break;

    bool done = pkt.kind == CHC_PKT_END_OF_STREAM;
    if (pkt.kind == CHC_PKT_DATA && pkt.block) { /* read columns as above */ }
    chc_async_packet_clear(c, &pkt);
    if (done) break;
}
```

Tu `pump` mueve bytes en ambos sentidos. En sentido saliente, `chc_async_pending_out` devuelve un puntero y una longitud
de los bytes en cola; después de que el socket acepte algunos, llama a `chc_async_consume_out` con esa cantidad; no pasa nada si la
escritura es parcial. En sentido entrante, pasa las lecturas del socket a `chc_async_submit`. Los envíos nunca se bloquean ni aplican
backpressure, así que vigila la longitud de salida pendiente y deja de enviar cuando crezca demasiado.

Hay un driver funcional de liburing en
[`test/test_async_uring.c`](https://github.com/ClickHouse/clickhouse-c/blob/main/test/test_async_uring.c).

<div id="allocator">
  ## La memoria y el asignador
</div>

Cada punto de entrada recibe una vtable `chc_alloc`, por lo que la asignación depende del mecanismo que use el host.

```c theme={null}
typedef struct chc_alloc {
    void *ud;
    void *(*alloc)  (void *ud, size_t bytes);
    void *(*realloc)(void *ud, void *p, size_t old_bytes, size_t new_bytes);
    void  (*free)   (void *ud, void *p, size_t bytes);
} chc_alloc;
```

Defina `CHC_PROVIDE_STDLIB_ALLOC` antes de incluir `clickhouse.h` y llame a `chc_alloc_stdlib()` para usar un
asignador estándar basado en `malloc`.

<div id="errors">
  ## Errores y excepciones del servidor
</div>

Las funciones devuelven `CHC_OK` (0) o un código `CHC_ERR_*` distinto de cero. El código es el valor de retorno; un
`chc_err` asignado en la pila del llamador contiene el mensaje legible para humanos. La biblioteca nunca asigna
un error en el heap.

```c theme={null}
typedef struct chc_err {
    int  server_code;           /* set when the return code is CHC_ERR_SERVER */
    char msg[CHC_ERR_MSG_LEN];  /* NUL-terminated, default 256 bytes */
    char server_name[64];       /* ClickHouse exception class, if SERVER */
} chc_err;
```

Los errores de consulta del servidor no son fallos de `chc_err`. Llegan por el flujo de paquetes como
`CHC_PKT_EXCEPTION`, con el `code`, `display_text` y `stack_trace` del servidor. Reserve la
comprobación de `chc_err` para los fallos de transporte, protocolo y decodificación.

<div id="supported-types">
  ## Tipos de datos compatibles
</div>

El lector de bloques decodifica:

* `Int8`–`Int256`, `UInt8`–`UInt256`
* `Float32`, `Float64`, `BFloat16`
* `Bool`
* `Decimal32`, `Decimal64`, `Decimal128`, `Decimal256`
* `Date`, `Date32`, `DateTime`, `DateTime64`, `Time`, `Time64`
* `String`, `FixedString(N)`
* `UUID`, `IPv4`, `IPv6`
* `Enum8`, `Enum16`
* `Nullable(T)`, `Array(T)`, `Tuple(...)`, `Map(K, V)`, `Nested(...)`
* `LowCardinality(T)`
* `Interval`
* `QBit(...)`
* `Point`, `Ring`, `Polygon`, `MultiPolygon`
* `SimpleAggregateFunction(f, T)`, que se decodifica como su `T` interna
* `JSON` y `Object('json')`, como columnas `String` con serialización de cadenas (consulte más abajo)

`JSON` y `Object('json')` solo se decodifican cuando la consulta establece `output_format_native_write_json_as_string=1`.
Cada fila llega como un documento JSON en una columna `CHC_COL_STRING`, por lo que los accesores de cadenas pueden leerlo;
el constructor escribe la misma estructura con `chc_block_builder_append_json_string`. Cualquier otra versión de serialización JSON
devuelve `CHC_ERR_TYPE` e indica el nombre de la configuración.

`Variant`, `Dynamic`, `AggregateFunction` aún no se decodifican y devuelven `CHC_ERR_TYPE`;
conviértalos a `String` del lado del servidor como alternativa.
