Skip to main content

Backward Incompatible Changes

Data type changes

  • icebergHash and icebergBucket now reject Int128, UInt128, Int256, UInt256, and Decimal256 arguments with an explicit error. Previously they silently truncated wider values and produced colliding hashes. The Iceberg spec only defines hashing for 32/64-bit integers and decimals with precision up to 38; cast to Int64 or Decimal128 to keep the previous behaviour for values that fit. #105866 (Algunenano).

Storage and index changes

  • Reject nested Dynamic/Variant in min/max aggregates and minmax indexes. Previously only top-level Dynamic/Variant were checked. #105468 (Avogar).
  • The default of the server setting insert_deduplication_version changes from compatible_double_hashes to new_unified_hash. Insert deduplication now works on the whole inserted block (per insert) rather than per part/partition: a retry of the same insert is still deduplicated, but two different inserts that produce an identical part are no longer cross-deduplicated, and reordered inserts of the same rows are no longer deduplicated. Set insert_deduplication_version = compatible_double_hashes to restore the previous behavior. Async-insert deduplication is also governed by the sync window under new_unified_hash: both whether it is enabled (replicated_deduplication_window) and its retention (replicated_deduplication_window_seconds, default 1 hour) follow the sync settings, so *_for_async_inserts is legacy and applies only to old_separate_hashes / compatible_double_hashes. Instances upgrading directly from a release that defaulted to old_separate_hashes should first run with compatible_double_hashes until the longest relevant deduplication window has elapsed (including replicated_deduplication_window_for_async_inserts, default one week, if async inserts are used) before relying on new_unified_hash. Async workloads switching from compatible_double_hashes should first raise replicated_deduplication_window[_seconds] to the async window (and run a full async window), quiesce async inserts, or stay on compatible_double_hashes, because new_unified_hash no longer checks the longer-lived async_blocks ids. #107886 (CheSema).
  • Fix hasToken with a separator-containing needle silently returning results via a text index instead of raising BAD_ARGUMENTS. #108189 (Ergus).

Removed features

  • Removed the obsolete allow_experimental_query_deduplication setting and its unsupported experimental query-deduplication behavior. #99398 (devcrafter).
  • ALTER TABLE ... REPLACE PARTITION ... FROM ... no longer silently drops the destination partition’s data when the source table has no parts in the requested partition. Previously such a request removed the destination partition and wrote nothing in its place. It is now rejected with BAD_ARGUMENTS by default. This is a backward incompatible change: a REPLACE PARTITION from an empty source that used to succeed (clearing the destination) will now throw after upgrade. To restore the previous silent-clear behavior, set the new setting allow_replace_partition_from_empty_source = 1 (per query or in the profile), or set compatibility to 26.5 or lower. To explicitly drop destination data, use ALTER TABLE ... DROP PARTITION .... #104939 (groeneai).
  • Removed the experimental KQL (Kusto) functions array_sort_asc and array_sort_desc, and their SQL backends kql_array_sort_asc and kql_array_sort_desc. These functions were experimental, implemented with low quality, and the source of correctness and parser bugs. Queries using these names now return UNKNOWN_FUNCTION. #108101 (groeneai).

New Features

Functions

  • Added support for the PromQL histogram_quantile function in prometheusQuery and prometheusQueryRange table functions. This enables computing quantiles over classic Prometheus histogram buckets identified by the le label. #103477 (Yasumoto).
  • Added the h3PolygonToCellsWithContainment function, which supports center-based, fully-contained, and overlapping containment modes. #104455 (yousefQadry).
  • User can now specify an optional precision argument to the formatReadableSize, formatReadableDecimalSize and formatReadableQuantity functions, controlling the number of digits after the decimal point. Default is 2, preserving the prior behavior. #104648 (antoniofilipovic).
  • Added min_by and max_by aggregate aliases for argMin and argMax. #105712 (itsjoeoui).

SQL and query features

  • formatReadableTimeDelta now accepts an INTERVAL expression of type other than Month and Year as input. #64315 (Beetelbrox).
  • Added the PNG output format support, allowing query output results to be directly rendered as PNG images. #74691 (m7kss1).
  • Introduced a memory reservation feature for workloads. See the workload scheduling documentation. #82414 (serxa).
  • Added IPV4_PREFIX_BITS and IPV6_PREFIX_BITS options for quotas keyed by IP_ADDRESS or FORWARDED_IP_ADDRESS, allowing quota limits to be shared by IP subnet instead of applied to each full address separately. #89270 (adityachopra29).
  • Implement ADD ENUM VALUES in ALTER TABLE queries to simplify appending new values to an existing Enum type without the need to specify all current Enum values again. #93830 (ilejn).
  • Add the GeoJSON input format for reading. #98124 (mneedham).
  • Added a postprocessor to the text index which transforms the tokens after tokenization. #98939 (Ergus).
  • Added opt-in support for TTL nodes in ClickHouse Keeper. TTL nodes expire automatically after their configured lifetime and cannot have children. #100397 (scanhex12).
  • Added token-position storage for text indexes to support exact hasPhrase searches. Enable the support_phrase_search index argument and the allow_experimental_text_index_phrase_search MergeTree setting. #103172 (ahmadov).
  • Add functions arrayTopK and arrayBottomK: - arrayTopK(k, array) returns the K largest elements in descending order - arrayBottomK(k, array) returns the K smallest elements in ascending order. #104563 (vitlibar).
  • Add support for selecting columns by name pattern with * LIKE '<pattern>' and * ILIKE '<pattern>', including qualified forms such as table.* LIKE '<pattern>' and table.* ILIKE '<pattern>'. LIKE matches column names case-sensitively, ILIKE case-insensitively. #104569 (niyue).
  • Added continuous queries for MergeTree tables using a sequence of snapshot reads. #105114 (Michicosun).
  • Added the RowBinaryWithNamesAndTypesAndDefaults format to improve schema evolution support. #105736 (mzitnik).
  • Added functions for serving Mapbox Vector Tiles directly from SQL: MVTEncodeGeom projects a geometry into the pixel space of a slippy-map tile and clips it, MVTEncode aggregates the projected geometries of a group into the binary bytes of a single-layer tile, and MVTBoundingBox / MVTBoundingBoxMercator return the bounding box of a tile for restricting rows to it. Point, line and polygon geometry is supported. Also available under the PostGIS aliases ST_AsMVTGeom and ST_AsMVT. #106107 (saarthak2002).
  • Added LOCALTIME and LOCALTIMESTAMP (SQL-standard / PostgreSQL syntax). LOCALTIMESTAMP is an alias for now() (returns DateTime); LOCALTIME returns the current time of day as a Time value. #106139 (thomas-cabral).
  • Added two new load_balancing strategies, hostname_longest_common_prefix and hostname_longest_common_suffix, which prefer the replica whose hostname shares the longest common prefix (respectively, suffix) with the initiator’s hostname. They are useful when the data center is encoded as a prefix or suffix of hostnames whose numeric segments have variable length, where the existing nearest_hostname and hostname_levenshtein_distance strategies pick the wrong replica. #107360 (den-crane).
  • New functions quantizeBFloat16ToInt8 and dequantizeInt8ToBFloat16: a scalar codec that compresses embedding components to 8-bit using a 256-level Gaussian Lloyd-Max quantizer, from which Int4/Int2/binary codes can be extracted by bit-truncation. #108102 (alexey-milovidov).

Table engines and storage

  • Added support for writing to Azure Data Lake Storage Gen2. #105406 (scanhex12).

Settings and configuration

  • Added a setting output_format_always_write_decimal_point_in_float_and_decimal to always print a decimal point for floating-point and Decimal numbers in text formats, even when the value is a whole number. For example, output 1. instead of 1. Disabled by default. #62614 (qoega).
  • Add a new immutable MergeTree setting allow_tuple_element_aggregation, disabled by default. When enabled, SummingMergeTree, AggregatingMergeTree and CoalescingMergeTree recursively flatten Tuple columns and aggregate each leaf element independently during merges, exactly as if it were a top-level column — SummingMergeTree sums it, AggregatingMergeTree merges its aggregate-function state, and CoalescingMergeTree keeps its last non-NULL value. The setting must be specified at table creation time and is silently ignored by engines that do not support it. #98039 (JingYanchao).
  • Add output_format_float_precision setting to control the number of decimal digits in floating-point text output. #99721 (phulv94).
  • Add materialize_projections_on_insert and materialize_projections_on_merge MergeTree table settings. When materialize_projections_on_insert = 0, INSERTs skip building projection parts, which improves insert throughput for tables with many projections. When materialize_projections_on_merge = 1, a merge rebuilds a projection that is missing from all of its source parts, so projections can be built during merges instead of on insert. Merges still combine only parts that share the same set of projections. #100993 (cwurm).
  • Added a new S3Queue setting after_processing_move_preserve_path. When enabled together with after_processing=‘move’ and after_processing_move_prefix, processed objects are moved while preserving their full source path under the destination prefix instead of being flattened to just the file name. #105354 (asya-ch).
  • Added the url_prefix and full_url_prefix HTTP-handler configuration elements for matching all paths with a given prefix, plus explicit url_regexp, full_url_regexp, and headers_regexp elements. The legacy <url>regex:...</url> form remains supported. #107492 (vitlibar).

Authentication

  • Add an optional external_id credential for S3 role-based access. #106941 (eliangidoni).
  • Added TLS client certificate information (subjects, serial number, issuer, and validity period) to the system.session_log table to improve observability of certificate-based authentication. #107679 (alexey-milovidov).

System tables

  • Added the system.iceberg_files table, which exposes per-file metadata for Iceberg tables with one row per data or delete file in each table’s current snapshot. #104415 (asya-ch).
  • Added hypothetical (what-if) skip indexes. Use CREATE HYPOTHETICAL INDEX ... ON t (expr) TYPE ... to define a session-scoped virtual skip index, then EXPLAIN WHATIF SELECT ... to estimate its skip ratio and cost without materializing it. Defined indexes are visible in the new system.hypothetical_indexes table. #104608 (yariks5s).
  • Added system.constraints table that provides information about all CHECK and ASSUME constraints across all tables, including constraint name, type, and expression. #105337 (PedroTadim).
  • Added a new system table system.documentation that collects the embedded reference documentation of the uniform components of the system (functions, table engines, data types, settings, formats, and others) into a single table, with the documentation rendered as Markdown. #107463 (alexey-milovidov).

Experimental Features

  • Added the experimental dphyp join reordering algorithm for inner joins as an option for the query_plan_optimize_join_order_algorithm setting, and the query_plan_optimize_join_order_max_searched_plans setting, which bounds the join-order search and falls back to the next algorithm in the chain when the bound is exceeded; set it to 0 to keep the previous unbounded search behavior. #98798 (davenger).
  • Added lazy posting list apply mode for the text index. When enabled via SET allow_experimental_text_index_lazy_apply = 1 and SET text_index_posting_list_apply_mode = 'lazy', posting lists are decoded on demand at packed-block granularity using a cursor-based approach instead of being fully materialized into Roaring Bitmaps, reducing memory usage and CPU time for selective text index queries. #100035 (fastio).
  • Allow for attaching prometheus handlers on the main http port with an optional prefix. #104975 (JTCunning).
  • Added an experimental MergeTree setting packed_skip_index_max_bytes that bundles small skip-index substreams into a single skp_idx.packed archive per part, reducing inode pressure when many skip indices are defined on a table. The decision is per substream at write time: substreams whose serialized size stays under the threshold go into the archive, anything larger keeps the standalone skp_idx_<name>.idx2 / .mrk2 layout. A single part can mix layouts. Full-text indices are not supported and are always per-file. Default is 0 (packing disabled). #105321 (Algunenano).
  • Support Buffers serialization for WebAssembly UDFs using ABI BUFFERED_V1, and add webassembly_udf_enable_fuel as a persisted WASM UDF function setting. #105574 (antonio2368).
  • Multi-stage distributed query execution: the planner splits the query plan into stages connected by scatter / broadcast / gather / shuffle exchanges and dispatches the plan fragments to worker nodes. The data between stages is streamed via TCP or passed via temporary files in shared object storage, the path supports distributed shuffle and broadcast hash joins, shuffle aggregation, and distributed sort. The feature is experimental and is disabled by default. #106020 (davenger).
  • The experimental distributed query plan engine (make_distributed_plan) can now use a different task-dispatch and streaming-exchange port per worker, configured per replica in <remote_servers> with stateless_worker_port and streaming_exchange_port. When unset, the previous server-level ports (stateless_worker_client.port and distributed_query.streaming_exchange_port) are used. This makes it possible to run several workers on one host. #107885 (davenger).

Performance Improvements

JOIN performance

  • Implement lazy application of selector and replication indexes in case of JOIN followed by a selective LIMIT or TopN or another JOIN. To control the number of payload columns for enabling lazy selector indexes use the setting query_plan_min_columns_for_join_lazy_indexing (0 means the optimization is disabled). To control the LIMIT for which the optimization is applied use the setting query_plan_max_limit_for_join_lazy_indexing. #98883 (m-selmi).
  • Allow ASOF JOIN to use the parallel_hash join algorithm, parallelizing the build across distinct equality-key values. Previously ASOF was unconditionally opted out of parallel_hash. #105375 (gregakinman).
  • Share the hash join’s FixedHashMap as the join runtime filter on the probe side. When the build-side hash table is (or can be converted to) a FixedHashMap, it is published as the runtime filter and replaces the Set/BloomFilter that BuildRuntimeFilterStep would otherwise install. Controlled by the new setting enable_join_runtime_filter_shared_fixed_hash_table (default true). #105640 (wudidapaopao).
  • DP JOIN reordering is now allowed with parallel replicas. #105889 (nickitat).
  • Improved query plan when JOIN uses runtime filters (default-on enable_join_runtime_filters): the join-reorder cost model now sees through WindowTransform (and other row-preserving plan steps) on the right subtree and uses the underlying row count and per-column NDV instead of falling back to no statistics. #107229 (UnamedRus).

Query optimization

  • Improved performance of text index analysis in multi-token searches by optimizing handling of rare tokens. #98226 (CurtizJ).
  • Improve performance of the encrypt, decrypt, and halfMD5 functions by avoiding implicit per-row OpenSSL provider lookups in OpenSSL 3.x. #99105 (thevar1able).
  • Squash source blocks before projection.calculate() during MATERIALIZE PROJECTION to reduce the number of temporary projection parts and merge overhead. ~3.4x speedup on a 50M-row table. #100047 (amosbird).
  • Users now profit from an improved performance of Approximate Runtime Filters and Bloom Filter Indices. #100201 (cv4g).
  • Speed up ORDER BY ... LIMIT BY queries by running LIMIT BY inside each parallel sorted stream during Sort when LIMIT BY’s columns are a prefix of the ORDER BY. This reduces the number of rows flowing through the final sort merge and any downstream pipeline steps. This optimization is controlled by the new setting query_plan_push_limit_by_into_sort (enabled by default). #104000 (nihalzp).
  • Reduce per-query overhead for simple SELECT queries (parsing, analysis and planning). For example, SELECT count() FROM hits from a single connection is roughly 50% faster. #104513 (Algunenano).
  • Improve performance of bitmapContains for non-UInt64 groupBitmap states by avoiding repeated rb_max calls during range checks. #105960 (niyue).
  • Improved insertion performance for LowCardinality columns with bloom_filter indexes. #106410 (EmeraldShift).
  • Improved performance of the L2DistanceTransposed and cosineDistanceTransposed functions for the QBit data type. #106701 (rienath).
  • Improve performance of query analysis for queries over tables with many columns: avoid computing column node hashes (which include the whole source table expression) when not needed. Analyzing nested SELECT * subqueries over a table with ~1200 columns is now about 50 times faster. #106957 (novikd).
  • Improve performance of the encrypt, decrypt, tryDecrypt, aes_encrypt_mysql, and aes_decrypt_mysql functions by up to an order of magnitude, recovering the performance lost in the BoringSSL to OpenSSL 3.x migration (24.4). #107339 (thevar1able).
  • Improve execution of arrayElement on Array(LowCardinality(String)) and map LIKE functions on maps with LowCardinality(String) keys or values by avoiding unnecessary string materialization. #107450 (EmeraldShift).
  • Reduced CPU usage and improved skip-index evaluation performance for queries filtering DateTime64 columns. #107707 (shankar-iyer).
  • Improved the performance of case-insensitive substring searches, including positionCaseInsensitiveUTF8, ILIKE, and multiSearchAnyCaseInsensitiveUTF8. #107882 (Algunenano).
  • Fix a ~16% throughput regression of the FPC floating-point codec on ARM that was introduced when its predictor tables started using VectorWithMemoryTracking. #108182 (groeneai).
  • Fixed a performance regression where a single lightweight DELETE disabled the query condition cache for the whole table. Repeated selective queries over a table that had ever been touched by a lightweight delete stopped pruning granules and fell back to reading every mark. #112947 (fm4v).
  • Bounded the cost of estimating the selectivity of col IN (...) from column statistics, which could add hundreds of milliseconds to the planning of a single query. The estimator no longer runs the subquery behind col IN (subquery) to fill a set it only needs one selectivity number from — an unbuilt set is skipped instead. For a set larger than the new statistics_max_set_size_for_exact_selectivity_estimation setting (default 10000), the selectivity is now derived from the size of the set and its bounding range. #114389 (nickitat).

Function and aggregation performance

  • Optimize primary key index analysis for long and high-cardinality primary keys. For a long primary key, the run time of index analysis now mainly depends on the complexity of the query’s filter (the key columns it actually uses), not on the length of the primary key — so extending the sorting key has negligible extra overhead on index analysis for queries that filter on only a few of its columns. For a high-cardinality primary key, where ClickHouse keeps only a selective prefix of the key columns in memory and does not load the trailing ones, index analysis now works on just that in-memory prefix instead of the whole key. The optimization is enabled by default and can be turned off with the new setting use_lightweight_primary_key_index_analysis. #91836 (nihalzp).
  • Reduced peak memory usage when merging partial two-level aggregation results with large aggregate states (e.g. groupArray), by freeing each bucket’s source states incrementally during the merge instead of keeping them all alive until the merge completes. #102330 (yurifedoseev).
  • New GROUP BY optimization for high cardinality evenly distributed keys that scatters rows across threads by hashing the grouping key, so each thread aggregates a disjoint subset of keys without a merge phase. Set enable_sharding_aggregator = 1 to enable it. #104233 (nihalzp).
  • Speed up LIMIT BY queries on partitioned MergeTree tables by running LIMIT BY inside each partition’s stream in parallel, instead of merging all streams into one before applying the limit. This applies when the partition expression is a deterministic function of the LIMIT BY columns, so no LIMIT BY group can span two partitions. Controlled by the new setting allow_limit_by_partitions_independently (enabled by default). #105126 (nihalzp).
  • Speed up SELECT ... LIMIT N BY <cols> queries when <cols> are a prefix of the table’s sorting key, or become one after WHERE col = const fixes leading columns. With this enabled the MergeTree reads data in primary-key order and LIMIT BY first filters in streaming mode with O(1) memory per sorted stream which filters out most of the data, then finally running normal LIMIT BY on reduced data to get the final result. Controlled by the new setting optimize_limit_by_in_order (enabled by default). #105135 (nihalzp).
  • Speed up LIMIT BY queries by removing redundant key expressions: a key that is a deterministic function of the other keys is dropped (e.g. LIMIT 5 BY x, f(x) becomes LIMIT 5 BY x), and an injective function of a key is replaced by its argument (e.g. LIMIT 5 BY toString(x) becomes LIMIT 5 BY x). This evaluates fewer and cheaper expressions per row. Controlled by the new settings optimize_limit_by_function_keys and optimize_injective_functions_in_limit_by, both enabled by default. #106818 (nihalzp).
  • Sped up query analysis for queries with many or deeply nested function calls by removing a redundant query-tree hash from the function resolution cache. #107516 (novikd).
  • Parallelize the processing of a recursive CTE’s result: a GROUP BY or other operation over a large WITH RECURSIVE result is no longer limited to a single thread. #107694 (alexey-milovidov).

Storage and I/O performance

  • S3 clients with the same endpoint and bucket share a cache, avoiding duplicate region discovery. #96802 (zvonand).
  • Improved object-storage copy performance by copying blobs in parallel. #105089 (asya-ch).
  • Avoid reading file contents when using the One input format with file-like table functions such as file and s3. #105157 (niyue).
  • The MergeTree primary key and skip indexes can now prune granules for filters where ifNull or coalesce wraps a condition, such as ifNull(key = 0, 0) or coalesce(key = 0, 0). Such predicates — often emitted by query generators to turn a possibly-NULL comparison into a definite boolean — were previously opaque to index analysis and could not skip granules. This extends the existing allow_key_condition_coalesce_rewrite setting (enabled by default). #106272 (andyzzhao).
  • Improved decoding performance for BYTE_STREAM_SPLIT-encoded Parquet FLOAT and DOUBLE columns. #106376 (Algunenano).
  • Fixed long login and query-startup stalls with replicated access storage when many access entities (row policies, roles, quotas, settings profiles) change at once. Each per-entity cache now recomputes once per notification batch instead of once per changed entity, removing quadratic work that could hold the access lock for minutes. #107672 (azat).
  • Fixed a performance regression where reading many small files from object storage via the s3 and other table functions stopped prefetching and fell back to synchronous reads, significantly slowing single-threaded or low-concurrency reads of many small files. #108872 (fm4v).
  • Enabled the initial small-object prefetch when object storage reads go through the filesystem cache (filesystem_cache_name). Previously, reads of many small files, such as S3Queue ingestion, remained synchronous and latency-bound when the filesystem cache was enabled. #109478 (fm4v).
  • Improved PREWHERE optimization for Map subcolumns by accounting for their on-disk size. #110623 (Avogar).
  • Lazy materialization is now applied to queries with FINAL, a filter, and a small LIMIT, even without ORDER BY (for ReplacingMergeTree). #110722 (KochetovNicolai).
  • Reduced deduplication CPU time for asynchronous insert flushes that span many partitions. #111150 (valerypetrov).
  • Fixed a CPU regression for queries that issue many independent small reads against the same MergeTree part when the table has LowCardinality columns. The check deciding whether a part has a single shared dictionary scanned every mark of the whole part on each read task; it now finds each run of equal marks by binary search instead. Queries of this shape on tables with a small index_granularity were up to several times slower since 26.6. #116134 (groeneai).

Memory optimization

  • Reduced the memory used by Enum type metadata by up to 10 times for tables with Enum columns. Value-to-name lookups remain similar or faster, while name-to-value lookups during parsing and deserialization can be slower. #95668 (qoega).
  • Reduced peak memory usage of BACKUP by no longer copying the internal list of file infos when writing backup entries (significant for backups containing millions of files). #111162 (jkartseva).

Improvements

Query and SQL

  • Identify columns by position (instead of by name) when removing unused columns in the query plan. This enables unused-column removal when duplicated column names are present. #100586 (antaljanosbenjamin).
  • Added SESSION_USER as a case-insensitive alias of currentUser() for PostgreSQL / SQL-standard compatibility. #106081 (takumihara).
  • Reduced cancellation latency for queries running over the PostgreSQL wire protocol: KILL QUERY now interrupts output serialization within a single chunk instead of waiting for the entire chunk to be sent to the client. #106535 (rvasin).
  • Fix an ILLEGAL_TYPE_OF_ARGUMENT error for distributed queries with serialize_query_plan = 1 that contain a lambda with a constant argument (e.g. arrayMap(t -> t.2, ...)). Constant columns of ActionsDAG INPUT nodes are now preserved during query plan serialization. #107124 (alexey-milovidov).
  • Reduced cancellation latency for queries running over the MySQL wire protocol: KILL QUERY now interrupts output serialization within a single chunk instead of waiting for the entire chunk to be sent to the client. #107228 (rvasin).

Functions

  • EXPLAIN SYNTAX now formats operators as function calls consistently in explain output (for example plus(1, 2) instead of 1 + 2). #94681 (1abdelhalim).
  • PostgreSQL-style expr OP SOME(array) / expr OP ALL(array) (non-subquery right-hand side) is now supported and rewritten to has / NOT has for =/<>, or to arrayExists / arrayAll lambdas for other comparison operators. ANY is not accepted for the array form because any is also an aggregate function; use SOME instead. The subquery form of ANY/SOME/ALL continues to be lowered to IN / NOT IN. #105129 (alexey-milovidov).
  • Added support for functions multiSearchAny, multiSearchAnyUTF8, and multiMatchAny in text indexes. Also improved text index analysis for the function match: now patterns with alternative groups can skip more granules. #106279 (CurtizJ).
  • Function h3PolygonToCells now enforces the maximum array size across all polygons of a MultiPolygon, validates the underlying H3 library return codes, and rejects MultiLineString arguments instead of silently returning an empty result. #106399 (Algunenano).
  • Deserialization of the states of the contingency, cramersV, cramersVBiasCorrected, and theilsU aggregate functions now validates that the stored counts form a consistent contingency table and throws a CORRUPTED_DATA exception otherwise. #107185 (nihalzp).
  • Improve cardinality estimation in the query plan optimizer: a column produced by a deterministic single-argument function (e.g. toYear(date)) now inherits its argument’s number of distinct values as an upper bound instead of being left without statistics, leading to more accurate join reordering. #107757 (davenger).

Table engines and storage

  • Fixes Kafka table engine consumers that kept using a short poll interval after partition assignment, so they return to the configured kafka_poll_timeout_ms and avoid excessive empty polls, smaller inserted parts, and extra merge overhead after rebalances. #100431 (sugaf1204).
  • Data lake table engines, including Iceberg and DeltaLake, can now use cache-wrapped S3 and Azure disks, allowing repeated reads to use the filesystem cache. #102017 (RinChanNOWWW).
  • Allow filters introduced after the initial PREWHERE selection (predicate pushdown, runtime filters, or explicit PREWHERE plus a WHERE set by the planner) to be merged into the existing PREWHERE on a second optimizer pass instead of staying as a separate Filter step above the MergeTree read. #105445 (yariks5s).
  • Added the wait_for_part_commit_in_dependent_materialized_views setting. When enabled, a cascading materialized view that joins back to its source can see the row currently being inserted. #105943 (ahmadov).
  • PostgreSQL-compatible EXTRACT(TIMEZONE_HOUR FROM dt) and EXTRACT(TIMEZONE_MINUTE FROM dt) for the hour and minute parts of a timezone offset, and EXTRACT(<unit> FROM INTERVAL n <unit>) / date_part('<unit>', INTERVAL n <unit>) for extracting the value out of an interval. #106227 (vinayakj592).
  • Implemented SYSTEM RESTART DISK <name>: it now reloads a disk’s in-memory metadata and re-scans the data parts of readonly-replica tables located on it. This lets a readonly replica of a table on shared plain_rewritable storage observe data written by another server on demand, without waiting for refresh_parts_interval or restarting the server. #106645 (jrdi).
  • Skip unnecessary mark file loads for JSON advanced shared data. #107051 (Avogar).
  • Added the create-time materialized_postgresql_use_extended_date_and_time_types setting for the MaterializedPostgreSQL database engine. By default (enabled), PostgreSQL date/timestamp columns are inferred as Date32/DateTime64; setting it to 0 at CREATE DATABASE time infers the narrower Date/DateTime types. The setting is not applicable to the MaterializedPostgreSQL table engine. #107428 (alexey-milovidov).
  • MergeTree can now read a compressed stream whose blocks use different codecs. This is the read-side prerequisite for adaptive codec selection. #108592 (rienath).
  • Use the bloom_filter skip index for IN predicates on a directly indexed column when transform_null_in = 1 and the IN-set contains no NULL value. Previously the index was skipped for such queries, forcing a full scan. #111329 (groeneai).

S3 and object storage

  • Record privileges in system.query_log.used_privileges for all granted access checks, including those that go through the non-throwing isGranted path (checkAccessWithFilter). Previously only privileges checked via throwing entry points (checkAccess / checkGrantOption) were recorded, which made READ ON FILE / READ ON S3 / READ ON AZURE / READ ON URL invisible in the audit log for DESCRIBE, CREATE TABLE AS, and similar queries that use file / s3 / azure / url table functions. Access enforcement is unchanged. #104693 (alexbakharew).
  • Complete the final S3 multipart upload request asynchronously via the task tracker, so it can overlap with the last part upload instead of running serially in finalize. #105487 (asya-ch).
  • Increased the default persistent_processing_node_ttl_seconds setting for S3Queue and AzureQueue from one hour to six hours, preventing long-running or restarted processing from losing its bucket lock too early. #106838 (kssenii).
  • Support the REDUCED_REDUNDANCY, STANDARD_IA, ONEZONE_IA, GLACIER_IR, and EXPRESS_ONEZONE values (in addition to STANDARD and INTELLIGENT_TIERING) for the s3_storage_class_name setting. #107251 (adityaksolves).
  • The hive partitioning sample path for object storage tables (e.g. S3) is resolved on the first use of the table instead of CREATE/ATTACH, so an unreachable endpoint no longer blocks table creation and server startup. #111842 (evillique).

Settings and configuration

  • Make hive partition strategy a default under compatibility setting file_like_engine_default_partition_strategy. #86746 (kssenii).
  • Added four new MergeTree table settings to control default parameters of text indexes: text_index_dictionary_block_size, text_index_dictionary_block_frontcoding_compression, text_index_posting_list_block_size, and text_index_posting_list_codec. These settings allow tuning text index behavior at the table level without specifying parameters in every index definition. Explicit per-index arguments still take precedence. #100626 (CurtizJ).
  • Add setting output_format_pretty_use_nbsp_for_padding to render table-layout padding in table-style Pretty formats as U+00A0 NO-BREAK SPACE when output_format_pretty_grid_charset is UTF-8. This helps copied Pretty output keep table alignment in tools that collapse regular spaces. The setting is disabled by default, and ASCII charset output keeps regular spaces. #103559 (ashrithb).
  • Support compatibility with the old analyzer under the analyzer_compatibility_allow_non_aggregate_in_having setting. If enabled, non-aggregate conjunctions will be moved from HAVING to WHERE. #104232 (novikd).
  • OPTIMIZE TABLE ... ON CLUSTER and other DDL no longer hang when the target table has table_readonly = 1. The setting now throws a new dedicated error code TABLE_IS_PERMANENTLY_READ_ONLY, which DDLWorker treats as non-retriable (distinct from the transient TABLE_IS_READ_ONLY that arises during temporary ReplicatedMergeTree ZooKeeper disconnects). The table_readonly setting is also now explicitly rejected for ReplicatedMergeTree, both at creation and via ALTER MODIFY SETTING. #105109 (alexey-milovidov).
  • Descending sort order in MergeTree sorting keys (e.g. ORDER BY (time DESC, key)) is now always supported and no longer requires the experimental setting allow_experimental_reverse_key, which became obsolete. #106440 (nikitamikhaylov).
  • Support keyed_by_normalized_query_hash for quotas defined in the static server configuration (users.xml), matching the existing CREATE QUOTA ... KEYED BY normalized_query_hash DDL syntax. #107654 (alexey-milovidov).
  • The compatibility setting no longer applies obsolete settings, so it does not mark them as changed or produce obsolete-setting warnings. #107737 (UberDever).
  • Added the setting analyzer_compatibility_multiple_joins_qualify_column_names (default false). When enabled and the FROM clause of a query contains two or more JOINs, result column names produced by the analyzer mimic the old analyzer’s multiple-joins rewrite: columns expanded from * are named <alias-or-table>.<column>, and an unaliased column reference in the SELECT list keeps its name exactly as written. This makes outer queries that reference such qualified names, like SELECT ll.Date FROM (SELECT * FROM t AS ll JOIN t1 ON ... JOIN t2 ON ...), work as they did with the old analyzer. Also fixed the analyzer losing qualified result column names for columns expanded from * when group_by_use_nulls is combined with ROLLUP, CUBE or GROUPING SETS, which produced duplicate result column names and broke outer references to those columns. #110746 (novikd).
  • Added setting analyzer_compatibility_apply_final_to_all_joined_tables that restores the old behavior, where the FINAL modifier on the left-most table of a JOIN was also applied to the other joined tables. The setting is registered in the settings changes history, so compatibility with versions before 26.6 restores the old semantics automatically. #111589 (fm4v).
  • Added a new MergeTree setting text_index_version that controls the on-disk format version of text indexes: v0_initial, v1_with_codec, or v2_with_positions. During a rolling upgrade or before a downgrade, set it to an older version so that newer servers keep writing text index parts in a format older servers can still read; the compatibility setting automatically adjusts it. #111803 (CurtizJ).

System tables and monitoring

  • Populate used_storages in system.query_log with the storage engine name when querying tables via DataLakeCatalog. #100706 (melvynator).
  • Add current_projection, current_projection_progress, projections_completed, and projections_remaining columns to system.merges to expose projection merge progress. #102611 (amosbird).
  • Table engines now carry embedded documentation, introspectable via the new description, syntax, examples, introduced_in, and related columns of the system.table_engines table. #106177 (alexey-milovidov).
  • Database engines now carry embedded documentation, introspectable via the new description, syntax, examples, introduced_in, and related columns of the system.database_engines table. #106178 (alexey-milovidov).
  • Data types now carry embedded documentation, introspectable via the new description, syntax, examples, introduced_in, and related columns of the system.data_type_families table. #106180 (alexey-milovidov).
  • Input/output formats now carry embedded documentation, introspectable via the new description, examples, introduced_in, and related columns of the system.formats table. #106181 (alexey-milovidov).
  • Aggregate function combinators now carry embedded documentation, introspectable via the new description, syntax, examples, introduced_in, and related columns of the system.aggregate_function_combinators table. #106185 (alexey-milovidov).
  • Added a new system.data_skipping_index_types table that lists the available data skipping index types together with embedded documentation (description, syntax, examples, introduced_in, related). #106186 (alexey-milovidov).
  • Added a new system.disk_types table that lists the available disk types together with embedded documentation (description, syntax, examples, introduced_in, related). #106187 (alexey-milovidov).
  • Added asynchronous metrics TotalUncompressedBytesOfMergeTreeTables and TotalUncompressedBytesOfMergeTreeTablesSystem, reporting the total uncompressed size of data stored in MergeTree-family tables. #106364 (alexey-milovidov).
  • Adds the GlobalMemoryLimitExceeded profile event so operators can monitor when the server-wide memory limit is hit. #106466 (sacheendra).
  • Added asynchronous metrics ExecutableUserDefinedFunctionMemoryResidentBytes and ExecutableUserDefinedFunctionProcesses, reporting the resident memory (VmRSS) and number of live processes of executable and executable_pool user-defined functions, including descendant processes and idle pool workers. #107300 (HanziJiang).
  • Added show_remote_databases_in_system_tables, enabled by default, to let users hide MySQL and PostgreSQL databases from system.tables, system.columns, and system.completions. show_data_lake_catalogs_in_system_tables continues to control only DataLakeCatalog visibility. #104416 (pamarcos). #109082 (pamarcos).

ClickHouse Keeper

  • Various changes to Keeper that make it around 2x faster overall (better batching, pipelining messages to leader, pipelining log appends). #101757 (al13n321).
  • Add more keeper profile (server-side + client-side) events for watches. #105336 (scanhex12).
  • Add a new keeper-only system.keeper_snapshots table with information about local ClickHouse Keeper snapshots. #105571 (mstetsyuk).
  • Add a new keeper-only system.keeper_changelogs table with information about local ClickHouse Keeper changelog (Raft log) files. #105617 (mstetsyuk).
  • Add a keeper-only system.keeper_cluster table. Contains one row per Raft cluster member as seen by the current Keeper. #105646 (mstetsyuk).
  • Reduce peak memory usage when applying received snapshots in ClickHouse Keeper with KeeperMemoryStorage. #105851 (antonio2368).
  • Added Keeper coordination_settings for NuRaft uncommitted log entry admission limiting and append-entries backward-probe throttling. #106108 (antonio2368).

