clickhouse-connect 1.1.0
This is the first minor release on the 1.x line. 1.1.0 consolidates everything from the 1.1.0a1 and 1.1.0a2 alphas i.e. the Alembic/SQLAlchemy integration work and adds a handful of bug fixes on top of 1.0.1.
If you're upgrading from a 0.15.x or earlier release, please read MIGRATION.md for the 1.0 breaking changes first.
Regarding Alembic support
This is the first clickhouse-connect release to ship Alembic integration. SQLAlchemy and Alembic each have a large surface area, and ClickHouse has plenty of dialect-specific quirks, so this release should best understood as a focused first cut. The common autogenerate/upgrade/downgrade flows for tables, columns, engines, dictionaries, comments, and operation-level settings rather than an exhaustive port of every Alembic feature. If you hit a gap, an inconvenience, or unexpected behavior, please open an issue with a repro. Enhancement requests are equally welcome and will help prioritize what to focus on.
Highlights
SQLAlchemy & Alembic
- Alembic migration support. Full Alembic integration for ClickHouse schema migrations: autogeneration of migration scripts from SQLAlchemy metadata, upgrade/downgrade lifecycle, and round-tripping of ClickHouse-specific DDL. Supported operations include create/drop table, add/alter/drop/rename column, type and nullability changes, defaults, comments,
IF EXISTSguards, column placement withAFTER, and operation-levelclickhouse_settings. ClickHouse table engines (MergeTree,ReplacingMergeTree, etc.) and dictionaries are preserved through the migration lifecycle. Install viapip install clickhouse-connect[alembic]. Seeclickhouse_connect/cc_sqlalchemy/alembic/WORKED_EXAMPLE.mdfor an end-to-end walkthrough. - ClickHouse
Dictionarytype support. Reflect, create, and drop ClickHouse dictionaries through SQLAlchemy and Alembic. - New Select constructs:
PREWHERE,LIMIT BY, and lambda expressions are now chainable onselect()for ClickHouse-specific queries. - Multi-column
ARRAY JOINlabel preservation.ARRAY JOINaliases now survive through compilation, fixing label-loss issues with parallel array expansion. - Migration shim for
clickhouse-sqlalchemyusers. Newcc_sqlalchemy.typesandcc_sqlalchemy.enginesimport-compatible modules ease migration fromclickhouse-sqlalchemy. Seeclickhouse_connect/cc_sqlalchemy/MIGRATING_FROM_CLICKHOUSE_SQLALCHEMY.md. - SQLAlchemy 1.4 and 2.x compatibility improvements across the dialect and DDL compiler paths.
- Inspector cloud quirk reflection fixes for ClickHouse Cloud-specific schema reflection edge cases.
- DDL compiler improvements including better handling of nested container types and Tuple adaptation.
Compatibility
- Async client now requires
aiohttp>=3.9.0. This is required to support TLS SNI override viaserver_host_name, because aiohttp added the per-requestserver_hostnameoption in 3.9. - The
alembicextra now requiresalembic>=1.16(previously>=1.9), to expose theIF EXISTS/IF NOT EXISTSoperation kwargs used by the integration.
Bug fixes
- Async client:
server_host_namenow also overrides the TLS SNI / certificate hostname, matching the sync client. Previously the async path only applied it to the HTTPHostheader, so connecting to host A while presenting SNI B (the 0.xpool_mgr=urllib3.PoolManager(server_hostname=...)pattern, useful for ClickHouse Cloud VPC endpoints reached via external DNS) was not expressible against the new aiohttp-based client. Closes #752. - Drain the full
retriesbudget on connection-error retries in_raw_requestinstead of only retrying once. Previously both the sync and async clients gated network-error retries onattempts == 1, so two consecutiveaiohttp.ServerDisconnectedErrors (orConnectionResetErrors on the sync path) surfaced asOperationalErroreven whenquery_retrieswould have allowed another attempt. Read paths will now drainquery_retries. Insert/command paths still get one retry. Syncraw_queryandraw_stream, the foundation forquery_arrow&query_arrow_streamnow also passquery_retriesso they match their async counterparts. Adds a0.1 * attemptsbackoff between connection-error retries to match the 429/503/504 branch. Closes #754. quote_identifiernow re-escapes inputs that start and end with`or"but contain unescaped inner occurrences of the same quote character, instead of passing them through unchanged. Validly pre-quoted identifiers like backslash or doubled-quote escaping still pass through untouched. Closes #737.- SQLAlchemy:
op.add_column(..., clickhouse_settings={...})now works through the public Alembic operations API. Previously the stockOperations.add_columnproxy had a fixed signature that rejectedclickhouse_settingswithTypeError, even though the underlying impl accepted it. RenderedAddColumnOpmigrations also preserve extra ClickHouse kwargs on round-trip, so autogenerated scripts containingclickhouse_settings=...survive regeneration. - SQLAlchemy: Alembic migrations now handle comments with ClickHouse-compatible syntax. Column comments on
CREATE TABLE/ADD COLUMNare rendered inline instead of emitting separateCOMMENT ON COLUMNstatements (which ClickHouse rejects). Table comments are now emitted in generated DDL, reflected for no-op autogenerate, and changed or dropped viaALTER TABLE ... MODIFY COMMENT.Inspector.get_table_comment(...)now raisesNoSuchTableErrorfor missing tables instead of silently returning{"text": None}. - SQLAlchemy: quote string-valued engine and operation settings as ClickHouse string literals when rendering
SETTINGSclauses. Previously settings likeMergeTree(settings={"storage_policy": "hot_cold"})orop.add_column(..., clickhouse_settings={"mutations_sync": "2"})emitted unquoted SQL (storage_policy = hot_cold), which ClickHouse rejected. Numeric and boolean settings are unchanged. - SQLAlchemy: preserve engine
settingson reflection.build_engine()previously hardcodedengine.settings = {}even when the reflected DDL contained aSETTINGSclause, so callers readingengine.settingsafter reflection saw an empty dict.settingsis now populated from the parsed engine kwargs, decoding ClickHouse string-literal escapes (\\,\',\n, etc.) and preserving float-valued settings as floats providing round-trip parity with the construction path. - SQLAlchemy:
Inspectorerror messages fromget_table_metadata()now report the resolved database name i.e. fromcurrentDatabase()whenschemawas not provided instead of literalNone. - SQLAlchemy: preserve
FINAL,SAMPLE,PREWHERE, andLIMIT BYmodifiers when aselect()is built from ORM-mapped attributes e.g.select(Event.id)rather than Core columns. Previously the ORM compile path rebuilt the inner Select viaSelect._create_raw_select, which dropped the modifier instance attributes, so the compiled SQL silently emitted no modifier. The compiler now falls back tocompile_state.select_statementwhich is the original user-built Select to recover the modifiers. Closes #730. - Alembic autogenerated migrations now render enum types (including nested container types) with the correct ClickHouse-compatible representation.
Pre-release tags rolled into 1.1.0
v1.1.0a1(2026-05-06) - initial alembic / SQLAlchemy alphav1.1.0a2(2026-05-07) - ORM modifier compile-path fix
Install
pip install clickhouse-connect
For Alembic integration:
pip install clickhouse-connect[alembic]