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

> Cliente C header-only para o protocolo nativo do ClickHouse, feito para ser embutido.

# Cliente C do ClickHouse

`clickhouse-c` é um cliente C header-only para o ClickHouse [protocolo nativo](/pt-BR/concepts/features/interfaces/tcp).
O código-fonte e a referência de cada cabeçalho estão no [repositório do GitHub](https://github.com/ClickHouse/clickhouse-c).

Ao contrário dos clientes de nível mais alto, ele faz pouca coisa por você de forma intencional. O cabeçalho principal decodifica e
codifica blocos no formato [Native](/pt-BR/reference/formats/Native) por meio de um callback de E/S fornecido por você. Você é responsável
pelo socket, contexto de TLS, alocador, tentativas e pool de conexões. Isso o torna pequeno o suficiente para
ser embutido: incluir apenas `clickhouse.h` não traz dependências de linkedição além da libc.

<Note>
  Esta biblioteca está em desenvolvimento ativo. A v1 decodifica os principais tipos do ClickHouse.
  Relate limitações ou funcionalidades ausentes por meio do [rastreador de issues](https://github.com/ClickHouse/clickhouse-c/issues).
  Mas entenda que a ausência de certas funcionalidades nesta biblioteca é intencional.
</Note>

<div id="non-goals">
  ## O que a biblioteca não faz
</div>

Estes itens estão deliberadamente fora do escopo. Lide com eles na sua aplicação ou com uma biblioteca complementar:

* Protocolo HTTP. Encapsule o libcurl diretamente para a [interface HTTP](/pt-BR/concepts/features/interfaces/http).
* Resolução de DNS, failover de endpoint, pool de conexões, retry e backoff.
* Ciclo de vida do contexto TLS. O backend OpenSSL usa um `SSL` ao qual você já se conectou.
* Uso de threads. Cada `chc_client` é de thread única por definição.
* E/S assíncrona na biblioteca. O cliente bloqueante chama `chc_io.read` de forma síncrona. Para um
  cliente orientado a loop de eventos que não faz nenhuma E/S por conta própria, use o [cliente sem E/S](#async-client).

<div id="headers">
  ## Como a biblioteca é organizada
</div>

`clickhouse-c` é fornecido como um conjunto plano de cabeçalhos. Cada cabeçalho reúne declarações e implementação,
protegidas por uma macro sentinela. Escolha os cabeçalhos de que sua compilação precisa.

| Cabeçalho                                                                                                        | Finalidade                                                                                          | Flags de linkedição |
| ---------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | ------------------- |
| [`clickhouse.h`](https://github.com/ClickHouse/clickhouse-c/blob/main/doc/clickhouse.md)                         | Núcleo: tipos, erros, alocador, vtable de E/S, parser de nomes de tipos, leitor e gravador de bloco | —                   |
| [`clickhouse-client.h`](https://github.com/ClickHouse/clickhouse-c/blob/main/doc/clickhouse-client.md)           | Loop de pacotes TCP: Hello, Query, Data, EndOfStream, Exception, Progress, Pong                     | —                   |
| [`clickhouse-async.h`](https://github.com/ClickHouse/clickhouse-c/blob/main/doc/clickhouse-async.md)             | Cliente sem E/S: o mesmo loop de pacotes, acionado pelo envio de bytes pelo chamador, sem socket    | —                   |
| [`clickhouse-compression.h`](https://github.com/ClickHouse/clickhouse-c/blob/main/doc/clickhouse-compression.md) | Layout de frame comprimido, CityHash128, despacho de codec e 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)` bloqueantes                                               | —                   |
| [`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">
  ## Configuração obrigatória do servidor
</div>

O decodificador lê nomes de tipo imprimíveis do wire, portanto eles precisam ser codificados como texto. O ClickHouse
os grava como texto por padrão, mas fixe essa configuração nas suas consultas para que um perfil de servidor ou de sessão
que a defina como binária não comprometa a decodificação:

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

<div id="adding-to-project">
  ## Adicionando isso ao seu projeto
</div>

Não há nenhum pacote para instalar, então você deve incorporar os cabeçalhos à árvore do seu projeto por meio de um git submodule ou de uma cópia.
Exatamente uma unidade de tradução define `CHC_IMPLEMENTATION` e inclui a implementação;
todas as outras unidades incluem os mesmos cabeçalhos apenas para as declarações.

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

Defina `CHC_PROVIDE_STDLIB_ALLOC` antes de incluir `clickhouse.h` para usar `chc_alloc_stdlib`.
Defina `CHC_NO_LZ4` ou `CHC_NO_ZSTD` em `clickhouse-compression.h` para remover as dependências de lz4/zstd.

<div id="connecting-over-tcp">
  ## Conectando via TCP
</div>

Para se comunicar com um servidor ClickHouse, você configura o socket por conta própria, o encapsula em um `chc_io` e o passa para
`chc_client_init`, que executa o handshake Hello de forma síncrona. A biblioteca não faz DNS,
failover, reconexão nem pooling — isso fica a cargo de quem faz a chamada.

```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` é de thread única e encapsula uma conexão. A biblioteca chama os
callbacks de `chc_io` de forma síncrona; o que esses callbacks fazem por baixo dos panos (`epoll`, `io_uring`,
`WaitLatchOrSocket`) fica a seu critério.

<div id="running-a-query">
  ## Executando uma consulta
</div>

Envie a consulta e, em seguida, consuma os pacotes até `CHC_PKT_END_OF_STREAM`. Use `chc_client_send_query_ex` para
anexar a [configuração de servidor necessária](#server-setting); a versão simples de `chc_client_send_query` envia uma
lista vazia de configurações e herda as configurações padrão do 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;
}
```

As exceções do servidor chegam como pacotes `CHC_PKT_EXCEPTION`, não como um retorno não OK de
`chc_client_recv_packet`. Somente falhas no nível de transporte retornam não OK. O primeiro pacote `CHC_PKT_DATA`
de um resultado é um bloco de cabeçalho que descreve o esquema com zero linhas; os blocos de dados vêm em seguida.
`chc_packet_clear` libera o bloco ou a exceção do pacote — primeiro defina esses campos do pacote como nulos para
assumir a propriedade deles.

<div id="reading-column-data">
  ## Leitura de dados de coluna
</div>

Os blocos são orientados a colunas. Cada coluna tem um layout físico, retornado por `chc_column_layout`, com
base no qual você faz o despacho; seu tipo declarado vem de `chc_block_column_type`. Layouts compostos são aninhados, então
ler um `Nullable(Array(String))` significa desempacotar o Nullable, percorrer os offsets do array e, em seguida,
recortar os dados de string.

| Layout                    | Acessores                                                                                                                                                  |
| ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `CHC_COL_FIXED`           | `chc_column_fixed_data(c, &elem_size)` — `n_rows * elem_size` bytes em little-endian                                                                       |
| `CHC_COL_STRING`          | `chc_column_string_data(c)`, `chc_column_string_offsets(c)` — `offsets[i]` é o fim exclusivo da linha `i` na ordem de bytes do host; a linha 0 começa em 0 |
| `CHC_COL_NULLABLE`        | `chc_column_null_map(c)` (um byte por linha, 1 = `NULL`), `chc_column_nullable_inner(c)`                                                                   |
| `CHC_COL_ARRAY`           | `chc_column_array_offsets(c)` (fins cumulativos), `chc_column_array_values(c)`; `Map` é decodificado como `Array(Tuple(K, V))`                             |
| `CHC_COL_TUPLE`           | `chc_column_tuple_arity(c)`, `chc_column_tuple_child(c, i)` — cada filho tem o mesmo número de linhas                                                      |
| `CHC_COL_LOW_CARDINALITY` | `chc_column_lc_key_size(c)` (1/2/4/8), `chc_column_lc_keys(c)`, `chc_column_lc_dict(c)`; o slot 0 do Dicionário é o valor padrão                           |

Um leitor para colunas numéricas simples, de string e 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;
    }
}
```

Os dados `CHC_COL_FIXED` são little-endian no wire; em hosts big-endian, você mesmo deve inverter a ordem dos bytes
dos inteiros com vários bytes. Offsets e chaves de LowCardinality já são convertidos para a ordem do host no momento da decodificação.
UUIDs são duas metades `UInt64` little-endian, IPv4 é um inteiro little-endian de 4 bytes, e IPv6 usa
network byte order. Os ticks de `DateTime64` são UTC — o fuso horário no tipo é apenas metadata.

Ao fazer ingestão a partir de um peer não confiável, chame `chc_column_validate` em cada coluna antes de percorrê-la.
`chc_block_read` não valida invariantes entre campos, como offsets de array e
chaves de LowCardinality, então um bloco forjado poderia acabar lendo além dos limites da coluna interna.

<div id="inserting-data">
  ## Inserindo dados
</div>

Construa um bloco com `chc_block_builder` e, em seguida, passe-o para `chc_client_send_data`. O builder registra
ponteiros em vez de copiar os dados, portanto os slabs das colunas devem permanecer válidos após o envio. Um INSERT envia a consulta,
aguarda o bloco de cabeçalho do servidor, envia um ou mais blocos de dados e, em seguida, envia um bloco vazio para
encerrar o fluxo.

```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` recebe `n_rows * elem_size` bytes little-endian;
`chc_block_builder_append_string` recebe offsets finais exclusivos cumulativos na ordem de bytes do host sobre uma
slab compactada. Encaminhar o builder por meio de `chc_client_send_data`, em vez do
`chc_block_write` de nível mais baixo, permite que o client defina as opções do bloco com base na revisão negociada e aplique
compressão.

<div id="compression">
  ## Compressão
</div>

Passe um modo de compressão e um codec configurado em `chc_client_opts`. O cliente descomprime os
pacotes de dados de entrada e comprime os de saída. O cabeçalho de compressão inclui adaptadores `LZ4` e `ZSTD`;
cada inicialização preenche apenas seus próprios slots, então chame ambas para dar suporte a qualquer um deles.

```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 uma biblioteca de compressão para a qual o projeto não inclui um binding, preencha você mesmo um `chc_codec`;
a vtable é declarada em `clickhouse-compression.h`.

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

`clickhouse-openssl.h` fornece um backend `chc_io` baseado em `SSL_read`/`SSL_write`. Você controla o OpenSSL:
a biblioteca nunca cria um `SSL_CTX`, verifica certificados, define SNI nem chama `SSL_connect` /
`SSL_shutdown`. Quando `chc_io.read` é acionado, o handshake já deve ter sido concluído.

```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](/pt-BR/products/cloud/getting-started/intro) e outras implantações com TLS ativado usam o protocolo nativo na
porta 9440. Ambos os backends aceitam um callback opcional `check_cancel`, verificado entre leituras, e um
limite de tempo de leitura via `chc_openssl_io_set_deadline` / `chc_posix_io_set_deadline`.

<div id="async-client">
  ## Cliente sem E/S (assíncrono)
</div>

`clickhouse-async.h` é uma variante sem E/S do cliente TCP para loops de eventos. Ele nunca acessa um
socket: você fornece os bytes recebidos e drena os bytes que ele quer enviar, controlando `epoll`,
`io_uring` ou `WaitLatchOrSocket` por conta própria. As opções, os tipos de pacote e o construtor de bloco são os
mesmos do cliente bloqueante.

`chc_async_client_init` não faz E/S e não pode bloquear. O handshake é executado depois como uma
máquina de estados que pode ser retomada, assim como todo envio e recebimento. Quando um parse ultrapassa os bytes que você forneceu, a
chamada retorna `CHC_WOULD_BLOCK` em vez de bloquear — forneça mais bytes de entrada e chame novamente, e o
parser retoma no meio do bloco.

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

Seu `pump` move bytes nos dois sentidos. Na saída, `chc_async_pending_out` retorna um ponteiro e o tamanho
dos bytes enfileirados; depois que o socket aceita alguns deles, chame `chc_async_consume_out` com essa contagem; uma
gravação parcial é aceitável. Na entrada, envie as leituras do socket para `chc_async_submit`. Os envios nunca bloqueiam nem aplicam
backpressure, então monitore o tamanho pendente de saída e pare de emitir envios quando ele ficar grande demais.

Um driver liburing funcional está em
[`test/test_async_uring.c`](https://github.com/ClickHouse/clickhouse-c/blob/main/test/test_async_uring.c).

<div id="allocator">
  ## Memória e o alocador
</div>

Todo ponto de entrada recebe uma vtable `chc_alloc`, então a alocação segue o esquema usado pelo 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` e chame `chc_alloc_stdlib()` para usar um
alocador padrão baseado em `malloc`.

<div id="errors">
  ## Erros e exceções do servidor
</div>

As funções retornam `CHC_OK` (0) ou um código `CHC_ERR_*` diferente de zero. O código é o valor de retorno; um
`chc_err` alocado na pilha do chamador contém a mensagem legível por pessoas. A biblioteca nunca aloca
um erro no 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;
```

Erros de consulta no servidor não são falhas `chc_err`. Eles chegam pelo fluxo de pacotes como
`CHC_PKT_EXCEPTION`, carregando `code`, `display_text` e `stack_trace` do servidor. Reserve a
verificação de `chc_err` para falhas de transporte, protocolo e decodificação.

<div id="supported-types">
  ## Tipos de dados compatíveis
</div>

O leitor de blocos 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 é decodificada como seu `T` interno
* `JSON` e `Object('json')`, como colunas `String` com serialização de string (veja abaixo)

`JSON` e `Object('json')` são decodificados somente quando a consulta define `output_format_native_write_json_as_string=1`.
Cada linha chega como um documento JSON em uma coluna `CHC_COL_STRING`, então os acessores de string o leem;
o builder grava o mesmo formato com `chc_block_builder_append_json_string`. Qualquer outra versão de serialização
de JSON retorna `CHC_ERR_TYPE`, informando o nome da configuração.

`Variant`, `Dynamic`, `AggregateFunction` ainda não são decodificados e retornam `CHC_ERR_TYPE`;
converta-os para `String` no servidor como fallback.