Memory management

  • Fixed syntax-error messages potentially including bytes from adjacent memory after the parser backtracked past the end of a statement. #105086 (groeneai).
  • Reduced memory usage when opening a backup (for RESTORE, or as the base of an incremental BACKUP). The .backup metadata is now parsed as a stream instead of being loaded into an in-memory XML document tree, which for large (especially incremental) backups avoids allocating a multi-gigabyte DOM tree. #109107 (jkartseva).
  • Reduced peak memory usage when finalizing a BACKUP. Writing the backup metadata no longer copies the file infos of all files into a temporary vector (which, for backups of millions of files, transiently cost several gigabytes); the infos are now iterated in place. #109861 (jkartseva).

Data formats

  • Users can now insert Avro Fixed fields for 8-bit/16-bit/32-bit/64-bit integer variants. #98139 (patrickpichler).
  • The AvroConfluent format now retries the Confluent Schema Registry HTTP client on transient failures (transport timeouts, connection refused, DNS errors, HTTP 5xx/408/429) with exponential backoff, instead of aborting the INSERT on the first network glitch. New settings format_avro_schema_registry_max_retries (default 5) and format_avro_schema_registry_retry_initial_backoff_ms (default 100) control the policy. Schema-validation errors (HTTP 409, malformed Avro JSON) remain fatal. #106661 (groeneai).
  • Preserved the original ClickHouse error codes for errors returned through Apache Arrow. #107267 (azat).

Named collections and dictionaries

  • Add enable_compression setting for the mysql table function, the MySQL table engine, the MySQL database engine, dictionary SOURCE(MYSQL), and named collections. When enabled, ClickHouse negotiates MySQL protocol-level compression for all data transferred over the connection. #103229 (bernardlim).
  • Added a new system.dictionary_layouts table that lists the available dictionary layouts together with embedded documentation (description, syntax, examples, introduced_in, related). #106182 (alexey-milovidov).
  • Added a new system.dictionary_sources table that lists the available dictionary sources together with embedded documentation (description, syntax, examples, introduced_in, related). #106184 (alexey-milovidov).
  • The effective named collections storage type is now available as named_collections_storage.type in system.server_settings and through getServerSetting('named_collections_storage_type'). #111806 (pamarcos).

Other improvements

  • Improved table name hints in error messages: no longer suggests the exact same name, and includes the database name in the suggestion (e.g., “Maybe you meant other_db.my_table?”). #95116 (matt-metivier).
  • Improved error message for unresolved identifiers in queries without a FROM clause to suggest adding one. #101769 (Onyx2406).
  • Fixed PREWHERE with IN subquery on primary key columns not using primary key index for granule pruning, causing full table scans instead of reading only relevant granules. #102570 (nikitamikhaylov).
  • Refreshable materialized views now continue refreshing after their target table is replaced using EXCHANGE TABLES and the old table is dropped. Previously, refreshes could fail with an UNKNOWN_TABLE exception because they retained the old table’s UUID. #102724 (seva-potapov).
  • Turn on enable_join_transitive_predicates by default. #103724 (davenger).
  • Refreshable materialized view now supports REFRESH DEPENDS ON to trigger refreshes on another RMV’s refreshes instead of time-based schedule. (REFRESH EVERY ... DEPENDS ON already existed, but couldn’t be used reliably for this use case.) See CREATE MATERIALIZED VIEW documentation. #104440 (al13n321).
  • Add EXPLAIN PIPELINE option to compact repeated processor chains. #104662 (niyue).
  • Added REGEXP_SUBSTR as a case-insensitive alias of regexpExtract for Oracle/MySQL/Snowflake compatibility. #105122 (alexey-milovidov).
  • Added date_part('unit', expr) as syntactic sugar for EXTRACT(unit FROM expr). Standard interval kinds and the PostgreSQL extras (epoch, dow, doy, isodow, isoyear, century, decade, millennium) are all supported. #105127 (alexey-milovidov).
  • The QueryConditionCache now records individually filtered-out granules even within read batches that partially pass PREWHERE, reducing the number of marks re-read by subsequent queries with the same condition. #105335 (hanfei1991).
  • Add BFloat16 support for numeric predicate functions. #105391 (mohhddhassan).
  • Support basic statistics, a compact per-column statistic that stores numeric min/max, average string length, and NULL counts where applicable. #106048 (hanfei1991).
  • Support a trailing NULL / NOT NULL modifier in ALTER TABLE ... ADD/MODIFY COLUMN, mirroring CREATE TABLE. #106150 (takumihara).
  • The PREWHERE optimizer now groups conjuncts that reference the same column set before estimating selectivity, so that conditions like a > 2500 AND a < 2502 are evaluated together as a combined range (~0.02% selectivity) rather than as two independent predicates (~50% each). This produces a more accurate ordering of PREWHERE conditions when column statistics are available. #106337 (hanfei1991).
  • Do not print the “Stack trace (when copying this message, always include the lines below):” preamble in exception messages when the stack trace is actually empty. #106524 (alexey-milovidov).
  • Fixed CREATE TABLE ... CLONE AS (and REPLACE / ATTACH PARTITION ... FROM) on encrypted disks copying data instead of hardlinking it. #106731 (nikitamikhaylov).
  • Fix DataLakeCatalog databases with catalog_type = 'onelake' reading table data by using the OneLake Blob endpoint (.blob.fabric.microsoft.com) by default. Set onelake_use_blob_endpoint = false to keep the previous DFS endpoint (.dfs.fabric.microsoft.com) behavior. #106843 (scanhex12).
  • Reduced CPU overhead of asynchronous logging under high log rates by notifying the log consumer only on the empty-to-non-empty queue transition instead of on every message. #107352 (nikitamikhaylov).
  • Fixed reading of the auth_response length in the MySQL handshake, where a length byte >= 128 was interpreted as a multi-gigabyte value because it was read as a signed char. #107384 (uwezkhan).
  • MaterializedPostgreSQL now maps PostgreSQL numeric(p, 0) columns with precision greater than 76 (e.g. numeric(78, 0) used for 256-bit integers) to ClickHouse Int256 instead of failing with “Precision too big”. Values that do not fit into Int256 are rejected with a clear error. #107431 (alexey-milovidov).
  • Nullable(Tuple(...)) is now Beta. Disabled by default, set enable_nullable_tuple_type = 1 to use it. #107754 (nihalzp).
  • Async inserts no longer log the full list of query_ids at trace/debug level, which since 26.2 could blow up text_log on services with heavy async insert traffic. Detailed lines are now at test level. #107852 (CheSema).
  • Reduced filesystem-cache metadata usage by releasing invalidated priority entries promptly. #107903 (kssenii).
  • A bloom_filter skip index on an Array column is now used for arrayJoin(column) IN (set), arrayJoin(column) GLOBAL IN (set) and arrayJoin(column) = const, the same way it was already used for hasAny(column, set) and has(column, const). Previously these arrayJoin ... forms fell back to a full scan. #109536 (groeneai).
  • IS NOT DISTINCT FROM and IS TRUE now use primary-key and minmax indexes to prune granules, the same as =. Previously k IS NOT DISTINCT FROM 42 and (k = 42) IS TRUE scanned all granules. #110006 (groeneai).

Bug Fixes

JOIN fixes

  • Fixed nested function (used internally by ARRAY JOIN) stripping LowCardinality from column types, causing Array(LowCardinality(String)) to become Array(String) in output. #98974 (Onyx2406).
  • Fix join reordering silently dropping unmatched rows of a RIGHT/LEFT JOIN when it is comma-joined (cross) with another table, e.g. t1 RIGHT JOIN t2 ON t1.c = t2.c, t3. The query previously returned the inner-join result instead of the outer-join one. #101684 (groeneai).
  • Fix a LOGICAL_ERROR (“Port is not connected”, code 49) that could occur when executing queries involving a VIEW with aggregation inside a JOIN.. #102574 (Ergus).
  • Fix max_rows_to_transfer and max_bytes_to_transfer being silently ignored for GLOBAL IN and GLOBAL JOIN queries under the new analyzer. The settings now raise SET_SIZE_LIMIT_EXCEEDED (or break, depending on transfer_overflow_mode) when the materialised external table exceeds the configured limit, matching the behavior of the old analyzer. #104119 (groeneai).
  • Fixed wrong results or query failures for JOIN queries using join_algorithm = 'direct' with a MergeTree table on the right side when optimization reordered the lookup columns. #104174 (groeneai).
  • Fixed a bug where queries combining arrayJoin with ORDER BY ... LIMIT after a JOIN could silently return zero rows. The query plan optimization that lifts function evaluation above the SortingStep no longer applies when the lifted expression contains arrayJoin, since arrayJoin can change the number of rows. #104558 (groeneai).
  • Fix logical error in negative LIMIT BY in some cases when used with ARRAY JOIN. #105403 (nihalzp).
  • Add a new compatibility setting analyzer_compatibility_prefer_alias_over_subcolumn (disabled by default). When enabled, the new analyzer prefers the alias-prefix interpretation over Tuple-subcolumn / dotted-column matches for multi-part identifiers, restoring the previous interpreter’s behavior. This avoids AMBIGUOUS_IDENTIFIER (and related) errors when a query joins a table whose name matches an inner table of a CTE/subquery that uses SELECT * over a join, where asterisk-renamed columns (e.g. b.id) would otherwise leak the inner table identifiers into the outer scope. #105491 (vdimir).
  • Fix possibly wrong results for queries that combine an outer join with a subsequent inner join referencing the outer join’s null-supplying side. Join reordering could pick a plan that pulled inner-join conditions into the outer join’s ON clause. #105992 (vdimir).
  • Better compatibility with the old analyzer. If the table has columns like x.a Array, x.b Array, x String, prefer arrays for ARRAY JOIN x. #106069 (KochetovNicolai).
  • Fixed an exception for INNER JOIN queries with an empty left MergeTree table when enable_parallel_replicas, query_plan_use_new_logical_join_step, and query_plan_optimize_join_order_algorithm = 'greedy' are enabled. #106338 (KochetovNicolai).
  • Fix a logical error (Left and right columns have same names) in the join order optimizer that could occur for joins executed with parallel replicas when two relations in the join graph share column names. #106418 (alexey-milovidov).
  • Fix a LOGICAL_ERROR (Invalid number of rows in Chunk) in JoiningTransform for a LEFT JOIN with unique right keys, a mixed ON condition, and a small max_joined_block_size_rows. #106928 (groeneai).
  • Fix an exception (LOGICAL_ERROR) in the join runtime filter when the join key contains a Variant or Dynamic type nested inside a Tuple, Array, or Map and the right side of the join has a single distinct value. #106931 (groeneai).
  • Fix a LOGICAL_ERROR (Expected the argument N to have X rows, but it has Y) when executing a function over a Dynamic column built by a JOIN over Dynamic (for example the non-joined rows of a RIGHT/FULL JOIN, or a correlated EXISTS subquery decorrelated into a join). #107095 (groeneai).
  • Fix a Bad cast from type DB::IColumn const* to DB::ColumnNullable const* logical error exception when a qualified asterisk (t.*) over a JOIN ... USING key is passed to an aggregate function and the other side of an outer JOIN has a Nullable key (with join_use_nulls = 0). #107129 (groeneai).
  • Fix a logical error (Unexpected return type from equals. Expected Nullable(UInt8). Got UInt8) when the disjunction (partial predicate) push-down optimization pushes a condition over a USING key whose type is widened by the JOIN. Also fix a server crash (segmentation fault) in the analyzer when resolving identifiers in JOIN ... USING queries that contain constant-foldable if/multiIf branches referencing unknown identifiers. #107407 (groeneai).
  • Fix runtime join filter producing wrong results for JSON columns. #107663 (Avogar).
  • Fix a Bad cast from type DB::ColumnNullable to DB::ColumnVector<...> logical error exception when a qualified asterisk (t.*) selects a JOIN USING key and that join is nested below a PASTE/CROSS/comma join or an outer ON join, with join_use_nulls = 0. #108043 (groeneai).
  • Fixed a bug in the new analyzer where FINAL on one table of a JOIN (e.g. FROM t1 FINAL JOIN t2) was incorrectly applied to the other joined tables as well, what could made such queries slower. #108979 (vdimir).
  • With analyzer_compatibility_join_using_top_level_identifier = 1, an identifier in JOIN ... USING can now be resolved from an alias defined on a subexpression inside the SELECT list (for example, SELECT uniqExact(lower(x) AS id) FROM t1 JOIN t2 USING (id)), matching the old analyzer behavior. Previously only top-level projection aliases were considered, and such queries failed with an UNKNOWN_IDENTIFIER exception even with the setting enabled. The error hint suggesting the setting is now also produced when the matching alias is nested. #110739 (novikd).
  • Fixed INCOMPATIBLE_TYPE_OF_JOIN error for an ANY join on a Join engine table when a WHERE filter on a right-side column allowed the query planner to rewrite the join to SEMI or ANTI. A Join engine table has a fixed declared strictness that cannot be changed, so the conversion is now declined for such tables. #111362 (groeneai).
  • Fix TYPE_MISMATCH (“Key type for complex key … does not match”) when a JOIN onto a dictionary uses a Nullable, LowCardinality, or LowCardinality(Nullable) join key while the dictionary key is not wrapped. The direct-join dictionary lookup now normalizes the key to the dictionary’s declared key type (as dictGet/dictHas already do), and a NULL key never matches. #111857 (groeneai).
  • Fixes AMBIGUOUS_COLUMN_NAME (Block structure mismatch in (columns with identical name must have identical structure)) for a query that repeats a JOIN ON condition in WHERE with join_use_nulls = 1. Such a query now returns its result instead of failing. #112007 (groeneai).
  • Fixed wrong results or server termination for direct dictionary JOINs whose key uses sparse serialization. #112327 (groeneai).
  • Fixed wrong results when a hash join has a single LowCardinality key of a type wider than 8 bytes (UInt128, Int128, UInt256, Int256, and their Nullable variants). Such a join silently returned rows whose key values are not equal. #113230 (groeneai).
  • Fix part and granule pruning for a JOIN ON condition involving constant columns of the other side. #113484 (vdimir).
  • Fix a wrong value returned for a virtual column such as _table or _database selected from the right side of a JOIN served by DirectKeyValueJoin (a key-value storage such as EmbeddedRocksDB, KeeperMap, Redis, or a dictionary). The query returned the contents of a data column instead, or failed with LOGICAL_ERROR. #113698 (groeneai).

Query and analyzer fixes

  • Fix a bug with splitMultipartQuery throwing an “Empty query” error for queries that ends with comment after semicolon. #85491 (yariks5s).
  • Fixes the case where an alias after a subquery in DESCRIBE TABLE is not accepted by the parser and results in a syntax error. #100205 (yariks5s).
  • Fixed FORMAT clause being consumed by INSERT instead of applying to EXPLAIN output in EXPLAIN INSERT INTO ... SELECT ... FORMAT .... #101772 (Onyx2406).
  • Fix SELECT queries being significantly slower when concurrent INSERTs are running. Previously an INSERT pipeline reserved CPU slots up to max_threads at query start even when most slots were never used, starving concurrent SELECTs. CPU slot allocation is now demand-driven: the pipeline only requests slots as it actually pushes parallelizable work. Applies both to concurrency control and to the preemptive CPU scheduler for workloads. New server setting concurrent_threads_lazy_allocation (default true) acts as a rollback lever. #102928 (seva-potapov).
  • Fix a bug where ALTER TABLE ... MODIFY SETTING on an EmbeddedRocksDB table could persist an invalid setting value to the table metadata file even when the server rejected the query. On the next server restart the table would fail to attach with CANNOT_PARSE_BOOL (or a similar parsing error), and in databases where load failures are fatal the server would refuse to start. Invalid setting values are now rejected before any metadata is written. #103417 (groeneai).
  • Fixed streaming INSERT with input_format_max_block_wait_ms for the HTTP interface and for INSERT SELECT FROM input, so partial input blocks are flushed before the request finishes. #104534 (alexey-milovidov).
  • Fix CASE expression returning the ELSE branch instead of the matching THEN when both the expression and a WHEN value were NULL. #105556 (Algunenano).
  • Fix support for using WASM SQL UDFs in MATERIALIZED VIEW definitions. #106161 (niyue).
  • Fix INTERPOLATE () throwing INVALID_WITH_FILL_EXPRESSION when ORDER BY columns are aliased in the SELECT list with the old analyzer. #106252 (yakov-olkhovskiy).
  • Functions base58Encode, base58Decode and tryBase58Decode now respect max_execution_time and query cancellation on large inputs, and reject inputs larger than 10 KB instead of running for a very long time. The limit is configurable via the new setting function_base58_max_input_size (0 disables it). #106428 (alexey-milovidov).
  • Fix wrong-results bug where WHERE c0 = const returned no rows for tables with ORDER BY f(c0) when f(const) evaluates to NaN, e.g. ORDER BY sqrt(c0) with a negative constant. The primary-key analysis incorrectly pruned every granule and poisoned the query condition cache for subsequent queries. #106507 (groeneai).
  • Fixed wrong results for SELECT c, count() FROM t WHERE c GROUP BY c against tables with the implicit _minmax_count_projection, an explicit aggregate projection, or a normal projection: every group used to collapse into one row with a constant key and the total row count. #106590 (groeneai).
  • Make FORMAT apply to the EXPLAIN output of EXPLAIN ... INSERT ... SELECT ... FORMAT ... also when SETTINGS precede the FORMAT or the output format is Values. #106686 (alexey-milovidov).
  • Fixed a rare spurious RESOURCE_ACCESS_DENIED error (“Scheduler queue with resource request is about to be destructed”) for a query that was actually granted access to a workload resource, caused by a reused thread-local request object retaining a previous request’s failure. #106690 (alexey-milovidov).
  • Fixed match, extract, extractAll and countMatches returning wrong results for regular expressions containing hex or octal escapes. #106709 (ofeliacode).
  • Fix an integer underflow in the PostgreSQL wire protocol parser where a message with a length field smaller than 4 caused a size - 4 wraparound and an oversized resize/ignore. #107485 (uwezkhan).
  • With enable_analyzer = 1 (the default), querying a table by its bare name when it only exists in another database now suggests the right table, e.g. SELECT * FROM functions reports Maybe you meant system.functions?. Previously the new analyzer gave a hint-less Unknown table expression identifier error, while the old analyzer already produced the helpful suggestion. #107550 (groeneai).
  • Fixed a server abort (std::terminate, signal 6) during teardown of a distributed-plan query (make_distributed_plan = 1) when a worker status-check failed to re-schedule the next check (for example CANNOT_SCHEDULE_TASK on shutdown or MEMORY_LIMIT_EXCEEDED). The query now fails cleanly and the server keeps running. #107575 (groeneai).
  • Fixed incorrect results from distributed queries selecting ALIAS columns that share a common subexpression: columns could be misaligned and values returned under the wrong column. #107675 (vdimir).
  • Fixed an Inconsistent AST formatting logical error for a no-argument window function inside a CODEC or engine declaration (e.g. CODEC(cume_dist() OVER (...))); such a function now keeps its parentheses so the query survives a format/parse round-trip. #107806 (alexey-milovidov).
  • getClientHTTPHeader is now correctly treated as non-deterministic, so its result is no longer incorrectly reused by the query result cache. #108029 (alexey-milovidov).
  • Fixed wrong results when using SELECT [...] SAMPLE [...] together with the query condition cache (setting use_query_condition_cache = 1 which is also the default). #108488 (groeneai).
  • Fix NOT_FOUND_COLUMN_IN_BLOCK when a compound predicate is aliased in GROUP BY and referenced again, with enable_identifier_resolve_cache enabled (the default). The optimize_and_compare_chain optimization was not idempotent and a shared resolved node accumulated a duplicate transitive conjunct. #108553 (groeneai).
  • Fix NUMBER_OF_COLUMNS_DOESNT_MATCH error when a subquery on a Distributed table reads two or more ALIAS columns that expand to the same expression (for example a1 String ALIAS toString(x), a2 String ALIAS toString(x)) and the subquery feeds an outer query, e.g. SELECT count() FROM (SELECT a1, a2 FROM dist GROUP BY a1, a2). #108725 (groeneai).
  • Fix UNKNOWN_IDENTIFIER error on ALTER TABLE ... DROP COLUMN when another column has a DEFAULT or MATERIALIZED expression that defines and references an inline alias. #109374 (alexey-milovidov).
  • Fix system.tables silently skipping databases for users with per-database grants when the query reads only the name/database columns. Introduced in 26.2. #109723 (samay-sharma).
  • Fix UNKNOWN_IDENTIFIER error for columns qualified by CTE name (cte_name.column) in queries stored in views when the analyzer is enabled. #111386 (novikd).
  • Fixes NOT_FOUND_COLUMN_IN_BLOCK when a FINAL query filters on a sorting-key column that is not in the SELECT list and the filter is moved to PREWHERE (for example SELECT s FROM t FINAL WHERE k GROUP BY s with optimize_move_to_prewhere_if_final = 1). #111721 (groeneai).
  • Fixed server startup and ATTACH failing with WITH RECURSIVE is not supported with the old analyzer for a view with a recursive CTE when enable_analyzer = 0 is the server default. #112784 (evillique).
  • Fix propagation of query settings and NULL handling in accurateCastOrDefault. #114912 (alexey-milovidov).
  • Fix read_rows, read_bytes, written_rows and written_bytes in system.query_thread_log reporting a thread lifetime running total instead of the values for the logged query. Rows of long-lived pooled threads, in particular the query’s initiating HTTPHandler thread, were affected. #115596 (groeneai).

