Pular para o conteúdo principal
clickhouse-c é um cliente C header-only para o ClickHouse protocolo nativo. O código-fonte e a referência de cada cabeçalho estão no repositório do GitHub. 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 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.
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. Mas entenda que a ausência de certas funcionalidades nesta biblioteca é intencional.

O que a biblioteca não faz

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.
  • 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.

Como a biblioteca é organizada

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.

Configuração obrigatória do servidor

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:

Adicionando isso ao seu projeto

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.
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.

Conectando via TCP

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.
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.

Executando uma consulta

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; 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.
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.

Leitura de dados de coluna

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. Um leitor para colunas numéricas simples, de string e Nullable:
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.

Inserindo dados

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.
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.

Compressão

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.
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.

TLS

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.
ClickHouse Cloud 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.

Cliente sem E/S (assíncrono)

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.
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.

Memória e o alocador

Todo ponto de entrada recebe uma vtable chc_alloc, então a alocação segue o esquema usado pelo host.
Defina CHC_PROVIDE_STDLIB_ALLOC antes de incluir clickhouse.h e chame chc_alloc_stdlib() para usar um alocador padrão baseado em malloc.

Erros e exceções do servidor

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.
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.

Tipos de dados compatíveis

O leitor de blocos decodifica:
  • Int8Int256, UInt8UInt256
  • 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.
Última modificação em 2 de julho de 2026