ClickHouse: Release 26.6 Call

Author: Alexey Milovidov, 2026-06-25.

ClickHouse Release 26.6

ClickHouse release 26.6

1. (50 min) What's new in ClickHouse 26.6.

2. (10 min) Q&A.

Release 26.6

ClickHouse "10 Year Anniversary" Release.

โ€” 56 new features ๐ŸŒž

โ€” 79 performance optimizations ๐Ÿ–๏ธ

โ€” 366 bug fixes ๐Ÿฆ

Small And Nice Features

ALTER TABLE … ADD ENUM VALUES

Append new Enum values without re-typing all the existing ones:

CREATE TABLE events (x Enum8('a' = 1, 'b' = 2)) ...; -- Before: ALTER ... MODIFY COLUMN x Enum8('a'=1,'b'=2,'c'=3,'d'=4) ALTER TABLE events MODIFY COLUMN x ADD ENUM VALUES('c' = 3, 'd' = 4); -- x is now Enum8('a' = 1, 'b' = 2, 'c' = 3, 'd' = 4)

— A pure metadata change — instant, no data rewrite.
— Forgetting an existing value is no longer possible.

Developer: Ilya Golshtein.

Number formatting controls

Precision arguments and decimal-point control for text output:

SELECT formatReadableSize(1234567890, 4); -- 1.1498 GiB (default precision is 2: 1.15 GiB) SELECT 1/3 SETTINGS output_format_float_precision = 4; -- 0.3333 SELECT 5.0 SETTINGS output_format_always_write_decimal_point… = 1; -- 5. (keep the decimal point even for whole numbers)

formatReadableSize / formatReadableQuantity / formatReadableDecimalSize take an optional precision.

Developers: Antonio Filipovic, phulv94, Ilya Yatsishin.

Cascading refreshable materialized views

A refreshable MV can now trigger off another RMV's refresh:

CREATE MATERIALIZED VIEW base REFRESH EVERY 1 HOUR ENGINE = MergeTree ORDER BY day AS …; CREATE MATERIALIZED VIEW rollup REFRESH DEPENDS ON base ENGINE = … AS SELECT day, sum(x) FROM base GROUP BY day;

Developer: Michael Kolupaev.

Control over projection materialization

Two new MergeTree settings decouple projection building from INSERT:

CREATE TABLE ... SETTINGS materialize_projections_on_insert = 0; -- INSERTs skip building projection parts โ†’ faster ingestion CREATE TABLE ... SETTINGS materialize_projections_on_merge = 1; -- a merge rebuilds a projection missing from all its source parts

— Tables with many projections ingest faster;
  projections are built during merges instead.

Developer: Christoph Wurm.

IP-prefix quotas

Key a quota by a subnet, not just a single address:

CREATE QUOTA q KEYED BY ip_address IPV4_PREFIX_BITS 24 FOR INTERVAL 1 hour MAX queries = 1000; -- every client in a /24 shares one quota bucket CREATE QUOTA q6 KEYED BY ip_address IPV6_PREFIX_BITS 64 …;

— Rate-limit a whole network range at once.
— Works with IP_ADDRESS and FORWARDED_IP_ADDRESS keys.
— Especially useful for IPv6.

Developer: Aditya Chopra.

Usability

help in the CLI

Type help <name> in clickhouse-client / clickhouse-local to read the docs
right in the terminal — backed by system.documentation:

:) help MergeTree

— Also /help, man, /man. Resolves ambiguity and typos.
— Markdown rendered in the terminal, with syntax-highlighted SQL.

Demo

Developer: Alexey Milovidov.

Docs embedded in every system table

System tables that describe the system now describe themselves — with
description, syntax, examples, introduced_in and related columns:

SELECT name, introduced_in FROM system.table_engines WHERE name ILIKE '%kafka%'; SELECT name, description FROM system.data_type_families;

Now self-documenting: table_engines, database_engines, data_type_families, formats, aggregate_function_combinators, dictionary_layouts, dictionary_sources, data_skipping_index_types, disk_types.

Developer: Alexey Milovidov.

system.documentation

The whole reference manual, as a table, inside the server:

SELECT type, count() FROM system.documentation GROUP BY type;

typecount
Function1592
Setting1548
Server Setting / MergeTree Setting412 / 316
Aggregate Function, Data Type, Format, …