MergeTree and storage fixes

  • Detached parts with a tryN suffix can now be dropped. #58957 (antaljanosbenjamin).
  • Fix a MULTIPLE_EXPRESSIONS_FOR_ALIAS exception for queries with duplicate projection aliases (for example SELECT *, day + 365 AS day) inside nested subqueries when running with parallel replicas. #80310 (alexey-milovidov).
  • Fixed incorrect compression codec selection for MergeTree parts when the table-level default_compression_codec setting was explicitly configured. Parts written on insert, during merges, and for projections used the server-wide default codec instead of the table-level setting (the empty part produced by a fully-deleting mutation is now covered as well). #101784 (Onyx2406).
  • Fix MULTIPLE_EXPRESSIONS_FOR_ALIAS errors thrown by remote replicas when running queries that reference projection aliases inside PREWHERE / WHERE / HAVING / QUALIFY (e.g. SELECT x AS a, y AS b, (a AND b) AS c FROM t PREWHERE c) or SELECT * over self-joins with overlapping column names, with parallel replicas and parallel_replicas_local_plan = 0. #103806 (groeneai).
  • Fix a broken patch part after ALTER TABLE ... DROP PARTITION ID 'patch-...' followed by DETACH/ATTACH TABLE. Previously the empty covering part was written without partition.dat and source_parts.dat, leading to a broken-on-start_patch-... entry in system.detached_parts after the next attach or server restart. #104353 (groeneai).
  • Fixed silent data replacement with default values when ALTER TABLE ... RENAME COLUMN ran concurrently with OPTIMIZE TABLE ... FINAL or another background merge. #104822 (groeneai).
  • Fixed two LOGICAL_ERROR: Reading from materialized CTE 'X' before it has been materialized shapes thrown by queries with enable_materialized_cte: (1) a reused materialized CTE filtered by IN (subquery) over another materialized CTE, and (2) a materialized CTE referenced both directly and inside a WHERE ... IN (...) filter that hits a MergeTree primary key through a nested IN-subquery. EXPLAIN on the same queries was affected too because the bugs fired during plan optimization. #105041 (novikd).
  • With skip_cache_on_disk_failure = 1, queries now continue when FileCache cannot create an on-disk cache directory. The failure is logged instead of failing the query. #105250 (Diskein).
  • Fixed the logical error exception Mutation of Memory table produced incomplete output raised by ALTER TABLE <memory_table> APPLY PATCHES and ALTER TABLE <memory_table> APPLY DELETED MASK. Memory tables do not own patch parts or deletion masks, so both commands are now correctly treated as no-ops. #105286 (groeneai).
  • Fix CANNOT_CONVERT_TYPE for Merge over Merge over Distributed with distributed_group_by_no_merge=1 #105330 (azat).
  • Fixed singleValueOrNullMerge returning a concrete value instead of NULL when merging a state that had already observed multiple distinct values. #105734 (fallintoplace).
  • Fix a crash in the mongodb table function, MongoDB storage and MongoDB dictionary source when the collection name is empty or contains NUL bytes. #105776 (Algunenano).
  • Fix ALTER TABLE ... CLEAR COLUMN being rejected for explicit SummingMergeTree and CoalescingMergeTree columns_to_sum columns. #105785 (antonio2368).
  • Fixed UNKNOWN_DATABASE errors when creating a parameterized view whose database name is supplied through a query parameter. #105799 (groeneai).
  • Restored read-in-order optimization for Merge tables when using the analyzer. #105867 (KochetovNicolai).
  • Fixed UPDATE and DELETE mutations on Iceberg tables accessed through a catalog, including repeated updates and deletes that match no rows. #106111 (scanhex12).
  • Fix optimize_skip_unused_shards did not apply (and force_optimize_skip_unused_shards falsely failing) when a Distributed table is queried through a Merge table or the merge table function, with the predicate applied above it. #106250 (KochetovNicolai).
  • Fix a logical error Column identifier ... is already registered when a mutation (DELETE/UPDATE) predicate contains an IN/EXISTS subquery that reads from a default table expression nested in another subquery. #106414 (alexey-milovidov).
  • Fix sporadic incorrect results for ORDER BY ... DESC queries when reading wide parts in reverse order with read_in_order_use_virtual_row_per_block = 1 and a small max_block_size. #106429 (vdimir).
  • Fixed a Bad cast exception for Redis dictionaries that use STORAGE_TYPE 'simple' with a cache/direct layout and a single string (complex) key; such dictionaries now work, and composite keys over simple storage report a clear error. #106501 (vdimir).
  • Fixed the exception Mutation of Memory table produced incomplete output raised by ALTER TABLE <memory_table> commands that have no per-row data effect on a Memory engine, including APPLY PATCHES, APPLY DELETED MASK, MATERIALIZE STATISTICS, MATERIALIZE INDEX, MATERIALIZE PROJECTION, and REWRITE PARTS. Memory tables do not own those structures, so these commands are now treated as no-ops. #106621 (groeneai).
  • Fix server failing to start with Too many marks in file ...skp_idx_idx.cmrk4, marks expected 0 (bytes size 0) when a MergeTree table has a skip-index part with zero granules and a non-empty marks file on disk. #106675 (groeneai).
  • Fixes the case when parallel replicas wasn’t applied for view with UNION due to empty table in the UNION. #106900 (devcrafter).
  • Fix a logical error exception conflicted_part_name.has_value() that could occur during a synchronous insert into a ReplicatedMergeTree table when the inserted block was fully deduplicated and the conflicting part’s deduplication node had already been removed (for example by a concurrent DROP PARTITION). #107026 (groeneai).
  • Fix a rare LOGICAL_ERROR “Attempt to release query context that does not exist” and the accompanying server crash when reading MergeTree tables through a filesystem cache disk created with enable_filesystem_query_cache_limit = 1. #107028 (groeneai).
  • Fix a server crash when moving an empty part to a plain_rewritable disk (for example with ALTER TABLE ... MOVE PARTITION ... TO DISK for an empty part kept by remove_empty_parts = 0). #107040 (groeneai).
  • Fix a Logical error: Too large size passed to allocator exception on INSERT into a MergeTree table when adaptive_write_buffer_initial_size is set to an extremely large value. The adaptive write buffer initial size is now clamped to the buffer maximum. #107104 (groeneai).
  • Fix ALTER TABLE ... ON CLUSTER batches that mix MODIFY SETTING/RESET SETTING with a comment change (for example MODIFY COMMENT 'x', MODIFY SETTING old_parts_lifetime = 123) being applied only on the leader replica, leaving the other replicas diverged. Also fix a positional or per-column-SETTINGS MODIFY COLUMN ... COMMENT being misclassified as a local comment-only metadata change, which left the column reorder out of the replicated metadata and could cause INCOMPATIBLE_COLUMNS on replica restart. #107142 (groeneai).
  • Fix the Argument ... of GROUPING function is not a part of GROUP BY clause error for queries that use the grouping function with the group_by_use_nulls setting enabled. #107206 (KochetovNicolai).
  • Fix lightweight UPDATE queries with legacy parallel replicas enabled for non-replicated MergeTree tables. #107246 (groeneai).
  • Fix a possible logical error exception in SYSTEM SYNC DATABASE REPLICA … STRICT and make the STRICT modifier actually take effect for database replicas. #107344 (PedroTadim).
  • Fix Logical error: 'Duplicate announcement received for replica number N' that could occur with parallel replicas when a scalar subquery contained nested subqueries reading the same table. #107381 (groeneai).
  • Fix silent data loss on plain (non-replicated) MergeTree when REPLACE PARTITION, MOVE PARTITION, DETACH PARTITION, or DETACH PART is run on a partition that still has unapplied lightweight UPDATE patches. These operations now reject the command (as ReplicatedMergeTree already does), pointing to ALTER TABLE ... APPLY PATCHES, instead of silently reverting the committed update. #107386 (groeneai).
  • Fix MaterializedPostgreSQL silently stopping replication of changes when the PostgreSQL database or table name contains upper-case letters (the pgoutput consumer requested an unquoted, lower-cased publication name that did not match the case-preserving publication). #107423 (alexey-milovidov).
  • Fix THERE_IS_NO_COLUMN error when optimize_if_transform_strings_to_enum = 1 and the optimized if/transform-over-string-literals expression is a GROUP BY or ORDER BY key over a Distributed table or parallel replicas. #107455 (groeneai).
  • Fixed SELECT ... FINAL and OPTIMIZE TABLE ... FINAL returning duplicate rows after CREATE TABLE ... CLONE AS, ATTACH PARTITION ... FROM or MOVE PARTITION ... TO TABLE adopted parts from a plain MergeTree into a ReplacingMergeTree, SummingMergeTree or AggregatingMergeTree. The adopted part’s merge level is now reset to 0 when the source and destination engines differ, so the destination deduplicates it on the next merge. #107481 (groeneai).
  • Query cancellation is now better tracked while waiting for the quorum in ReplicatedMergeTree. #107513 (nickitat).
  • Account memory used by the rapidjson library (in prettyPrintJSON, JSONMergePatch and the rapidjson JSON parser) against the memory tracker, so pathological inputs are rejected with MEMORY_LIMIT_EXCEEDED instead of allocating without bound. #107555 (Algunenano).
  • Fix a Logical error: Stream ... variant_discr ... is not found that could occur when merging or reading a MergeTree part produced by a mutation of a table with a Dynamic column. #107562 (alexey-milovidov).
  • Fix a LOGICAL_ERROR (updateFormatPrewhereInfo called more than once) raised when querying a file(), url(), or object-storage source with both an explicit PREWHERE and a WHERE while optimize_prewhere_after_pushdown was enabled. #107568 (groeneai).
  • Fixed a server exception (Digest does not match logical error) that could happen on RENAME TABLE, RENAME DATABASE, or CREATE OR REPLACE TABLE involving a TimeSeries table inside a Replicated database. Renaming a TimeSeries table is now supported. #107583 (groeneai).
  • Fix DROP TABLE of a TimeSeries table in a Replicated database, which previously leaked the inner tables and left the background drop task retrying forever (DROP TABLE ... SYNC would hang). #107604 (groeneai).
  • Fixed two path-traversal issues in the replicated part fetch protocol that could let a malicious replica write files outside the part directory. #107606 (antonio2368).
  • Fixed ALTER TABLE ... REPLACE PARTITION on a plain MergeTree table resurrecting the replaced-out parts after a server restart, which made the table return both the replacement rows and the stale replaced rows. #107623 (groeneai).
  • Fixed a LOGICAL_ERROR (“Block structure mismatch … between ConvertingTransform and RemovingReplicatedColumnsTransform”) when inserting into a materialized view whose TO target table declares a column with a wider Enum than the view’s SELECT produces. The valid Enum widening is now applied on the materialized-view insert path, like a direct INSERT ... SELECT. #107648 (groeneai).
  • Fix NUMBER_OF_COLUMNS_DOESNT_MATCH error when querying a Distributed table (or using parallel replicas) that has several ALIAS columns expanding to the same expression and referencing them together with ORDER BY/GROUP BY/HAVING. #107913 (yakov-olkhovskiy).
  • A transient error while refreshing data parts of a read-only table no longer permanently stops the background refresh task. #108034 (alexey-milovidov).
  • index_granularity_bytes is now honored for AggregateFunction state columns. Previously the granule byte cap was ignored for such columns (for example uniqExact states in AggregatingMergeTree tables and aggregating projections), producing granules far larger than the configured limit and increasing read amplification and query-time memory. #108297 (groeneai).
  • Fixed insert_quorum = 'auto' not rejecting inserts up front when fewer than a majority of replicas were alive. Such inserts now fail immediately with TOO_FEW_LIVE_REPLICAS instead of writing a local part and later timing out with UNKNOWN_STATUS_OF_INSERT. #108800 (gagandhakrey).
  • Fixed a server crash on asynchronous insert with deduplication when optimize_on_insert makes the inserted block empty (for example, rows summing to zero in SummingMergeTree). #109229 (Diskein).
  • Fix data loss of a column with no default expression when a merge runs concurrently with ALTER TABLE ... RENAME COLUMN, or when the column’s only values come from a lightweight UPDATE. The column could be dropped from the merged part, so all of its values read back as NULL. #109356 (groeneai).
  • Fixed a rare server termination when merging uniqExact aggregate states in parallel with GROUPING SETS, ROLLUP, or CUBE. #109389 (groeneai).
  • Fixed rollup merges being rescheduled indefinitely. #109410 (Michicosun).
  • Fixed GROUP BY mutations for tables with materialized or persistent virtual columns. #109532 (Michicosun).
  • Fixed UNKNOWN_IDENTIFIER: Missing columns: '_block_offset' error when running ALTER TABLE ... MATERIALIZE INDEX on the implicit minmax index created by add_minmax_index_for_block_number_column/add_minmax_index_for_block_offset_column on a table that has freshly inserted (0-level) parts. The index is now built for those parts instead of failing. #110236 (groeneai).
  • Fixed a read-only object-storage replica (a disk configured with read_only = true) not discovering new parts via refresh_parts_interval, and table_disk = true being rejected on such a disk with “table_disk is not supported for non-ObjectStorage disks”. #110460 (jkartseva).
  • Fix a LOGICAL_ERROR (“No set is registered for key”) in ALTER TABLE … DROP COLUMN, ALTER TABLE … DELETE/UPDATE mutations, and lightweight DELETE, on tables that have an ALIAS column whose expression uses an IN operator (including through another ALIAS column). #111039 (PedroTadim).
  • Fixes cases where row policy was not used for MergeTree Projections during query execution. #112329 (yariks5s).
  • Fix ReadBufferFromEncryptedFile: Wrong file position logical error when reading a Compact part from an encrypted disk with direct I/O, which made merges get stuck in system.replication_queue. #112943 (alexey-milovidov).
  • Fixed reading the internal _temporary_and_external_tables database through the merge table function and the Merge table engine, which allowed one session to read the temporary tables of other sessions and other users, and caused an exception on the old analyzer path. A database regexp now skips that database, and naming it explicitly is denied, the same way as direct access to it is. #113224 (alexey-milovidov).
  • Fixed a row policy containing a scalar subquery being evaluated only once and then reused forever after the table had been read through a Merge table. The parsed policy condition is cached and shared by all queries, and reading it through Merge rewrote the cached expression in place, which also caused a data race between concurrent queries using the same policy. #113563 (alexey-milovidov).
  • Fixed CANNOT_CONVERT_TYPE errors when reading a Merge table containing a Distributed table with custom-key parallel replicas. #113742 (alexey-milovidov).
  • Fix Logical error: No set is registered for key ... when reading through a Merge table whose child declares an ALIAS column containing IN, and the children disagree about that column’s default. #113757 (groeneai).
  • Fixed a row policy bypass where the mergeTreeIndex table function exposed primary key and minmax index values of the rows hidden by a SELECT row policy on the source table; reading mergeTreeIndex for a table with a row policy is now denied. #115304 (yariks5s).

