deniz.in

Markets

Weather

Loading weather

· via Hacker News – Front Page (hnrss.org)

ClickHouse ships header-only C client for its native binary protocol

ClickHouse has released clickhouse-c, a header-only C client for its native binary protocol, designed for embedding in PostgreSQL extensions with no dependencies beyond libc.

ClickHouse ships header-only C client for its native binary protocol

What the library does

ClickHouse has published clickhouse-c, a client library for C distributed entirely as headers, which speaks the database's native binary protocol over TCP instead of HTTP. Hosted under the ClickHouse organisation on GitHub, the project surfaced on the Hacker News front page. Its stated purpose is small, embedded deployments, with PostgreSQL extensions called out as the main target.

The core is a single header, clickhouse.h, containing type definitions, error handling, the allocator and I/O interfaces, a type-name parser, and the block reader and writer. According to the project's README, that header alone provides a complete block decoder driven by a caller-supplied I/O callback, so a program can parse the Native-format output of clickhouse local arriving through a pipe without opening a socket, enabling compression, or linking anything beyond libc.

Further headers each add one capability and can be pulled in independently: clickhouse-client.h implements the TCP packet loop covering Hello, Query, Data, end-of-stream, Exception, Progress and Pong, and supports ClickHouse 23.3 and newer; clickhouse-compression.h covers the compressed-frame layout, CityHash128 checksums and LZ4 and ZSTD codecs behind optional link flags; clickhouse-posix-io.h supplies blocking reads and writes with EINTR handling and a cancellation hook; and clickhouse-openssl.h wraps SSL_read and SSL_write. A separate header, clickhouse-async.h, offers the same packet loop with no I/O at all, with the caller feeding bytes in from an event loop.

Built for embedding in PostgreSQL

The design accounts for PostgreSQL's execution model — arena allocation through palloc and longjmp-based error unwinding — while containing no PostgreSQL-specific code, the README notes. A standard malloc-based allocator ships behind a preprocessor define, and the documentation points to the pg_clickhouse and pg_stat_ch extensions as references for wiring palloc allocation into a real extension.

Integration follows the familiar single-header pattern: exactly one translation unit in the consuming project defines CHC_IMPLEMENTATION before including the headers, and every other file includes them for declarations only.

One configuration detail matters in practice: the server setting output_format_native_encode_types_in_binary_format must be set to 0, either in the Query packet over TCP or as a flag to clickhouse local. If it is left enabled, the server emits binary type tags and chc_block_read fails with CHC_ERR_TYPE.

Scope, testing and limits

The test suite runs through test.sh. TCP tests can launch a clickhouse server binary from PATH or target an already-running instance through CHC_TEST_PORT, which the documentation suggests using to check compatibility with older releases, for example one running in Docker. A capture tool can also proxy a live server to record traffic fixtures for replay.

The non-goals are as informative as the feature list. There is no HTTP support — the recommendation is to wrap libcurl directly — and no DNS resolution, endpoint rotation, connection pooling, retry logic, or SSL context lifecycle management. There is also no threading: each client object is deliberately single-threaded. The blocking client invokes its I/O callback synchronously, but the README notes the callback is free to use epoll, io_uring or PostgreSQL's WaitLatchOrSocket underneath. The first version also skips decoding for Variant, Dynamic, JSON and AggregateFunction columns, because the wire format for those types is still shifting across the 25.x and 26.x release lines.

Why it matters

Most software talks to ClickHouse over HTTP with text or JSON row formats, which means serialisation on one side, parsing on the other, and extra copies in between. A header-only client that decodes the native block protocol in-process removes that overhead and, crucially, can allocate through the host program's own memory system — the difference between a library that merely runs inside PostgreSQL and one that behaves correctly within it. The tight scope is the point: with no pooling, threading or TLS lifecycle to own, the code stays small enough to vendor into an extension or another database tool. For anyone building foreign data wrappers, monitoring agents or bulk transfer paths between PostgreSQL and ClickHouse, this closes a gap that previously demanded either heavyweight client libraries or hand-written protocol code.

  • #clickhouse
  • #c-language
  • #postgresql
  • #databases
  • #open-source

Related posts