News

ClickHouse 26.8 LTS: pipe syntax, background queries and a faster GROUP BY

The 26.8 long-term-support release adds pipe SQL, queries that survive a dropped connection, IEJoin, new text tokenizers and an adaptive GROUP BY, and changes max_insert_threads to auto.

Source: clickhouse.com

Why it matters: 26.8 is a long-term-support release, so it is the version many teams will standardize on for the next year. It is also a release where several defaults change what your existing workload does: inserts parallelize by default, a new adaptive GROUP BY replaces the old heuristics, and views over Distributed tables are pushed down to the shards. The new SQL surface (pipe operators, background queries, IEJoin) is worth a look, but the upgrade checklist is what decides whether the move is quiet.

Sources: the official 26.8 changelog (released 2026-08-27) and the release post, which counts 98 new features, 128 performance optimizations and 556 bug fixes. PR numbers link to ClickHouse/ClickHouse. These features are newer than the 26.4 build used for this site’s tested examples, so the snippets are quoted or adapted from the changelog and were not run here.

New SQL and server features

  • Pipe operators (#111151), in the style of GoogleSQL: FROM t |> WHERE x > 1 |> AGGREGATE count() AS c GROUP BY y |> ORDER BY c DESC |> LIMIT 10. Each operator wraps the previous query as a subquery, so the resulting AST is the same as the nested-subquery form. It reads top to bottom, which suits exploration, and since it produces the same plan there is no performance reason to avoid it.
  • run_query_in_background (#112816). The server accepts the query, returns an empty result immediately and runs it to completion whatever happens to the connection; the result is discarded and you track the query by query_id in system.processes and system.query_log. It is meant for long INSERT ... SELECT, CREATE TABLE ... AS SELECT and CREATE MATERIALIZED VIEW ... POPULATE statements that should not die with a laptop’s Wi-Fi.
  • CREATE MATERIALIZED VIEW ... POPULATE is locally atomic (#108715): the view subscribes to new inserts and the existing data is snapshotted together under a brief exclusive lock on the source, so rows inserted concurrently through the same server are no longer missed or duplicated (materialized_views_populate_atomically, on by default). The guarantee is local to one server.
  • IEJoin (#109920), enabled by adding ie_join to join_algorithm, handles ON clauses with two inequality comparisons, such as interval-overlap joins. Previously these ran as a CROSS JOIN with a filter, which is slow on large tables.
  • GROUPS frames for window functions (SQL:2011), where N PRECEDING counts whole peer groups rather than rows (#108653).
  • HTTP interface. CREATE HANDLER, ALTER HANDLER and DROP HANDLER define custom HTTP handlers from SQL (#106231). Tables and databases can be addressed by URL path, as in /database/table.format.gz?filter=a>0, and the X-ClickHouse-Format header now overrides the FORMAT clause (#105249). Framing formats such as EventStream (server-sent events) multiplex data, progress, totals and exceptions in one response stream (#110127).
  • system.user_query_log shows every user their own query log records without access to the full table (#110156). VALID FOR <interval> is a shorthand for VALID UNTIL on CREATE USER and ALTER USER.
  • Data lakes and integrations. A bigquery table function and engine (#110166), the S3 Tables catalog with working INSERT (#113505), Snowflake Horizon for Iceberg reads and writes (#114547), the Puffin file format (#103936), and a URL database engine plus s3_base for relative S3 URLs (#111512).
  • Text index tokenizers for Japanese (MeCab), Chinese (dictionary and HMM), locale-aware ICU segmentation and a regexp splitter that preserves tokens such as C++ (#111420, #89945, #109940, #110002).
  • Keeper on disk with a custom LSM tree (use_lsmt_storage, storage_memory_only = false), with performance the changelog describes as similar to the previous in-memory storage (#113903). Also new: the gini aggregate function (#112280), mergedJSONPatch for latest-state aggregation over JSON (#108349), and mergeTreeCodecBlockCounts to see how many blocks use each codec per part and column (#109623).

Performance

  • Adaptive GROUP BY (enable_adaptive_aggregator, on by default, #111459): each thread aggregates into its own hash table until it holds adaptive_aggregator_freeze_threshold keys, then freezes it. Frequent keys keep updating small cache-resident tables without coordination; rare keys are routed by hash into per-bucket backlogs and aggregated once in the parallel merge. Expect the biggest differences on skewed, high-cardinality grouping, and watch memory on the first runs.
  • GROUP BY ... ORDER BY ... LIMIT keeps a bounded heap during aggregation to drop groups that cannot appear in the result (enable_group_by_top_k_optimization, #96630). Smaller hash-table cells for single-String keys (#110573) and a parallel final merge of single-level tables (#110395) are also on by default.
  • Parquet. Row-group skipping through dictionary pages for equality and IN conditions on fully dictionary-encoded columns (#106952), lazy materialization for ORDER BY ... LIMIT n reads from object storage and Iceberg (#110970), and GeoParquet bounding-box pruning at row-group and page level (#104435).
  • Order-by-key reads. read_in_order_use_virtual_row is on by default: for ORDER BY primary_key LIMIT n over a table with many parts, only parts that can contribute are read, which cuts peak memory (#106215).
  • Joins. A parallel_full_sorting_merge algorithm shards a full sorting merge join by key hash (#109005), and a join whose ON is always false no longer reads the non-contributing side.
  • Text and protocol. text_index_posting_list_apply_mode defaults to lazy (#113521); the native protocol sends String columns as an offsets stream plus concatenated data (#110320); clickhouse-client no longer pings the server before every query (#111990); formatting Decimal values as text is up to 190 times faster for a high-scale Decimal256 (#112457).

Experimental

The experimental Cascades cost-based optimizer for distributed plans (enable_cascades_optimizer with make_distributed_plan, #86353), adaptive codec selection per block (enable_adaptive_codec_selection, #111834), a wasm64 build of the clickhouse binary that runs clickhouse local under Node.js or in a browser (#113404), and a large PromQL and TimeSeries push. Packed skip-index storage (packed_skip_index_max_bytes = '1M') was promoted to beta and enabled by default (#111816).

Plan before you upgrade

  1. max_insert_threads is now auto (#109000): INSERT SELECT parallelizes across the cores available to the server, and plain INSERT can fan out when the write path allows. That can change the number of parts created and the order of inserted rows. Set it back to 1, or use compatibility below 26.8, if a pipeline depends on part count or order.
  2. Views over a single Distributed table push the whole outer query to the shards (#101791). FINAL and SAMPLE written on the view reference now reach the shard-local table instead of being ignored, so results can change. Disable with optimize_trivial_view_pushdown_to_distributed = 0.
  3. Lightweight UPDATE patch parts use a new v2 format (#103182). During a rolling upgrade keep patch_parts_version = 'v1' or use compatibility until every replica runs 26.8.
  4. Date32 range grows to 0000-9999 (#111534), matching DateTime64; toDate32(N) for large N is now read as a day number instead of being clamped. arrayIntersect and arraySymmetricDifference stop treating repeated values in one argument as appearing in several (#113021).
  5. Security tightening. The PostgreSQL database engines respect remote_url_allow_hosts; MySQL source TLS files can no longer be given as paths from SQL (use the *_pem settings); SYSTEM ... CACHE ON CLUSTER commands check their own privileges; NATS credentials move to nats_credentials. Test any named collection or dictionary that carries a file path.
  6. Removals. The library dictionary source is gone (#111980), and the Apache Arrow library-based reader and writer are removed, so the native implementation that has been the default since 26.7 is the only one. include_from no longer defaults to /etc/metrika.xml, and asynchronous per-core and per-device metrics became single Map metrics, which breaks dashboards that read OSUserTimeCPU0 and friends.

For an LTS move, the cheapest safety net is the compatibility setting: set it to the version you run today, upgrade, then lower it one setting at a time with EXPLAIN ANALYZE (from 26.7) on your top queries to attribute each change.

release26.8lts

Discussion

Comments live in GitHub Discussions. Sign in with GitHub to join in.