Data type and serialization fixes

  • Fixed an exception in ARRAY JOIN when LowCardinality numeric types are used. #91784 (Ergus).
  • Fix NOT NULL columns being silently created as Nullable when data_type_default_nullable = 1 and the table is created in a Replicated database or via ON CLUSTER. #97572 (xiaohuanlin).
  • Fixed wrong row count returned by a MaterializedView query with query_plan_enable_optimizations = 0 when the view maps an integer column to a Bool column. #100692 (Maximus5).
  • Fix inconsistent part metadata after mutations of columns with non-default serializations. #102817 (korowa).
  • Fix NULL propagation when reading subcolumns extracted from Nullable(Tuple(...)) columns. For example, for tup Nullable(Tuple(s Nullable(String))), SELECT tup.s now correctly returns NULL in rows where the outer tuple is NULL instead of garbage values. This covers all element types that can represent NULL: Nullable, Dynamic, Variant and LowCardinality(Nullable(...)). #102942 (nihalzp).
  • Fixed recursiveRemoveLowCardinality erasing custom geometry type names (e.g. LineString vs Ring, MultiLineString vs Polygon), which caused misinterpretation of the geometry type. #103041 (jh0x).
  • Fix dictGetOrNull silently overwriting other columns in the SELECT projection with NULL when called with a Nullable key column whose values are missing in the dictionary. The function was mutating an input-aliased null map in place; it now deep-clones the result column before mutation. #104327 (groeneai).
  • Fixed an exception for distributed queries containing an empty IN tuple on the sharding key when optimize_skip_unused_shards_rewrite_in is enabled. #104966 (alexey-milovidov).
  • Fixed count-min statistics PREWHERE selectivity estimation for Float32 columns, including comparisons with Float64 literals. #105047 (hanfei1991).
  • Fixed ILLEGAL_TYPE_OF_ARGUMENT when merging quantileExactWeightedInterpolated, quantileDD, or quantilePrometheusHistogram aggregate states with their plural quantilesXxxMerge counterparts (and vice versa). The singular and plural variants of these three quantile families share the same internal aggregate state but were not listed in the internal name-mapping table, so cross-function state merge — and the function-fusion optimization for these families — were rejected. #105189 (groeneai).
  • Fix incorrect results of toStartOfWeek, toLastDayOfWeek, toMonday, toStartOfMonth, toLastDayOfMonth, toStartOfQuarter and toStartOfYear for Date32 and DateTime64 arguments whose result falls outside the Date range: instead of overflowing into arbitrary dates, results before 1970-01-01 are now clamped to 1970-01-01 and results after 2149-06-06 are clamped to 2149-06-06. This also fixes wrong query results (incorrectly pruned parts and granules) when such functions were used in WHERE over a Date32 or DateTime64 key containing out-of-range values. #105244 (yariks5s).
  • Fix a logical error in arrayRemove when the first argument is an array of Variant whose alternatives are all incompatible with the type of the second argument and variant_throw_on_type_mismatch is disabled. The function now treats the comparison as “never equal” and returns the array unchanged instead of triggering a server-side assertTypeEquality failure. #105248 (groeneai).
  • Fix toFloat64/toUInt32/toString/etc. on Dynamic ignoring cast_keep_nullable. #105467 (Avogar).
  • Fix a server segfault in uniqStateOrNull / uniqStateOrDefault / uniqOrNullState (and similar combinator chains over uniq) when used with GROUP BY ... WITH ROLLUP, WITH CUBE, or WITH TOTALS and a Nullable argument. #105470 (groeneai).
  • Fix toStartOfMillisecond and toStartOfMicrosecond returning a result off by nearly a second for negative (pre-epoch) DateTime64 values, and fix UndefinedBehaviorSanitizer signed-integer-overflow in toStartOfSecond, toStartOfMillisecond, and toStartOfMicrosecond for DateTime64 inputs near INT64_MIN. #105482 (groeneai).
  • Fixed a server termination when deserializing singleValueOrNull states for JSON. #105535 (Avogar).
  • Fix ignoring input_format_try_infer_datetimes during insertion into shared data in JSON. #105544 (Avogar).
  • Fix DateTime wrapping around for out-of-range values in JSONExtract and text deserializations. #105551 (Avogar).
  • Fix crash when inserting tuples of different sizes in the same VALUES clause into a String column. #105582 (Avogar).
  • Fixes NOT_IMPLEMENTED error on toString from DateTime with Timezone containing NULL value. #105587 (yariks5s).
  • Added validation for malformed flattened Dynamic columns in Native input. #105666 (Avogar).
  • Fix a LOGICAL_ERROR (Unexpected return type from if) raised during query planning for if expressions whose result type is Variant and whose second-or-third branch is a constant-condition if over a UInt64 literal that fits into Int64. #105680 (groeneai).
  • Fix Bad get: has Decimal32, requested Decimal128 from sumMap and sumMapWithOverflow over a Nested(... Nullable(Decimal(P, S))) column when the aggregate state is serialised (e.g. parallel replicas, sumMapState via a binary-state formatter, external aggregation). #105816 (groeneai).
  • Fix Expected ColumnLowCardinality, got String / Bad cast from type DB::ColumnString to DB::ColumnLowCardinality errors when apply_mutations_on_fly = 1 is used on a table with pending on-fly UPDATE/DELETE mutations queued before an ALTER MODIFY COLUMN ... LowCardinality(...) mutation. #105847 (Algunenano).
  • Fix Cannot find column error for distributed queries with IN Array(...) filter for the new analyzer. #105894 (KochetovNicolai).
  • Fixed a server termination when a column with STATISTICS was modified to Nullable while another ALTER concurrently dropped it. #105917 (groeneai).
  • Reject out-of-range IntervalKind bytes during RowBinaryWithNamesAndTypes type decoding (input_format_binary_decode_types_in_binary_format = 1) with a clear INCORRECT_DATA error instead of constructing a DataTypeInterval with an invalid kind that could subsequently trip undefined behavior in hash paths. #106261 (groeneai).
  • Read ‘null’ subcolumn as JSON path in Nullable(JSON) instead of null-map. #106295 (Avogar).
  • Fixed an out-of-bounds read in sipHash64Keyed, sipHash128Keyed and sipHash128ReferenceKeyed when hashing a column whose arrays are all empty and the key is not constant. #106355 (Algunenano).
  • Fixes a bug where users could not use scalar subqueries in the first argument of IN where the second argument is a non-constant tuple. #106610 (yariks5s).
  • Fix session_timezone being ignored when serializing LowCardinality(DateTime) columns to text formats (CSV, TSV, JSONEachRow, etc.). Previously, after the first write of a LowCardinality(DateTime) column on a server, every subsequent query that wrote such a column rendered the wall-clock string in whichever timezone was first seen, regardless of session_timezone. #106634 (groeneai).
  • Fixed a server termination in if and multiIf when the condition is a constant Nullable(Nothing) value. #106678 (groeneai).
  • Revert a change that made sumMap / the -Map combinator reject custom-named numeric value types (such as SimpleAggregateFunction(sum, T) and Bool) with ILLEGAL_TYPE_OF_ARGUMENT: Values for -Map cannot be summed, breaking previously-working aggregations. #106729 (fm4v).
  • Fixed a Bad cast exception while pruning parts by MinMax statistics when a key column is LowCardinality and the predicate constant is LowCardinality(Nullable(...)). #106793 (groeneai).
  • Fix logical error in parseDateTime with non-ASCII input bytes. #106856 (Avogar).
  • Fix THERE_IS_NO_COLUMN exception for distributed queries involving optimize_rewrite_aggregate_function_with_if optimization when the aggregate function argument requires a cast to Nullable type. #106908 (yakov-olkhovskiy).
  • Fixed a signed integer overflow (undefined behavior) in arrayLevenshteinDistanceWeighted and arraySimilarity when the weight arrays contain large integer values. The weighted distance is now accumulated in a wide integer for integral weights, so large integer weights no longer overflow and stay exact. #106934 (groeneai).
  • Fix a crash (Source column is not Map / SIGSEGV) when merging sorted blocks that contain a Variant column with a Map variant whose local storage order differs from its global order. #107011 (groeneai).
  • Fix TYPE_MISMATCH error (“Cannot convert string … to type …”) for ORDER BY <numeric column> ... LIMIT n queries when lazy materialization placed another column before the sort column. The top-K threshold is now read from the correct sort column. #107060 (groeneai).
  • Fix a compound ALTER TABLE ... RENAME COLUMN a TO b, RENAME COLUMN c TO a that reuses a freed column name (a “swap”) between columns of different types. The materialized part recorded the wrong column type, so a later SELECT failed with Conversion between numeric types and IPv6 is not supported (or aborted on part load in debug builds). #107064 (groeneai).
  • Fixed non-deterministic results of the roundDown function when the boundaries array contains NaN. The same input value could return a finite boundary or NaN depending on the surrounding rows in a batch. NaN boundaries are now ignored, so the result depends only on the finite boundaries. #107065 (groeneai).
  • Fixed quantileTDigest and quantileTDigestWeighted throwing DECIMAL_OVERFLOW for Date and DateTime arguments when the interpolated quantile is fractional but in range (for example quantileTDigestWeighted(date, weight) on values that all fit in the type). The fractional result is now truncated to the result type, matching quantilesTDigestWeighted; genuine out-of-range values still raise an error. #107066 (groeneai).
  • Fixed silent truncation of out-of-range integer values in Enum8/Enum16 type definitions. Enum8('a' = 200) now throws ARGUMENT_OUT_OF_BOUND instead of silently creating Enum8('a' = -56). #107081 (groeneai).
  • Fix wrong row order (and a LOGICAL_ERROR “Rows are not sorted with permutation” in debug builds) for multi-column ORDER BY ... LIMIT queries that sort by Nullable columns when several rows tie on the leading columns. #107094 (groeneai).
  • Fix Logical error: 'Bad cast from type DB::ColumnVector<...> to DB::ColumnTuple' when reading an Array(Tuple(...)) column whose value is filled with defaults, e.g. after ALTER TABLE ... ADD COLUMN or after an unfinished ALTER TABLE ... CLEAR COLUMN mutation applied on the fly. #107232 (groeneai).
  • Reject unsupported uses of AggregateFunction columns in TTL expressions at CREATE TABLE time (e.g. TTL toDateTime(state)) instead of failing later during TTL execution with ILLEGAL_TYPE_OF_ARGUMENT. This also covers states carried inside Variant alternatives and Dynamic values. Valid state-aware consumers such as finalizeAggregation are still accepted. #107366 (Ria-K912).
  • Fix arrayResize with a Decimal size argument: the size is now interpreted by its real value (e.g. arrayResize([1, 2, 3], 1.5::Decimal(2, 1)) returns one element) instead of the raw unscaled representation. #107389 (alexey-milovidov).
  • Fix LOGICAL_ERROR: Unexpected return type from materialize (and similar type-mismatch errors) when apply_mutations_on_fly = 1 is used on a table with a pending on-fly UPDATE whose target column is also read as a function input by an earlier on-fly UPDATE, before an ALTER MODIFY COLUMN ... LowCardinality(...) mutation. #107475 (groeneai).
  • Fix NULL values being silently converted to empty strings when inserting Arrow/ORC data into a LowCardinality(Nullable(…)) column. This was a regression introduced in 26.5. #107532 (Ergus).
  • Fixed a LOGICAL_ERROR (“Input nodes size mismatch in dag”) when a query with make_distributed_plan = 1 joins on a function-wrapped key whose two sides have no common type (for example ON intDiv(-1, t1.key) = t2.key with a UInt64 right key). #107701 (groeneai).
  • Fixed insert deduplication computing wrong hashes for String and Array columns with the server setting insert_deduplication_version = new_unified_hash: identical inserts could fail to deduplicate because the deduplication hash depended on the row’s position within the inserted block. #107915 (CheSema).
  • Fixed a server termination in has when searching a Map with Dynamic keys using a LowCardinality argument. #107956 (groeneai).
  • Fixed a performance regression for Map subcolumns used with PREWHERE. #107988 (Avogar).
  • Fix a logical error when casting a Dynamic or Variant column nested inside a Tuple to a non-Nullable element type with accurateCastOrNull or accurateCastOrDefault. #108061 (alexey-milovidov).
  • Fix a Bad cast from type DB::ColumnSparse to DB::ColumnVector<char8_t> logical error exception when a LIKE query reads from a text index via the direct-read fallback path over a column stored sparse. #108068 (groeneai).
  • Fix a LOGICAL_ERROR (“Unexpected return type from if”) when reading a column under apply_mutations_on_fly = 1 after an ALTER UPDATE col = ... WHERE <cond> with a non-constant or false condition followed by ALTER MODIFY COLUMN col <new type>. #108128 (groeneai).
  • Fix signed integer overflow (undefined behavior) in dateDiff with hour and minute units on extreme DateTime64 values close to the Int64 range limits. #108229 (groeneai).
  • Fix a server exception (Logical error in IColumn::insertFrom) when casting an Array(Dynamic) or Array(Variant) to QBit with accurateCastOrNull, e.g. accurateCastOrNull(CAST(range(114), 'Array(Dynamic)'), 'QBit(Float32, 114)'). #108288 (groeneai).
  • Fixes parseDateTimeBestEffort with timezone throwing CANNOT_PARSE_DATETIME on NULL rows of toString(Nullable(DateTime64)). #108310 (yariks5s).
  • Fixed a server crash (stack overflow) caused by deeply nested expressions such as [[[ ... ]]] or array(array( ... )) when max_parser_depth is set to a large value. #108493 (Algunenano).
  • Fix a SELECT * projection returning a column’s type default (e.g. 0) instead of its DEFAULT value (e.g. -1) when reading a column added with ALTER TABLE ... ADD COLUMN ... DEFAULT after the projection was created. Reads from the base table were already correct; only reads answered by the projection were affected, until the projection was rebuilt. #108569 (tiandiwonder).
  • Fixed partition and primary key pruning being silently disabled when a LowCardinality(FixedString) (or LowCardinality(Nullable(FixedString))) key column is wrapped in a function in the key, for example PARTITION BY sipHash64(k) % N with WHERE k = 'literal'. Such queries scanned all partitions instead of pruning them. #108777 (groeneai).
  • Preserve original key order in bucketed Map serialization to fix comparison operations that depend on it. #109178 (Avogar).
  • Fix schema inference for the Arrow, ArrowStream, Avro formats and the legacy ORC and Parquet readers returning Nullable(Tuple) for nullable struct columns while the Nullable(Tuple) type is not allowed (allow_experimental_nullable_tuple_type is disabled). DESCRIBE returned a type that CREATE TABLE rejects, so creating a table or inserting data using the inferred schema failed with the error Nullable Tuple type is not allowed. #109185 (nihalzp).
  • Fixed a bug where column DEFAULT values were not applied for INSERT INTO TABLE FUNCTION (for example remote(...) or file(...)) with inline VALUES data when the server parses the inline data itself (send_table_structure_on_insert_with_inline_data = 0). An explicit NULL inserted into a non-Nullable column with a DEFAULT became 0 instead of the declared default. It now behaves like a plain table INSERT and the HTTP protocol. #109258 (groeneai).
  • Fix a segfault in groupArrayLastMerge. Deserialization of aggregated function state is now validated. So a broken state does not lead to OOB. #109485 (mstetsyuk).
  • Fix segfault in largestTriangleThreeBuckets aggregate function by rejecting broken aggregate function state at the deserialization level. #109492 (mstetsyuk).
  • Fix reading a Parquet column with a non-nullable Tuple when the requested ClickHouse type wraps it in Nullable (e.g. Nullable(Tuple(...))), which previously failed with TYPE_MISMATCH. #109615 (groeneai).
  • The Parquet v3 native reader can now read a physically nullable struct column (a Parquet OPTIONAL group) as Nullable(Tuple(...)). Previously it threw TYPE_MISMATCH. #109898 (groeneai).
  • Fixed a server crash (native stack overflow) when a deeply nested Array/Tuple/Map/Object literal is copied or destroyed, for example a query with a very deeply nested literal at a raised max_parser_depth. #110393 (Algunenano).
  • Fix wrong IS NULL / IS NOT NULL / count() results with optimize_functions_to_subcolumns on a column that was made Nullable by a metadata-only ALTER MODIFY COLUMN T to Nullable(T). For parts written before the conversion, the .null subcolumn was filled from the storage-type default (NULL) instead of being derived from the physically-present parent column, so existing not-null rows were wrongly reported as NULL. #110584 (groeneai).
  • Fixed a LOGICAL_ERROR (“Bad cast from ColumnString to ColumnLowCardinality”) during primary-key index analysis when a LowCardinality key column is wrapped in a nested CAST chain that re-introduces LowCardinality, e.g. WHERE CAST(CAST(s, 'LowCardinality(String)'), 'String') < '5'. In debug and sanitizer builds this aborted the server; in release it failed the query. #111050 (groeneai).
  • Fixed reading Arrow and ArrowStream data with empty nested Array or Map columns produced by Apache Arrow Java before 19.0.0 (bundled with Apache Spark), which were previously rejected with an INCORRECT_DATA error about the offsets buffer being too small. #111101 (Algunenano).
  • Fixed a logical error Block structure mismatch (in debug and sanitizer builds) and an Illegal types of arguments error for set operations over compatible aggregate-state columns (e.g. quantileState and quantilesState(0.9)) nested inside container columns such as Tuple, Array, Map, Nullable, or Variant. #111191 (alexey-milovidov).
  • Added validation for corrupted replicated-index data received over the native protocol. #112331 (Avogar).
  • Fixed an out-of-bounds read during insert deduplication when an INSERT passes through a materialized view that changes the row count before writing to a partitioned target table. #112649 (CheSema).
  • Fixed an out-of-bounds write when reading a Parquet DECIMAL column whose physical type is wider than the type its declared precision maps to, for example DECIMAL(9, 2) stored as physical INT64. Such files are valid Parquet and other writers produce them, but the reader sized the destination column from the declared precision while the decoder wrote the physical width, corrupting memory. Reading such a file now also raises DECIMAL_OVERFLOW when a value does not fit the declared precision, instead of returning corrupted data, and reads losslessly with a type hint at least as wide as the physical type. #113046 (groeneai).
  • Fixed ATTACH PARTITION FROM, REPLACE PARTITION, MOVE PARTITION TO TABLE and adding a ReplicatedMergeTree replica failing with Tables have different ..., METADATA_MISMATCH or INCOMPATIBLE_COLUMNS for tables whose definitions were written with redundant parentheses, such as PARTITION BY (a), ORDER BY (b), INDEX ix (b * c) TYPE minmax, PROJECTION p (SELECT (b) ...), CONSTRAINT c CHECK (a > 0), TTL (d + INTERVAL 10 YEAR) or DEFAULT (a + 1). #114188 (alexey-milovidov).
  • Fixes a bug where an access entity carrying a Map-valued setting, such as a settings profile with http_response_headers or additional_table_filters, is stored in a form that ClickHouse cannot read back, leaving the entity permanently unloadable after a restart. #114620 (groeneai).
  • Fixes has, indexOf, countEqual, mapContainsKey, mapContainsValue and Map subscript returning “not found” for a constant LowCardinality needle equal to the element type’s default value, such as an empty String or a zero number. #114624 (groeneai).
  • Fixed wrong results of the trivial GROUP BY ... LIMIT optimization (setting optimize_trivial_group_by_limit_query) for queries with DISTINCT, QUALIFY, window functions, or arrayJoin in the projection: these consume or filter the groups after the aggregation, so capping the aggregation at LIMIT + OFFSET keys could return too few rows or wrong values. The optimization no longer applies to such queries. #114695 (alexey-milovidov).
  • Fixed wrong results for SELECT count(arrayJoin(arr)) with the default optimize_trivial_count_query = 1. The stored row count was returned instead of the number of array elements, and on file() and url() the query returned 0. #115227 (groeneai).
  • Fixed uniq, uniqExact, uniqHLL12 and uniqTheta returning a wrong result for an argument wrapped in an injective function that hides nullability, such as uniqExact(tuple(x)) over a Nullable column. The optimize_injective_functions_inside_uniq optimization removed the wrapping function, after which NULL rows were skipped instead of counted. #115466 (vdimir).
  • Allow KeeperMap readers to accept shared metadata when equivalent primary keys differ only by redundant outer parentheses. #115642 (skuznetsov-clickhouse).

