Skip to content

ClickHouse/clickhouse-c

Repository files navigation

clickhouse-c

Header-only C client for the ClickHouse Native wire format. One core header, plus optional follow-up headers for TCP, compression, codecs, and I/O backends.

Designed first for embedding inside a PostgreSQL extension — palloc arena, siglongjmp error path — but with no PG-specific code in the library itself. Including clickhouse.h alone gives a pure block decoder over caller-supplied chc_io, usable for reading clickhouse-local's FORMAT Native from a pipe with no TCP, no compression, no link-time deps beyond libc.

Quickstart

Decode a clickhouse local query without TCP or compression.

/* demo.c — cc -std=c11 -O2 -I. demo.c -o demo */

#define CHC_PROVIDE_STDLIB_ALLOC
#define CHC_IMPLEMENTATION
#include "clickhouse.h"
#include "clickhouse-posix-io.h"

#include <stdio.h>
#include <unistd.h>

int main(void) {
    /* Spawn `clickhouse local --format Native -q "..."` and read its stdout
       via a pipe; see examples/minimal_decode.c for the full plumbing. */
    int fd = /* read end of pipe from child */;

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

    chc_block_opts opts = {0};   /* clickhouse-local: no BlockInfo, no custom serialization */

    for (;;) {
        chc_block *block = NULL;
        chc_err err = {0};
        if (chc_block_read(&io, &al, &opts, &block, &err) != CHC_OK) {
            fprintf(stderr, "decode: %s\n", err.msg);
            return 1;
        }
        if (!block) break;

        for (size_t r = 0; r < chc_block_n_rows(block); r++)
            for (size_t c = 0; c < chc_block_n_columns(block); c++)
                /* dispatch on chc_column_layout(chc_block_column(block, c)) */;

        chc_block_destroy(block, &al);
    }
}

Runnable end-to-end version: examples/minimal_decode.c.

Required server setting (Query packet for TCP, -- flag for clickhouse local):

output_format_native_encode_types_in_binary_format = 0

Without it, the server emits binary type tags & chc_block_read returns CHC_ERR_TYPE.

Integration

clickhouse-c is distributed as a flat set of headers: each header contains both declarations & implementation, guarded by a sentinel macro. Exactly one translation unit per consumer defines the implementation macro & includes headers; everyone else includes for declarations only.

/* consumer/src/clickhouse.c — the one TU that links the library in */
#define CHC_IMPLEMENTATION
#include "clickhouse.h"
#include "clickhouse-posix-io.h"
/* …include clickhouse-client.h, clickhouse-compression.h here for the
   features you use. */

Other TUs include only the public declarations:

#include "clickhouse.h"
#include "clickhouse-posix-io.h"
/* no CHC_IMPLEMENTATION here */

For PostgreSQL extension embedding, see examples/pg_alloc_thunks.c for palloc/repalloc/pfree wiring; a stdlib malloc/realloc/free helper is available behind #define CHC_PROVIDE_STDLIB_ALLOC before including clickhouse.h.

Headers

Header Purpose Links
clickhouse.h Core: types, errors, chc_alloc, chc_io, type-name parser, block reader & writer
clickhouse-client.h TCP packet loop: Hello / Query / Data / EOS / Exception / Progress / Pong
clickhouse-compression.h Compressed-frame layout, CityHash128, chc_codec dispatch, LZ4 & ZSTD adapters (opt out with CHC_NO_LZ4 / CHC_NO_ZSTD) -llz4 -lzstd
clickhouse-posix-io.h chc_io over blocking read(2)/write(2) with EINTR loop & cancel hook
clickhouse-openssl.h chc_io over SSL_read/SSL_write -lssl -lcrypto

Each follow-up header is independent; pick what your build needs.

Per-header reference in doc/; inline declarations & comments in the headers themselves. Worked examples in examples/.

Testing

Use test.sh to run tests:

./test.sh

If errors report missing headers or linker errors, use CFLAGS and LDFLAGS to point to the appropriate directories, e.g., using Homebrew:

CFLAGS=-I/opt/homebrew/include LDFLAGS=-L/opt/homebrew/lib ./test.sh

The script runs individual tests in test. To run individual tests, pass their base names (without the trailing .c) as arguments:

./test.sh test_cancel test_block_decode

Non-goals

  • HTTP protocol — wrap libcurl directly.
  • DNS, endpoint round-robin, connection pooling, retry/backoff.
  • SSL/TLS context lifecycle. chc_io callbacks; caller drives OpenSSL.
  • Threading. Each chc_client is single-threaded by design.
  • Async I/O. Caller's chc_io.read can do whatever it wants under the hood (epoll, io_uring, WaitLatchOrSocket), but the library calls it synchronously.
  • Variant / Dynamic / JSON / AggregateFunction decoding in v1. The upstream wire format is still shifting in 25.x / 26.x.

About

minimalist header-only library for embedded contexts

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors