Connector

ClickHouse access governance — every statement reviewed before it runs

Last updated

AccessFlow governs ClickHouse as a full query proxy: your team connects to AccessFlow, not to the database, and every statement is parsed, checked against a schema allow-list, risk-scored, routed to an approver, and only then executed — under column masking, row-level security and a row cap. The pipeline itself — parse, risk-score, route, approve, execute, audit — is the same for every relational engine and is described in full on the database access governance page. What follows is what is specific to ClickHouse.

Family
Relational
Query language
SQL
Connects via
Database driver
Default port
8123
Default SSL mode
REQUIRE
Install
One click — checksum-verified download

The driver and the connection

ClickHouse is a built-in catalog connector that uses the generic SQL dialect rather than one of the five with a dialect of their own. Its driver, com.clickhouse:clickhouse-jdbc, is pinned in the catalog and installed in one click, and it is loaded on its own — so the ClickHouse driver version you pick cannot disturb any other datasource.

That generic dialect is the same path any other JDBC-compatible engine can use: upload a driver JAR with its SHA-256, and AccessFlow will govern it the same way. ClickHouse is simply a driver that ships pre-declared in the catalog so you do not have to.

The datasource takes host, port (8123 by default for the HTTP interface), database, username and password, AES-256-GCM encrypted at rest. SSL defaults to DISABLE, reflecting the common in-cluster deployment — set it to REQUIRE or a verifying mode for anything crossing a network boundary.

AccessFlow builds the connection string from the datasource fields using the template the connector manifest declares, so what actually reaches the driver is predictable and reviewable:

connector manifest
jdbc:ch://{host}:{port}/{database_name}
com.clickhouse.jdbc.ClickHouseDriver

The driver is fetched the first time a datasource of this type is created or tested, and every driver AccessFlow loads is kept separate from every other one. Two datasources on different driver versions — even two ClickHouse datasources — never share a copy and cannot interfere with each other.

What AccessFlow parses

Statements are parsed before anything else happens, and the schema allow-list is enforced against the tables the statement really references. Because ClickHouse runs on the generic dialect, the parse uses the standard SQL grammar rather than a ClickHouse-aware one — a statement using syntax outside that grammar is refused with HTTP 422 rather than passed through unparsed, which is the fail-closed direction.

What it refuses

Multi-statement input is refused, as is anything the parser cannot read. The rejection happens before AccessFlow reaches the database at all.

Row-level security and masking

A policy is spliced into the WHERE clause as a bound predicate, and shapes that cannot be provably narrowed are refused. Masking is applied to the fetched result by the same masker every engine shares.

What the rewrite actually does

A policy restricting events to the caller's tenant narrows the read before ClickHouse receives it:

SQL
-- submitted
SELECT event_type, count() FROM events WHERE day >= '2026-08-01' GROUP BY event_type;

-- executed (policy: events.tenant = the caller's tenant)
SELECT event_type, count() FROM events
 WHERE (day >= '2026-08-01') AND events.tenant = ? GROUP BY event_type;

The predicate is added before the GROUP BY, so the aggregate is computed over the rows the caller is allowed to see. Filtering afterwards would produce a different — and wrong — number, which is why the rewriter works on the parsed statement rather than on its text.

Introspection, dry-run and cost estimates

ClickHouse has no dialect-specific plan reader, so a dry-run reports itself unsupported with a localized reason rather than guessing at a plan. Everything else in the pre-flight path still runs — the statement is parsed, the allow-list checked, row security resolved, and the automatic submission estimate persisted with that unsupported state recorded — so a reviewer always sees a definitive answer rather than a blank panel.