Text index and skip index fixes

  • Fix server abort when a query uses nested coalesce/ifNull comparisons (e.g. WHERE coalesce(a, b, coalesce(c, d), e) = const) on a MergeTree table with multiple minmax skip indexes and use_skip_indexes_for_disjunctions = 1. The skip-index rewrite of <op>(coalesce(...), const) is now applied recursively to inner coalesce arguments, so the per-index KeyCondition RPN matches the template’s RPN as the disjunction-tracking code already assumes. #103929 (groeneai).
  • Fix NOT_FOUND_COLUMN_IN_BLOCK thrown by ALTER TABLE ... MATERIALIZE INDEX on parts that were created in 25.8 and contain a skip index over a column that was added with a separate ALTER TABLE ... ADD COLUMN. The mutation now correctly reads every column required by every pre-existing skip index and projection on the part during force-recalculation. #105039 (groeneai).
  • Fixed a silent under-count in SELECT queries when use_query_condition_cache = 1 (default). A query of the shape PREWHERE pk_prefix = X WHERE non_pk IN (...) against a column with a bloom-filter skip index poisoned the QueryConditionCache for the pk_prefix = X predicate, so a subsequent benign SELECT count() ... WHERE pk_prefix = X returned an incorrect, under-counted result. Affected all 26.x releases. #105686 (groeneai).
  • Fixed an exception when a vector search query uses a vector index and uses another skip index like minmax and use_skip_indexes_on_data_read = 1 #106473 (shankar-iyer).
  • Fix “Too many marks” for text index on an empty merged part. #106867 (azat).
  • Fixed wrong results when querying a ReplacingMergeTree table with FINAL and a filter on a text index while query_plan_optimize_lazy_final was enabled. The lazy FINAL optimization built reading steps that did not reproduce the direct read from the text index, so the filter dropped all matching rows. #106894 (Ergus).
  • Fix LOGICAL ERROR (Bad cast from type DB::ColumnString to DB::ColumnLowCardinality) when a Variant constant containing a LowCardinality member is compared to a key column whose key expression is a non-monotonic deterministic function (for example a minmax skip index over sipHash64(col)). #107111 (groeneai).
  • Fix wrong (often empty) results from ORDER BY <col> LIMIT n when the use_skip_indexes_for_top_k optimization is active and a part with a minmax skip index on the sort column has had rows removed by a lightweight DELETE. The optimization no longer ranks the stale minmax of lightweight-deleted parts ahead of the parts that hold the live top rows. #107320 (groeneai).
  • Fix set skip index not pruning granules over LowCardinality columns. #107868 (thevar1able).
  • The setting use_skip_indexes_on_data_read can now be reverted to its pre-26.1 default (false) via the compatibility setting, providing an escape hatch for a performance regression where the on-data-read path defeats minmax/set/bloom_filter skip-index mark-range pruning. #108330 (egor-click).
  • Fixed direct read from multiple partially materialized text indexes. #108607 (CurtizJ).
  • Fixed a text index defined on mapValues(map) or mapKeys(map) being silently not used when a table was queried through a Distributed engine table with the analyzer. The index was used for the local table and via cluster()/remote(), but a query through a Distributed engine table skipped it (and failed with INDEX_NOT_USED under force_data_skipping_indices). #109188 (groeneai).
  • Fixed a server crash on CREATE HYPOTHETICAL INDEX ... TYPE set (and ngrambf_v1/tokenbf_v1) when the required index argument is omitted. Such statements are now rejected with a clear error. #109294 (groeneai).
  • Fixed wrong results for functions has, mapContainsKey and mapContainsValue with an empty needle when a text index is present. #110246 (rschu1ze).
  • Fix ATTEMPT_TO_READ_AFTER_EOF error when merging parts with a text index if one of the merged parts was empty, for example after a mutation that deleted all rows of the part. #112490 (CurtizJ).
  • Fixed a query plan optimization stall when a WHERE clause contains a large string constant with many dots and the table carries a bloom_filter, tokenbf_v1, ngrambf_v1 or text skip index. Matching a filter column name against JSONAllPaths(...) index columns enumerated every dot split of the name, which made skip-index condition building quadratic in the constant’s length. #113289 (groeneai).
  • Fixed ORDER BY ... LIMIT returning fewer rows than requested, possibly none, when a row policy was the only filter of the query and the sort column had a minmax skip index. The top-K optimization narrowed the read before the row policy was applied. #114073 (alexey-milovidov).