— The same docs as the website — queryable, greppable, offline.
— Good for integration with tools.

Developer: Alexey Milovidov.

New system tables

More of ClickHouse, introspectable as tables:

SELECT table, name, type, expression FROM system.constraints;

system.constraints — every table's CHECK / ASSUME constraints.
system.iceberg_files — introspection of files in Iceberg tables.

Keeper related tables (available on clickhouse-keeper):
system.keeper_snapshots
system.keeper_changelogs
system.keeper_cluster.

Developers: Pedro Ferreira, Miัhael Stetsyuk, Han Fei.

Which AI agent is querying you?

ClickHouse now detects the AI coding agent that launched the client,
from its environment, and records it in a new client_agent column:

SELECT query_id, client_agent, interface FROM system.processes;

query_idclient_agentinterface
ai_democlaude-code1

— Recognizes Claude Code, Cursor, Codex, Gemini CLI, Goose, …
— Also in system.query_log and system.query_thread_log.

Developer: Alexey Milovidov.

Transform clickhouse-local to a server

clickhouse-local can now start listening for connections on the fly:

clickhouse-local :) SYSTEM START LISTEN TCP; :) SYSTEM START LISTEN HTTP; -- now other clients can connect to your local session

— Turn an ad-hoc analysis session into a shareable endpoint.
SYSTEM STOP LISTEN to close it again.

Developer: Alexey Milovidov.

Schema Visualizer

A new built-in web UI that draws the dependency graph of your database:

http://localhost:8123/schema

— Tables, materialized views, refreshable MVs, dictionaries,
  distributed tables and views — and the arrows between them.
— See at a glance how data flows from ingestion to rollups.
— Embedded in the server.

Developer: Nikita Mikhaylov.

PNG output format

Render query results as an image. One row per pixel (r, g, b or v),
with implicit or explicitly defined pixel coordinates (x, y):

WITH number DIV 1024 AS y, number MOD 1024 AS x, L2Norm((x - 512, y - 512)) / 512 AS radius, 60 AS stripe_size, round((atan2(x - 512, y - 512) / pi() * 180 + exp(radius) * 90) / stripe_size) * stripe_size AS alpha, radius <= 1 ? abs(1 - (radius - 0.5) * (radius - 0.5) * 4) : 0 AS a, colorOKLCHToSRGB((0.7, 0.15, alpha)) AS rgb SELECT rgb.1::UInt8 AS r, rgb.2::UInt8 AS g, rgb.3::UInt8 AS b, a FROM numbers(1048576) FORMAT PNG SETTINGS output_format_image_terminal_mode = 'kitty'

— The Web UI shows the image directly.
— Great for heatmaps, sparklines, generated art.

Developer: Maksim Dergousov.

SQL Compatibility

date_part and EXTRACT

date_part('unit', expr)
— PostgreSQL-style sugar for EXTRACT(unit FROM expr):

SELECT date_part('year', toDateTime('2026-06-25 10:30:00')); -- 2026 SELECT date_part('doy', toDate('2026-06-25')); -- 176 (day of year) SELECT date_part('dow', toDate('2026-06-25')); -- 4 (day of week)

— All the PostgreSQL extras: epoch, dow, doy, isodow, isoyear, century, decade, millennium.
— Also: EXTRACT(TIMEZONE_HOUR / TIMEZONE_MINUTE FROM dt)
  and EXTRACT(… FROM INTERVAL n …).

Developers: Alexey Milovidov, Vinayak Joshi.

Miscellaneous functions and names

More SQL-standard / PostgreSQL spellings:

SELECT LOCALTIMESTAMP; -- now() โ†’ DateTime SELECT LOCALTIME; -- current time of day โ†’ Time SELECT SESSION_USER; -- alias of currentUser()

— No parentheses needed — they are reserved-word constants.
— Makes more ported queries and BI-tool SQL just work.

Developers: Thomas Cabral, Takumi Hara.

More compatibility aliases

min_by / max_by — aliases for argMin, argMax for PostgreSQL, BigQuery:

SELECT min_by(user, score), max_by(user, score) FROM t; -- aliases of argMin / argMax

REGEXP_SUBSTR — alias of regexpExtract, for Oracle/MySQL/Snowflake:

SELECT REGEXP_SUBSTR('order 12345 shipped', '[0-9]+'); -- 12345

"These aliases are stupid, real engineers use the original ClickHouse names." — the changelog

ALTER TABLE MODIFY COLUMN x UInt32 NULL; -- same as Nullable(UInt32)

Developers: Joey Yu, Alexey Milovidov, Takumi Hara.

SOME / ALL for arrays

PostgreSQL array quantifiers with a literal array on the right:

SELECT 3 = SOME([1, 2, 3]); -- 1 โ†’ rewritten to has(...) SELECT 5 > ALL([1, 2, 3]); -- 1 โ†’ arrayAll lambda SELECT 2 <> ALL([1, 3, 5]); -- 1

The subquery form of ANY / SOME / ALL still lowers to IN / NOT IN.

Developer: Alexey Milovidov.

Select columns by name pattern

Pick columns with * LIKE and * ILIKE instead of listing them:

SELECT * ILIKE '%user%' FROM events; -- user_id, user_name, ... (case-insensitive) SELECT t.* ILIKE '%id', count() FROM t GROUP BY ALL; -- qualified form works too

Similar to * MATCH 'regexp' and COLUMNS('regexp'),
but with SQL-style LIKE patterns instead of a regular expression.

Developer: Yue Ni.

ESCAPE in LIKE

Define a custom escape character so % and _ can be literal:

SELECT '50% off' LIKE '50!% off' ESCAPE '!'; -- 1 (the !% matches a literal percent sign) SELECT 'a_b' LIKE 'a!_b' ESCAPE '!'; -- 1 (the !_ matches a literal underscore)

— SQL-standard syntax, also accepted by PostgreSQL, MySQL, …

Developer: Ilya Yatsishin.

Geospatial

GeoJSON input format

Read a GeoJSON FeatureCollection — one row per feature:

SELECT id, properties.name, toTypeName(geometry) FROM file('places.geojson', GeoJSON);

idproperties.namegeometry
1LondonGeometry (Point)
2squareGeometry (Polygon)

— Columns: id String, geometry Geometry, properties Nullable(JSON).
— Point, LineString, MultiLineString, Polygon, MultiPolygon supported natively.

Developer: Mark Needham.

Mapbox Vector Tiles from SQL

Serve map tiles straight out of ClickHouse with the new MVT functions:

-- Project lon/lat into a tile's pixel space: SELECT MVTEncodeGeom((13.37, 52.52)::Point, 10, 550, 335); -- (124, 3384) -- Aggregate a group's geometries into one binary tile: SELECT MVTEncode(geom_in_tile_space) FROM ...; -- Tile bounding box for the WHERE clause: SELECT MVTBoundingBox(12, 1205, 2557);

— Point, line and polygon geometry.
— Also available as PostGIS aliases ST_AsMVTGeom and ST_AsMVT.

Developer: Saarthak Gupta.

Performance Improvements

ClickHouse Keeper is 2x faster

A round of Keeper improvements makes it more twice as fast overall:

— Better request batching.
Pipelining messages to the leader.
Pipelining log appends.
— Snapshot I/O moved to a background thread (nuraft_use_bg_thread_for_snapshot_io).
— Lower peak memory when applying received snapshots.

On a realistic workload with keeper-bench:

26.5: 2899 RPS, 41.4 ms latency
26.6: 7230 RPS, 18.3 ms latency

Developers: Michael Kolupaev, Antonio Andelic, Mikhail Filimonov.

Lighter, faster query startup

Per-query overhead for simple queries is significantly reduced:

SELECT count() FROM hits; -- parsing + analysis + planning ~50% faster from a single connection

More analysis wins, all enabled by default:

— Identifier-resolution caching during analysis.
— Fast analysis of deeply nested subqueries.
— Faster resolution for deeply nested function calls.
— Faster processing of changing access control entities.

Deeply nested SELECT * over a 1200-column table:
  analysis 1.46 s → 0.023 s  (~64x), 26.5 vs 26.6.

Developers: Raรบl Marรญn, Dmitry Novik, Max Justus Spransy, Azat Khuzhin.

Primary key analysis is faster

Long or high-cardinality primary keys used to make index analysis slow.

Now (use_lightweight_primary_key_index_analysis, on by default):

— For a long primary key, analysis time depends on the columns the query
  actually filters on — not on the length of the key.
  Extending the sorting key is now nearly free for index analysis.

— For a high-cardinality key, analysis works on just the selective
  in-memory prefix instead of loading the whole key.

Developer: Nihal Z. Miaji.

Faster sorting

The general-purpose comparison sorts are now C++ ports
of ipnsort and driftsort:

— Faster ORDER BY on non-numeric columns and for stable sorts.
— Removes a worst case on already reverse-sorted input.
— State-of-the-art, adaptive, and battle-tested algorithms.

ORDER BY 20M strings, single thread:
7.1 s → 5.9 s  (~1.2x), 26.5 vs 26.6.

Developer: Alexey Milovidov.

Faster GROUP BY and JOIN

Sharded aggregation for high-cardinality, evenly-distributed keys
  (enable_sharding_aggregator = 1): hash the key to scatter rows across
  threads, so each thread aggregates a disjoint subset — no merge phase.
— Hash-table prefetching for string-key GROUP BY: ~8% faster.
— Lower peak memory when merging large two-level aggregation states
  (e.g. groupArray).
— More optimal handling of Nullable columns in aggregation.
— Packed keys32 / keys64 methods in HashJoin and Set.

Measured — 300M-row high-cardinality GROUP BY, 16 threads:
enable_sharding_aggregator: 2.31 s → 1.87 s.

Developers: Nihal Z. Miaji, Le Zhang, Yuri Fedoseev, Nikita Taranov.

LIMIT BY, optimized many ways

A whole family of new optimizations for LIMIT BY (all on by default):

— Run LIMIT BY inside each parallel sorted stream during ORDER BY
  when its keys are a prefix of the sort key.
— Stream LIMIT N BY cols in primary-key order with O(1) memory per stream.
— Apply LIMIT BY per partition in parallel when the partition is a
  function of the keys.
— Drop redundant / injective key expressions
  (LIMIT 5 BY toString(x)LIMIT 5 BY x).

MeasuredLIMIT 3 BY g over a 50M-row MergeTree (g is in the sort key):
  memory 86 MiB → 22 MiB (~4x), time 0.15 s → 0.09 s.

Developer: Nihal Z. Miaji.

JOIN optimizations

A batch of JOIN planner and runtime improvements, mostly on by default:

enable_join_transitive_predicates on by default — push derived
  predicates across the join (filter both sides for free).
— Build-side FixedHashMap doubles as the probe-side runtime filter.
Lazy selector / replication indexes when a JOIN is followed by a
  selective LIMIT / TopN / another JOIN.
parallel_hash is now allowed for ASOF JOIN.
— Packed keys32 / keys64 in HashJoin; fast path for single-row probes.
— Dynamic Programming join reordering now works with parallel replicas.

Developers: A. Gololobov, N. Taranov, H. Selmi, Xiaozhe Yu, G. Maher.

Something Interesting

Continuous queries ๐Ÿงช

A SELECT that never ends — the first step toward streaming queries. Append STREAM and it keeps emitting new rows as they are inserted:

SET enable_streaming_queries = 1; SELECT id, msg FROM live_events STREAM; -- blocks, keeps streaming

Applications: filtering and real-time alerting on a stream of events.

Advanced usage with cursors:

SELECT _block_number AS bn, _block_offset AS bo, id, msg FROM events STREAM CURSOR {'all': {'block_number': 2, 'block_offset': 0}}

Developer: Mikhail Artemenko.

Multi-stage distributed query execution ๐Ÿงช

The experimental distributed planner splits a query plan into stages
connected by exchanges — and ships fragments to worker nodes:

scatter / broadcast / gather / shuffle exchanges.
— Distributed shuffle and broadcast hash joins, shuffle aggregation, distributed sort.
— Data between stages streamed over TCP or temp files in object storage.
— Several workers per host (stateless_worker_port / streaming_exchange_port).

Controlled by a new setting, make_distributed_plan.

Developer: Alexander Gololobov.

Memory reservations for workloads

The workload scheduler — which already manages CPU, I/O and concurrency — now manages memory too:

CREATE RESOURCE memory (MEMORY RESERVATION); CREATE WORKLOAD all; CREATE WORKLOAD prod IN all SETTINGS max_memory = '100G'; CREATE WORKLOAD reports IN all SETTINGS max_memory = '20G'; CREATE WORKLOAD vasya IN reports SETTINGS weight = 1; CREATE WORKLOAD petya IN reports SETTINGS weight = 2; -- route a query to a workload: SELECT … SETTINGS workload = 'prod'; -- reserve memory before processing: SET reserve_memory = '5G';

Developer: Sergei Trifonov.

Hypothetical indexes + EXPLAIN WHATIF

Ask "what if I had this skip index?" — without building it:

CREATE HYPOTHETICAL INDEX idx_level ON logs (level) TYPE set(2) GRANULARITY 4; EXPLAIN WHATIF SELECT count() FROM logs WHERE level = 'error';

Baseline (PK + partition + existing indexes): marks: 612, 19.56 MiB With idx_level (set, hypothetical): status: applicable marks: 200 (was 612) est_bytes: 6.39 MiB skip_ratio: 67.3% Estimation: empirical, sampled 5/5 parts

— Session-scoped, visible in system.hypothetical_indexes.
Tune indexes data-driven.

Developer: Yarik Briukhovetskyi.

aiEmbed ๐Ÿงช

Generate text embeddings from inside ClickHouse, via an LLM API:

SELECT aiEmbed('qwen3-8b', review_text) AS embedding FROM reviews LIMIT 10; -- Array(Float32) per row, ready for cosineDistance / vector search

— Build a semantic-search or RAG pipeline without leaving SQL.
— Pairs with the new quantization functions for compact vector indexes.
— Experimental — configure your embedding provider and model.

Developer: George Larionov.

Quantization functions

A scalar codec that compresses embedding components to 8bit and below:

SELECT quantizeBFloat16ToInt8(1.5::BFloat16); -- 107 SELECT dequantizeInt8ToBFloat16(quantizeBFloat16ToInt8(1.5::BFloat16)); -- 1.5 (near-lossless round-trip)

— 256-level Gaussian Lloyd–Max quantizer; one byte per component.
— Int4 / Int2 / binary codes fall out by bit-truncation — trade size for recall.
— Shrinks vector indexes 4x (or more) for cheaper similarity search.

Developer: Alexey Milovidov.

Bonus

https://clickhouse.com/benchmarks/costbench

Meetups


— ๐Ÿ‡ฆ๐Ÿ‡บ Sydney: Agentic AI Unplugged, Jun 24
— ๐Ÿ‡บ๐Ÿ‡ธ Seattle Iceberg Meetup, Jun 25
— ๐Ÿ‡ฒ๐Ÿ‡พ Kuala Lumpur Meetup, Jun 26
— ๐Ÿ‡ฐ๐Ÿ‡ท Seoul: ClickHouse + Confluent, Jun 30
— ๐Ÿ‡บ๐Ÿ‡ธ San Francisco: AI Demo Night, Jul 1
— ๐Ÿ‡ฎ๐Ÿ‡ณ Mumbai: Lakes to Queries, Jul 4
— ๐Ÿ‡บ๐Ÿ‡ธ New York: AI Builders Night, Jul 8
— ๐Ÿ‡ฉ๐Ÿ‡ช Berlin: WeAreDevelopers, Jul 8
— ๐Ÿ‡จ๐Ÿ‡ฆ Montreal: OSS Happy Hour, Jul 9
— ๐Ÿ‡ฎ๐Ÿ‡ณ Bangalore: Fast Data at Scale, Jul 11
— ๐Ÿ‡ฆ๐Ÿ‡บ Melbourne Meetup, Jul 16
— ๐Ÿ‡ฏ๐Ÿ‡ต Tokyo: Google Cloud Next, Jul 30

Reading Corner ๐Ÿ“–

QR: clickhouse.com/blog/open-source-10

https://clickhouse.com/blog/

— Ten years of open source
— How ClickHouse became fast at joins
— Integrating the Rust Delta Kernel into ClickHouse
— Introducing multi-stage distributed query execution
— CostBench: an open benchmark for data warehouse cost-performance
— TPC-H for less than a cent: ClickHouse Cloud
  vs. Snowflake, Databricks, BigQuery, and Redshift
— The end-to-end cost-performance: Snowflake vs. ClickHouse Cloud
— The future of observability: thousands of agents, not one proprietary AI

Q&A