Data lake fixes

  • Fix logical error exception when reading Iceberg tables whose format version was upgraded by an external tool (e.g. Spark). #100407 (alexey-milovidov).
  • Fix an exception (LOGICAL_ERROR: 'PREWHERE passed to format that doesn't support it') when reading Iceberg tables containing ORC data files with PREWHERE optimization enabled. #101206 (groeneai).
  • Fix LOGICAL_ERROR exceptions when reading Iceberg or DeltaLake data lake tables through paths that can reach the read pipeline without a pinned datalake_table_state, such as concurrent Iceberg metadata updates or merge reads over DeltaLake tables. #102033 (groeneai).
  • Fix sporadic Logical error: 'Database <name> not found' from DataLakeConfiguration::getCatalog when an Iceberg engine table is loaded inside a regular database during async metadata loading. #103775 (groeneai).
  • Fix excessive catalog/S3 metadata reads when an INSERT or DDL statement references a non-existent table in a DataLake catalog database with show_data_lake_catalogs_in_system_tables enabled. The typo-hint suggestion path loaded full per-table Iceberg metadata for the whole catalog, which could exhaust memory on large catalogs. #104124 (il9ue).
  • Fix inflated read_bytes (and the derived bytes/s shown in system.query_log, progress bar, etc.) when reading Parquet files. The previous implementation reported the row group’s total compressed size on every chunk, so reading K of N columns overcounted by N / K. It is now summed only across the selected columns. Also fixes file-level progress tracking for Iceberg tables, which previously never reported the data file size. #105413 (groeneai).
  • Fix wrong results when reading an Iceberg table with iceberg_use_version_hint = 1 after another writer (such as the icebergLocal/icebergS3 table function) without the setting advances the table. version-hint.text is now kept in sync by every writer once the file exists, so subsequent readers using the hint see the latest snapshot. #105682 (groeneai).
  • Iceberg writes now preserve NULL values in Nullable(T) partition columns. Previously, a NULL written by ClickHouse showed up as the default value of the inner type (0 for int) when read back by Spark or other Iceberg readers. #105862 (groeneai).
  • Fixed Iceberg v2 merge-on-read position deletes returning wrong rows when a single delete file references multiple data files and several delete files apply to the same data file. Delete entries are now filtered by their referenced file path. #105888 (groeneai).
  • Fix IcebergLocal table engine becoming read-only after a DETACH + ATTACH cycle or a server restart, which made every subsequent INSERT fail with Local object storage Local is readonly. (READONLY). #106016 (groeneai).
  • Fixed the filesystem cache being silently disabled for Azure Blob Storage (e.g. Delta Lake tables over Azure) because object metadata did not include the blob ETag. #106091 (thewisenerd).
  • Added path validation for Delta Lake tables to prevent metadata from accessing objects outside the configured storage location. #106115 (scanhex12).
  • Iceberg partition pruning now correctly handles WHERE partition_col = (SELECT ... FROM ...) filters where the analyzer wraps the scalar subquery result in an internal _CAST(Const, 'TargetType') with matching source and target types. Previously such filters disabled partition pruning and triggered a full table scan. #106204 (groeneai).
  • Fixed Iceberg REST catalogs containing tables being incorrectly reported as empty. #106301 (LefterisXefteris).
  • Fixed a NOT_FOUND_COLUMN_IN_BLOCK exception when querying an Iceberg or S3 table with a compound WHERE containing IS NOT NULL on a column that is not in the SELECT list, using the Parquet V3 native reader. #106443 (tiandiwonder).
  • Fix inflated progress reporting when reading from Iceberg tables with _file or _path filters. Previously, total_bytes_to_read progress included all files from the manifest regardless of filtering. #106491 (PedroTadim).
  • Fixed a server exception (std::out_of_range logical error) when inserting into an Iceberg table whose write block column names do not match the field ids of the latest schema (for example after a concurrent writer renames a column within the iceberg_metadata_staleness_ms window). The insert now fails with a clean query error instead of aborting the server. #107279 (groeneai).
  • Fixed possible server stack overflow (crash) when reading a deeply nested schema or value in the MsgPack, BSON, ORC, Parquet, JSON, DeltaLake, Iceberg and Paimon formats. Such deeply nested input is now rejected with an exception. #107341 (Algunenano).
  • Fixed a crash (LOGICAL_ERROR in debug builds) and a silent wrong-results bug (in release builds) when reading an Iceberg table whose metadata re-binds an existing schema-id to a different schema across metadata versions. Such metadata is now rejected with ICEBERG_SPECIFICATION_VIOLATION. #107370 (groeneai).
  • Fixed reading Iceberg v3 tables whose Parquet data files contain reserved row-lineage columns (such as _row_id); the native Parquet reader no longer raises ICEBERG_SPECIFICATION_VIOLATION for reserved field ids that are not part of the table schema. #107377 (gregakinman).
  • Fix a spurious filesystem error: in last_write_time: No such file or directory exception when listing a local-disk object storage directory (e.g. an Iceberg table on a local disk) while files are being concurrently replaced. A concurrently removed entry is now omitted from the listing instead of aborting it. #107432 (groeneai).
  • Fixed ‘Account must be specified error’ when reading a Delta Lake table over Azure. #107620 (SmitaRKulkarni).
  • Fix a crash when reading Iceberg tables with equality delete files. If a column is nullable in the equality delete file but non-nullable in the table schema (or vice versa), the values read from the delete file were inserted into a column of a different type through an unchecked cast (a column type confusion), corrupting the column and crashing the server. #109551 (mstetsyuk).
  • Fixed a false ICEBERG_SPECIFICATION_VIOLATION error when reading an Iceberg table whose decimal (or other parameterized primitive) type is serialized with different whitespace across metadata files, e.g. decimal(20,0) in the table metadata and decimal(20, 0) in the manifest. Such spec-equivalent type strings are now compared ignoring whitespace. #109676 (groeneai).
  • Fix reading Iceberg tables whose default sort order references a column that needs quoting (e.g. @timestamp). Such tables were unreadable because the synthesized storage ORDER BY was built from the raw column name and failed to parse with SYNTAX_ERROR. #110233 (groeneai).
  • Fixed reading Paimon tables that contain a nullable ARRAY or MAP column. Such a table could not be read at all, because the schema mapper wrapped the composite type in Nullable, which ClickHouse forbids, so both DESC and SELECT failed with Nested type Array(Nullable(Int32)) cannot be inside Nullable type. A nullable composite column is now mapped to a non-Nullable composite type and a NULL value is read as an empty one. #113450 (groeneai).
  • Register Iceberg namespace in the catalog before writing table files (needed for SeaweedFS) #114285 (azat).

S3/Azure/object storage fixes

  • Fix “Distributed task iterator is not initialized” exception when using url, s3, or similar table functions in queries with parallel replicas enabled. #100146 (alexey-milovidov).
  • Fixed a possible server segfault in cluster table functions (s3Cluster, urlCluster, fileCluster, …) when the planner produces a SELECT with the recursive_with flag set but no WITH expression. #105433 (groeneai).
  • Fixed Parquet and ORC filter pushdown for IN (subquery) predicates, allowing row-group/page/bloom-filter pruning to work for file, url, s3, and object-storage reads. #105863 (arsenmuk).
  • Fix LOGICAL_ERROR exception during cache predownload when a remote S3 object is overwritten with shorter content between listing and reading. #106375 (fm4v).
  • Fix the s3 table function silently ignoring a lowercase positional partition_strategy (e.g. hive). #107297 (jkartseva).
  • Fixed a regression where setting compatibility = '26.6' (which implicitly enables the hive partition strategy) silently accepted {_partition_id} in S3 and object-storage table paths instead of raising BAD_ARGUMENTS. #107437 (LefterisXefteris).
  • Fixed s3 and other object storage table functions throwing LOGICAL_ERROR instead of BAD_ARGUMENTS when a key-value argument is duplicated, e.g. s3('http://...', format = 'CSV', format = 'TSV'). #107670 (groeneai).
  • Fixed a server exception (Logical error: 'index >= result.start') when formatting a malformed query that mixes the positional and named secret-argument forms of the s3/gcs table functions, e.g. s3('url', 'a', 'b', secret_access_key = 'c'). #107818 (groeneai).
  • Fix S3 settings priority so a URL-scoped <s3> endpoint block takes precedence over the top-level <s3> defaults. #109251 (bharatnc).
  • Fix 411 Length Required errors from Azure services: the Poco-based Azure HTTP transport now sets Content-Length from the request body for SDK clients that do not set the header themselves (e.g. Azure Key Vault). #110299 (thevar1able).

S3Queue fixes

  • Fix a server crash (out-of-bounds access) in S3Queue/AzureQueue with enable_hash_ring_filtering = 1 when a batch contained a non-processable file and the Keeper request to set the batch as processing failed at the same time. #108977 (groeneai).
  • Fix credential leaks in SHOW CREATE, system.query_log, server logs and EXPLAIN output. Every S3 locator form now masks session_token and Google ADC secrets, extra_credentials/headers values at any argument position, duplicated or expression secret keys, invalid positional forms (fail closed), and credentials embedded in S3 URLs; this covers the explicit-url and named-collection s3/s3Cluster table functions, the S3-backed table engines (S3, GCS, the data-lake engines, S3Queue), the S3 database engine, BACKUP ... TO S3 and the Backup database engine. In addition, secret arguments of encrypt/decrypt/HMAC that are built by an expression (including ones inlined from a SQL UDF) are now hidden in projection names, EXPLAIN QUERY TREE and EXPLAIN actions. #109768 (Algunenano).
  • Fix S3Queue/AzureQueue ordered mode with persistent processing nodes: bucket locks are now refreshed during streaming, so that the TTL cleanup (persistent_processing_node_ttl_seconds) does not remove locks of a live processor. If lock ownership is nevertheless lost, it is detected and reported (a logical error and the ObjectStorageQueueBucketLockLostOwnership profile event), and streaming recovers with a fresh file iterator. #110292 (kssenii).

Security and access control fixes

  • Fix a server crash (SIGSEGV) reachable by any user with CREATE TABLE rights when sending CREATE TABLE ... TO INNER UUID '...' without an ENGINE clause over HTTP or the native protocol. The same bug also crashed the client. The parser now reports a proper BAD_ARGUMENTS error instead of dereferencing a null pointer. #105579 (groeneai).
  • Fixed a server crash when querying DeltaLake tables with allow_experimental_delta_kernel_rs enabled and a credential or option that contained invalid bytes (the Rust FFI panicked across the extern "C" boundary). #106109 (Algunenano).
  • Fix multiple heap out-of-bounds reads in the Arrow IPC format reader (ArrowColumnToCHColumn). A malformed Arrow file could declare more rows than its buffers contain, declare list/struct/map child lengths inconsistent with their parent, supply non-monotonic list offsets, or truncate a child validity bitmap, causing reads past the end of heap allocations. This is reachable by any user with SELECT privilege via file(), format(), table functions, or ArrowFlight inputs. All data, offsets, view-struct, and validity-bitmap buffers are now validated before any raw pointer access, and list/struct/map shapes and offsets are checked for consistency. #106395 (Algunenano).
  • Fixed a bug where the used_privileges and missing_privileges columns of system.query_log could contain privilege strings leaked from unrelated earlier queries of a different user, database, or session. #106425 (alexey-milovidov).
  • Fixed a heap buffer overflow (server crash) in decodeHTMLComponent when decoding strings containing the expanding HTML entities &nGt; or &nLt;, reachable by any user with a single SELECT. #106741 (Algunenano).
  • Fixed wrong query results caused by the query condition cache when on-fly mutations (apply_mutations_on_fly) or patch parts filtered rows before PREWHERE. A query reading with apply_mutations_on_fly = 1 could poison the cache so that a later query with apply_mutations_on_fly = 0 and the same predicate skipped marks it should have read and returned too few rows. The same fix also covers row-level security policies, which are prepended as a filter ahead of PREWHERE: a query run under a restrictive row policy could poison the cache for a later query that uses the same predicate without that policy. #107145 (groeneai).
  • Fixed a heap buffer overflow (server crash) in windowFunnel when finalizing a crafted aggregate-function state with an out-of-range event type, reachable by any user with a single SELECT. #107412 (uwezkhan).
  • Fixed currentUser(), user(), SESSION_USER and authenticatedUser() evaluating to an empty string on the asynchronous insert flush path (with async_insert = 1). This affected DEFAULT/MATERIALIZED column expressions and materialized views that reference these functions, which silently stored an empty string instead of the inserting user. #107541 (groeneai).
  • When several quotas are assigned to the same user or role, all of them are now enforced together (a query is rejected if any of them is exceeded), instead of only one quota being enforced and chosen non-deterministically. SHOW QUOTA and system.quota_usage now show all quotas enforced for the current user. #107664 (alexey-milovidov).
  • SYSTEM RESET DDL WORKER now requires the new SYSTEM RESET DDL WORKER privilege. Previously any authenticated user (including readonly ones) could run it and repeatedly reset the DDL worker state, blocking ON CLUSTER DDL. #108460 (groeneai).
  • Fixed ssl_certificate user identification so a single * wildcard matches exactly one name component (RFC 6125 6.4.3). Previously a wildcard in a CN or DNS: SAN subject (for example *.corp.example.com) also matched multi-label names such as evil.deep.corp.example.com, letting a holder of a certificate for a deeper subdomain authenticate as the wildcard user. URI: SAN matching is unchanged. #108472 (groeneai).
  • The MySQL wire protocol commands COM_FIELD_LIST (mysql_list_fields) and COM_INIT_DB (USE database) now enforce the same access control as their SQL equivalents (SHOW COLUMNS/DESCRIBE and USE). Previously they could disclose column names of tables the user only had partial column grants on, and switch the current database without the SHOW DATABASES privilege. #108508 (groeneai).
  • Match http_forbid_headers case-insensitively. HTTP header names are case-insensitive, so forbidding Authorization now also blocks authorization, AUTHORIZATION and other case variants. Configured header_regexp patterns are now matched case-insensitively without needing an explicit (?i) flag. #108509 (groeneai).
  • Fix a metadata-disclosure where DESCRIBE loop('db', 'table') and DESCRIBE loop(<inner table function>) bypassed the SHOW COLUMNS / source access check, letting an unprivileged user read a table’s column schema. #108624 (groeneai).
  • Fixed a startup failure where a DataLakeCatalog database created by an older version (25.12 or earlier) with a malformed auth_header could not be attached after upgrading to 26.2 or later, preventing the server from starting. The auth_header is now validated only on CREATE, and on ATTACH the catalog is built lazily on first use instead of during startup, so a single misconfigured or unreachable catalog database no longer blocks server startup. #108674 (groeneai).
  • Hardened RabbitMQ connections against maliciously large AMQP frames and applied remote_url_allow_hosts checks consistently to rabbitmq_address. #112479 (kssenii).
  • Fixed a case where CREATE TABLE ... ENGINE = Distributed(...) without a column list could reveal the structure of a local table the creating user is not allowed to see. For a CREATE the local server executes itself, the structure is now inferred under the user’s own context, so SHOW COLUMNS on the target table is required, as it already is for the Remote engine. A CREATE replayed from the DDL queue (ON CLUSTER, or one inside a Replicated database) is not covered. #113220 (groeneai).
  • Fixed a case where CREATE TABLE ... ENGINE = Buffer(...) without a column list could reveal the structure of a destination table the creating user is not allowed to see. For a CREATE the local server executes itself, the structure is now inferred under the user’s own context, so SHOW COLUMNS on the destination is required, as it already is for Merge and Remote. A CREATE replayed from the DDL queue (ON CLUSTER, or inside a Replicated database) is not covered. #113372 (groeneai).
  • Bound the size of the startup message of the PostgreSQL wire protocol, which is read before authentication. #115708 (alexey-milovidov).

Backup and restore fixes

  • Fix a server abort during RESTORE of backups containing tables with cyclic dependencies. #103824 (thevar1able).
  • Fixed the S3 storage class (s3_storage_class / s3_storage_class_name) being ignored for objects written via multipart upload on S3 disks and object storage, which caused large objects to be created with the default STANDARD class. The option name is now accepted both as s3_storage_class and s3_storage_class_name for disks, object storage and backups. #106214 (alexey-milovidov).
  • Fixed SYSTEM RELOAD CONFIG discarding per-endpoint Azure Blob Storage settings (such as use_native_copy), which caused a disk’s configured settings to be ignored for BACKUP/RESTORE until the server was restarted. #106357 (jkartseva).
  • Fixed backups failing with FILE_DOESNT_EXIST when a refreshable materialized view’s REPLACE target is collected on a Replicated or Shared database whose materialized view isn’t yet instantiated on the backup-initiating replica. #106411 (jkartseva).
  • Fixed Azure BACKUP/RESTORE ignoring endpoint settings for legacy-form azure_blob_storage disks. #106784 (jkartseva).
  • Fixed BACKUP to AzureBlobStorage: copying a data file inside a backup wrote the destination object outside the backup directory. Backup object existence checks on S3 destinations now use exact HeadObject requests instead of prefix listing, preventing false matches of similarly-prefixed keys. #107153 (pamarcos).
  • Incremental backups no longer store S3 credentials in the <base_backup> locator of the .backup metadata file. Backups created with use_same_s3_credentials_for_base_backup = 1, or with explicit base backup credentials matching this backup locator, store a non-secret marker and are restored without extra restore-time settings; for backups created with different explicit base backup credentials or extra base authentication arguments, pass them to RESTORE with the base_backup setting. Backups created by older versions with embedded credentials remain restorable. #107357 (pamarcos).
  • Fixed RESTORE for ReplicatedMergeTree tables so duplicate-content parts from a backup are preserved instead of being silently deduplicated. #107652 (pamarcos).
  • A {_partition_id} placeholder in the path of a file-like engine (S3, AzureBlobStorage, URL, etc.) with no explicit partition_strategy implies the wildcard strategy again, regardless of file_like_engine_default_partition_strategy. This restores backward compatibility for pre-26.6 DDL that started failing with BAD_ARGUMENTS (“Partition strategy hive can not be used with a ‘_partition_id’ wildcard in the path”). #111279 (fm4v).

ClickHouse Keeper fixes

  • Fix Keeper snapshot cleanup after failed writes so partial snapshots are cleaned up safely and failed writes can be retried without advancing latest_snapshot_meta. #105779 (antonio2368).
  • Fix Keeper failures during follower catch-up when the new request dispatcher response queue could fill before the response thread started. #106049 (antonio2368).
  • Fixed Keeper sometimes getting stuck on startup when setting nuraft_max_log_gap_in_stream is set to non-default value (default is 0, i.e. disable pipelining of append_entries requests). #106220 (al13n321).
  • Keeper’s internal Raft TLS now honors the openSSL.client.verificationMode setting. Previously peer certificate verification was always enabled for inter-Keeper Raft communication regardless of this setting, so none was silently ignored. Now none explicitly disables Raft peer-certificate verification, while an absent setting keeps the previous secure-by-default behavior. Configurations that explicitly set none will stop verifying Raft peer certificates after upgrade, matching the configured intent. #106726 (antonio2368).
  • Fix spurious ZooKeeper session recreation on config reload. #107096 (azat).
  • Fixed a bug in ClickHouse Keeper where the snapshot metadata reported via last_snapshot (and zk_latest_snapshot_size in mntr) could move backwards after a stale or duplicated snapshot install, which could also let a same-index local snapshot overwrite a registered snapshot file in place while it was still being streamed to a peer or uploaded to S3. #107321 (antonio2368).
  • Fixed refreshable materialized view getting stuck if zookeeper connection is lost at the wrong moment. #108234 (al13n321).
  • Fix mutations with a query parameter as the partition (ALTER TABLE ... UPDATE/DELETE ... IN PARTITION {param:Type}): the substituted partition value was serialized into the mutation entry in a form that could not be parsed back, which broke loading of the table (for replicated tables, on every replica). Mutation commands are now also verified to be parseable back before they are written to ZooKeeper or disk, so that a similar mismatch would fail the ALTER query instead of breaking the table. #111518 (al13n321).
  • Fixed numeric overflow while parsing data for system.zookeeper_info. #111629 (kssenii).

Crash and stability fixes

  • Fix NOT_FOUND_COLUMN_IN_BLOCK exception when using LIMIT BY with constant columns alongside DISTINCT and ORDER BY with the new analyzer. #93195 (ashrithb).
  • Fixed HiveCatalog connection stability by adding automatic retry mechanism and reconnection logic for handling TTransportException errors when communicating with Hive Metastore. #98471 (otselnik).
  • Fix analyzer-time constant folding for short-circuit functions (if, multiIf, and, or, etc.) so that statically unreachable branches no longer raise exceptions at analysis time. For example, WITH 0 AS n SELECT multiIf(n = 0, 0, intDiv(100, n)) now correctly returns 0 instead of failing with a division-by-zero error. #103157 (fastio).
  • Fixed a server termination when runningAccumulate was called on an aggregate function that returns its own state. #105085 (antaljanosbenjamin).
  • Fix getServerSetting to return the live effective value for runtime-changeable server settings (such as max_server_memory_usage, mark_cache_size, max_concurrent_queries, thread pool sizes, etc.), matching what system.server_settings reports. #105172 (alexey-milovidov).
  • Fix NOT_FOUND_COLUMN_IN_BLOCK exception when combining ORDER BY ... WITH FILL INTERPOLATE and LIMIT N BY with the analyzer enabled. #105481 (yakov-olkhovskiy).
  • Fix a server exception (Trying to execute PLACEHOLDER action logical error) when a MATERIALIZED CTE whose body is a correlated subquery is used as the right-hand side of IN. Such a CTE is now rejected at analysis time, consistent with how the same pattern is already rejected when the CTE is referenced directly in FROM. #105518 (groeneai).
  • Fix variant_throw_on_type_mismatch/dynamic_throw_on_type_mismatch=false not catching exceptions during function execution. #105543 (Avogar).
  • Fix NOT_FOUND_COLUMN_IN_BLOCK exception when TTL expression references a subcolumn. #105578 (Avogar).
  • Fixed a server crash that could occur when a query reading from PostgreSQL — via the postgresql table function, the PostgreSQL table engine, or a dictionary with a PostgreSQL source — was cancelled (for example with KILL QUERY) and cancelling the remote PostgreSQL query failed. #105949 (rorylshanks).
  • Fixed a possible crash due to a too-large string literal sent within the query. #105996 (nickitat).
  • Fix INVALID_WITH_FILL_EXPRESSION exception when using INTERPOLATE () (empty) with a sorting prefix in ORDER BY and use_with_fill_by_sorting_prefix enabled. Sorting prefix columns are now correctly excluded from the implicit interpolation set, matching the behavior of explicitly named INTERPOLATE (col). #106001 (yakov-olkhovskiy).
  • Malformed AggregateFunction(uniqTheta, ...) states from RowBinary input or query parameters are now rejected with CORRUPTED_DATA instead of terminating the server. #106260 (groeneai).
  • Fix a crash and a possible NOT_FOUND_COLUMN_IN_BLOCK error when constraint-based optimization (optimize_using_constraints) is used with correlated subqueries. #106349 (Algunenano).
  • Fix NUMBER_OF_COLUMNS_DOESNT_MATCH exception when querying a Distributed table that has two or more ALIAS columns expanding to the same expression (e.g. both defined as toString(x)), or when the same expression is written both as an ALIAS column reference and directly in the SELECT list, with an ORDER BY clause. #106404 (yakov-olkhovskiy).
  • Fixed a LOGICAL_ERROR “Trying to get name of not a column: ExpressionList” raised by queries that pass an asterisk inside multiIf to a table function argument, e.g. numbers(multiIf(*, ...), 2). The query now rejects the unresolvable matcher with UNSUPPORTED_METHOD. #106647 (groeneai).
  • Reject pathological file glob patterns that would cause unbounded recursion in directory listing. A maximum recursion depth of 1000 is now enforced; queries that exceed it raise TOO_DEEP_RECURSION instead of aborting the server with a stack overflow. #106676 (groeneai).
  • Fix exception 'Trying to read from input() twice.' raised when the table function input is wrapped in a non-MATERIALIZED CTE that is referenced from more than one place in the query. The query is now rejected with a clean INVALID_USAGE_OF_INPUT error at planning time. input is a one-shot client stream and can only be consumed by a single source in the query plan. #106682 (groeneai).
  • Fix inconsistent columns (that leads to LOGICAL_ERROR later) on exception (i.e. MEMORY_LIMIT_EXCEEDED) during parsing. #106802 (azat).
  • Fix server crash (SIGSEGV) when reading truncated Protobuf data with input_format_allow_errors_num > 0. #106905 (atsarevskiy).
  • Fix a server crash (null pointer dereference) when running TRUNCATE or DROP on an EmbeddedRocksDB table whose RocksDB handle was released, for example a read_only table whose data directory was emptied by a prior TRUNCATE. #106940 (groeneai).
  • Fix an exception (std::length_error reported as a LOGICAL_ERROR) when reading from a *Cluster table function such as urlCluster with a very large max_streams_for_files_processing_in_cluster_functions setting. The number of streams is now bounded to a sane value. #106946 (groeneai).
  • Fixed a server termination when a distributed query was cancelled immediately before being sent to a shard. #106950 (groeneai).
  • Fixed a server exception (logical error this->visited_views == right->visited_views) on INSERT when two materialized views on the same source table write to the same target table and a dependent view reads that target, with materialized_views_squash_parallel_inserts enabled. #107027 (groeneai).
  • Fixed a LOGICAL_ERROR exception when inserting into a DeltaLake table with columns that do not match its write schema (for example a Nested column that flattens to subcolumns, or a table function with an explicit column subset). Such inserts now fail with a user-facing INCOMPATIBLE_COLUMNS error instead. #107058 (groeneai).
  • Fix a LOGICAL_ERROR (“Table expression … data must be initialized”) raised when a qualified asterisk matcher (for example x.*) referenced a recursive CTE by name inside its own recursive term. Such matchers now expand the recursive table’s columns, the same way a qualified column (x.a) or an unqualified matcher (*) already does in that position. #107144 (groeneai).
  • Fixed a LOGICAL_ERROR (“Unexpected exception in refresh scheduling”) that could put the server into a crash-loop on restart when a refreshable materialized view has a REFRESH ... DEPENDS ON <name> dependency whose unqualified name matches a temporary table or a CTE name. #107156 (groeneai).
  • Fix a LOGICAL_ERROR (Variant N (T) has size X, but expected Y) when a function such as toString or concat is applied to a Variant or Dynamic column that holds a single non-empty variant together with NULLs, and the function returns its input column unchanged. #107374 (groeneai).
  • Fix a LOGICAL_ERROR (block.rows() == getRows()) raised on an async INSERT into an Alias table when use_strict_insert_block_limits was enabled. #107400 (groeneai).
  • Fix a server exception (Logical error: Not-ready Set is passed as the second argument for function 'in') when a key expression (ORDER BY, PRIMARY KEY, PARTITION BY, or a skip INDEX) contained an IN operator with a table on the right-hand side, e.g. ORDER BY (x IN some_table). Such key expressions are now rejected at table creation time. #107424 (groeneai).
  • Fixed a rare server crash in DISTINCT processing that could occur when an allocation failed (for example, when hitting a memory limit) while the set of distinct keys was being initialized. #107467 (groeneai).
  • Fixed DeltaLake operations failing after temporary S3 credentials expired by refreshing cached credentials before the next operation. STS assume-role credentials are also refreshed after authentication failures. #107480 (ahmadov).
  • Fixed a Not-ready Set is passed as the second argument for function 'in' (LOGICAL_ERROR) when querying a table with a PARTITION BY key and an IN/NOT IN subquery wrapped inside a larger expression, for example WHERE (c0 IN (SELECT ...)) != 0. #107515 (groeneai).
  • Fix a crash (null pointer dereference) that could happen when a distributed query plan was executed locally (make_distributed_plan + distributed_plan_execute_locally) with log_formatted_queries = 1. #107570 (groeneai).
  • Fix a server crash when reading a materialized view whose target is a Distributed table while the query runs with enable_analyzer = 0. #107653 (groeneai).
  • Fix server crash (SIGSEGV) when reading Protobuf data with input_format_allow_errors_num > 0 and a valid message precedes a bad (skippable) message in the same block. #107739 (atsarevskiy).
  • Fixed the odbc and jdbc table functions hanging for minutes and ignoring query cancellation (KILL QUERY, max_execution_time) when the bridge becomes unresponsive while inferring the remote table structure. #107809 (alexey-milovidov).
  • Fixed a possible crash (null pointer dereference) when the database/db override of a named collection passed to remote()/remoteSecure() is not a constant database name, e.g. remote(nc, database = (SELECT 1)). The query now fails with a clear error instead of crashing. #108271 (groeneai).
  • Vector search queries that SELECT from the _distance column now return a proper error instead of failing with a LOGICAL_ERROR. #108423 (rschu1ze).
  • Fixed a possible crash (heap-buffer-overflow) when a quantileTDigest-family aggregate-function state column was used as a GROUP BY key and serialized concurrently by several threads. #110263 (groeneai).
  • Fix incorrect pruning from primary key index analysis on tables with a reverse (descending) sorting key (ORDER BY (g, r DESC)). A granule spanning a change of a leading key column followed by a descending key column could be pruned incorrectly, dropping matching rows. #111059 (nihalzp).
  • Fix a logical error block.rows() == getRows() (an out-of-bounds read and broken insert deduplication in release builds) when an INSERT flows through a dependent materialized view whose target is an Alias and whose inner query changes the number of rows, with a deduplicating table reachable behind the alias hop. #111103 (alexey-milovidov).
  • Fix a segfault due to out-of-bounds memory access when deserializing a malformed aggregate function state containing a String value. Such states are now validated and rejected instead of leading to segfaults. #111606 (mstetsyuk).
  • Fixed a crash when reading a Parquet file with inconsistent bloom filter metadata. Such files could also silently return fewer rows than they should. #112498 (tiandiwonder).
  • Fixes a segfault and silent data corruption when an INSERT into the File engine or the file table function appends to a non-empty file in a format that does not support appending, such as Avro. Writing through a file descriptor or a partitioned path bypassed the existing check, so the format prefix was suppressed and a second header was written after the existing bytes, leaving the file unreadable. Such an INSERT is now rejected with CANNOT_APPEND_TO_FILE, as it already is for a plain path. #112839 (groeneai).
  • Fix DROP TABLE and SYSTEM STOP VIEW hanging when a Refreshable Materialized View is blocked while planning its refresh query. #113188 (evillique).
  • Fixed a rare server termination when asynchronous insert queue entries had identical deadlines. #113363 (mstetsyuk).
  • Fixed heap memory corruption when reading Parquet through an input format that owns its read buffer, for example a dictionary with SOURCE(FILE(... format 'Parquet')). Background prefetch and decode tasks could still read and write through the buffer after the pipeline released it, which could abort the server. #114668 (groeneai).
  • Fixed a hang when dropping a TimeSeries table whose name sorts lexicographically below its inner tables’ names, for example a table named -ts, or when dropping a materialized view declared with ENGINE = TimeSeries. The drop self-deadlocked on the DDL guard and could not be cancelled with KILL QUERY. #114953 (groeneai).

Other bug fixes

  • Functions like, ilike, notLike, notILike, and match now support constant haystack with non-constant needle (e.g. 'foo' LIKE pattern_column), which previously threw ILLEGAL_COLUMN. #100479 (Onyx2406).
  • Fix NOT_FOUND_COLUMN_IN_BLOCK error when selecting from a VIEW over a table with a normal projection. #101218 (amosbird).
  • Reject negative Float64 values (e.g. -100.5) in workload settings like max_bytes_per_second, max_cpus, etc. Previously only negative integers were validated, allowing negative floats to silently create broken scheduler nodes. #101842 (groeneai).
  • Object-storage reads now respond promptly to query cancellation. #103016 (SmitaRKulkarni).
  • Fixed input_format_max_block_size_bytes being silently ignored during INSERT parsing when max_insert_block_size_bytes is 0 (the default). The setting now correctly limits the size of blocks produced by row input formats. #103068 (Fgrtue).
  • Fixed ClickHouse occasionally producing invalid GSSAPI tokens due to incorrect stripping of trailing null bytes. #103114 (EmeraldShift).
  • EXPLAIN SYNTAX expands Parameterized Views. #103263 (jrdi).
  • Fix data corruption when writing Parquet (and other trailer-bearing formats such as ORC and Arrow) to HDFS via INSERT INTO FUNCTION hdfs(...). Since 26.1, WriteBufferFromHDFS did not flush its working buffer on finalize(), so the last up to DBMS_DEFAULT_BUFFER_SIZE bytes of every file were silently lost, including the Parquet PAR1 footer. Reading such files returned Not a Parquet file (wrong magic bytes at the end of file). #103268 (groeneai).
  • Fix wrong results and a possible logical error for correlated subqueries when a join size limit (max_rows_in_join / max_bytes_in_join) is set together with join_overflow_mode = 'break'. The join created internally to evaluate a correlated subquery now ignores those user limits, so it can no longer stop early and drop rows. #103322 (groeneai).
  • Fix wrong results or missed projection when an aggregate projection contains multiple sumIf aggregates with different IN (...) conditions. #104765 (Ergus).
  • Fix deltaSumTimestamp returning wrong results for signed integer types crossing zero. #104830 (thevar1able).
  • Fix Logical error: 'Metadata is not initialized' raised by DELETE FROM on a freshly-attached Iceberg, DeltaLake, or Hudi table whose metadata file is corrupted or unloadable. A regular user-facing exception is reported instead, and the server keeps running. #104917 (groeneai).
  • ATTACH TABLE name <clauses>; queries that supply storage clauses (ORDER BY, PARTITION BY, PRIMARY KEY, SAMPLE BY, TTL, UNIQUE KEY, or engine SETTINGS) without an ENGINE now throw BAD_ARGUMENTS instead of silently re-attaching the table with its stored definition and discarding the user-supplied clauses. Query-level session SETTINGS (such as log_comment) are still applied. Use ATTACH TABLE t; to re-attach with stored metadata, or ALTER TABLE t MODIFY SETTING ... after ATTACH to change settings. #105068 (groeneai).
  • Fixed a heap-buffer-overflow when reading Arrow or ArrowStream files with corrupted intermediate offsets in a binary or string column, and a null-pointer dereference when reading geo-tagged Arrow columns. #105449 (Algunenano).
  • Fixed a server termination when reading Dynamic subcolumns from a compressed Memory table after ALTER. #105464 (Avogar).
  • Include skip_first_lines in schema cache key for WithNames formats. #105469 (Avogar).
  • Fix histogram producing wrong results for small unsorted inputs. #105548 (Avogar).
  • Fix usage of insertion table in table functions when optimize_trivial_insert_select is enabled. #105555 (Avogar).
  • Fix estimateCompressionRatio window function losing accumulated data between rows. #105581 (Avogar).
  • Hive partition value extraction now honors the cast_string_to_date_time_mode setting and accepts ISO 8601 timestamps with timezone suffixes (e.g. +0000, +00:00, Z) in partition keys by default. #105584 (alexey-milovidov).
  • Fix Template input format error recovery after malformed rows. #105735 (niyue).
  • Fix SYSTEM INSTRUMENT ADD formatting so handler arguments are separated by a single space, and reject invalid SLEEP instrumentation argument lists with more than two values or a range where the minimum is greater than the maximum. #105984 (pamarcos).
  • Fix ALTER TABLE partition operations silently failing for Bool partition keys. #106004 (Avogar).
  • Fix JSONExtractRaw and JSONHas for typed JSON paths with default values. #106005 (Avogar).
  • Fixed an incorrect / prefix on empty base paths in data lake configurations. #106013 (thewisenerd).
  • Fix system.dictionaries returning 0 rows with partial SHOW DICTIONARIES revoke. #106105 (Avogar).
  • Fix incorrect results for queries against tables whose ORDER BY contains a monotonically decreasing function such as (c0 / -42) or intDiv(c0, -42). Predicates on the underlying column (for example, c0 < 0) could wrongly prune granules that contained matching rows, producing missing results. #106136 (nihalzp).
  • Added validation for malformed DDSketch aggregate states during insertion. #106236 (yariks5s).
  • Fixed filesystem-cache consistency after failed SLRU downgrades, preventing entries from remaining stuck in an eviction state. Also fixed System/Data split-cache resizing and free-space cleanup so limits are updated atomically and space can be reclaimed from both cache segments. #106286 (kssenii).
  • Fixed an incorrect conversion of subnormal Float16 values to Float32 (e.g. when reading them from Numpy .npy files), caused by an off-by-one mantissa shift. #106343 (jh0x).
  • Fix regexpExtract(haystack, pattern) so that patterns without a capturing group return the whole match instead of throwing INDEX_OF_POSITIONAL_ARGUMENT_IS_OUT_OF_RANGE. #106374 (groeneai).
  • Fix a logical error when building a polygon dictionary from source data containing NaN or infinite point coordinates. Such coordinates are now rejected with a clear error. #106423 (alexey-milovidov).
  • HTTP dictionaries can now specify request headers through named collections. #106459 (ZelvaMan).
  • Malformed Avro enum values are now validated and rejected with an exception instead of causing out-of-bounds memory access and server termination. #106476 (mstetsyuk).
  • Fix LIMIT WITH TIES and fractional LIMIT WITH TIES not respecting the collation from ORDER BY ... COLLATE when determining ties. Rows that are equal according to the collation (for example '1' and '01' under numeric collation) were compared byte-wise, so some tied rows were wrongly dropped from the result. #106539 (nihalzp).
  • Fix DISTINCT in order and LIMIT BY in order optimizations (including negative LIMIT BY) returning wrong results when the input is sorted with a collation (ORDER BY ... COLLATE). Rows that are equal according to the collation (for example 'a' and 'A' under a case-insensitive collation) are ordered by collation key and are not adjacent by value, so the in order optimization is now skipped when a collator is used. #106564 (nihalzp).
  • Fixed a race during NATS consumer shutdown that could terminate the server. #106692 (mstetsyuk).
  • Fixed multiple memory-safety and resource-exhaustion issues in format readers reachable from untrusted input: a heap out-of-bounds read in the native Parquet reader’s DataPageV2 definition/repetition level-length handling, a stack overflow on Parquet files with deeply nested schemas, and allocations that ignored max_memory_usage when parsing GeoParquet WKB/WKT geometry and Avro strings/bytes. #106739 (Algunenano).
  • Fixed keeper_server.http_control.secure_port serving plaintext HTTP responses to HTTPS clients. The secure port now serves HTTPS correctly. #106822 (linjiayu1025-collab).
  • Fix SHOW CREATE ROW POLICY emitting restrictive/permissive in lowercase instead of uppercase, inconsistent with other keywords. #106865 (valerypetrov).
  • Fixed parts being marked as broken and detached on any part reload (server restart, DETACH, or ATTACH) for tables with a LowCardinality(Nullable(...)) column in the partition key. Since 26.5, the per-part minmax index file was not written when such a column’s minimum and maximum were NULL, while the part consistency check still required the file. Parts written by affected versions lack the minmax index file and still need to be reattached manually. #106945 (PedroTadim).
  • Fixed elapsed_us always being zero, and read_rows/read_bytes being undercounted, in system.processors_profile_log and system.query_log for asynchronous insert flush (AsyncInsertFlush) queries. #106982 (cwurm).
  • Fixed a Block structure mismatch in UnionStep stream logical error (server abort on debug/sanitizer builds, Code: 49 on release builds) that occurred when one branch of a UNION/INTERSECT/EXCEPT read a Sparse-serialized column while the sibling branch read the same column as a full one (for example when pushing to a materialized view). #107041 (groeneai).
  • Fixed incorrect row order in ORDER BY queries over UNION ALL with optimize_read_in_order and read_in_order_use_virtual_row enabled. #107053 (vdimir).
  • Fixed a syntax error when a FORMAT, SETTINGS, or INTO OUTFILE clause follows SHOW ROW POLICIES or SHOW MASKING POLICIES (e.g. SHOW ROW POLICIES FORMAT TabSeparated). #107061 (groeneai).
  • Fix trimLeft, trimRight, and trimBoth (and aliases ltrim, rtrim, trim) throwing TOO_LARGE_STRING_SIZE when the custom trim character set is longer than 16 characters. Trim sets of any length are now supported again. #107071 (fm4v).
  • Fixed a signed integer overflow in quantileExactExclusive, quantilesExactExclusive, quantileExactInclusive and quantilesExactInclusive that could produce a wrong result for Int64 inputs spanning a large range. #107154 (groeneai).
  • Configuration reloads no longer rerun startup scripts, preventing one-shot startup actions from executing again. #107187 (mstetsyuk).
  • Fix incorrect result order for ORDER BY over UNION ALL with optimize_read_in_order enabled when the union pipeline was narrowed due to max_streams_for_union_step settings; narrowing is now skipped when the plan relies on sorted UNION output streams. #107208 (vdimir).
  • Fixed a possible null pointer dereference while resolving proxy configuration during late server shutdown. #107231 (PedroTadim).
  • Fix ORDER BY ... WITH FILL producing extra rows when an ORDER BY column before the fill column uses a COLLATE collation. The rows are now grouped by the sorting prefix using that collation, matching the sort order. #107365 (groeneai).
  • Fixed undefined behavior when a non-finite floating-point value (such as nan or inf) is passed as the timestamp/duration argument of the prometheusQuery / prometheusQueryRange table functions. Such an argument now raises BAD_ARGUMENTS instead of producing a garbage timestamp. #107417 (groeneai).
  • Fix incorrect results from the optimize_rewrite_aggregate_function_with_if optimization for aggregate functions that preserve NULL payload values (the *_respect_nulls family: anyRespectNulls, first_value_respect_nulls, anyLast_respect_nulls, last_value_respect_nulls). The optimization no longer rewrites f(if(cond, x, NULL)) into the -If form for such functions. #107430 (groeneai).
  • Added validation for malformed bounds in ORC input. #107580 (al13n321).
  • Fix an unauthenticated memory-exhaustion denial of service on the MySQL protocol port. #107599 (tiandiwonder).
  • Fixed the MySQL interface being unusable with MySQL Connector/J 8.2.0 and newer (including 9.x). The info field of the OK packet is now length-encoded, matching the MySQL server, so the JDBC driver can connect. #107693 (alexey-milovidov).
  • Fixed a Block structure mismatch in UnionStep stream logical error (server abort on debug/sanitizer builds, Code: 49 on release builds) that occurred when sibling branches of a UNION/INTERSECT/EXCEPT differed only in their WHERE predicate and one branch’s predicate constant-folded to a Const column. #107719 (groeneai).
  • Reading Arrow and ArrowStream data with empty String or Binary columns produced by Apache Arrow Java before 19.0.0 (including Apache Spark) no longer throws INCORRECT_DATA. #107764 (Algunenano).
  • Malformed Native blocks whose Variant discriminator references a nonexistent variant are now rejected safely. #107991 (uwezkhan).
  • Fixed a performance regression when reading Dynamic columns with multiple threads. #107997 (Avogar).
  • The deprecated data lake setting storage_catalog_url is now correctly rejected by the catalog guard (previously only storage_catalog_type and storage_aws_access_key_id were checked), and the error message lists all deprecated settings. #108040 (alexey-milovidov).
  • Fixed the ArrowFlight table function/engine rejecting a named collection that omits the optional dataset key with a No such key 'dataset' error. #108041 (alexey-milovidov).
  • Secrets and credentials are now masked in system.query_views_log.view_query instead of being exposed in logged view SQL. #108214 (Fidelaggio).
  • Fix type_json_allow_duplicated_key_with_literal_and_nested_object not working with typed paths in JSON. #108218 (Avogar).
  • Fixed undefined behavior (null pointer passed to memcpy) in the detectCharset and detectLanguageUnknown functions when the input string is larger than 32768 bytes and no character set can be detected. #108250 (groeneai).
  • Fix NOT_FOUND_COLUMN_IN_BLOCK error for queries that use the _part_starting_offset/_part_offset virtual columns in WHERE together with lazy materialization. #108287 (vdimir).
  • Hide secret arguments of functions such as encrypt, decrypt, and HMAC in EXPLAIN actions, EXPLAIN header, and EXPLAIN PIPELINE output when format_display_secrets_in_show_and_select is disabled (the default). #108386 (Algunenano).
  • CREATE OR REPLACE now succeeds for a refreshable materialized view when its TO target is already owned by that same view. Targets owned by another view remain rejected. #108392 (evillique).
  • Restrict the model path of catboostEvaluate to the user_files directory, like file() and the dictionary sources. Previously the function accepted an arbitrary filesystem path with no containment check, which allowed probing the existence of and triggering reads of files outside user_files. Models must now be located inside user_files. #108463 (groeneai).
  • Fixed CREATE OR REPLACE MATERIALIZED VIEW ... POPULATE leaving the new view unsubscribed from its source table, which silently dropped every row inserted after the replace. #108728 (alexey-milovidov).
  • Fix a segmentation fault when merging uniqExact aggregate states with GROUPING SETS, ROLLUP or CUBE and max_threads > 1. #108928 (Algunenano).
  • Fix possible logical error “Unexpected substream … for column …” during bump of compatibility setting. #109496 (Avogar).
  • The getClientHTTPHeader function now treats header names as case-insensitive, according to RFC 9110; in particular, the authorization header is now filtered out regardless of case. #109791 (Felixoid).
  • Fixed silent data loss with async inserts and deduplication (async_insert=1, async_insert_deduplicate=1). When several async-insert entries with distinct insert_deduplication_token values were coalesced into one flush that wrote to disjoint partitions, each token was registered in the deduplication log of every partition the flush touched, not only the partition its own rows landed in. A later insert reusing one of those tokens in a partition it never wrote to was then silently deduplicated away. Tokens are now registered only against the partition their rows actually landed in. #111049 (groeneai).
  • Fixed asynchronous Native-format inserts so one buffered entry that becomes incompatible after ALTER ... MODIFY COLUMN no longer causes the whole batch to fail. #111108 (Felixoid).
  • Fix CREATE OR REPLACE of a dictionary with an object of another kind: it failed with CANNOT_DETACH_DICTIONARY_AS_TABLE after the replace was already committed, leaving an orphan _tmp_replace_* table. #111142 (evillique).
  • Fixed a peer-certificate memory leak during TLS handshakes with certificate verification enabled. #111425 (thevar1able).
  • Fix a logical error Block structure mismatch in IntersectOrExceptStep stream: different number of columns that could happen when the filter split optimization (query_plan_split_filter) ran on a WHERE whose filter column name is also an input column name. The optimization left an internal __split_filter column in the branch output header, which then diverged from the sibling branch of a set operation such as INTERSECT or UNION. #111930 (groeneai).
  • Fixed incorrect results for filters or ORDER BY expressions using toString with Time, Time64, or DateTime values in daylight-saving time zones. Also restored read-in-order optimization for ORDER BY over a prefix of the table’s sorting key and for conversions from String to Nullable(String). #113291 (vdimir).
  • Fixed ATTACH of a Kafka table when kafka_num_consumers exceeds the limit derived from the number of CPU cores. #113390 (evillique).
  • Disabled distributed index analysis when projections are used to prevent incorrect query results. #115132 (azat).
  • Fixed a bug in the function formatRowNoNewline that could produce incorrect results or a logical error when a row is formatted to an empty result. #115669 (alexey-milovidov).
  • Do not return uninitialized memory in the result of a binary string literal whose length is not a multiple of eight, and from the LZ4 decompressor when a compressed block has no body. #115704 (alexey-milovidov).
  • Included object paths in deduplication hashes to prevent distinct object values from being deduplicated incorrectly. #115866 (Felixoid).
Last modified on August 31, 2026