Backward Incompatible Changes
Query, function, and syntax changes
- For views whose body is a plain
SELECTover a singleDistributedtable, the whole outer query is now pushed to the shards (new settingoptimize_trivial_view_pushdown_to_distributed, enabled by default). This changes observable behaviour for such views:FINALandSAMPLEwritten on the view reference are now propagated to the shard-local table instead of being ignored, andextremesis not reported on single-shard clusters. Setoptimize_trivial_view_pushdown_to_distributed = 0to restore the previous behaviour. #101791 (simonmichal). - Lightweight
UPDATEpatch parts now use a new v2 on-disk format sorted by (sorting_key..., _block_number, _block_offset) and applied with a new merging algorithm. Peak memory is bounded by the largest equal-sort-key run instead of the full patch, and updates that cross merge boundaries no longer fall back to in-memory Join apply. Old-format patch parts remain readable. During a rolling upgrade from a version before 26.8, keeppatch_parts_version = 'v1'or use thecompatibilitysetting until all replicas are upgraded. #103182 (CurtizJ). AggregatingMergeTreenow rejects, at table creation time, schemas where a column is neither part of the sorting key nor an aggregate-state measure (AggregateFunction/SimpleAggregateFunction). Such columns are silently collapsed to an arbitrary value during background merges, producing wrong results for queries thatGROUP BYor filter on them. Setallow_dimensions_outside_sorting_key = 1to restore the previous behavior. #108087 (alexey-milovidov).- ClickHouse now always uses the unified insert deduplication hash for both synchronous and asynchronous inserts; the legacy per-insert deduplication behaviours are removed. The server setting
insert_deduplication_versionis kept as a migration guard: the server refuses to start if it is set to a legacy value (old_separate_hashesorcompatible_double_hashes). To upgrade from a version that used a legacy value, first run a release that supportscompatible_double_hashes(which writes both the legacy and unified hashes). For replicated tables run it for at leastreplicated_deduplication_window_seconds(one hour by default; the default windows retain the unified hashes of all inserts for that window, which is considered enough for an insert retry loop). For non-replicated tables withnon_replicated_deduplication_window> 0 that window is count-based rather than time-based, so runcompatible_double_hashesfor at least that many inserts. Then remove the setting (or set it tonew_unified_hash) before upgrading to this version. #108361 (CheSema). - The setting
use_legacy_to_timeis now0by default, sotoTimeconverts values into theTimedata type instead of converting a date with time to a fixed date. The legacy behavior is still available via thetoTimeWithFixedDatefunction or by settinguse_legacy_to_time = 1. #108729 (alexey-milovidov). - Naive Bayes models (used by
naiveBayesClassifier) are now configured as a dictionary with theNAIVE_BAYESlayout, built at load time from a table of pre-aggregated per-class n-gram counts, instead of the previous server-side configuration (an XML config file referencing serialized.binmodel files), which is no longer supported — existing models must be recreated as dictionaries. Three new functions are added alongsidenaiveBayesClassifier:naiveBayesClassifierWithProbreturns the predicted class together with its probability,naiveBayesClassifierWithAllProbsreturns every class with its probability, andnaiveBayesNgramstokenizes text into n-grams the same way the dictionary does, for building the training data from raw labelled text. Additionally, several performance optimizations have been applied: on a 9.7 MiB code-point trigram language model, it uses ~49× less memory (from 1.84 GiB to 38 MiB), loads ~11× faster, and classifies ~11× faster. #108773 (nihalzp). - The default value of the
max_insert_threadssetting has been changed from1(no parallelism) toauto, which resolves to the number of CPU cores available to the server (reduced under memory pressure viamax_insert_threads_min_free_memory_per_thread). This parallelizesINSERT SELECTby default and can also parallelize the writing side of an eligible plainINSERTwhen the destination write path can safely fan out. It can change the number of parts created and the order of inserted rows. To restore the previous behavior, setmax_insert_threadsto1, or setcompatibilityto a version below26.8. #109000 (alexey-milovidov). #109006 (alexey-milovidov). - Reading a CSV bare
Tuplenow takes one field per element in every position, so a\Nin the field of a top-level element is that element andinput_format_null_as_defaultapplies to it. Previously a leading\Nwas taken for the whole column being null, so the row was replaced by the column default and the remaining element cells then failed to parse. This is backward incompatible for a row that supplies a single field for a whole bareTuple: it is now short by the remaining elements and is rejected, and for a one-elementTuple, where the field count is the same either way, it silently yields the element default instead of the columnDEFAULT. Setinput_format_csv_deserialize_separate_columns_into_tuple = 0to read such a field as the whole column again. #109744 (groeneai). - Added the
VALID FOR <interval>clause toCREATE USERandALTER USERas a shorthand forVALID UNTIL. The expiration deadline is computed as the current time plus the given interval at query execution time and stored in theVALID UNTILform. Thevalid_untilcolumn of thesystem.userstable now has the typeArray(DateTime64(0))instead ofArray(DateTime), so that deadlines beyond the year 2106 are represented exactly; tooling that reads this column should handle the new type. #110171 (alexey-milovidov). EXPLAIN SYNTAXnow returns the reformatted query as a singleStringrecord (with embedded newlines) instead of one record per line, so its output is a recoverable single row that is directly usable (for example,SELECT count() FROM (EXPLAIN SYNTAX ...)returns1). This is controlled by the newsingle_recordoption, which defaults to1; setsingle_record = 0to restore the historical one-record-per-line output. OtherEXPLAINkinds (PLAN/PIPELINE/AST) keep their per-line tree output. #110479 (alexey-milovidov).- The
hasColumnInTablefunction no longer accepts the optionalhostname,username, andpasswordarguments for checking a column on an arbitrary remote server; only thehasColumnInTable(database, table, column)form remains. The removed remote mode was a security concern: it let any user trigger outbound connections to arbitrary hosts and leaked credentials into query logs, and it was not gated by any privilege. #110881 (alexey-milovidov). - The
PostgreSQLandMaterializedPostgreSQLdatabase engines now respect the server’sremote_url_allow_hostsconfiguration, like thePostgreSQLtable engine, thepostgresqltable function, and DDL-created dictionaries already do. The policy is enforced onCREATE DATABASEand on a user-issuedATTACH DATABASE: ifremote_url_allow_hostsis configured on your server, creating or attaching aPostgreSQLorMaterializedPostgreSQLdatabase pointing at a host outside the whitelist is now rejected withUNACCEPTABLE_URL. Databases that already exist keep loading at server startup, so an upgrade cannot leave the server unable to boot. #111135 (alexey-milovidov). - Removed the Apache Arrow library-based reader and writer for the
ArrowandArrowStreamformats. The native ClickHouse implementation, which has been the default since 26.7, is now the only one. The settingsinput_format_arrow_use_native_readerandoutput_format_arrow_use_native_writerare obsolete: they are still accepted, but have no effect, so a query that set them to0to force the Apache Arrow implementation now uses the native one. #111996 (alexey-milovidov). - The TLS credentials of a MySQL source (
ssl_ca,ssl_cert,ssl_key) can no longer be specified as file paths from SQL — in a named collection created withCREATE NAMED COLLECTION, as a query argument, or in aCREATE DICTIONARYquery — because the server opens those files with its own privileges. Paths remain supported in the server configuration file. Elsewhere, pass the contents of the certificate or key in the newssl_ca_pem,ssl_cert_pemandssl_key_pemparameters, which are masked in logs and inSHOWqueries like passwords are. #112070 (alexey-milovidov). disable_insertion_and_mutationnow prevents background consumption fromKafka,RabbitMQ, andNATStables while still allowing direct writes to external storage. GatedKafka2,NATS, andRabbitMQtables do not initialize consumers for directSELECT. The recently introducedmessage_queue_disable_insertionsetting now requires a server restart. #113660 (cwurm).- A window
PARTITION BYorORDER BYover anAggregateFunctioncolumn is now rejected withILLEGAL_COLUMN, as top-levelORDER BYover such a column already was. Previously such a query was accepted by at least one analyzer, and windowPARTITION BYpartitioned differently depending onmax_threads. The refusal also covers a state nested inArray,Tuple,Map,VariantorSimpleAggregateFunction. ASimpleAggregateFunctionover an ordinary type,QBit, andGROUP BYandDISTINCTover a state, are unaffected. #113878 (groeneai). - Fix access checks for the
SYSTEM ... CACHE ON CLUSTERcommands. They required theSYSTEM DROP CACHEprivilege group instead of the privilege of the individual command, which both denied holders of a single granular cache privilege and let a holder of the group runSYSTEM SYNC FILESYSTEM CACHE ON CLUSTERwithout holdingSYSTEM SYNC FILESYSTEM CACHE. #114042 (groeneai). - The
NATStable engine accepts credentials inline in the newnats_credentialssetting (the same payload as a.credsfile), and no longer acceptsnats_credential_filefrom SQL: the path is a reference to a file on the server filesystem, which the server opens with its own privileges, so it can only be specified in a named collection defined in the server configuration file, or asnats.credential_filein the server configuration itself. A query may replace such a configured path with inlinenats_credentials, unless the operator pinned it with<nats_credential_file overridable="false">. Tables created before this restriction keep working after an upgrade. #114644 (alexey-milovidov).
Data type and format changes
- Reject the
zip/zipxbackup archive format for backups stored on object storage — directS3(...)/AzureBlobStorage(...)destinations andDisk(...)destinations backed by S3 or Azure — for bothBACKUPandRESTORE. Zip requires seeking to read its central directory, which is very slow over object storage. Use a tar-based format such astar.gzinstead. #101770 (Onyx2406). - Extended the supported range of
DateTime64from[1900-01-01, 2299-12-31]to[0000-01-01, 9999-12-31]. Values outside the former range are now computed correctly (via cctz) instead of being clamped to the boundary. With precision 8 or 9 the range remains narrower because the ticks are stored asInt64(with nanosecond precision the maximum is still2262-04-11). Backward compatibility: if you used out-of-range date-times before, but relied on the very specific saturation rules inside the old range, keep in mind that now the results are changing to be more correct. #107907 (alexey-milovidov). - An unquoted JSON number for a
DateTime64/DateTimecolumn inJSONEachRowand similar formats is now interpreted as a Unix timestamp (seconds since the epoch) with optional sub-second precision, consistent with theValuesformat,CASTandtoDateTime64. Previously a number with a fractional part (e.g.1703363853.035) was rejected withCANNOT_PARSE_INPUT_ASSERTION_FAILED, and a bare integer (e.g.1703363853) was read into the raw scaled value of aDateTime64, producing a1970-...timestamp. This is a backward incompatible change for unquoted integers fed toDateTime64columns; quoted strings and ClickHouse’s own (always quoted) JSON output are unaffected. #108091 (alexey-milovidov). - The experimental
ALPcodec now performsFloat32scaling arithmetic inFloat64, which eliminates exceptions on decimal data and significantly improves compression ratios. #111627 (rienath). - Reject timespan setting (milliseconds/seconds) values that overflow Int64 microseconds #112757 (azat).
- Asynchronous metrics can now have the
Mapdata type, and the per-CPU-core and per-device metrics were converted to single key-value metrics:OSUserTimeCPU0,OSUserTimeCPU1, … became a singleOSUserTimeCPUmetric with a map from the CPU core number to the value (and similarly for the otherOS*TimeCPU*metrics,CPUFrequencyMHz_*,Temperature*,EDAC*,Block*_*,Network(Receive|Send)*_*,Disk*_*,*BlobsQueueEstimate, andAsyncLogging*QueueSize).system.asynchronous_metricshas a newkey_values Map(LowCardinality(String), Float64)column (thevaluecolumn isNaNfor such metrics),system.asynchronous_metric_loglogs them as one row per key using a newkeycolumn, the Prometheus endpoint exports them with a label (e.g.ClickHouseAsyncMetrics_BlockReadBytes{device="sda"}), and the GraphiteMetricsTransmittersends them as<prefix>.<Metric>.<key>. If your monitoring reads the old metric names, setasynchronous_metrics_key_values_modetolegacy_namesto retain them or tobothduring migration; apply this server setting withSYSTEM RELOAD CONFIGwithout restarting. #115333 (alexey-milovidov). #116791 (alexey-milovidov). - Object-storage disks now use native metadata-storage transactions by default, improving the consistency of their metadata updates. #89658 (Michicosun).
Settings and configuration changes
- Removed the long-deprecated functions
snowflakeToDateTime,snowflakeToDateTime64,dateTimeToSnowflakeanddateTime64ToSnowflake. They were deprecated back in v24.6 in favor ofsnowflakeIDToDateTime,snowflakeIDToDateTime64,dateTimeToSnowflakeIDanddateTime64ToSnowflakeID, which should be used instead. The settingallow_deprecated_snowflake_conversion_functions(which used to re-enable them) is now obsolete and has no effect. #108711 (alexey-milovidov). - Extended the supported range of
Date32from[1900-01-01, 2299-12-31]to[0000-01-01, 9999-12-31], matchingDateTime64. Parsing and conversions now accept the extended range instead of silently clamping to the old boundaries. Backward compatibility notes: in the numeric conversiontoDate32(N), values in[120530, 2932896]are now interpreted as day numbers (dates from2300-01-01to9999-12-31) instead of Unix timestamps in early 1970, matching the rule that a number that fits into the day-number range is a day number; numbers below the day number of0000-01-01and timestamps after9999-12-31saturate to the new boundaries. #111534 (alexey-milovidov).
Other backward incompatible changes
arrayIntersectandarraySymmetricDifferenceno longer treat a value repeated inside a single argument as if it appeared in several arguments. Queries relying on the previous behavior can now return different results:arrayIntersect([1], [2], [1, 1])returns[]instead of[1],arrayIntersect([1, 2], [2], [1, 1, 2])returns[2]instead of[1, 2], andarraySymmetricDifference([1], [2], [1, 1])returns[2, 1]instead of[2]. ForarraySymmetricDifferencetwo arguments are already enough:arraySymmetricDifference([1], [2, 2])returns[2, 1]instead of[1]. A value is now counted for an argument only when it was present in every argument before it, so the result contains exactly the values present in all of the arguments. As part of the same change,arrayIntersectbuilds its hash table from the smallest argument rather than from all of them, which makes it up to 1.85x faster and use a third less memory when the arguments differ a lot in size.arrayUnionis not affected. #113021 (alexey-milovidov).- Deprecated the
minmaxcolumn statistics type. #108680 (hanfei1991). - Fix the access check for
SYSTEM PREWARM PRIMARY INDEX CACHE ... ON CLUSTER, which incorrectly required theSYSTEM PREWARM MARK CACHEprivilege instead ofSYSTEM PREWARM PRIMARY INDEX CACHE. #109198 (Algunenano). - Fixed
SYSTEM STOP/START CLEANUPandSYSTEM STOP/START VIRTUAL PARTS UPDATE ON CLUSTERrequiringSYSTEM PULLING REPLICATION LOGinstead ofSYSTEM CLEANUP/SYSTEM VIRTUAL PARTS UPDATE. #110289 (groeneai). - The
/glob (any amount of directories) now also matches the same directory. Previously,/in glob patterns was not handled as a special case, sodata/**/file.txtwould not matchdata/file.txt(zero directory levels). #97676 (alexey-milovidov).
New Features
Functions and data types
- Added the
parseQueryToJSONandformatQueryFromJSONfunctions to serialize and deserialize ASTs as JSON, and an experimentalclickhouse_jsondialect enabled byenable_json_ast_dialect. #100412 (alexey-milovidov). #113480 (fm4v). - Added system table
system.stemmerswhich shows all available stemmers languages that can be specified for thestemfunction. #100611 (Ergus). - Added support for the standard SQL
AT TIME ZONEandAT LOCALpostfix operators as syntactic sugar fortoTimeZone. The expressionexpr AT TIME ZONE zoneis now equivalent totoTimeZone(expr, zone), andexpr AT LOCALis equivalent totoTimeZone(expr, timeZone()). #106092 (lvzhipin03). - The
URLtable engine andurltable function now dispatch to the appropriate backend based on the URL scheme:file://is served by theFileengine,s3:///gs:///gcs:///oss://byS3,az:///azure:///abfss:///abfs://byAzureBlobStorage,hdfs://byHDFS, andhttp(s)://by theURLengine as before. Theurl_basesetting is applied before scheme dispatch. Only the S3 schemes resolved by the defaulturl_scheme_mappersare dispatched; other S3-compatible vendor schemes (cos,obs, …) are not, and require using thes3engine/function directly. #106093 (alexey-milovidov). - Added the
dotProductTransposedfunction (aliasscalarProductTransposed) that computes the approximate inner product between aQBitcolumn and a reference vector, complementing the existingL2DistanceTransposedandcosineDistanceTransposedfunctions. #108100 (alexey-milovidov). - Added an optional stride parameter to the
QBitdata type (QBit(T, dimension, stride)) that stores groups of dimensions in separate streams, so a vector search can read only the first dimensions efficiently (e.g. for Matryoshka embeddings). The transposed distance functions accept an optional fourthused_dimsargument to read a reduced number of dimensions. #108103 (alexey-milovidov). - Added support for the
Int8element type in theQBitdata type, enabling storage and transposed-distance vector search (L2DistanceTransposed,cosineDistanceTransposed) over quantized 8-bit integer vectors. #108105 (alexey-milovidov). - New function
randomHadamardTransform(vector[, seed[, output_dims]]): a deterministic randomized Hadamard transform of a float vector — an orthogonal, norm-preserving rotation useful for preprocessing embeddings before quantization, and (when truncated) as a Johnson–Lindenstrauss / subsampled-randomized-Hadamard random projection. #108227 (alexey-milovidov). - Implement aggregate function
mergedJSONPatchfor RFC 7396-style merge-patch aggregation over JSON values, including state merge support and AggregatingMergeTree usage. Users can aggregate a JSON column using a timestamp or another ordering column as the sort key to compute the latest state. #108349 (larryluogit). - Added function
xxHash64Spark, which computes Spark-compatiblexxHash64values forStringandNULLinputs using seed42and returnsInt64. #108436 (lalitium). - Add
digits(n, offset[, length])function which returns the number innwhich starts at indexoffset(1-indexed)and haslengthnumber of digits. Iflengthis not provided, the function returns the number from offset till end. #109012 (1000ms). - Add the
sqrarithmetic function for calculating the square of a number. #109061 (lalitium). - Added quantized transposed distance functions
cosineDistanceTransposedQuantized,L2DistanceTransposedQuantizedanddotProductTransposedQuantizedthat operate on aQBit(Int8)ofquantizeBFloat16ToInt8Lloyd-Max codes, dequantizing the stored codes on the fly. A floating-point reference vector is the full-precision query, compared atFloat32precision (aFloat64query is narrowed); anArray(Int8)reference is itself dequantized, for a symmetric quantized-vs-quantized distance. #109405 (alexey-milovidov). - Added a new function
dictGetRootwhich returns the topmost ancestor (the root) of a key in a hierarchical dictionary. It is a convenient equivalent ofdictGetHierarchy(dict_name, key)[-1]. #109459 (alexey-milovidov). - Added the
mergeTreeCodecBlockCounts(database, table)table function that reports, per (part, column, substream) of aMergeTreetable, how many compressed blocks use each codec. #109623 (rienath). - Add function
notHas, the negation ofhasfor arrays, maps, and JSON. When the haystack is a constant array,notHas(constant_array, x)is rewritten tox NOT IN constant_arraybyoptimize_rewrite_has_to_in(enabled by default), so it executes via a set lookup and can prune by the primary key index likeNOT IN. #109926 (nihalzp). - Added the
MultiPointgeo data type, stored asArray(Point), and included it in theGeometrytype. #109951 (davidmenggx). - Functions
arrayElement(thevec[n]operator) andarraySlicenow work for theQBitdata type:qbit[n]returns the n-th vector element at full precision, andarraySlice(qbit, offset, length)returns a projection to a subset of dimensions. Both read only the bit planes of the stride groups they need, and slices aligned to stride-group boundaries reuse the stored streams without copying. #109953 (Utkal059). - Added a
splitByRegexptokenizer for text indexes and thetokensfunction. It splits the input into tokens using a regular expression as the separator, for exampletokenizer = splitByRegexp('[^\p{L}\p{N}#+]+'), which allows preserving tokens containing special characters such asC++orC#that other tokenizers would break apart. #110002 (Ergus). - Added functions
geometryIntersectCartesianandgeometryIntersectSphericalthat return whether two geometries intersect. UnlikepolygonsIntersectCartesian/polygonsIntersectSpherical, they accept any geometry data type (Point,LineString,MultiLineString,Ring,Polygon,MultiPolygon), including the commonGeometrytype, and the two arguments may be of different types. #110062 (alexey-milovidov). - The native ORC reader now supports ORC’s
uniontype, mapping it to the ClickHouseVarianttype (previously such columns were rejected withUnsupported ORC type). #110078 (alexey-milovidov). - The native ORC output format can now write the ClickHouse
Varianttype, mapping it to an ORCuniontype(previously it failed withILLEGAL_COLUMN). #110085 (alexey-milovidov). - Add framing formats, selected by the new setting
framing_output_format: they multiplex different response parts of the query in a single HTTP response stream — chunks of data, totals and extremes, progress packets, profile events, server logs, and exceptions. Implemented framing formats:None(default, everything works as before),EventStream(HTTP server-sent events),JSONEachPacketBase64, andJSONEachPacketString(a JSON object per packet with base64-encoded or string data). #110127 (alexey-milovidov). - Added the
bigquerytable function and theBigQuerytable engine for reading from and writing to Google BigQuery tables, including public datasets. The table structure is inferred from the BigQuery table schema. Authentication supports an OAuth access token, a service account key in JSON format, and an OAuth client with a refresh token. #110166 (alexey-milovidov). - New function
aiRedactthat detects and redacts personally identifiable information (PII) in text using an LLM provider. Specify the categories to redact (e.g.['email', 'name']) or pass an empty array to use a default set of PII categories. Matched values are replaced with a token ([REDACTED]by default). #110464 (davidmenggx). - Add
aiFilterfunction that evaluates a natural-language condition against text with an LLM and returnsUInt8for use inWHERE,PREWHERE, andJOIN ... ON. #110594 (ylw510). - Support TLS/SSL connections to PostgreSQL for the
PostgreSQLtable engine, thepostgresqltable function, thePostgreSQLandMaterializedPostgreSQLdatabase engines, andPostgreSQLdictionaries:sslmodeplus the certificates and the key, given either as literal contents (sslrootcert_pem,sslcert_pem,sslkey_pem; masked like passwords) or as paths (sslrootcert,sslcert,sslkey; accepted only from a named collection defined in the server configuration file). #110615 (alexey-milovidov). - The
postgresqltable function andPostgreSQLtable engine can now be used to connect to another ClickHouse server over the PostgreSQL protocol (when a table name is used; thequery(...)variant is not supported yet). Added the PostgreSQL-compatibility functionsformat_typeandcurrent_setting. #110760 (alexey-milovidov). - New function
aiSimilaritythat computes the semantic similarity between two texts using an embedding model. Returns aNullable(Float32)in[-1, 1]where1means the texts are identical, orNULLif an operand isNULL/empty or its embedding failed. #110777 (davidmenggx). - Added the
RemoteandRemoteSecuredatabase engines that provide real-time access to the tables of a database on a remote ClickHouse server, forwardingSELECTandINSERTqueries to it. They are the ClickHouse-to-ClickHouse counterparts of theMySQLandPostgreSQLdatabase engines. #110975 (alexey-milovidov). - Support pipe operators in SQL queries:
FROM t |> WHERE x > 1 |> AGGREGATE count() AS c GROUP BY y |> ORDER BY c DESC |> LIMIT 10, similar to the pipe syntax of GoogleSQL. AnySELECTquery can be followed by a chain of|>operators, and each operator wraps the query before it into a subquery, so the resulting AST is the same as for the equivalent query with nested subqueries. In a query that starts with theFROMclause, theSELECTclause is now optional and defaults toSELECT *. #111151 (alexey-milovidov). - Added aggregate function
ginito calculate the Gini coefficient of finite, non-negative numeric values, including grouped and distributed aggregations. #112280 (amirreza1307). #114643 (groeneai). - Support animated PNG in the
PNGoutput format. Atcolumn turns the result of a query into an animation, with the time scale oftset byoutput_format_image_time_multiplier_secondsandoutput_format_image_time_divisor_seconds, andoutput_format_image_streaming_animationto write the frames out as the query produces them instead of buffering them in memory. #112846 (alexey-milovidov). - Added a
finish_timecolumn tosystem.mutationsthat records when a mutation was completed. Unfinished mutations and mutations whose completion time is unknown report zero. #113474 (nikitamikhaylov). - Add a new
bucketedschema type forsystem.metric_log, which stores all metrics in a singleMap(Enum16(...), Int64)column using the bucketedMapserialization with 128 buckets, plus a per-metricALIAScolumn for compatibility: the table consists of a few columns instead of thousands, zero values are not stored, and reading a single metric reads only one of the 128 buckets. In addition, aMapwithEnumkeys can now be indexed by the string name of the enum value, e.g.map['name']. #115380 (alexey-milovidov). - Added a
chinesetokenizer for thetokensfunction andMergeTreetext indexes. It segments Chinese text into words using a dictionary and a Hidden Markov Model (the algorithm follows jieba), withcoarse_grained(default) andfine_grainedgranularities. #89945 (amosbird). - Add
groupFormataggregate function that formats rows in each group using a specified output format and returns the result as a string. #93201 (wandersofb). - Add aggregate function combinator
-Tuple, which applies the underlying aggregate function to each element of aTuplecolumn independently and returns aTupleof the results, preserving element names:sumTuple(t)fort = (a, b)returns(sum(a), sum(b)). Aggregate functions with several arguments take one tuple per argument, paired by position:corrTuple((a1, a2), (b1, b2))returns(corr(a1, b1), corr(a2, b2)). Unlike-ForEachover arrays, elements may have different types, and per-element result types and names are preserved. #98190 (RinChanNOWWW).
SQL and query features
- Added support for WHERE clauses in projection definitions. Projections with WHERE only materialize rows matching the predicate, and the optimizer can use them (cost-based) when the query’s WHERE implies the projection’s WHERE. #102347 (SBALAVIGNESH123).
- Added a way to access tables and databases via URL paths in the HTTP interface (e.g. `/database/table.format.gz?filter=a>0`), plus new settings (`http_allow_database_as_path`, `http_allow_table_as_file`, `http_allow_filters_as_path`, `http_allow_filters_as_unrecognized_url_parameters`, `select`, `order`, `sort`, `filter`, `page`, `compression`, `format`, `input_format`, `output_format`, `default_format`, `database`) that compose with one another and with the existing `query` URL parameter. #105249 (alexey-milovidov).
- Added the
RemoteandRemoteSecuretable engines, the persistent counterparts of theremoteandremoteSecuretable functions.CREATE TABLE ... ENGINE = Remote('addresses', db, table, ...)now works in addition toCREATE TABLE ... AS remote(...). #106189 (alexey-milovidov). - Added
EXPLAIN ANALYZEfor examining query performance: the query is executed and the actual execution metrics are rendered in the familiar query-plan format. #106586 (Fgrtue). #110668 (Fgrtue). - Added engine-agnostic
SYSTEM STOP,SYSTEM START,SYSTEM PAUSE,SYSTEM CANCEL, andSYSTEM REFRESHcommands, and their... ALL BACKGROUNDserver-wide forms, to control the background activity ofKafka,RabbitMQ,NATS,S3Queue/AzureQueuetables and refreshable materialized views through one unified interface. For refreshable materialized views they alias the existingSYSTEM ... VIEWcommands. As part of supporting these controls on NATS, JetStream tables now acknowledge messages only after a successful insert (at-least-once, previously messages were auto-acknowledged on delivery and could be lost if the insert failed). A newnats_wait_for_flush_intervalsetting (defaultfalse, preserving the previous low-latency behaviour) optionally keeps a consumption cycle open for the whole flush interval instead of flushing as soon as the queue drains. A newnats_commit_on_selectsetting makes a directSELECTon a JetStream table consume (acknowledge) the messages it reads. #107476 (NIKTONIKTO717). - Added support for the
mysql,postgresql, andsqlitetable functions and table engines to accept a user’s query (instead of a table name) and pass it to the external database as is, written either as a subquery(SELECT ...)or asquery('SELECT ...'). The structure of the resulting table is inferred from the query result, and such a table is read-only. #107740 (alexey-milovidov). - Introduce the
QueryRunnertable engine. Records inserted into aQueryRunnertable represent queries that the engine executes. The engine can be used for asynchronous query execution, batch execution of generated queries, directing queries to remote clusters, benchmarks, fuzzing, and testing with shadow traffic. #107888 (mstetsyuk). - Support the
GROUPSframe mode for window functions (SQL:2011), e.g.any(price) OVER (PARTITION BY symbol ORDER BY ts GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING). In aGROUPSframe the boundaries count whole peer groups — sets of rows that are equal on theORDER BYkey — soN PRECEDING/N FOLLOWINGmeanNpeer groups before/after the current row’s peer group, rather than physical rows (ROWS) orORDER BYvalue distances (RANGE). #108653 (nihalzp). - Plain
CREATE MATERIALIZED VIEW ... POPULATEis now locally atomic: the view is subscribed to new inserts of the source table and the existing data is snapshotted together under a brief exclusive lock on the source, so rows inserted through the same server concurrently with the population are no longer missed or duplicated (new settingmaterialized_views_populate_atomically, enabled by default). The guarantee covers the local insert path only - inserts arriving on another replica or through a distributed write path are outside the cut - and it requires a source that can provide a pinned snapshot (theMergeTreefamily andMemory); other sources, as well asCREATE OR REPLACE/REPLACEand views created inReplicateddatabases, keep the legacy non-atomic population.POPULATEcan now also be used together withTOto backfill the target table. #108715 (alexey-milovidov). ALTER USER,ALTER ROLEandALTER SETTINGS PROFILEnow acceptSET name = valueas an alias forMODIFY SETTING name = value. It changes individual settings in place while keeping the rest, unlike the bareSETTINGSclause which replaces the whole settings list. This makes it less likely to accidentally wipe a user’s other settings. #108722 (groeneai).- Support
ALTER TABLE ... MODIFY CONSTRAINT [IF EXISTS] name CHECK exprto change the expression of an existing constraint in place. #108768 (alexey-milovidov). - Added the sort-based IEJoin algorithm for joins whose
ONsection has two inequality comparisons (<,<=,>,>=) between the joined tables, enabled by addingie_jointo thejoin_algorithmsetting. Supported kinds areALL INNER/LEFT/RIGHT/FULL JOINandSEMI/ANTILEFT/RIGHT JOIN. Previously such queries were executed as aCROSS JOINwith a filter (INNERonly), which is much slower on large tables. #109920 (vdimir). - Added a new system table,
system.user_query_log, which shows every user their own query log records without requiring access to the query log table. The original implementation and the idea belong to Yue Ni (@niyue). #110156 (alexey-milovidov). - New setting
run_query_in_background. The server accepts the query, immediately returns an empty result, and runs it to completion regardless of what happens to the connection. The result is discarded. Track the query by itsquery_idinsystem.processesandsystem.query_log. Intended for long queries likeINSERT ... SELECT,CREATE TABLE … AS SELECT, orCREATE MATERIALIZED VIEW … POPULATEthat must not die with a dropped connection. #112816 (mstetsyuk). - Support ALTER TABLE … MODIFY PROJECTION to change projection-level settings (e.g. index_granularity) of an existing projection without rebuilding it. The new settings apply lazily via merges. #113343 (diegomestre2).
- The S3 tables-engine catalog for data lakes now supports
INSERTas well as querying existing tables. #113505 (scanhex12). - The
Icebergcatalog now supports Snowflake Horizon Catalog withcatalog_type = 'horizon', allowing reads and writes to Snowflake-managedIcebergtables. #114547 (melvynator). - Added the
skip_unavailable_shards_modesetting (also available as aDistributedengine setting) to control which exceptions from a remote shard are silently ignored whenskip_unavailable_shardsis enabled. This provides finer control over query behavior in distributed environments. #79091 (byte-sourcerer).
Storage, data lakes, and object storage
- Added support for Puffin file format (https://iceberg.apache.org/puffin-spec/). Now this format can be used in table functions like
file,url,s3and so on.. #103936 (scanhex12). - Added “packed” data part storage for
MergeTreetables, which stores all of a part’s files in a single archive (data.packed) instead of a file per stream. It is controlled by themin_bytes_for_full_part_storage,min_rows_for_full_part_storageandmin_level_for_full_part_storagesettings and is disabled by default. Compatibility note: once a table writes packed parts, older server versions cannot read them, so enabling this format prevents downgrading the server. #108118 (Algunenano). - Add
nats_credentialssetting to the NATS table engine, allowing users to specify NATS credentials inline as a string (matching the payload of a.credsfile). #110733 (addshore).
Data formats and ingestion
- Added the
HiveTextoutput format that writes data in Apache HiveLazySimpleSerDetext form. Fields are separated by\x01, rows by the newformat_hive_text_rows_delimitersetting (default\n), and nestedArray,Map, andTuplevalues use Hive’s separator list. The output targets Hive’s defaultLazySimpleSerDeand is not symmetric with ClickHouse’sHiveTextinput for nested values or custom row delimiters. #107582 (alexey-milovidov). - Added the
GeoJSONoutput format for writing one-feature-per-row GeoJSONFeatureCollectiondocuments. The newformat_geojson_validate_geometrysetting (enabled by default) validates GeoJSON shapes on reads and writes; theGeoJSONinput format now infersidasNullable(String), distinguishing absent ornullIDs from empty strings. #108065 (nihalzp).
Settings, access, and observability
- Added a built-in documentation search page, available at the
/docspath of the HTTP interface, that provides instant search over thesystem.documentationtable and renders the reference documentation (with syntax highlighting, math, and cross-links). #108345 (alexey-milovidov). - Added a japanese tokenizer for text indexes and the tokens/hasAnyTokens/hasAllTokens functions, using the MeCab morphological analyzer. The dictionary is loaded at runtime from a location set in the server configuration and verified against a SHA-256 checksum before use. #111420 (Ergus).
- Added a new filesystem cache setting
system_cache_extensionsthat controls which files are stored in the system cache whenuse_split_cacheis enabled. #111575 (kirillgarbar). - Add the
always_fetch_mutated_partsetting, which allows a replica to download mutated parts from other replicas instead of executing mutations locally. #113020 (rorylshanks).
Other new features
- Add support for
WITH TIESfor negativeLIMIT. #100930 (nihalzp). - Add
restore_table_data,restore_access_entities, andrestore_functionsrestore settings for granular control over what gets restored. These can overridestructure_onlyfor individual categories — e.g.structure_only=true, restore_access_entities=truerestores table definitions and access entities without table data. #102402 (fm4v). - Dictionaries can now be configured for lazy loading individually with
dictionary_lazy_loadin the dictionary definition, overriding the globaldictionaries_lazy_loadserver setting. This allows some dictionaries to load lazily while others load eagerly. #108314 (mstetsyuk). - The array subscript operator supports an array of integers as the index:
arr[indexes]returns the elements at all of the given positions, equivalently toarrayMap(i -> arr[i], indexes). #108371 (ucasfl). - Added functions
geoToUTM,UTMToGeo,geoToMGRSandMGRSToGeofor converting between WGS84 geographic coordinates and the UTM and MGRS coordinate systems. #108939 (alexey-milovidov). - Add
SYSTEM UNLOAD DICTIONARYandSYSTEM UNLOAD DICTIONARIEScommands to release dictionary memory without dropping the dictionary definition. Dictionaries will be reloaded lazily on next access. #109639 (Manerone). - Added a new
icutokenizer for text indexes and thetokens,hasAnyTokens, andhasAllTokensfunctions, providing locale-aware Unicode word segmentation (including dictionary-based segmentation for languages such as Chinese, Japanese, and Thai). #109940 (Ergus). - Added a new codec
ZXC— an asymmetric LZ codec with slow compression and very fast decompression, with a compression ratio betweenLZ4andZSTD. #110620 (alexey-milovidov). - Added the
s3_basesetting so relative URLs in thes3,s3Cluster,gcs, andosstable functions and theS3table engine can be resolved against an S3 base URL. Added theURLdatabase engine, which resolves table names as URLs against an optional base and dispatches them to the appropriate backend. #111512 (alexey-milovidov). - Enable wildcard expansion for
url()and theURLengine by listing HTTP index pages (HTML or plaintext directory listings) and extracting matching URLs. Wildcards are matched against the URL path component, with limits on the index page size and on the number of directories read. #95181 (niyue). - Added
AWS_MSK_IAMas a supported value forkafka_sasl_mechanism, enabling ClickHouse to authenticate with Amazon MSK using IAM roles without managing SASL/SCRAM credentials. #96100 (kalavt). - Added a postprocessor to the text index which transforms the tokens after tokenization. #98939 (Ergus).
Experimental Features
Query planning and execution
- Added “drivers” for executable user-defined functions. A driver declared in
<user_defined_executable_function_drivers_config>can be used inCREATE FUNCTION name ARGUMENTS (...) RETURNS T ENGINE = DriverName(...) AS '...code...'to compile or otherwise process a user code snippet at function-creation time and produce a runnable executable UDF. The resulting configuration is stored in<dynamic_user_defined_executable_functions_path>, and the originating query is persisted asATTACH FUNCTIONso the function survives server restarts. A proof-of-conceptc_function_bodydriver compiles and runs C function bodies inside sandboxed Docker containers inexecutable_poolmode. #105131 (alexey-milovidov). - Prometheus Query API requests to
/api/v1/queryand/api/v1/query_rangeare now recorded insystem.query_logwithread_rowsandread_bytesmetrics. #106611 (JTCunning). - Support distributed query-plan reads for
SELECT ... FINALon MergeTree-family tables whenmake_distributed_planis enabled. #108148 (davenger). - Add experimental plan-based parallel replicas execution for MergeTree queries. #108504 (devcrafter).
- Added an experimental table function
eval, which evaluates a constant expression to a query string and executes the resulting singleSELECTquery. The feature is disabled by default and can be enabled with the settingallow_experimental_eval_table_function. Author: Yue Ni #110132 (alexey-milovidov). - Plan-based parallel replicas (
parallel_replicas_plan_based): the local/remote boundary is now chosen by a plan-level optimization over a normal query plan, and queries over views with aUNIONare parallelized withoutparallel_replicas_allow_view_over_mergetree. Queries that useIN (subquery)now fall back to a local plan instead of failing. #111063 (devcrafter). #112443 (devcrafter). - Harden the experimental AI functions: deny insecure (
http) endpoints to remote hosts by default (new settingai_function_allow_insecure_endpoint), bound outbound API calls per query by default (ai_function_max_api_calls_per_querynow defaults to1000), and sanitize provider error responses written to the logs. #111286 (george-larionov). - Plan-based parallel replicas (
parallel_replicas_plan_based) now distribute eligible JOINs (INNER,LEFT,RIGHT, includingSEMI/ANTI): the whole join is executed in a distributed plan fragment with one side coordinated across replicas and the other broadcast. #112268 (devcrafter). - Report the error that cancelled a distributed query (
make_distributed_plan = 1) instead of a bareQUERY_WAS_CANCELLED. A failing worker task’s real error was previously visible only in the server log. #112590 (davenger). - Support distributed execution of query plans with
IN (subquery)without rewriting it to aJOIN. The set is built once on the initiator and its values are shipped with the worker tasks. The forcedrewrite_in_to_joinoverride is removed; the rewrite remains available as an explicit setting. #113826 (davenger). - A distributed plan query (
make_distributed_plan) now stops its upstream stages when aLIMITis satisfied, instead of computing the full result and discarding it. #114806 (davenger). - Fix the error
Illegal type Decimal(18, 0) of argument of function toIntervalNanosecondwhen a PromQL query usesoffsetinside a range selector, e.g.rate(m[2m] offset 5m). #114997 (nikitamikhaylov). - Support
GROUPING SETSaggregation in distributed plans built by the Cascades optimizer (make_distributed_plan = 1, enable_cascades_optimizer = 1). #115426 (davenger). TimeSeriestables can now keep a “recent samples” table: when the new engine settingrecent_samples_ttl_secondsis non-zero, the engine creates a TTL’d copy of the samples table (partitioned byrecent_samples_partition_by, by day by default) written on every insert, and PromQL queries whose time range fits in the TTL window automatically read from it, making alert/recording rules and short-range dashboards several times faster on large tables. The preference can be disabled with the new query-level settingtime_series_prefer_recent_samples_table. #115441 (nikitamikhaylov).- Distributed query plans (
make_distributed_plan) now stop idle upstream stages promptly after a satisfiedLIMIT: an idle exchange sink readsNoMoreDataNeededas soon as it arrives, and an idle exchange source notices its closed output and forwards the stop upstream. Previously such stages kept reading and computing data that nobody consumes. #115690 (davenger). - Automatic parallel replicas can now collect runtime statistics and enable parallel replicas for supported queries when
parallel_replicas_plan_basedis enabled. #115788 (devcrafter). - Added an experimental Cascades cost-based optimizer for distributed query plans, enabled by
enable_cascades_optimizer = 1together withmake_distributed_plan = 1. It chooses between shuffle, broadcast, replicated, and local join strategies, two-phase, shuffle, and local aggregation, two-stage distributed top-N, and parallel and replicated reads by estimated cost, inserting exchange operators as needed. #86353 (davenger).
Storage, data lakes, and indexes
- Add an in-memory SLRU cache for deserialized Paimon metadata files (manifest lists and manifests). When enabled via the
use_paimon_metadata_files_cachesetting, repeated queries against the same Paimon table skip re-downloading and re-parsing metadata from object storage. #104657 (Binnn-MX). - The experimental
ReaderExecutor(use_reader_executor, off by default) can now hold a remote source connection open and reuse it across sequential reads, reducing the number of object storage requests on scans. #107735 (CheSema). - Added a read-through filesystem cache to the experimental
ReaderExecutorread path (use_reader_executor, disabled by default). #110029 (CheSema). - Added an experimental
MergeTreesettingallow_experimental_adaptive_codec_selection. When enabled, merges and mutations pick the smallest-output codec per block for columns that use the default codec. #111834 (rienath). - Added a userspace page cache tier to the experimental
ReaderExecutorread path (use_reader_executor, off by default). On a miss the executor now populates the userspace page cache and serves subsequent reads from it, zero-copy. #114890 (CheSema). Icebergmanifest-file compaction is now available throughOPTIMIZE TABLE ... MANIFESTto reduce the number of metadata files. It is gated by theallow_experimental_iceberg_compactionsetting. #98178 (SmitaRKulkarni).
Functions, formats, and AI
- Experimental: serve
SELECT count(*) FROM t WHERE col <op> default(col)from per-column sparsity statistics inserialization.jsonwithout any data scan when the predicate exactly partitions the column into defaults and non-defaults. #105890 (Algunenano). - Added the experimental
Quantize(...)vector codec family and an opt-in two-stage approximate vector-search rewrite over quantized companion streams. #108565 (shankar-iyer). - Revive the experimental
SZ3error-bounded lossy compression codec forFloat32/Float64(and arrays of them) columns. It requiresallow_experimental_codecs. #108788 (alexey-milovidov). - Fixed reading data compressed with the experimental
SZ3('ALGO_LORENZO_REG', ...)codec, which previously failed withCORRUPTED_DATA, and avoided undefined behavior when compressing non-finite floating-point values (NaNand infinities) with the experimentalSZ3codec. #110762 (alexey-milovidov). - PromQL: date/time functions (
hour(),minute(),month(),year(),day_of_week(),day_of_month(),day_of_year(),days_in_month()) can now be called without arguments, defaulting tovector(time())as in Prometheus. #111869 (valerypetrov). - PromQL: fixed operations on instant vectors without tags, e.g.
vector(1) + vector(2),topk(1, vector(1)),label_replace(vector(1), ...)and binary operators withon(), which failed with a type error. #111872 (valerypetrov). - PromQL: implemented function
deriv, fixing a bug where its backing aggregate function returned values 1000x too small with millisecond-resolution timestamps. #112360 (valerypetrov). - Apply the read-in-order optimization to
ORDER BYqueries whenparallel_replicas_plan_basedis enabled: the sort is now shipped to the replicas and merged on the initiator, so each replica reads in the sorting key order. Also fixesUnknown function __topKFilterforORDER BYon a non-primary-key column with this setting enabled. #114315 (devcrafter). - Fixed vector-search reads after reloading a data part (for example after
ATTACHor a server restart): parts no longer lose theirQuantizedcodec subcolumns, so searches read the quantized subcolumns rather than falling back to the full-precision column. #114763 (shankar-iyer). - The experimental
TimeSeriestable engine no longer applies theGorillacodec to auto-createdvaluecolumns of the samples inner table; they now default toCODEC(ZSTD(1)). Explicitly declared columns keep whatever codecs the user wrote. Thetimestampcolumn default is unchanged (CODEC(DoubleDelta, ZSTD(1))). #112110 (nikitamikhaylov). #114790 (nikitamikhaylov). - Support
ROLLUP,CUBE,GROUPING SETSand thegroupingfunction in queries executed withmake_distributed_plan = 1. #114950 (davenger). - Add a Real Doubles (RD) variant and an optional
ALP(AUTO|STD|RD)argument to the experimental ALP codec. Bare ALP now auto-selects between the existing STD scheme and RD instead of always using STD. #99654 (nazarii-piontko).
Other experimental features
- Add static partition-to-shard affinity for StorageKafka2 via kafka_partition_shard_num and kafka_shard_count settings, allowing multiple ClickHouse shards to deterministically consume disjoint subsets of Kafka partitions. #108886 (JingYanchao).
- You can now authenticate to OneLake using a pre-obtained bearer token via the
onelake_bearer_tokensetting, instead of aonelake_client_idandonelake_client_secret. This avoids sharing a long-lived client secret. The token is not refreshed, so the database must be recreated once it expires. #109104 (asya-ch). - Fix
data_uncompressed_bytesfor skip indices packed intoskp_idx.packed(viapacked_skip_index_max_bytes): the reported uncompressed size was the compressed size, which also could preventdistributed_index_analysisfrom activating. #109272 (Algunenano). - Added Zstandard compression support to the experimental Prometheus remote-write v1 HTTP handler. #110907 (JTCunning).
- PromQL: implemented functions
clamp,clamp_min,clamp_max, andround. #111870 (valerypetrov). - PromQL:
quantilewith a quantile level outside [0, 1] now returns -Inf/+Inf/NaN like Prometheus instead of throwing an error, andhistogram_quantilenow ignores input series whoselelabel is missing or unparsable, like Prometheus. #111871 (valerypetrov). - PromQL: implemented functions
changes()andresets(). #112352 (valerypetrov). - Correctly collect insert stats and queries through the Prometheus remote-write endpoint. #112825 (JTCunning).
- PromQL: preserve finite values when using modulo with an infinite divisor. #113746 (fallintoplace).
- Reject literal line feeds in ordinary quoted PromQL strings. #114381 (fallintoplace).
- A very large
distributed_plan_workers_numno longer makes the server abort with astd::length_errorwhen the experimentalmake_distributed_planandenable_cascades_optimizerare enabled. Such a node count is now rejected withINVALID_SETTING_VALUE. #115398 (groeneai). - Implement the
/api/v1/seriesendpoint of the Prometheus HTTP API forTimeSeriestables:match[]series selectors (repeated values are a union), an optionalstart/endtime range, and an optionallimitparameter. #115676 (nikitamikhaylov).
Performance Improvements
JOIN and aggregation performance
- Enable the query condition cache for queries that go through TopK dynamic filtering (
ORDER BY ... LIMIT n). The QCC entry is partitioned by the TopK plan parameters so the same query reuses cached WHERE filter results, while a differentLIMIT, sort column, or sort direction produces a fresh entry. #104478 (alexey-milovidov). - Push LIMIT into aggregation-in-order to enable early termination when GROUP BY key matches ORDER BY key and table sorting key, significantly reducing the number of rows read. #104859 (thevar1able).
- Enable
allow_aggregate_partitions_independentlyby default. When aGROUP BYkey suits the partition key, ClickHouse can aggregate each partition independently and skip the global merging step. Runtime heuristics automatically skip the optimization when the partition layout would make it unfavorable (too few partitions, too many partitions, or significantly skewed partition sizes). #105128 (alexey-milovidov). - Enable
read_in_order_use_virtual_rowby default. When reading in order of the primary key (e.g.ORDER BY primary_key LIMIT n) over a table with many parts, only the parts that can actually contribute to the result are read (plus a read-ahead window of at mostmax_threadsparts that keeps reading parallel), which significantly reduces peak memory consumption. #106215 (alexey-milovidov). - Reduced memory usage and improved the performance of
JOINs. The right-hand side of a hash join now uses a compact 8-byte index-based row reference, so the hash-table entries ofALLjoins are as small as those ofANYjoins for every key type. Benchmarked on largeparallel_hashjoin queries —INNERandANYjoins of 100–300 million row tables onUInt64andStringkeys, with both unique and duplicated keys: the median query became about 12% faster and used about 15% less peak memory, with no query becoming slower.INNERjoins onUInt64keys gained the most (median about 21% faster with 38% less memory). #107189 (harikrishnan94). - Added the dpsub join-order enumeration algorithm, providing optimal join plans with lower optimization overhead than dpsize and support for non-inner joins. #107351 (fkastrati).
- Avoid converting single-level aggregations with small memory usage to two-level aggregations by attaching a memory tracker for the aggregation state instead of using whole query memory. #107490 (m-selmi).
- Avoid building the full query AST during analysis of aggregating queries by computing the aggregation hash-table size-statistics cache key from the query plan instead of the AST. #107643 (novikd).
- Enabling switching for
hashjoin in the first query run toparallel_hashin subsequent runs based on runtime statistics collected in the first run. #108125 (m-selmi). - Use hash table size from runtime join statistics to optimize the size of join runtime filters in subsequent query executions. Can be controlled by the new setting
join_runtime_filter_size_from_hash_table_stats. #108313 (m-selmi). - Speed up and reduce the memory usage of
DISTINCTqueries on partitionedMergeTreetables by keeping each partition’s rows within a single stream, so each stream’s preliminaryDISTINCTworks on a disjoint set of keys, instead of the same set of keys appearing in multiple preliminaryDISTINCTs. This applies when the partition expression is a deterministic function of theDISTINCTcolumns, and is gated by a cost heuristic on partition count and skew (which can be toggled by settingforce_distinct_partitions_independently, disabled by default). The same per-partition disjointness is propagated throughExpression,FilterandARRAY JOINsteps to the finalDISTINCT,LIMIT BYandGROUP BYconsumers so they can skip their merge too. Controlled by the new settingsallow_distinct_partitions_independently(enabled by default) andmax_number_of_partitions_for_independent_distinctto tune the heuristic. #108326 (nihalzp). - Runtime filters are now built on equality keys even when the
ONclause also contains non-equality predicates, reducing rows entering the hash join for mixed equi + non-equi joins.LEFT ANTI JOINis unaffected. #108579 (29antonioac). - Lower the software-prefetch threshold for aggregation and join hash tables from
4 * L2 cache sizetoL2 cache size, so prefetch is enabled once the hash table no longer fits in L2. This recovers a regression onGROUP BYandJOINover medium-sized hash tables (~1-8 MiB) on platforms whose reported L2 is large (e.g. aarch64 with a 2 MiB L2). #108655 (groeneai). - Improve window function performance. Window queries over wide tables that include
SELECT *now use significantly less memory and run faster. Window aggregates, includingquantile*,uniq*, andgroupArray, are faster withOVER ()andOVER (ORDER BY ... RANGE ...). Thecume_distfunction is also faster, and windows usingPARTITION BYorORDER BYnow perform better on columns with many repeated values. #108688 (nihalzp). - Aggregation queries without aggregate functions now use
HashSet-based methods instead ofHashMap(for supported key types). Speedups up to 1.8x times were observed. #108862 (nickitat). - Reduce the overhead coming from lock-contention in
parallel_hashjoin algorithm. #108938 (m-selmi). - Added a new
join_algorithmvalueparallel_full_sorting_merge: for hash-compatible equality joins, it shards a full sorting merge join by the hash of the join keys into independent per-shard merge joins running on all threads. It keeps the low, streaming memory usage of a merge join while parallelizing it (in a benchmark, ~2.4x faster and ~3.3x less memory thanparallel_hash).ASOFjoins fall back to a singlefull_sorting_merge; hash-incompatible key types (floating-point,JSON,Object,Dynamic) skip only the hash-scatter rewrite and can still be sharded at the source by primary-key ranges whenquery_plan_join_shard_by_pk_rangesis enabled. The result is not ordered. #109005 (alexey-milovidov). - Column statistics are now materialized on
INSERTby default when the table’s current active size plus the written block size is at most the newmaterialize_statistics_on_insert_max_table_sizesetting (default 25 GiB); the check is per written block, so a first bulk load into an empty table may still materialize statistics for each written block. This gives the cost-based join optimizer accurate estimates for freshly-loaded dimension tables and avoids pathological join orders (for example, TPC-H Q5 and Q8 no longer time out at scale factor 40), while large established fact tables keep materializing statistics during merges. #109454 (alexey-milovidov). - Optimized aggregate function
uniqCombinedin case of aggregation without key. #109794 (CurtizJ). - Optimized aggregate functions
uniqHLL12anduniqCombined. #109831 (CurtizJ). - Parallelize processing of non joined rows in
parallel_hashjoin algorithm in two more cases: when there is a residual filter and in the case of small join keys (single-level hash map). #110008 (m-selmi). - Push a filter below a window function when the predicate references only the window
PARTITION BYcolumns. Such a predicate written after the window (an outerWHEREaround a windowed subquery, or a conjunct inQUALIFY) now reaches storage and enables primary key / partition pruning, skip indexes and projections. For exampleSELECT ... row_number() OVER (PARTITION BY key ORDER BY ts) AS rn ... QUALIFY rn = 1 AND key = 'x'no longer reads and windows the whole table. #110114 (groeneai). - With
join_use_nulls = 1, a null-rejectingWHEREthat references columns from both sides of an outer join (e.g.WHERE l.k = 42 AND r.k = 42) now converts the join toINNER(orLEFT/RIGHT) and prunes the primary key, the same as withjoin_use_nulls = 0. Previously the conversion was skipped in this case and both tables were read in full. #110121 (groeneai). - Optimized functions
uniq,uniqExact, anduniqHLL12in aggregation without key. #110150 (CurtizJ). - A
JOINwhoseONcondition is constant-false (e.g.ON 1 = 2,ON NULL, or a condition that folds to false such asa.t = 'A' AND a.t = 'B') no longer reads the non-contributing side. A new query-plan optimization replaces the input sides that cannot contribute a row with an empty source. Controlled by the settingquery_plan_short_circuit_constant_false_join(default enabled). #110234 (groeneai). - Parallelize the final merge of single-level aggregation hash tables: the key space is split into disjoint hash partitions that the threads merge independently (setting
enable_parallel_single_level_merge, enabled by default). Speeds upGROUP BYqueries whose per-thread hash tables stay below the two-level threshold but whose merge dominated the runtime. #110395 (nihalzp). - Single-
Stringaggregation keys now use a smaller hash-table representation, improving string-heavyGROUP BYworkloads. The newenable_packed_string_keys_in_aggregationsetting is enabled by default; disabling it, or settingcompatibilitybelow26.8, restores the legacyStringHashTablepath, which can be faster for very low-cardinality keys longer than 11 bytes. #110573 (harikrishnan94). #112089 (harikrishnan94). - Improve full sorting merge join performance up to 1.5-3x times on sparse scenarios. #111200 (vdimir).
- Enable
join_runtime_filter_from_fixed_hash_tablewhen the probe and build join key types differ (for example, a Nullable probe key with a non-Nullable build key). #111244 (m-selmi). - Improve performance of
RIGHTandFULLjoins with theparallel_hashalgorithm when the right side containsNULLjoin keys or rows filtered by theONexpression by avoiding null maps rebuilding. #111258 (m-selmi). - New adaptive algorithm for parallel
GROUP BY(controlled via settingenable_adaptive_aggregator, enabled by default): each thread aggregates into its own hash table until it holdsadaptive_aggregator_freeze_thresholdkeys and then freezes it, so frequent keys keep updating the small cache-resident tables with no coordination, while rare keys are routed by their hash into per-bucket backlogs and aggregated exactly once, inside the bucket-parallel merge. #111459 (nihalzp). - Sped up row-wise hashing of JSON/
Objectcolumn values stored in shared data (used e.g. byuniq/uniqExactover a JSON column and by hashedGROUP BY/DISTINCTkeys) by avoiding a per-valueColumnDynamicreconstruction. Hash values are unchanged. #111501 (valerypetrov). - Improve performance of RIGHT and FULL hash JOIN with multiple
ORdisjuncts in the ON section, and of RIGHT/FULL JOIN with residual (inequality) conditions. #111590 (m-selmi). - Reading in order through a JOIN now also works when a hash join has an automatic spill-to-disk threshold configured (the default since 26.5). When this optimization applies, the join stays in memory so it preserves the left-side order; set
query_plan_read_in_order_through_spilling_join = 0to restore the previous behavior, where such joins are not used for read-in-order and remain free to spill. As part of this, anORDER BY ... LIMITover aLEFT JOINread in order no longer reports the whole table as the number of rows it intends to read, so it no longer tripsmax_rows_to_readwithread_overflow_mode = 'throw'for a read that stops early. #111973 (alexey-milovidov). - Re-enables the consecutive keys optimization for
TTL ... GROUP BYaggregation during merges. The aggregator was constructed withmin_chunk_bytes_for_parallel_parsing(a byte threshold, default 10 MiB) in the slot that expectsmin_hit_rate_to_use_consecutive_keys_optimization(a hit rate in[0, 1], default 0.5), so the optimization was permanently disabled on that path. Results are not affected. #112314 (groeneai). - Lazy column replication for
ARRAY JOINis no longer lost when a query plan is serialized. Withserialize_query_plan = 1, the node executing a remote plan fragment used to ignoreenable_lazy_columns_replicationand always materialize every replicated copy, even though the setting defaults to enabled. #112330 (groeneai). - A
WHEREequality is now merged into theJOINcondition when its operands have different types but a common supertype, such asInt32andNullable(Int32). This allows a CROSS join to be converted to an INNER Join. #112630 (m-selmi). - Fixed quadratic reallocation when replicating an
Arraywhose elements holdJSONvalues in shared data, for example a constantArray(JSON)passed to an aggregate function. Such queries copied a number of bytes that grew quadratically with the row count. #112897 (groeneai). - Restores part and granule pruning when the bounds of a range predicate on a MergeTree primary or partition key are supplied through a
JOIN ONclause against a single-row constant side, for exampleSELECT count() FROM t JOIN bounds ON t.d >= bounds.lo AND t.d <= bounds.hi. Such a query read every part since 25.2, and thequery_plan_use_logical_join_stepsetting that used to restore pruning became obsolete in 26.5, leaving no way to recover it. #112957 (groeneai). - Fixed cubic complexity of query planning for a JOIN with a
Mergetable over many tables with different structures. Such queries could previously spend minutes in query planning without responding tomax_execution_timeorKILL QUERY. #113140 (alexey-milovidov). - Fixed a performance regression where an aggregation without
GROUP BYkeys serialized its whole input plan subtree, including the full contents of constant-folded literals, on every execution to compute a hash-table-size cache key it can never use. A query such asSELECT quantileMerge(arrayJoin(arrayMap(x -> state, range(5000000))))spent about 63 ms per execution in query-plan optimization and now spends 0.15 ms. #113333 (groeneai). - Evaluate the PromQL aggregation operators
topk,bottomkandlimitkwith a streaming plan whose selection state isO(time_steps * k)instead of collecting all series into a single row withO(time_steps * series^2)intermediate memory. Queries over high-cardinality metrics that previously failed withMEMORY_LIMIT_EXCEEDEDnow run in bounded memory, and the evaluation is parallelized instead of single-threaded. #113656 (nikitamikhaylov). - Replaced the per-bucket hash map inside the
timeSeries*ToGridaggregate functions with a flat sorted array of samples: sample ingestion becomes an O(1) append for in-order inputs (the overwhelmingly common case) and the per-bucket copy-and-sort at finalization is gone. #113681 (nikitamikhaylov). - Evaluate PromQL subexpressions shared by multiple plan steps (the
topk/bottomk/limitkoperand, the left side ofor) once instead of twice: the SQL generated from PromQL now materializes shared subqueries. This removes a duplicated scan of the samples table;topkoverrategets ~2x faster cold, and kubernetes-mixin rule queries improve by 10–29%. #113772 (nikitamikhaylov). - The query condition cache is now enabled by default for queries that use the
ORDER BY <column> LIMIT n(TopK) optimization. Setuse_query_condition_cache_for_top_k = 0to opt out. #111492 #114539 (alexey-milovidov). - Parallelize the single-level to two-level conversion of per-thread aggregation states before merging. This fixes a 2-3x regression of short
GROUP BYqueries with heavy aggregate states (e.g.COUNT(DISTINCT ...), ClickBench Q10/Q11) on machines with very many cores, introduced in 26.7 development builds when the aggregation memory accounting was made accurate. #114691 (alexey-milovidov). - Speed up window functions on partitioned
MergeTreetables by reading each partition through its own stream and evaluating the windows per partition, skipping the hash scatter that ordinarily reshuffles every row across threads before the window sort. This applies when the table partition expression is a deterministic function of the windowPARTITION BYcolumns and is gated by the same cost heuristic as per-partitionGROUP BY/DISTINCT(enough partitions relative tomax_threads, no dominant partition). Controlled by the new settingsallow_window_partitions_independently(enabled by default),force_window_partitions_independently, andmax_number_of_partitions_for_independent_window. #114783 (nihalzp). - Speed up the
timeSeries*ToGridaggregate functions: per-sample bucket math now avoids Int128 division, and consecutive samples in the same bucket skip the hash-map lookup. #114889 (nikitamikhaylov). - Process samples of the
timeSeries*ToGridaggregate functions in runs of consecutive samples falling into one grid bucket, speeding up the sample-ingestion stage oftimeSeriesRateToGridand related functions by 1.2-2x on sorted time series data. #115041 (nikitamikhaylov). - An aggregation without
GROUP BYkeys no longer fans its single-row output out tomax_threadsstreams, so queries whose plan continues after such an aggregation get a much smaller pipeline (fewer processors to create, schedule and profile) without losing any parallelism. #115170 (nikitamikhaylov). - Faster evaluation of PromQL instant queries: the
timeSeries*ToGridaggregate functions use the vectorized sample-classification path for single-point grids, andtimeSeriesIdToGroupfills its result column directly. #115267 (nikitamikhaylov). - A
JOINwhoseONsection has severalOR-ed equalities no longer materializes the right-hand key columns that the query does not select. #115465 (nickitat). - Speeds up building the hash table of an
ANY,SEMIorANTIjoin when the right-hand side repeats the same join keys many times: the join no longer re-reads thejoin_any_take_last_rowsetting from memory for every right row. #115820 (nickitat). - Speeds up
GROUP BYwithout aggregate functions overString,FixedStringandLowCardinalitykeys: the query no longer rewrites an unused placeholder into the hash table for every row, only for the rows that add a new key. #115834 (nickitat). - Reduced memory usage and query-plan optimization time for keyed aggregations over large constant-folded literals. The aggregation hash-table-statistics cache key is now hashed as it is serialized instead of being accumulated in memory first, so a plan carrying a large literal no longer holds a copy of it during optimization. For an 80 MB literal, peak memory drops by about 250 MB and optimization time by about 2.5x. The computed key is unchanged. #115847 (groeneai).
- Optimize
GROUP BY ... ORDER BY ... LIMITandGROUP BY ... LIMITqueries by maintaining a bounded heap during aggregation to prune groups that cannot appear in the result, significantly reducing memory usage and execution time for high-cardinality grouping. Controlled by theenable_group_by_top_k_optimizationsetting. Based on the initial implementation by Dmitriy Terenichev #96630 (thevar1able).
Query optimization and execution
- Reading in-order with Parallel Replicas now uses the same logic of splitting the table into
max_threadsparts as the local reading for better parallelism. #101434 (nickitat). - Adds GeoParquet spatial pruning at row group and page levels to the Parquet reader. Row group pruning skips entire row groups whose bounding box doesn’t overlap the query geometry. Page-level pruning uses the
covering.bboxcolumn index to skip irrelevant pages within row groups. Also generalizes spatial predicate pushdown throughIFunctionBase::isSpatialPredicate()and adds aGeoFilterthat evaluates spatial predicates during Parquet row reading. #104435 (bacek). - Added the
use_constant_folding_in_index_analysissetting (disabled by default). When enabled,MergeTreeprimary-key,MinMax, and skip-index analysis fold partition-level constants into the filter predicate separately for each part, improving pruning for filters whose branches depend on partition values, e.g.(a = 1 AND b >= 1) OR (a = 2 AND b > 10)withPARTITION BY a. #104582 (Michicosun). - Apply exact row-positioning for vector search queries with
vector_search_with_rescoringbefore exact distance evaluation in the query pipeline. #105591 (skuznetsov). - Added a query plan optimization that pushes volume-reducing functions (
length,lengthUTF8,empty,notEmpty) below theSortingandFiltersteps, replacing the wideString/FixedStringargument with the fixed-size result, so it is neither buffered by a sort nor copied by a filter. Controlled by the new settingquery_plan_push_down_volume_reducing_functions, enabled by default. #106199 (fastio). - Speed up filesystem cache loading on startup by encoding each cache file’s size in its name (
<offset>_<size>), avoiding astatper file. Loading of cache files written by older versions remains supported. #107415 (alexey-milovidov). - Improved performance of text-search queries combined with a primary key filter. #108114 (CurtizJ).
- Improve performance of the
pointInPolygonfunction with a constant polygon. The preprocessed-polygon cache is now keyed on the raw constant arguments, so a constant polygon is parsed only once (on a cache miss) instead of being re-parsed on every input block. #108184 (groeneai). - Avoid maintaining
system.predicate_statistics_logselectivity counters on the MergeTree read path when the feature is disabled (predicate_statistics_sample_rate = 0, the default). This removes a per-granule atomic update and anO(rows)filter popcount from every read, recovering a performance regression that was most visible on aarch64. #108190 (groeneai). - Add the
merge_use_batch_sorting_queueMergeTreesetting to optionally use the batch sorting queue for ordinaryMergeTreemerges, reducing CPU and wall-clock time for merge workloads where sorted merging is a significant cost. #108468 (rorylshanks). - Speed up query planning under the analyzer for expressions that reference the same
WITHalias or repeat the same subexpression many times (for example deeply nestedif/multiIfchains), by not rebuilding shared subexpressions when constructing the actions DAG. #108523 (novikd). - Sped up query analysis for queries with many or very large
AND-chains of comparisons: theoptimize_and_compare_chainoptimization is now bounded by a work budget (new settingoptimize_and_compare_chain_max_hash_work) instead of hashing a large fraction of the query tree, and lambda resolution no longer recomputes lambda-body hashes for its recursion guard. #108757 (alexey-milovidov). - Filter-only postprocessors (
if(..., '', token)) now build the granule from raw tokens and drop the empties at build time, avoiding the general path’s per-occurrence materialization andActionsDAGrun. #109049 (ahmadov). - Remove a single-threaded bottleneck in
ShuffleSendStepof the distributed query plan: each upstream stream now scatters its rows by destination bucket independently instead of funneling the whole fragment through oneScatterByPartitionTransform. #109206 (davenger). - Reduce CPU overhead of expression evaluation for queries with a large number of columns (e.g. vector search over a
QBitcolumn with a small stride, where each vector expands into hundreds of bit-plane sub-columns that are all fed into a single*DistanceTransposedcall). #109380 (alexey-milovidov). - Push a filter below
LIMIT BYwhen the predicate references only theLIMIT BYkey columns and the clause keeps at least one row per non-empty group (LIMIT n BYwithn >= 1andOFFSET 0). This lets such predicates (written as an outerWHEREaround aLIMIT BYsubquery) reach storage for primary key pruning, skip indexes and projections, instead of reading the whole table and applying the per-group top-N first, without changing exception semantics forOFFSET,LIMIT 0 BY, or negativeLIMIT BYforms. #110116 (groeneai). - Avoid a heap allocation per hyperrectangle check in primary key index analysis, speeding up mark filtering by 5-26% depending on the query shape. #110153 (raimannma).
- Parquet filter push-down (row-group pruning and column indexes) now supports conversions between
DateTime64variants,Decimalvariants, andFloat32/Float64. #110457 (al13n321). - Optimized building of text and bloom filter based skip indexes with sparseGrams tokenizer #110541 (UnamedRus).
- Combine I/O cost with selectivity in PREWHERE condition ordering. It fixes performance regression in PREWHERE execution in some cases introduced after https://github.com/ClickHouse/ClickHouse/pull/101275. Part of https://github.com/ClickHouse/ClickHouse/issues/110462 #110695 (Avogar).
- Reduce hashing of tokens defined in the postprocessor
IN/NOT INfilter. #111690 (ahmadov). - Functions mapContainsKeyLike, mapContainsValueLike, mapExtractKeyLike and mapExtractValueLike with a non-constant pattern use less memory and time when enable_lazy_columns_replication is enabled. The pattern is no longer eagerly copied per map entry at capture time; the copy is deferred to the execution of the internal lambda and is freed sooner, so one fewer expanded copy of the pattern exists at peak. #111749 (diegomestre2).
- Enabled by default (and promoted to beta) two on-disk features: 1)
packed_skip_index_max_bytes = '1M': skip-index substreams up to 1 MiB are now written into a singleskp_idx.packedarchive per part, reducing object count and write requests on object storage. Compatibility: only newly written parts are affected and they stay fully readable on older versions; versions before 26.6 simply do not use the packed indices for pruning (they are treated as not materialized). Setpacked_skip_index_max_bytes = 0to keep the standalone per-file layout. 2)compute_exact_num_defaults_for_sparse_columnsplusoptimize_trivial_count_with_sparsity_filter: exact per-columnnum_defaultscounters are persisted and used to answerSELECT count() FROM t WHERE <pred>without a data scan when the predicate splits rows into defaults vs non-defaults. Compatibility: theexact_num_defaultsflag inserialization.jsonis ignored by older versions, so parts remain fully readable after a downgrade; the count rewrite only applies to parts that carry exact counters, so results stay correct on tables with mixed parts. #111816 (Algunenano). - The transitive-conditions optimization now uses its additional filter only for index analysis when it cannot apply the whole filter. See the
optimize_and_compare_chainsetting. #112076 (yariks5s). - The Prometheus query API (
/api/v1/query,/api/v1/query_range) of the experimental TimeSeries engine now executes queries in parallel instead of on a single thread. #112108 (nikitamikhaylov). randomHadamardTransformof a constant vector is now evaluated once instead of once per row. This speeds up vector search queries that rotate the query vector withrandomHadamardTransformbefore comparing it against aQBitcolumn. #112921 (alexey-milovidov).- Restores set sharing between the part tasks of one mutation filtering on
IN (subquery)over a primary key column.enable_sharing_sets_for_mutationsis on by default, but the set built for primary key analysis stopped participating in the shared cache, so a table with N parts materialized the same set N times. #112941 (groeneai). - In distributed query plans (
make_distributed_plan),ORDER BY ... LIMIT know uses lazy materialization on each replica: wide columns are read only for that replica’s local top-k rows instead of for every candidate row. #113111 (shankar-iyer). - Speed up query planning when
uniq_v2column statistics are used. The estimated number of distinct values is now cached instead of being recomputed on every request, which query planning issues many times per query. #113357 (groeneai). - Filter pushdown now works for
Tuplesubcolumns in Parquet and ORC files. A predicate such asWHERE tup.1 = 555555overfile(),s3()orurl()now prunes row groups and row index strides using the tuple element’s own statistics instead of reading the whole file. #113383 (groeneai). - Improve PromQL query performance over the TimeSeries engine: the internal selector SQL now reads the samples-table primary-key columns directly (without no-op casts) and checks the timestamp range before the series-id set, which makes cold selector-heavy queries 30-44% faster. #113768 (nikitamikhaylov).
- PromQL selectors that match all series of one metric now filter the samples table of a
TimeSeriestable with a continuous primary-key range onidduring index analysis instead of a largeid IN <set>condition, when the id layout is a two-component tuple with the canonical id generator. Removes the dominant single-threaded index-analysis cost of selector-heavy PromQL queries: up to −45% cold latency on dashboard and rule query shapes, −11% cold geomean over the full suite on a 62-billion-sample table. #114131 (nikitamikhaylov). - The Prometheus HTTP API endpoints
/api/v1/queryand/api/v1/query_rangenow evaluate a PromQL subquery shared by several plan steps once, as theprometheusQueryandprometheusQueryRangetable functions already did. Previously a request made withenable_analyzer = 0, or by a user whose profile sets it, rescanned the shared subquery once per referencing step. #114261 (groeneai). - Fixed a regression in 26.7: for a
MergeTreetable ordered bytoUnixTimestamp(or another integer conversion) of aDateTime64column, a plain range filter on that column no longer used the primary key and read all granules of the matched parts. Additionally, a filter liketoInt64(ts) >= cover a table ordered by the rawDateTime64column now uses the primary key. #114413 (alexey-milovidov). - Speed up execution of
INover clustered (e.g. sorted by primary key) columns by reusing the previous row’s result for equal consecutive rows. #114853 (nikitamikhaylov). - Fixed quadratic time when the functions
h3kRing,h3ToChildren,h3PolygonToCellsandh3PolygonToCellsWithContainmentbuild their result over many rows. A query over 196 000 rows spent 838 seconds copying memory in a CI stress run; it now takes about 5 seconds. #115773 (groeneai). - Optimize inverse dictionary lookups. A constant equality predicate such as
WHERE dictGet(dict, attr, key_expr) = valueis now constant-folded directly into a key filter (key_expr = const,key_expr IN [...], orWHERE 0) instead of being rewritten to anIN (SELECT ... FROM dictionary(...))subquery, and the constant path ofdictGetKeysnow executes in parallel. #91164 (nihalzp). - Add a new setting
merge_tree_generic_exclusion_search_max_stepsthat limits the number of steps the generic exclusion search algorithm spends analyzing the primary key index of each data part. The budget is spent on the largest remaining key ranges first, so even a small budget prunes the bulk of the data; ranges that were not fully analyzed are read whole, so query results stay correct but more granules may be read. The limit is applied loosely: it may be exceeded by at mostmerge_tree_coarse_index_granularitysteps, plus one step for each range the part is already divided into (for example, by the query condition cache). The default value 0 means unlimited steps. #92779 (EmeraldShift). - Queries with
ORDER BYandLIMITonVIEWs over distributed tables now use the merge-sorted-streams optimization. The outerORDER BY/LIMITis pushed into a simple view’s inner query so each shard sorts its data locally and the coordinator merges pre-sorted streams, instead of performing a full sort on the coordinator. #94102 (matanper).
Storage, I/O, and data lake performance
- Use partition minmax index bounds to prune more granules during primary key analysis for
MergeTreetables, when a primary key column is also an input column of the partition key. For example, in aMergeTreetable withORDER BY (id, event_time)andPARTITION BY toYYYYMM(event_time), ClickHouse will use the partition minmax index onevent_timeduring primary key index analysis to make more informed granule-pruning decisions. Controlled by the new settinguse_partition_minmax_for_primary_key_pruning(enabled by default). #103480 (UnamedRus). - Speed up operators that scan a sorted stream for runs of equal key values —
DISTINCTin order,LIMIT BYin order, negativeLIMIT BYin order,full_sorting_mergeandpartial_mergejoins. #106502 (nihalzp). - The Parquet v3 reader can now skip whole row groups using the dictionary page (in addition to min/max statistics and bloom filters) for equality and
INconditions, when a column chunk is fully dictionary-encoded. Controlled by the new settinginput_format_parquet_dictionary_filter_push_down. #106952 (alexey-milovidov). - Use libdeflate for gzip/zlib/deflate compression and decompression, making it faster (compression ~1.15× with a better ratio, decompression ~1.4–1.5×) for
.gz/HTTP/url/s3data and the Parquet GZIP codec. #108074 (alexey-milovidov). - Speed up the default-whitespace
trimLeft/trimRight/trimBothfunctions (and their aliasesltrim/rtrim/trim): consecutive rows that need no trimming are copied in a single batch, and the per-row space scan runs only for rows that actually have a leading or trailing space. #108177 (groeneai). - Speed up reading of Iceberg tables by prefetching the next manifest file while the current manifest is being parsed, overlapping I/O with CPU work. #108543 (asya-ch).
- Vector search queries using rescoring now restrict exact distance calculation to vector-index candidate rows instead of brute-force rescoring neighbouring rows from the same
MergeTreegranule. #108846 (skuznetsov). - Added the
merge_tree_min_bytes_per_read_streamsetting to reduce excessive read streams and pipeline overhead for ordinary local unordered narrow-columnMergeTreescans on high core-count servers. #109035 (jiebinn). - Speed up serialization and merges of
JSONcolumns whose shared data contains many sparse paths. When flattening shared data into per-path columns,flattenAndBucketSharedDataPathsno longer scans every accumulated path column on every row and no longer inserts a default one cell at a time; the per-row scan over all paths is removed and gaps are backfilled in bulk. #109341 (groeneai). - The native protocol now sends
Stringcolumns as a separate stream of cumulative byte offsets (the same layoutArrayuses for its offsets) followed by the concatenated data, instead of a per-value varint length prefix, once both peers are on protocol revision 54489 or newer. A client can then readStringcolumns with exact buffer preallocation and a single bulk copy, about 4 times faster than the per-value layout. Old clients and servers are unaffected: the layout is negotiated through the protocol revision. The same layout is available in theNativeandBuffersformats through the new settingsoutput_format_native_write_string_with_size_stream/input_format_native_read_string_with_size_stream(off by default, so the formats stay portable). #110320 (vahid-sohrabloo). - Queries with
FINALthat read in the order of the primary key (ORDER BYa prefix of the sorting key) with a smallLIMITno longer usesplit_intersecting_parts_ranges_into_layers_final. #110431 (KochetovNicolai). - Lazy materialization (late materialization) for reading Parquet files from object storage, including Iceberg tables: for
ORDER BY ... LIMIT nqueries, the columns that are not needed for sorting and filtering are read only for thenrows that survive theLIMIT. On a 200 MB Parquet file on S3,SELECT k, s ... ORDER BY k DESC LIMIT 10reads 3.3 MB instead of 171 MB (51x less IO, 8x faster). Controlled by the new settingquery_plan_optimize_lazy_materialization_for_object_storage(enabled by default, also requiresquery_plan_optimize_lazy_materialization). #110970 (alexey-milovidov). - Queries on Iceberg tables with many delete files now start faster. ClickHouse reads and decodes the delete manifest files concurrently instead of one at a time, so their storage reads overlap. The new setting
iceberg_delete_manifest_decode_concurrency(default4) controls how many are decoded at the same time. #112679 (asya-ch). - Add opt-in Parquet serialization of UInt128/UInt256/Int128/Int256 as DECIMAL to enable row group and page pruning. #113347 (bobrik).
- Fixed multiplied read requests on remote disks when a step of the readers chain reads over fragmented mark ranges, e.g., ranges filtered by primary or skip indexes. #113584 (CurtizJ).
- Skip the redundant sortedness scan when writing
MergeTreeparts whose sorting keys are all constant. #113899 (perfloop-agent). - Lazy materialization for
ORDER BY ... LIMIT nqueries now also applies to local Parquet files read with thefiletable function and theFiletable engine: the columns that are not needed for sorting and filtering are read only for thenrows that survive theLIMIT. The second read of a surviving file fails close with the newFILE_CHANGED_DURING_READerror if the file was modified between the two passes. Controlled by the new settingquery_plan_optimize_lazy_materialization_for_file(enabled by default). Also fixes lazy materialization for object storage failing withNot found column or subcolumn ... in blockwhen a requested subcolumn (e.g. of aJSONcolumn) is deferred. #114262 (alexey-milovidov). - Speed up set building for
IN (subquery)on partitionedMergeTreetables by keeping each partition’s rows within a single stream and deduplicating each stream independently, so the single set-filling transform — previously hashing every row serially — only sees unique rows. This applies when the partition expression is a deterministic function of the subquery’s output columns. The optimization is not applied when the largest partition holds more than twice the rows of the average partition; the new settingforce_creating_set_partitions_independently(disabled by default) bypasses this check. Controlled by the new settingallow_creating_set_partitions_independently(enabled by default). #114645 (nihalzp). - Mark rotated non-replicated
MergeTreesystem log tables without TTL astable_readonlyto avoid unnecessary background operations. Thetable_readonlyMergeTreesetting now also suppresses all background work on a plainMergeTreetable — regular, TTL (DELETE/MOVE/recompression) and recompression merges, background mutations, and background part moves — and rejects the mutating partition commands (ATTACH/MOVE/DROP/DROP DETACHED/FETCH/REPLACE PARTITIONandMOVE PARTITION ... TO TABLEinto the table), in addition to the inserts, mutations, andOPTIMIZEit already rejected. As a result, atable_readonlytable with a TTL no longer reclaims its expired data while the setting is enabled. #95079 (matt-metivier).
Memory and resource efficiency
- Reduce memory usage of
UNLOCK SNAPSHOTby not loading per-data-part metadata that the unlock path does not use, avoiding out-of-memory for snapshots with a very large number of parts. #111849 (jkartseva). - Reduce memory allocations when reading backup metadata: each file entry is now moved into the in-memory maps instead of being copied. #111718 (jkartseva).
Function, type, and format performance
- Formatting
Decimalvalues as text is much faster — up to 190 times for aDecimal256with a large scale, and about 1.6 times for aDecimal64. On processors with AVX-512 IFMA, converting 64-bit integers to text is about 1.4 times faster. Also fixed the rounding oftoDecimalString, which dropped a carry out of the fractional part:toDecimalString(toDecimal64('9.995', 3), 2)returned9.00and now returns10.00. #112457 (thevar1able). - Improved the performance of the
partial_mergejoin algorithm onFixedStringkeys by comparing runs of values at once instead of one comparison call per value. #105737 (4ertus2). - Single-column non-nullable string LowCardinality keys are natively supported now in
HashJoin. #107264 (nickitat). - Improve the performance of
replaceAllandreplaceRegexpAllwhen the pattern is a single character and the replacement is a single character (for examplereplaceRegexpAll(s, ' ', '_')). Such replacements no longer change the string layout, so the column is now copied once and matching bytes are rewritten in place instead of running a per-match search loop. #108178 (groeneai). - Speed up parsing of the canonical
YYYY-MM-DD hh:mm:ssdate-time representation in best-effort mode (date_time_input_format = 'best_effort',cast_string_to_date_time_mode = 'best_effort'), which is the default. This recovers a parsing performance regression introduced when those defaults were switched frombasictobest_effort. #108187 (groeneai). - Parse deeply nested array and tuple literals in linear instead of quadratic time. #108892 (alexey-milovidov).
- Speed up function
toStartOfInterval(aliasestime_bucket,date_bin) by up to 5x forSECOND,MINUTEandHOURintervals. #109729 (raimannma). - Functions
arraySortandarrayReverseSortare several times faster over numeric arrays (includingDecimalandDateTime64) when called without a lambda. #109832 (raimannma). - Speed up
addDays,addWeeks,subtractDays, andsubtractWeeksonDateTimeandDateTime64values in fixed-offset time zones (such as UTC) by taking an arithmetic fast path. #109836 (raimannma). - Vectorize decompression of the
Deltacodec. Decoding is now 1.5–5 times faster for 8/16/32-bit data types, making scans ofDelta-compressed columns up to 20% faster. #110189 (raimannma). - Speed up string search functions (
like,position,match,countSubstrings,hasToken, etc.) overEnumcolumns with a constant needle by searching only the distinct enum names and mapping the results back per row, instead of searching every row. #110325 (alexey-milovidov). - Improved the performance of substring search functions (
position,countSubstrings,multiSearch*,replaceAll/replaceOne, and their case-insensitive and UTF-8 variants) with a non-constant needle by up to 1.5x, by using the SIMD-backed string searchers on the per-row path instead of a naive fallback. #110580 (Algunenano). - Improved performance of squashing and merging
DynamicandObjectcolumns with many dynamic variants or JSON paths. #110583 (cv4g). - Faster
estimateCompressionRatiowith theNONEcodec #111107 (rienath). - Speed up sorting and other comparison-based operations over
JSONcolumns whose paths are stored in shared data. Previously each value comparison materialized a temporaryDynamiccolumn and ran a full binary deserialization for both sides, makingORDER BYover such columns pathologically slow. #111894 (groeneai). - Division of 256-bit integers (
Int256,UInt256, andDecimal256) now computes the quotient one 64-bit limb at a time, using a loop of 128 / 64 divisions when the divisor fits in 64 bits and Knuth’s Algorithm D otherwise, instead of one bit at a time with shift-and-subtract long division. This affectsintDiv,modulo,Decimal256arithmetic and conversion of these types to text. #112477 (thevar1able). - Reading a
Nestedcolumn from a binary-encoded type (Native,RowBinarywithinput_format_binary_decode_types_in_binary_format) no longer renders the type back into a type name and parses it again. Also, a malformed nestedTupletype name is now reported as a syntax error instead of taking time exponential in its nesting depth and failing withTOO_SLOW_PARSING. #112560 (alexey-milovidov). - Speed up PromQL queries over TimeSeries tables:
timeSeriesIdToGroupnow reuses the previous row’s group when consecutive rows carry the same series id, skipping the per-row id serialization and hash lookup. #113580 (nikitamikhaylov).
Other performance improvements
- Implemented a native reader and writer for the
ArrowandArrowStreamformats that does not use the Apache Arrow library, avoiding extra data copies and conversions. It is now the default (settingsinput_format_arrow_use_native_readerandoutput_format_arrow_use_native_writer) and is faster for both reading and writing. #106522 (alexey-milovidov). - Optimize
nullIf(key, sentinel) = constpredicates to build key ranges and enable primary key, partition, and skip-index pruning. #107308 (adityaksolves). - Improve the performance of `INTERSECT ALL` and `EXCEPT ALL` (the default mode for `INTERSECT` and `EXCEPT`) by several times, by keying the multiset on the row value instead of hashing each row with SipHash. #107649 (Algunenano).
- Speed up the
match,extract,extractAll,replaceRegexpOneandreplaceRegexpAllfunctions for simple regular expressions by compiling them to native code with LLVM. Controlled by the new settingcompile_regular_expressions(enabled by default); patterns outside the supported subset transparently fall back to the RE2 engine. #108004 (alexey-milovidov). - Sped up ZSTD decompression on AArch64 (ARM) for columns with small match offsets, such as fixed-width integer columns, by vectorizing short-offset overlapping copies with NEON. For example, decompression of a
UInt64column is up to ~2.3x faster on AWS Graviton 4. #108049 (alexey-milovidov). - LZ4 decompression speed was improved for fixed-size, low-cardinality columns. #108175 (nickitat).
- Improved the performance of
levenshteinDistance(andeditDistance) for medium-length strings on ARM by avoiding per-row heap allocations in the blocked Myers code path. #108185 (groeneai). - Speed up parsing of floating-point numbers from text with
precise_float_parsing = 1, making it as fast as or faster than the default parser on almost all inputs. Also bumps the bundledfast_floatlibrary to v8.2.10. #108205 (Algunenano). - Base64 encoding and decoding functions now use
simdutf, improving their performance. #108333 (thevar1able). - Optimize
DISTINCTfor expensive high cardinality keys. #108366 (nihalzp). - Improved math-function performance by enabling compiler optimizations that do not preserve
errno. #108628 (nickitat). - Speed up
estimateCompressionRatioforT64-encoded columns by computing the compressed size analytically instead of compressing. #109054 (rienath). - JOINs can now use primary key index or skip index on the left hand side table to prune granules #109085 (shankar-iyer).
- Improve the performance of H3 geo functions (
h3ToGeoBoundary,h3ToGeo,h3CellAreaM2/h3CellAreaRads2,geoToH3) by computing paired sine/cosine together and eliminating redundant trigonometric calls in the coordinate transforms. Results are unchanged (bit-for-bit identical). #109399 (alexey-milovidov). - Improve performance of comparisons (
<,>,<=,>=) of wide integer types (Int128,UInt128,Int256,UInt256and types based on them, such asDecimal128) by up to 7 times, and of converting signed integers toInt256(up to 7 times on mixed-sign data), by making the comparison and sign-extension code branchless. #109474 (raimannma). - Functions with a single non-const
Nullableargument now share the argument’s null map with the result instead of allocating and merging a new one. #110151 (raimannma). - Improve performance of functions
arrayMinandarrayMaxover numeric arrays by ~1.3-1.5x by using a vectorized reduction instead of a per-element comparison loop. #110163 (raimannma). - Improve performance of
DISTINCTwhen the input stream is sorted by a prefix of the distinct columns, for exampleSELECT DISTINCT a, b FROM table ORDER BY a. Such queries run up to 2.4 times faster. #110170 (nihalzp). - Vectorized batch paths for
groupBitOr/groupBitAnd/groupBitXorand the variance family (varPop,varSamp,stddevPop,stddevSamp,skewPop,skewSamp,kurtPop,kurtSamp,covarPop,covarSamp,corr): up to 4x faster with the-Ifcombinator on unpredictable conditions, and up to 3x faster for the variance family without it. #110461 (raimannma). - Optimized analysis of the text index. #110530 (CurtizJ).
- Speed up parsing of
UUIDvalues from text (e.g.JSONExtractintoLowCardinality(UUID),toUUID,CAST AS UUID) by validating and converting hex digits in a single pass instead of two. #110625 (groeneai). - Sped up ZSTD decompression on x86_64 for columns with small match offsets, such as fixed-width integer columns, by vectorizing short-offset overlapping copies with SSSE3
pshufb(the x86_64 counterpart of the AArch64 NEON optimization from #108049). #110909 (thevar1able). - Speed up
groupUniqArrayfor numeric types by up to 3x by using CRC32 hashing (as inuniqExact) and inlining the per-row insertion. #110917 (raimannma). - Improve efficiency of minmax and basic column statistics for floating point columns. #111221 (cv4g).
- Returns
COUNT()queries directly from the text index cardinality metadata. #111494 (ahmadov). - Speed up
minandmaxon 128-bit and 256-bit types (Int128,UInt128,Int256,UInt256,Decimal128,Decimal256). Up to 1.8x faster for wide integers and up to 3.4x faster for wide decimals. #111965 (raimannma). - Fix a performance regression in
system.query_logingestion. Building theSettingsandasynchronous_read_countersmap columns was about 6 times slower than before, which could makeSYSTEM FLUSH LOGS query_logexceed its 180 second timeout on servers that log many queries with many changed settings. #112210 (groeneai). - Add optional caching for tokens missing from text indexes. #112742 (rorylshanks).
- Asynchronous logging now uses a bounded lock-free queue: threads writing log messages never block on a mutex or wake up the logging threads. #112803 (alexey-milovidov).
- Use the primary key index for
pointInPolygonwhen the point argument is a whole key column of typePoint(or anotherTupleof two numeric elements), e.g.pointInPolygon(coord, [...])for a table ordered bycoord. Previously only thepointInPolygon((x, y), [...])form with two scalar key columns was analyzed. #112956 (alexey-milovidov). text_index_posting_list_apply_modenow defaults tolazy, so text index queries decode posting lists on demand at packed-block granularity with a cursor instead of eagerly materializing them into bitmaps for text indexes with setposting_list_codec. #113521 (CurtizJ).- Speed up vectorized TimeSeries tag transformations by replacing hash-based remapping with direct indexing when tag-group IDs occupy a compact range. #114244 (fallintoplace).
- Improved performance of merges of text indexes. #114525 (CurtizJ).
- Use compact presence masks for PromQL set operators. #115224 (fallintoplace).
- Attaching the system tables no longer copies each table’s metadata, including its full column description, twice just to set the table comment. #115490 (thevar1able).
- Speed up duplicate-series checks for all-zero PromQL condition vectors. #115822 (fallintoplace).
- Speed up
toUTCTimestamp/to_utc_timestampandfromUTCTimestamp/from_utc_timestamponDateTime64arguments, restoring the throughput they had after earlier overflow corrections. Results are unchanged. #115838 (groeneai). - Reduced the fixed setup cost of random data generation (
rand,rand64,randomString,randomFixedString,generateUUIDv4,generateUUIDv7) on x86-64. Noticeable when blocks are small; generated values are unchanged. #116158 (Algunenano). - You can now enable early short-circuit folding for builtin
andandorduring analysis with theenable_function_early_short_circuitsetting. This can skip executing dead single-rowcount()scalar subqueries while preserving normal type inference and semantic validation. #83505 (fhw12345). - Use a single buffer IO call for trivially serializable vectors #89842 (Ergus).
- Enable
optimize_or_like_chainby default. #94517 (alexey-milovidov). - Improves the performance of fetching a single user record from system.users when the server has many users. #96699 (alistairjevans).
Improvements
Query and SQL improvements
- Materialized CTEs referenced from multiple branches of a
UNIONquery are now materialized once and shared across all branches. Previously each branch received its own copy of the CTE, which was inlined and evaluated separately. #102107 (novikd). - Skip JOIN runtime filter when the probe side is estimated to be small. New setting
join_runtime_filter_min_probe_rows(default1000) controls the threshold #104860 (vdimir). - Make
EXPLAIN [PLAN] actions=1, compact=1, pretty=1the default. #105036 (Fgrtue). - Reduced cancellation latency for the positive forms of LIMIT BY (LimitByTransform, LimitBySortedStreamTransform): KILL QUERY and Ctrl+C now interrupt processing within a single chunk instead of waiting for the entire step to complete. #106070 (rvasin).
- JOIN queries are supported with automatic parallel replicas. #106073 (nickitat).
- Adds a new asynchronous metric UntrackedMemory (visible in
system.asynchronous_metrics) that reports memory already allocated by threads but not yet accounted in the global memory tracking counter. Each thread accumulates small allocations locally and reports them in bulk. This helps explain discrepancies betweenMemoryTrackingand the process’s actual memory usage.MEMORY_LIMIT_EXCEEDEDerror messages now also include the amount of untracked memory, making it easier to understand why a query or the server hit its memory limit. #106386 (mstetsyuk). - When an
INSERTfails because a materialized view’sTOtarget table rejects the write while the insert pipeline is being built (for example the target is aView, or it does not support transactions), the error message now names the materialized view and its target table instead of only reporting the bare storage error. #107234 (groeneai). ALTER TABLE ... MODIFY COLUMN <col> Tuple(...)on a namedTupleis now metadata-only when only adding subfields, matching the speed of top-levelADD COLUMN. Gated behindSET allow_metadata_only_named_tuple_alter = 1. #107305 (amosbird).- Added the create-time
materialized_postgresql_use_extended_date_and_time_typessetting for theMaterializedPostgreSQLdatabase engine. By default (enabled), PostgreSQLdate/timestampcolumns are inferred asDate32/DateTime64; setting it to0atCREATE DATABASEtime infers the narrowerDate/DateTimetypes. The setting is not applicable to theMaterializedPostgreSQLtable engine. #107428 (alexey-milovidov). EXPLAIN PLANnow marks a join’s estimated result rows as approximate (~~...) when the estimate came from a hint or randomized fallback rather than table statistics, helping identify plans that lack reliable statistics for join reordering. #107666 (vdimir).INSERTinto aMergeTreetable now honors query cancellation andmax_execution_timewhile writing many parts, instead of potentially running long after being killed. #107929 (al13n321).arrayFoldnow respects query cancellation andmax_execution_time. Previously a fold over a very long array (for examplearrayFold(acc, x -> arrayPushFront(acc, x), range(number), emptyArrayUInt64())) ran the whole fold inside a single function call and could not be interrupted, soKILL QUERYand time limits were ignored until the fold finished. #108192 (groeneai).- Add per-phase query pre-execution
ProfileEvents:QueryParseMicroseconds,QueryAnalysisMicroseconds,QueryPlanBuildMicrosecondsandQueryPipelineBuildMicroseconds. They expose where time is spent before query execution (parsing, analysis, query plan building, pipeline building) and are available insystem.query_logandsystem.events. #108282 (jrdi). ALTER TABLEoperations that would produce table metadata exceedingmax_query_sizeare now rejected upfront, preventing tables from becoming unloadable by components such as DDL distribution and replica recovery. #108283 (lockie).- Added
ConstantJoinfor cartesian joins and analyzer-planned constant-predicate joins, so these queries no longer fail because of an incompatiblejoin_algorithmsetting when the analyzer is used. #108289 (antaljanosbenjamin). - Added a
sourcecolumn tosystem.documentationcontaining the path of the source file where each entity’s documentation is defined. The table now also documents compression codecs, profile events, current metrics, asynchronous metrics, and the system tables themselves (with their columns), and the documentation of settings now includes their type and default value. #108346 (alexey-milovidov). - If you provide WHERE with
tableanddatabasefilters thensystem.iceberg_historywill list only matched databases. It can save some time if you have many non-related remote databases on the server. #108492 (Diskein). - Query parameters (
{name:Type}substitutions) can now be used as setting values, both in theSETTINGSclause of a query (such asSELECTorINSERT) and in standaloneSETqueries, e.g.SELECT ... SETTINGS max_threads = {threads:UInt64}andSET max_threads = {threads:UInt64}. #108760 (alexey-milovidov). - Allows to calculate the combined skip-index benefit in EXPLAIN WHATIF. It shows the data ratio, which is the intersection of the performance of all existing suitable hypothetical indices. #108934 (yariks5s).
- A query with a single CTE is now formatted with the same newline and indentation as a query with multiple CTEs. Previously
WITH a AS (...)kept the CTE on the same line asWITH, while two or more CTEs putWITHon its own line with each CTE indented. #109092 (groeneai). - Support the Query Condition Cache for local Parquet files read via the
Filetable engine (previously supported only for object storage and data lakes). #109247 (alexey-milovidov). - Key the Query Condition Cache for remote (object storage) Parquet files by ETag in addition to the path, so that overwriting an object in place no longer serves stale cached results. #109310 (alexey-milovidov).
- An unqualified reference to an
INNER JOINkey that is equated in theONcondition (e.g.SELECT id FROM a INNER JOIN b ON a.id = b.id) is no longer reported as an ambiguous identifier, since both sides are guaranteed to carry the same value. #109366 (alexey-milovidov). - The transposed distance functions over
QBit(L2DistanceTransposed,cosineDistanceTransposed,dotProductTransposed, and their quantized counterpartsL2DistanceTransposedQuantized,cosineDistanceTransposedQuantized,dotProductTransposedQuantized) now accept a reference vector longer than the number of dimensions being searched, ignoring the extra trailing elements. This allows reusing a full-size query vector for a reduced-dimension (Matryoshka) search without slicing it first. #109388 (alexey-milovidov). - Added a sanity check that a distributed query always carries a known client version, throwing a logical error instead of silently forwarding a zero version to remote shards. Server-initiated queries (background flushes, streaming consumers, dictionary reloads, asynchronous insert flushes) now report the server’s own version as the initiator version. #109408 (alexey-milovidov).
- This patch fixes the usage of qualified column names in the where clause of the mutation operation over the merge tree table engines. #109491 (Michicosun).
- Support non-partitioned window functions (
OVER (ORDER BY ...)) in distributed query plans (make_distributed_plan). Previously any query with a window function failed becauseWindowStepcould not be serialized for remote execution; windows withPARTITION BYare still rejected for now. #109802 (VighneshPath). - Now it’s possible to alter some auth settings of one-lake catalog with
ALTER DATABASE ... MODIFY SETTING = 'xxx'. #110019 (alesapin). - A subquery on the right side of
INwhose single column is an array one dimension deeper than the left argument is now interpreted as the set of the array’s elements (like an array literal or an array-returning function), instead of failing with a confusing type-mismatch error. For example,x IN (SELECT groupArray(x) FROM ...)now works. #110169 (alexey-milovidov). - Queries accelerated by TopK dynamic filtering (
ORDER BY ... LIMIT k) now make fuller use of the query condition cache: the cache is populated even when lazy materialization is applied, and such queries can reuse entries previously written by an ordinary query with the sameWHEREpredicate. #110507 (shankar-iyer). SHOW CREATE TABLE(andSHOW CREATE VIEW/SHOW CREATE DICTIONARY) now suggests a similarly-named table in the error message when the requested table does not exist, the same waySELECTqueries do. #110633 (alexey-milovidov).- Add STREAM BOUNDED modifier, which read only the first snapshot of a streaming query, then finish instead of subscribing for updates. #110653 (SmitaRKulkarni).
- Fix the query result cache for PromQL queries. The
promqldialect bypassed the non-deterministic-function check and could serve stale results anchored atnow(); the Prometheus HTTP API (/api/v1/query,/api/v1/query_range) never stored cache entries at all. #110887 (nikitamikhaylov). EXPLAIN ANALYZEnow reports per-side join row counts, match rates, fanout, and algorithm-specific state such as hash-table memory and spilling. Usematches = 1to collect match metrics that need additional bookkeeping. #110892 (Fgrtue).- Fixed a heap-use-after-free in the pipeline executor that could crash the server when a query removes processors at runtime (for example
FINALwith lazy reads or external sort), found by the AST fuzzer. #111017 (groeneai). - Fix a use-after-free (segmentation fault) when a query with a shared storage snapshot (setting
enable_shared_storage_snapshot_in_query) strips data parts from the snapshot — e.g. a streaming subquery reading the sameMergeTreetable that another part of the query reads concurrently. #111192 (alexey-milovidov). - Support distributed query plans (
make_distributed_plan) for queries whose source provably produces no rows at planning time, such as a read from an emptyMergeTreetable. Such queries previously failed withSUPPORT_IS_DISABLED. #111941 (groeneai). - Fixed the query condition cache not being populated for granules eliminated by any but the last conjunct of a
WHEREclause with multipleANDconditions. #112083 (shankar-iyer). - Reduced peak memory when writing JSON columns with bucketed shared data serialization (map_with_buckets and advanced) by splitting the shared data into buckets one at a time on write instead of materializing all buckets simultaneously. This is mostly beneficial for Array(JSON) columns: for scalar JSON the shared data is split per granule (bounded by index_granularity_bytes), so the saving is small, whereas a single Array(JSON) granule can hold many nested rows and thus a large amount of shared data, where the one-bucket-at-a-time split significantly lowers peak memory #112172 (Avogar).
- Memory reservation scheduling (experimental) no longer evicts an allocation when a pending memory release would free the room needed by an over-limit reservation increase: the eviction decision now waits for in-flight decreases to be applied, avoiding unnecessary query kills under memory pressure. #112299 (serxa).
SYSTEM DROP FILESYSTEM CACHEnow removes cache metadata keys in parallel, making large filesystem-cache clears faster. The new file-cachedrop_cache_threadssetting controls the number of removal workers and defaults to 16; a value of 1 performs removal in the query thread. #112532 (kssenii).- The modifiers of a column declaration -
COMMENT,CODEC,STATISTICS,TTL,COLLATE,PRIMARY KEYand per-columnSETTINGS- can now be written in any order inCREATE TABLE, and each of them at most once. Previously only one fixed order was accepted, and, for example,x UInt64 CODEC(ZSTD) COMMENT 'text'was a syntax error. InALTER TABLE ... ADD COLUMN/MODIFY COLUMN, the supported modifiers -COMMENT,CODEC,STATISTICS,TTLand per-columnSETTINGS- can also be written in any order; per-columnSETTINGSinADD COLUMNand a declaredSTATISTICSinADD COLUMN/MODIFY COLUMNare now applied instead of being silently dropped, andCOLLATEandPRIMARY KEYin theseALTERcommands now throw an exception instead of being silently ignored. #112788 (alexey-milovidov). - A query over a
Mergetable (or themergetable function) that matches many tables now reacts toKILL QUERYandmax_execution_timewhile the per-table query plans are being built, instead of only after the planning of all children has finished. #113415 (alexey-milovidov). - A
SELECTfrom aMergetable that exceedsmax_execution_timewithtimeout_overflow_mode = 'break'now stops building query plans for the remaining matched tables, instead of running full query analysis for every matched table before stopping. The query still finishes without an error, as that mode documents. #113642 (groeneai). - When a column’s declared type and its data diverge during a
MergeTreeread (mixed type provenance, e.g. after anALTER TABLE ... MODIFY COLUMNwhose mutation has not finished), the server now reports a clear exception naming the column and both structures, instead of an unchecked cast: debug and sanitizer builds previously aborted with a bareBad cast from type A to Bnaming no column, and release builds walked mismatched memory silently. #114087 (alexey-milovidov). - Enabled
ie_joinby default: the default value ofjoin_algorithmis nowdirect,parallel_hash,hash,ie_join. AJOINwhoseONsection has only inequality conditions (two comparisons<,<=,>,>=between expressions of the joined tables) is now executed with the sort-based IEJoin algorithm instead of aCROSS JOINwith a filter, andLEFT/RIGHT/FULL/SEMI/ANTIjoins with such conditions are supported. Sinceie_joinis last in the list, it is used only when the other algorithms do not apply. #114327 (vdimir). - Propagate the OpenTelemetry trace context into the fibers used for asynchronous connection establishment and remote query execution (hedged requests,
async_socket_for_remote,async_query_sending_for_remote). Spans of remote queries in a distributed query are now correctly parented under the initiator’sConnection::sendQueryspan instead of being attached directly to the client-supplied trace context, and each asynchronous task execution produces its own span. #114813 (diegomestre2). DROP DATABASEnow drops tables in reverse topological order of both loading and referential dependencies (previously only loading dependencies), so a crash in the middle ofDROP DATABASEcannot leave a table whose dependencies were already dropped. #114952 (alexey-milovidov).- Support PostgreSQL-style regular expression match operators:
~(an alias for thematchfunction),~*(case-insensitive match),!~and!~*(negations). The\d,\dt,\dvcommands ofpsqlnow work when connected to ClickHouse over the PostgreSQL compatibility protocol, and a failed query no longer terminates the connection. #115066 (alexey-milovidov). EXPLAIN WHATIFnow says why the empirical estimate was skipped, in a newempirical_reasonline shown whenempirical_statusisunsupported. #115140 (yariks5s).- When no join algorithm enabled by the
join_algorithmsetting can execute aJOIN, the error message now names the algorithms that were tried and the strictness and kind of the JOIN that failed, instead of reporting only that none of them worked. #115226 (elkinal). - Vector search queries can now use the vector similarity index when the reference vector is an integer array literal, e.g.
ORDER BY L2Distance(vec, [1, 2]). Previously, such queries silently fell back to a brute-force scan. #115255 (rschu1ze). BACKUPno longer leaves a lock file behind in its destination when the attempt that created it fails before writing anything — whether it fails while claiming the destination or while opening an archive. No later backup could match such a lock, so every retry to the same destination failed until the file was deleted by hand. Backups toS3andAzuredestinations also create the lock exclusively, and a lock that belongs to another backup is reported as a concurrent backup rather than as whatever error the storage returned. #115387 (jkartseva).- A trace started by sampling (
opentelemetry_start_trace_probability) is now written back into the query’s client info, so remote and distributed secondary queries, and DDL entries, join the same trace instead of starting disjoint ones. #115619 (diegomestre2). - Fixed
CREATE TABLEfor Iceberg tables backed by Amazon S3 Tables, which manages table locations itself. #115652 (scanhex12). - The adaptive
GROUP BYaggregation (enable_adaptive_aggregator) now measures whether deferring rare keys pays off and returns to the ordinary algorithm when their keys or aggregate arguments are too wide to copy profitably, so aggregations over heavy string columns (e.g.min(URL)per user) run at the ordinary algorithm’s speed and memory. Addsadaptive_aggregator_freeze_threshold_bytesto bound each thread’s frozen table in bytes (whichever of it and the key-count threshold is reached first freezes the table). #115755 (nihalzp). - Adds setting
query_plan_aggregation_bucket_top_k(enabled by default) to toggle the plan optimization that materializes only each two-level bucket’s bestngroups when a final aggregation feedsORDER BYover its outputs withLIMIT n. The optimization is now also visible inEXPLAINoutput. #116124 (nihalzp). - Improve
DESCRIBEquery support for parameterized views. Support parameterized views in scalar expressions in the new analyzer. #68978 (novikd). - Queries like
SELECT * FROM t WHERE idwill now use index skipping on theidcolumn. #89603 (adityachopra29). - Added the
query_plan_merge_expression_into_joinsetting to allow merging Expression steps into JOIN steps during join reordering optimization. This enables join reordering across subqueries that wrap joins (e.g., when a JOIN is inside a subquery with computed columns), leading to better optimization of complex join trees. #98533 (vdimir). - Added an
is_wildcardcolumn to thesystem.grantstable that indicates whether a grant uses wildcard prefix matching (e.g.GRANT SELECT ON db*.*). Previously, wildcard and exact grants on the same name were indistinguishable insystem.grants. #98577 (il9ue).
Functions and data types
- Added a new setting
allow_lossy_numeric_supertype(disabled by default). When enabled,if,multiIf,coalesce,ifNull,arrayandmapover numeric arguments that have no lossless common type (for example aDecimaland aFloat64, or anInt64and aFloat64) resolve to the numeric supertypeFloat64instead of aVariant, so the result can be used with aggregate functions likesum,avg,minandmax. The setting name is now mentioned in the relevant aggregate function error messages. #107236 (groeneai). - The
SOME/ALLarray quantifier (expr OP SOME(array)/expr OP ALL(array)) now also supports the keyword comparison predicatesIS DISTINCT FROMandIS NOT DISTINCT FROM, and the string-search predicatesLIKE,ILIKE,NOT LIKE,NOT ILIKE, andREGEXP, rewritten toarrayExists/arrayAll. #107454 (alexey-milovidov). - Add
input_format_json_max_object_sizesetting to limit JSON object size on parsing. #107669 (Avogar). - Fixed a memory leak that occurred when opening a SQLite database failed (for example, when the
sqlitetable function or theSQLitedatabase engine is given a path that cannot be opened). #107807 (alexey-milovidov). - Support the
lengthfunction for theQBitdata type. It returns the dimension of the vector as a constant. #108071 (alexey-milovidov). - Support
CASTfrom aQBitto anArray, reconstructing the original vector. This is the inverse of the existingArraytoQBitconversion. #108072 (alexey-milovidov). - The
MySQLdatabase engine, table engine and table function now map MySQL’s spatial column types (LINESTRING,POLYGON,MULTILINESTRING,MULTIPOLYGON, and the genericGEOMETRY) to the corresponding ClickHouse geometric types instead ofString. This is controlled by the newgeometryflag of themysql_datatypes_support_levelsetting, enabled by default.POINTis still always converted toPoint. The genericGEOMETRYcolumn maps to the umbrellaGeometrytype; reading a value whose subtype has no ClickHouse counterpart (MULTIPOINT,GEOMETRYCOLLECTION) throws an exception at read time. #108944 (alexey-milovidov). - Use the precise (closest-representable) float parsing algorithm by default and apply the
precise_float_parsingsetting to input formats (CSV, TSV, JSON, VALUES, …) and numeric literals, not justtoFloat*/CAST. Setprecise_float_parsing = 0for the previous, faster in some cases but less accurate, behavior. #109086 (Algunenano). - Support
reinterpretof anArrayof fixed-size elements as aString, the inverse of the existingreinterpretof aString/FixedStringas anArray. #109383 (alexey-milovidov). - Allow
CASTbetweenQBittypes that differ in the element type and/or the stride, as long as the dimension stays the same (for exampleCAST(x AS QBit(Float64, N))from aQBit(Float32, N)). Stride-only changes are lossless; element-type changes follow the correspondingArrayconversion semantics (for exampleFloat32toBFloat16may lose precision, andaccurateCast/accurateCastOrNullreject rows that are not exactly representable). #109387 (alexey-milovidov). - Functions
quantizeBFloat16ToInt8anddequantizeInt8ToBFloat16now also acceptArrayandQBitarguments, applying the Lloyd-Max codec to the whole vector (returningArray/QBitof the corresponding element type), in addition to the existing scalar overloads. #109398 (alexey-milovidov). - The MySQL-style format specifier
%finparseDateTime/parseDateTime64(and theirOrZero/OrNullvariants) now accepts between 1 and 6 fractional digits, interpreted as left-aligned microseconds like MySQL’sSTR_TO_DATE, instead of requiring exactly 6. Also fixed misalignedPrettyCompacttables in the built-in function documentation examples. #109421 (alexey-milovidov). - The
basicstatisticsNullCountsub-statistic is renamed toDefaultCount: instead of countingNULLrows of aNullablecolumn,buildnow counts rows equal to the column type’s intrinsic default viaIColumn::getNumberOfDefaultRows. #109977 (hanfei1991). - Reduced the binary size by ~10.5 MB by executing comparison and arithmetic operations on rarely used mixed type pairs (
Decimalvs integer of a different width, and pairs involvingInt128/UInt128/Int256/UInt256) via a conversion to a common type instead of a dedicated compiled kernel for every combination of types. Same-type pairs, commonly used pairs, and the memory-boundplus/minus/multiplykeep their dedicated kernels; results, result types and exceptional cases are unchanged. #110131 (alexey-milovidov). - Reading from a
SQLitetable engine orsqlitetable function no longer busy-spins a full CPU core when the SQLite database is locked by another connection. The read now idles while waiting for the lock and stays cancellable. #110248 (groeneai). - Round elapsed time, rate, and ratio values in log and exception messages to three digits, so numbers like
1.345844286 sec.are no longer printed at full precision. #110277 (alexey-milovidov). - Fixed
MergeTreereads ofStringcolumns with a separate size stream after leading rows are skipped, which could return wrong data or read past bounds from the substreams cache. Also fixed possible wrong data when concurrent or repeated reads access aJSONsub-object stored with map shared-data serialization. #110471 (alexey-milovidov). - Support the
SETTINGSclause for thePostgreSQLtable engine and thepostgresqltable function (for exampleSETTINGS postgresql_connection_pool_size = 50), bringing feature parity with theMySQLengine. #110614 (alexey-milovidov). - Functions
bitmaskToArrayandbitmaskToListnow support(U)Int128and(U)Int256arguments. FunctionbitPositionsToArraynow runs in time proportional to the number of set bits for big integer arguments, instead of the position of the highest set bit. #110743 (raimannma). - Fixed GCD codec handling of signed fixed-width values with negative numbers. Previously it computed the divisor from signed values directly, which could miss the common divisor and significantly reduce compression efficiency for signed integer and decimal columns. It now computes the divisor from magnitudes for signed types while preserving existing behavior for unsigned types and decompression. #113798 (kshcherbatov).
- A window function with
PARTITION BYnow works undermake_distributed_plan. When the plan shape allows, the window runs in parallel across buckets that each receive complete partitions. #114111 (VighneshPath). - 64-bit hash function is used now for external nullable fixed-width aggregation methods (avoids collision disaster for very high cardinality aggregation). #114210 (nickitat).
x IN (subquery)over aLowCardinalitycolumn now returnsLowCardinality(UInt8)— the same type asx IN (literal list). Previously the two forms returned different types, and distributed plans with such anINneeded a special case in the plan type check. #114229 (davenger).- Support parsing integers that exceed the 64-bit range in the
JSONdata type,JSONExtract, andisValidJSON. Such values are read into wide integer types such asInt128/UInt256instead of causing the whole document to be rejected. #114379 (Avogar). - Added
keywordas an alias for text index tokenizerarrayfor improved compatibilty with OpenSearch, Elasticsearch, SolR, and Lucene. #115507 (rschu1ze). - Functions
toDateOrNull,toDateTimeOrNullandtoDateTime64OrNullnow accept integer arguments of all native integer types (interpreted the same way as bytoDate,toDateTimeandtoDateTime64, with an optional timezone argument), returningNULLfor values out of range of the result type. For example,toDateTimeOrNull(1583851242, 'Asia/Shanghai')returns2020-03-10 22:40:42andtoDateTimeOrNull(4294967296)returnsNULL. #79791 (jitendra1411).
Storage, data lakes, and object storage
- Added opt-in Prometheus metrics for filesystem cache eviction activity (
filesystem_cache_evictions_total,filesystem_cache_evicted_bytes_total,filesystem_cache_evicted_segment_hits,filesystem_cache_evicted_segment_size_bytes, and their per-user variants labeled withuser_id), exposed per cache viasystem.dimensional_metrics,system.histogram_metrics, and the Prometheus endpoint. They are controlled by the cache disk-config settingsexpose_prometheus_eviction_metricsandexpose_prometheus_eviction_metrics_per_user(both off by default), which can be toggled at runtime viaSYSTEM RELOAD CONFIG. #105020 (sacheendra). Aliasengine now supports parallel replicas read when the target table is aMergeTreefamily engine. #107830 (nauu).- Limit the number of concurrently staged parts in Azure Blob Storage read-then-write copy (used by backups when native copy is unavailable) to max_inflight_parts_for_one_file, preventing excessive memory usage when many large files are copied in parallel. #108232 (SmitaRKulkarni).
- Support
_etagvirtual column for HDFS storage #108255 (zhangyifan27). - Added
settingsandengine_settingscolumns tosystem.backupsandsystem.backup_log.settingsexposes the backup/restore-specific settings requested for an operation (e.g.allow_s3_native_copy,deduplicate_files,structure_only), andengine_settingsexposes the settings effectively used by the backup engine’s reader/writer (e.g. the S3 request settings such asallow_native_copy, which may differ from what was requested after merging endpoint configuration). This makes it possible to see which settings aBACKUP/RESTOREoperation actually ran with. #108334 (jkartseva). - Added
system.s3_queue_metadataandsystem.azure_queue_metadatafor inspecting the Keeper state of registeredS3QueueandAzureQueuetables, including processed, processing, and failed-file counts. #108522 (kssenii). - Reduced the memory usage of the text index header cache by 2-2.5 times. More headers of data parts fit into the cache (setting
text_index_header_cache_size), reducing disk reads for text search queries. #109332 (CurtizJ). - Added MySQL-compatibility columns to
INFORMATION_SCHEMAviews:SCHEMATA.DEFAULT_COLLATION_NAMEandDEFAULT_ENCRYPTION;TABLES.ENGINEand related MySQL columns; andCOLUMNS.COLUMN_KEY,PRIVILEGES,GENERATION_EXPRESSION,SRS_ID. This lets MySQL-aware clients run their catalog introspection queries without hittingUNKNOWN_IDENTIFIER. #109351 (alexey-milovidov). - Reduced memory usage of
MergeTreetables with many data parts by sharing the schema-derived per-part metadata (column list, column descriptions, serializations, and columns substreams) across parts of a table instead of copying it into every part. The effect is largest for wide tables with many small parts. #109816 (Algunenano). - Added a new setting
use_projection_index_in_read_pools(disabled by default). When enabled, mark ranges that are fully filtered out by a projection index are dropped insideMergeTreeread pools before read tasks are created for them, instead of being skipped granule by granule during reading. This avoids creating read tasks and setting up readers for data that will not be read, and keeps read task sizes measured in surviving marks. #110291 (KochetovNicolai). - Reduced the verbosity of the per-part primary-key binary-search diagnostics in
MergeTreeby lowering them from trace logging to test logging. #110324 (alexey-milovidov). - Fixed spurious reconnections of pooled HTTP keep-alive connections over TLS (HTTPS, S3). The stale-connection check used
pollon the socket, which misreports a live secure connection carrying an unread TLS post-handshake record (a session ticket orKeyUpdate) as closed; it now uses a non-blockingMSG_PEEKon plain sockets andSSL_peek/SSL_has_pendingon TLS sockets, which also correctly detects an orderly TLS shutdown and a TLS record that has only partially arrived. #110402 (alexey-milovidov). - OneLake catalogs can now use OAuth refresh tokens to renew access tokens. #110413 (alesapin).
- Added the
DuplicationDataHashComputationsProfileEvent, which counts the column-wise data-hash computations performed while deduplicating INSERTed blocks to*MergeTreetables. #111173 (valerypetrov). - Write the UNIQUE KEY index SST directly into the part through the storage abstraction, instead of staging it in a local temporary file. #111189 (JingYanchao).
- Support concurrent writes to Iceberg tables on a local disk:
LocalObjectStoragenow implements conditional writes, so the compare-and-swap that publishes a new snapshot throughversion-hint.textworks there and concurrent writers cannot lose each other’s updates. #112556 (alexey-milovidov). - The bucket-region cache of the
S3client now also works for data lake catalogs; previously every request to them resolved the region again. #113330 (scanhex12). - Azure client-side network failures (connection reset/refused, DNS and TLS errors) are now retried as transport errors instead of surfacing as a synthetic
500, matching the S3 client. #115269 (arsenmuk).
Formats and ingestion
- Allow snappy compression in the HTTP interface (
Accept-Encoding: snappy) and add thesnappy_modesetting to choose the Hadoop snappy block format or the snappy framing format for genericfile/urlsnappy I/O. #100752 (alexey-milovidov). - Added a setting
input_format_csv_missing_nullable_as_empty_string(disabled by default). When enabled, a missing value ofNullable(String)in CSV input is read as an emptyStringinstead ofNULL, regardless ofinput_format_csv_empty_as_default. #107577 (alexey-milovidov). - Processing protobuf messages with
input_format_protobuf_oneof_presencewe do not require exact match of tags to allow schema changes. #109174 (ilejn). - Removed the legacy Apache Arrow-based ORC reader. The ORC input format now always uses the native ClickHouse decoder (previously the default). The
input_format_orc_use_fast_decodersetting is obsolete (still accepted, but has no effect). #110074 (alexey-milovidov). - The Hive engine now reads ORC file metadata (min/max indexes, row counts) with ClickHouse’s native ORC reader instead of the Apache Arrow ORC adapter. #110086 (alexey-milovidov).
Security, access, and observability
- Added a new column
skipping_indices_typesto thesystem.tablestable. It contains the distinct types of data-skipping indices defined for each table. #106388 (CurtizJ). - The automatic value of
max_threadsand similar settings is now shown insystem.settingsasauto(8)instead of'auto(8)'; the surrounding single quotes were a long-standing artifact baked into the value. Cross-version compatibility is preserved: the legacy quoted form is still accepted when parsing settings received from older servers. #108657 (alexey-milovidov). - Render an empty setting default value as empty string in
system.documentationinstead of empty backticks. #108708 (alexey-milovidov). - The documentation of a setting in
system.documentation— shown by the built-in/docspage and by thehelpcommand — now includes the history of the changes of its default value: the version in which the setting was introduced and every later change of its default, with the previous value, the new value and the reason for the change. #112177 (alexey-milovidov). - The documentation of all SQL statements (e.g. name, syntax, description, examples) can now be retrieved from system table
system.statements. #115341 (rschu1ze).
Settings and configuration
- Added a new server setting
memory_worker_rss_speculative_reserve_ratio(default1.0, or0in sanitizer builds) which makes the global memory tracker speculatively reserveratio * min(resident - previous_resident, resident - tracked)on top of the observed RSS eachMemoryWorkertick. This biases the tracker upward when RSS growth is outpacing the tracker bookkeeping between samples, so allocations getMEMORY_LIMIT_EXCEEDEDearlier and the kernel OOM-killer is less likely to fire first. Set the ratio to0to disable speculation. Sanitizer builds default to0because theresident - trackedgap is dominated by sanitizer shadow memory there. #104976 (alexey-milovidov). - The text index lazy posting-list apply mode is no longer experimental and can be selected with
text_index_posting_list_apply_mode = 'lazy'withoutallow_experimental_text_index_lazy_apply. The density-threshold setting was renamed fromtext_index_density_thresholdtotext_index_lazy_intersection_density_threshold. #108814 (CurtizJ). - The
merge_selector_enable_heuristic_to_lower_max_parts_to_merge_at_oncesetting is now enabled by default: the merge selector automatically lowers the maximum number of parts to merge at once based on how full the partition is. #110726 (Michicosun). - Accept PostgreSQL cleanup commands
RESET,UNLISTEN, andDISCARDas no-ops in the PostgreSQL wire protocol instead of failing them with a syntax error. This improves compatibility with drivers such as Skunk that sendRESET ALLandUNLISTEN *during connection setup and cleanup. #110780 (alexey-milovidov). - AI text functions (
aiGenerate,aiClassify,aiExtract,aiTranslate) now reject truncated or otherwise incomplete provider responses (e.g. when the model hits themax_tokenslimit) instead of silently returning partial output. Behavior follows theai_function_throw_on_errorsetting. #111830 (george-larionov). - Improve reduced-precision
L2DistanceTransposedQuantized,cosineDistanceTransposedQuantized, anddotProductTransposedQuantizedby reconstructingp < 8QBit(Int8)codes with Gaussian conditional-mean prefix centroids. Full precision (p = 8) remains bit-exact; reduced-precision approximate distances may change, and recall/latency gains are workload-dependent. #111867 (skuznetsov). - Make
write_snapshot_versionsetting hot re-loadable. #112150 (alesapin). - Added observability for the stacks of fibers, which are used for asynchronous communication with remote replicas: profile events
FiberStackAllocs,FiberStackAllocBytes,FiberStackAllocNanoseconds,FiberStackFreeNanoseconds, and metricsFiberStacks,FiberStackBytes. #114541 (alexey-milovidov). - The Prometheus remote-write protocol now supports asynchronous inserts: data from multiple concurrent remote-write requests is batched before forming parts, following the
async_insertsetting (enabled by default; addasync_insert=0to the handler URL to keep the synchronous behavior). A request is acknowledged only after the data is flushed to all inner tables of the targetTimeSeriestable. #115688 (nikitamikhaylov).
Other improvements
- Added two settings,
shrink_over_allocated_columns_min_waste_ratioandshrink_over_allocated_columns_min_waste_bytes, that make INSERTs shrink over-allocated columns (whose reserved memory exceeds used memory due to power-of-two growth of variable-length columns) to fit before materialization and part writing, reducing peak memory usage. Disabled by default.. #112161 (Avogar). - Skip data reading step entirely for
TTLDropmerges to avoid excessive memory usage, especially on read/prefetch buffers allocations. #105859 (Avogar). - Added support for
BFloat16indotProductand improved dot product performance by batching the SIMD path. #106569 (nickitat). - Made
max_named_collection_num_to_throw,max_table_num_to_throw,max_replicated_table_num_to_throw,max_view_num_to_throw,max_dictionary_num_to_throw, andmax_database_num_to_throwchangeable without a server restart. #106821 (UberDever). - Writing to
DeltaLaketables, controlled byallow_experimental_delta_lake_writes, was promoted to Beta. #107034 (kssenii). - Added the
uniq_v2column statistics type, a lightweight alternative touniqbased on theuniqCombined64sketch. #107863 (hanfei1991). - Added ProfileEvents
DistributedPlanRemoteTasks,DistributedPlanLocalExecution, andDistributedPlanHostsUsedto observe make_distributed_plan execution. #107985 (shankar-iyer). - Requests to REST data lake catalogs such as OneLake now include a ClickHouse
User-Agentheader. #108117 (scanhex12). MVTEncodeGeomnow snaps geometry to the integer pixel grid before clipping and clips polygons withwagyu, so clipped output is valid (self-intersecting rings are repaired) and edge-aligned, matching PostGISST_AsMVTGeom. #108248 (saarthak2002).- Enabled all three text index caches globally - previously, they were only enabled within queries. Also, the posting lists cache size is now zero (which effectively disables it again). This is because posting lists are large and caching them is too costly. #108274 (rschu1ze).
- Start the Prometheus endpoint (for metrics-only configurations) and asynchronous metrics collection before tables are loaded, so that metrics are observable during the potentially long metadata loading phase. #108402 (cwurm).
- Support BFloat16 in binary math functions #108442 (zhangyifan27).
- The transposed vector distance functions
L2DistanceTransposed,cosineDistanceTransposedanddotProductTransposednow apply the partial bit-plane read optimization toNullable(QBit)columns, reading only the requested bit planes instead of the whole column. #109358 (alexey-milovidov). - Support
arraySum,arrayAvg, andarrayProductfor arrays ofBFloat16. #109420 (alexey-milovidov). - Functions
L1Normalize,L2Normalize,LinfNormalizeandLpNormalize(and their aliases) now work for arrays, not only for tuples, consistently withL2Normand the distance functions. #110052 (alexey-milovidov). - Pooled connections are no longer pinged before each use. This removes a
Ping-Ponground trip that was added to every reused connection, reducing the latency of distributed queries and ofclickhouse-benchmark. A stale pooled connection is detected with a zero-timeout poll (a non-blocking check that adds no round trip) and recovered by reconnecting. #110068 (alexey-milovidov). - Send big list parts for distributed index analysis as scalars (avoids possible failures due to max_ast_elements/max_query_size and removes AST parsing overhead) #110419 (azat).
- Clicking the cloud logo in the Playground now opens the Clickhouse website in a new tab so you don’t loose progress while queries are running. #110422 (williamhatcher).
- Change default
auto_statistics_typesfrombasic, uniqtobasic, uniq_v2#110878 (hanfei1991). - When an
RWLockImpl::getLock()re-entrancy assertion fires (RWLock is already locked in exclusive mode/Cannot acquire exclusive lock while RWLock is already locked), the error message now names the otherquery_ids currently owning the lock and how many locks the requestingquery_idalready holds, to make such lock-ordering errors diagnosable. #110971 (groeneai). randomHadamardTransformnow computes an exact, length-preserving transform for any vector length whose largest odd factor is at most 64 (for example3584 = 512 * 7, common in embedding models), extending the previous2^N,2^k * {12, 20}, and2^k * 9families. A full transform of a length that cannot be represented exactly now raises an exception instead of silently zero-padding to a longer vector; passoutput_dimsto compute a truncated projection of an arbitrary length. #111006 (alexey-milovidov).- Return HTTP code
403 Forbidden(instead of500 Internal Server Error) forACCESS_DENIEDexceptions over the HTTP interface. #111043 (Schum-io). UTMToGeonow accepts the MGRS latitude band letter returned bygeoToUTMas its fourth argument (in addition to the integer hemisphere flag), so ageoToUTMresult round-trips throughUTMToGeodirectly. #111521 (alexey-milovidov).MaterializedPostgreSQLnow preserves unchanged PostgreSQLTOASTvalues during updates instead of replacing them with default values. #111552 (OrpheusAgent).- Added the
text_index_max_processed_tokens_before_flushandtext_index_max_memory_usage_before_flushsettings to control when text index builders flush temporary segments. #111573 (rorylshanks). - Add STREAM UNORDERED modifier: skip the per-snapshot commit-order sort #111794 (SmitaRKulkarni).
- Now
make_distributed_plan=1will disable not yet supported features.. #112463 (alesapin). - Do not write ANSI escape sequences into INTO OUTFILE when stdout is a tty #113252 (azat).
- Renamed
allow_experimental_delta_kernel_rstoallow_delta_kernel_rs. The old name is kept as an alias. #113476 (kssenii). - Add settings
enable_alp_codec,enable_sz3_codec,enable_zxc_codec, andenable_quantized_codecto enable each experimental compression codec individually. #113824 (rienath). - Report a stack overflow instead of dying silently: the deadly signal handlers now run on the alternative signal stack, so
SIGSEGV,SIGABRT,SIGILL,SIGBUS,SIGSYS,SIGFPEandSIGTRAPproduce a stack trace even when the faulting thread has exhausted its stack. #113927 (groeneai). - Support the Prometheus HTTP API lookback_delta parameter for instant and range queries. #113971 (fallintoplace).
- QueryRunner tables now start worker threads on demand and release them once idle, instead of occupying threads for the table’s whole lifetime. #114522 (mstetsyuk).
- With this change, while using the WHATIF indices, the projection-served baseline reports per-candidate
not_applicableinstead of failing the statement. #114846 (yariks5s). - Optimize AND chains with multiple comparison conditions on the same expression: detect contradictions (e.g.,
a < 3 AND a > 5→false), prune redundant conditions (e.g.,a = 3 AND a < 5→a = 3). #99736 (wudidapaopao).
Bug Fixes
MergeTree, replication, and storage fixes
- Fixed a race between
ALTER TABLE ... RENAME COLUMNand a concurrentOPTIMIZE TABLE ... FINAL(or any background merge) inMergeTreethat could silently replace the renamed column’s data with default values. The race window between publishing the new in-memory metadata and registering the rename mutation incurrent_mutations_by_versionis now closed by holdingcurrently_processing_in_background_mutexacross both operations. #104822 (groeneai). - Fixed incorrect query results when using GROUP BY with ORDER BY and
arrayJoinin the projection. #101775 (Onyx2406). - Invalid Azure upload-size settings (for example
azure_min_upload_part_size = 0orazure_max_blocks_in_multipart_upload = 0) are now rejected early with a clear error instead of triggering an internal exception (second_size > 0) deep in the write path. This applies to both query settings and Azure disk configuration. #103394 (statxc). - Throw
BAD_ARGUMENTSinstead ofLOGICAL_ERRORwhenALTER TABLE ... MOVE/REPLACE/ATTACH PARTITIONis run between twoMergeTreetables with incompatible granularity settings (one adaptive, one non-adaptive). #103881 (groeneai). - Fix a
LOGICAL_ERROR“Expected one block from input stream” thrown byKILL QUERY/KILL MUTATION/KILL PART_MOVE_TO_SHARD/KILL TRANSACTIONwhen theirWHEREclause contains a per-row subquery, or whenmax_block_sizeis small enough that the internalSELECTover the relevantsystem.*table emits more than one block. #104927 (groeneai). - Fix a leak in Refreshable Materialized Views (
MATERIALIZED VIEW ... REFRESH ...) where the temporary inner table that the refresh task rotates out viaEXCHANGE TABLEScould not be dropped if it was larger than the server’smax_table_size_to_drop. Every subsequent refresh would create yet another temporary inner table while still failing to clean up the previous ones, growing the view’s data directory until the disk was exhausted. The refresh task’s internal drop now bypassesmax_table_size_to_dropandmax_partition_size_to_drop; those safety nets still apply to user-issuedDROP TABLEof the view itself. #105106 (groeneai). - Fix an unexpected
DATA_TYPE_CANNOT_BE_USED_IN_KEYerror onALTERqueries that cannot change the sorting key (settings, comments, codecs, adding a non-key column, etc.) forMergeTreetables that have aSimpleAggregateFunction(or another type allowed only withallow_suspicious_primary_key = 1) in the sorting key. #105111 (groeneai). OPTIMIZE ... DRY RUNinterrupted by a query timeout (max_execution_timewithtimeout_overflow_mode = 'break') now returnsTIMEOUT_EXCEEDEDinstead of a logical error aboutrows_sources(which aborted the server in debug and sanitizer builds). #107114 (groeneai).- Fixed
NOT_FOUND_COLUMN_IN_BLOCKwhen reading hive-partitioned files withuse_hive_partitioning=1and aWHERE/PREWHEREclause that filters a real (non-virtual) column while a hive partition column is also selected. #107505 (groeneai). - Fix wrong query results from incorrect primary key and partition pruning when
toStartOfDay,toStartOfISOYear,toDaysSinceYearZero,toRelativeSecondNum,toRelativeMinuteNum,toRelativeHourNum,toRelativeWeekNum,toRelativeDayNum,toMonthNumSinceEpoch, ortoYearNumSinceEpochis applied to aDate32key column containing dates outside the range the function can represent (in particular dates before1970-01-01). These functions report themselves as monotonic to the primary index, but previously wrapped around for such arguments, so the index could prune granules that actually contained matching rows. They now saturate at the bounds of their result type and stay monotonic over the wholeDate32range, so pruning is correct. #108018 (nihalzp). - Fixed
CREATE TABLE dst AS src SETTINGS ...(and similar variants withORDER BY/PARTITION BYbut without an explicitENGINE) silently dropping the source table’s engine, storage clauses (such as tableTTLand keys) and settings. The engine and the non-overridden storage clauses are now inherited from the source table, and the specified settings are merged on top of the source table’s settings. #108085 (alexey-milovidov). - Fixed
INSERTintoMergeTreetables failing withfilesystem error: in rename: Permission deniedon filesystems backed by Windows (WSL, CIFS/SMB, Docker Desktop bind mounts), caused by the part writer leaving file descriptors open across the part-directory rename. #108089 (alexey-milovidov). - Fixed a wedge on a MergeTree table attached with a legacy
hypothesisskip index (a removed index type kept only forATTACHcompatibility). Previously a lightweightDELETE,OPTIMIZE, a plainINSERT, or a filteredSELECTon such a table failed withILLEGAL_INDEX(“Index of type ‘hypothesis’ is no longer supported”), and theDELETEmutation retried forever. The dead index is now treated as inert: it is carried forward untouched during merge/mutation, skipped on insert and query planning, and can still be dropped withALTER TABLE ... DROP INDEX. #108217 (groeneai). - Fix
NOT_FOUND_COLUMN_IN_BLOCKerror (e.g.Column _part not found) when selecting virtual columns from aMergeTreetable under parallel replicas, including viaSELECT *withasterisk_include_virtual_columns = 1. #108451 (groeneai). - Fix
ATTACH PARTITION ... FROMrejecting tables whose primary keys are equivalent but declared differently (one explicitPRIMARY KEY, the other implicit fromORDER BY). #108590 (jordiori). join_any_take_last_rowis now respected by all supported hash-based join paths, including joins that use automatic spilling to disk. #108936 (antaljanosbenjamin).- Fix
NOT_FOUND_COLUMN_IN_BLOCKerror whenquery_plan_optimize_lazy_finalis enabled together with aWHEREfilter partially pushed toPREWHERE(viaoptimize_move_to_prewhere_if_final). The lazy FINAL optimization no longer requests prewhere-computed predicate columns from storage. #109073 (groeneai). - Fixed
ALTER TABLE ... ADD PROJECTIONover a table-levelALIAScolumn leaving the table unusable (subsequentINSERTfailed withUNKNOWN_IDENTIFIER, “Missing columns”) when the session hadoptimize_respect_aliases = 0. #109091 (groeneai). - Fix a null-pointer assertion (
px != 0inTreeRewriter::analyzeSelect, aborting the server in debug and sanitizer builds) when runningORDER BY ALL(orGROUP BY ALL) over aMerge-engine table joined with another table under the old analyzer (allow_experimental_analyzer = 0). #109139 (groeneai). - Fixed a wrong result of correlated
EXISTSand scalar subqueries when the correlation appears only in the subquery projection together with a non-correlatedWHEREclause (the subquery could evaluate to false /NULLfor every row). Fixes https://github.com/ClickHouse/ClickHouse/issues/105760. #109186 (novikd). - Fixed a bug where merging a
MergeTreetable that has projections while theenable_block_number_columnorenable_block_offset_columnsetting is enabled produced projection parts containing a spurious_block_number/_block_offsetcolumn. The merged projection part then no longer matched the projection definition and the insert-produced projection parts, soCHECK TABLEandOPTIMIZE ... DRY RUNreported it as corrupted (CORRUPTED_DATA). #109284 (groeneai). - Fix
UNKNOWN_IDENTIFIERerror when runningALTER TABLE ... RENAME/ADD/MODIFY COLUMNon a MergeTree table that has an implicit min-max index over the persistent virtual columns_block_numberor_block_offset(enabled byadd_minmax_index_for_block_number_column/add_minmax_index_for_block_offset_column). #109428 (groeneai). - Fix skip indices being dropped from a data part (or
CHECK TABLEfailing withUNEXPECTED_FILE_IN_DATA_PART) after a full-part-rewrite mutation, such asALTER TABLE ... DROP COLUMNof aMATERIALIZEDcolumn, on aWidepart. #109616 (groeneai). - Fixed stale skip indices (
text,bloom_filter, etc.) and projections after materializing lightweight updates withALTER TABLE ... APPLY PATCHES. Previously the index and projection files that depend on a patch-updated column were left unchanged, so queries using them could return wrong results (e.g.hasTokenmissing an updated row, or a projection returning stale aggregates once the spent patch part was removed). #109709 (groeneai). - Fixes a mutation that references the
_sample_factorvirtual column failing mid-execution withUnexpected const virtual column: _sample_factorand leaving the mutation stuck. A mutation now reads it as1, the same value a query withoutSAMPLEreturns. #109813 (groeneai). - Fixed two errors raised when a query with
GROUP BY GROUPING SETS(or a scalar or mutation subquery containing one) runs withforce_aggregation_in_order = 1:Trying to get name of not a column: ExpressionListunder the old analyzer (enable_analyzer = 0), andMemory bound merging of aggregated results is not supported for grouping sets.for a distributed query withenable_memory_bound_merging_of_aggregation_results = 1under the analyzer. #109862 (groeneai). - Fix three issues with
use_constant_folding_in_index_analysis: a logical errorInvalid partition key sizethat could abort aSELECTusing a normal projection when the table setspart_minmax_index_columns = 'with_block_number_offset'; wrong results (silently dropped rows) for a filter on a modulo partition key such asPARTITION BY id % 200; and a crash (data race) when atextindex with asparseGramstokenizer is queried with aLIKEpredicate over many partitions withmax_threads > 1. #109896 (groeneai). - Fix wrong results when a
minmaxskip index is built on aLowCardinality(Nullable(...))column:WHERE/PREWHERE/HAVING ... IS NULLpredicates pushed to storage previously pruned every granule and returned 0 rows even though the column contained NULLs. #110061 (groeneai). - Fixed slow
ORDER BY ... LIMITqueries onDistributedtables whenprefer_localhost_replicaselects a local replica. #110136 (EmeraldShift). - Fixed an exception (
UNION mode UNION_DEFAULT must be normalized) when aDELETEorALTER UPDATEmutation used a set operation (UNION/UNION ALL/UNION DISTINCT/EXCEPT/INTERSECT) inside a subquery in itsWHEREcondition or in anUPDATEassignment. #110196 (alexey-milovidov). - Fixed slow server shutdown and unresponsive
KILL MUTATIONwhen a mutation withx IN (subquery)was building the subquery set during primary-key analysis; the set build is now cancelled promptly. #110198 (alexey-milovidov). - Fixed a race between
KILL TRANSACTIONandALTER TABLE ... UPDATE/DELETEexecuted inside a transaction: a rollback landing between the mutation’s registration in the transaction and its registration in the table left an orphaned mutation entry, which raised a logical error (Cannot find transaction ... that has started mutation ...) in background jobs and blocked all subsequent mutations of the affected parts until server restart. #110226 (tiandiwonder). - Fixed
system.tablesfor aDataLakeCatalog(Iceberg/Glue) database aborting the whole query, or silently dropping tables, when a single table’s metadata is unresolvable. Such a table now stays listed by name with default/NULL values for the columns that need the opened storage object (engine,total_rows, etc.), regardless ofdatabase_datalake_require_metadata_access. Direct access to the broken table still reports the error. #110242 (groeneai). - Join-order cardinality estimation now composes column statistics over the parts surviving partition/PK pruning instead of all active parts. Previously a query pruned to a small partition was planned against table-wide statistics (observed 2500x row overestimation flipping the hash-join build side), and disabling statistics paradoxically produced a better plan. Also fixed: lazy FINAL was silently disabled for MergeTree relations under a JOIN because the join-order optimizer’s memoized index-analysis result was mistaken for an applied projection. #110283 (skuznetsov-clickhouse).
ALTER TABLE ... MOVE/REPLACE PARTITIONbetween two MergeTree tables with incompatible granularity (one adaptive, one non-adaptive) now throwsBAD_ARGUMENTSinstead ofLOGICAL_ERROR. This is a user-reachable condition, so it should not surface as an internal error (which aborts the server in debug/sanitizer builds). #110535 (groeneai).- Fix a
TTL ... DELETE WHERE <cond>never removing rows that started matching<cond>because of anALTER TABLE ... UPDATEon a column referenced in theWHERE. The mutation now recalculates the row TTL, so the newly matching expired rows are deleted instead of being silently retained. #110648 (groeneai). - Fixed a
LOGICAL_ERROR“Invalid partition key size: 0” that could abort anINSERTinto a partitionedMergeTreetable withnon_replicated_deduplication_windowenabled, when an async insert batch was only partially deduplicated (someinsert_deduplication_tokens were duplicates and some were new). #110651 (groeneai). - Fix constant folding under the new analyzer silently changing the type of a constant
Geometryvalue.Geometryis a namedVariantwhose alternatives can share the same storage layout (LineString/Ring,MultiLineString/Polygon, and empties), so two folded constants that differ only in their alternative collapsed into one, e.g. a constantMULTIPOLYGON EMPTYcame out asLINESTRING(). #110709 (groeneai). - Fix a wrong result (mis-ordered merge) for read-in-order queries where
distinct-in-orderoraggregation-in-orderwidens the read to a longer sort-key prefix than the oneORDER BYset up the read-in-order virtual row for. The extra sort columns of the virtual row were default-filled, which could mis-order theMergingSortedTransform(and abort with aLOGICAL_ERRORin debug/sanitizer builds). #110725 (groeneai). - Propagate a query’s active roles (SET ROLE / role=) to remote nodes on parallel-replica and Distributed reads over an interserver-secret cluster; previously they fell back to the user’s default roles, evaluating row policies inconsistently with the initiator. #110867 (evillique).
- Fixed a rare race where a comment-only or settings-only
ALTERof aReplicatedMergeTreetable could silently drop a column added by a concurrentALTER ... ADD COLUMN, leaving a replica on an outdated table structure. #111029 (tiandiwonder). - Fix
NUMBER_OF_COLUMNS_DOESNT_MATCHerror for aGROUP BYwith an aggregate over aMergetable wrapping aDistributedtable, when the grouping key is a function of a column and an aggregate reads the same column (e.g.SELECT toString(g), min(g) FROM merge_over_distributed GROUP BY toString(g)). #111334 (groeneai). - Fix wrong result of a
FULL JOINon aJoinengine table (StorageJoin) withUSINGwhen the left key isNullableand the storage key is not: an unmatched left row with aNULLkey returned0instead ofNULL. #111371 (groeneai). ALTER TABLE ... DETACH PARTITION/PARTnow fsyncs the clone it creates underdetached/(the part subtree plus the directory chain up to the disk root) when the table’sfsync_part_directorysetting is enabled, for both Full (directory) and Packed part storage. Previously the clone was never fsynced while the empty covering part that removes the rows from the active set was, so a power loss right after the acknowledgement could destroy the detached data on btrfs (the un-synced clone directory entries roll back, leavingdetached/empty and nothing toATTACH). Gated on the existingfsync_part_directorysetting; default behavior is unchanged. #111426 (groeneai).- Reject a
Nullable(Nothing)(or any other type not allowed in tables) column atCREATE TABLEtime when the schema is inferred from the storage (e.g. a columnlessremote()/Remote/Distributedtable over a view that doesSELECT NULL), instead of persisting metadata that fails to load on the next server restart and blocks loading of unrelated tables. #111487 (groeneai). - Fix a
LOGICAL_ERROR(Got read request from replica N for unknown stream ...) that could abort the server when reading with parallel replicas and the query is fully answered by a projection optimization (exact-count, minmax-count, or a stored aggregate/normal projection selecting no ranges) on the initiator. #111689 (groeneai). - Fixed DeltaLake partitioned
INSERT(allow_experimental_delta_lake_writes=1) corrupting partition values that contain path characters and rejectingNULL. A value likea/bwas silently committed asa,%produced a data file that could not be read back, and aNULLpartition value failed withNOT_IMPLEMENTED. Partition values are now percent-encoded into a single path segment, the true value is stored inpartitionValuesinstead of being parsed back out of the path, and aNULL(or empty-string) partition value is written as__HIVE_DEFAULT_PARTITION__with a nullpartitionValuesentry. #111793 (groeneai). - Fixed
Expected UInt64 column for __grouping_set, got BLOBandColumnBLOB should be converted to a regular column before usagelogical errors on a distributed query executed on a shard withenable_parallel_blocks_marshalling(enabled by default), for example withGROUP BY GROUPING SETSover a cluster that has a local replica, or when reading aMergetable that spans a local and a distributed table.BlocksMarshallingStepis no longer added to plans whose blocks are consumed in the same process. #111997 (groeneai). - Fixed a
LOGICAL_ERROR(Type mismatch when building statistics for column) during aMergeTreemutation of a table whose column type was changed by a metadata-onlyALTER MODIFY COLUMN(for exampleEnum8toInt8orDateTimetoUInt32) while the column carries statistics. The mutation now recomputes only the statistics of columns it actually rewrites and carries the rest over unchanged, and a column rewritten byMATERIALIZE COLUMN, by anUPDATEof a column aMATERIALIZEDcolumn depends on, or byCLEAR COLUMNnow gets its statistics recomputed instead of keeping stale content. #112009 (groeneai). - Fixes silently missing rows when a query filters on
intDiv(constant, key)ordivide(constant, key). Primary-key and partition analysis reports the wrong direction of monotonicity for a constant divided by a key column, so matching granules are pruned away and the query returns fewer rows with no error. Also fixesintDivwith an unsigned constant dividend whose high bit is set, stops claiming monotonicity when aDecimaloperand makes the division compute in the decimal’s native width, and stops key analysis from raisingCannot compare DB::IPv4 with longfor a valid query that divides anIPv4/IPv6constant by a key column. #112156 (groeneai). - Fixes a parameterized view whose
SELECTbody usesINTERSECT/EXCEPT, or aUNIONchain that mixesUNION DISTINCTwithUNION ALL, being classified as an ordinary view. Such a view was created successfully but its metadata could not be read back: loading it threwInvalid storage definition in metadata file, which made the view permanently unloadable and aborted a debug or sanitizer server on startup.system.tables.parameterized_view_parametersalso reported no parameters for those views. #112211 (groeneai). - Fixes
max_execution_timeandKILL QUERYbeing ignored while a query evaluatesgeohashesInBox. Such a query expanded every row of a block before it could be interrupted, so cancellation took effect only minutes later; the function now checks the query’s time limit as it walks the rows. Evaluations inside a persisted table expression, such as a sorting key, a skip index or aPARTITION BY, are not covered and are tracked separately. #112273 (groeneai). - Fixes a logical error when PREWHERE is used on a Merge table over another Merge table or a materialized view whose column type differs. Also fixes a row policy being silently ignored on tables that read from remote servers (a Distributed table, or a table wrapping one), which could expose rows the policy was meant to hide; such queries are now rejected with an ILLEGAL_PREWHERE error. Define these policies on the underlying local tables on each remote server instead; note that such a policy is still not applied to reads shipped with
serialize_query_plan = 1, which is a separate, pre-existing bug. #112326 (PedroTadim). - Fixes reading a stale secondary (skip) index after an
ALTER TABLE ... MODIFY COLUMNtype change whose mutation never ran, for example becauseKILL MUTATIONremoved it: the granules on disk were written with the old type but decoded with the new one, raisingLOGICAL_ERROR, requesting multi-exabyte allocations, or silently returning a wrong result. Also fixes a wrong result from an index over an expression whose meaning changes while every stored byte stays identical, so no mutation is created at all: aMODIFY COLUMNaltering only aDateTimetimezone, or only a custom type name such asUInt8toBool. Such an index is now skipped for the affected part. #112484 (groeneai). - Fixes a
LOGICAL_ERROR(Cannot add transform Resize to Pipes because it has no outputs) when a query aggregating over a cluster runs withserialize_query_plan = 1and plan-based parallel replicas, caused by the merge step resizing the pipeline to zero streams. #112506 (groeneai). - Fixes a
LOGICAL_ERROR(“Part … contains column … that is absent in table …”) when aReplicatedMergeTreereplica mutates a part it fetched from another replica before applying its own pendingALTER ... ADD COLUMN. Such a mutation is now postponed until the metadata change is applied. #112510 (groeneai). - Fixes the read counters (
read_rows,read_bytes,ProfileEvents['SelectedRows'],ProfileEvents['SelectedBytes']) and theREAD_ROWSandREAD_BYTESquotas being over-charged for queries whoseORDER BYspills to disk. Rows re-read from the external sort temporary files were counted again as source reads, so a spilling query over-reported what it read and could be rejected withQUOTA_EXCEEDEDeven though it stayed within its quota. #112645 (groeneai). - Fixes
ProfileEvents['JoinResultRowCount']under-reporting the result size of aJOINthat spills to disk. Rows emitted from delayed buckets were counted only inJoinDelayedJoinedTransformRowCountand were missing from the total. Query results were always correct; only the profile event was wrong. #112673 (groeneai). - Fixes
max_execution_timeandKILL QUERYbeing silently ineffective for a distributed query that is establishing connections to replicas. Such a query now stops at the next connection attempt instead of working through every remaining replica and retry. #112689 (groeneai). - Fixed
ALTERonMergeTreetables being applied while an incompleteRENAME COLUMNalter-mutation is still in progress. This could update the storage metadata incompatibly and block later mutations completely. #112783 (Michicosun). - Fix wrong results of
ORDER BYwhen reading from aMergetable overDistributedtables: rows could be returned out of order, and rows belonging in the result could be missing whenLIMITwas used. #112931 (alexey-milovidov). - Fixed wrong results when filesystem read prefetch is enabled and a
Nestedcolumn is read beside a subcolumn that was dropped and re-added, on tables withshare_nested_offsetsenabled (the default). Such a query could return default values for a physically present column that theALTERs never touched. This needs no non-default settings on object storage, whereremote_filesystem_read_prefetchdefaults to1; on local disks it needslocal_filesystem_read_prefetch = 1. A separate, prefetch-independent defect still corrupts such reads when a range spans several blocks; that one is not covered here. #112997 (groeneai). - Fixed a
Pipeline stucklogical error in queries that scatter data by partition, such as a window function withPARTITION BYor a join withjoin_algorithm = 'parallel_full_sorting_merge', when one shard’s downstream finished while a block was only partially distributed. #113190 (groeneai). - Fixed a hang of
ALTER,OPTIMIZE,TRUNCATEand partition manipulation statements withalter_sync = 2onReplicatedMergeTreewhen the table starts shutting down while the statement waits for another replica, for example because of a concurrentDROP DATABASE ... SYNC. Such a statement now fails withUNFINISHEDand also honoursmax_execution_timeandKILL QUERY. #113228 (groeneai). - Fixed
Cannot insert element into Set(NOT_IMPLEMENTED) whenread_in_order_use_virtual_rowis enabled and aMergeTreeread-in-order query combinesWITH FILL ... INTERPOLATEwith anINorhasfilter. #113264 (groeneai). - Fix a logical error (exception)
Chunk info was not set for chunk in MergingAggregatedTransformwhen the settinginject_random_order_for_select_without_order_byis enabled and an aggregation query reads from aMergetable containing aDistributedchild: the randomORDER BY rand()wrapper is no longer injected into queries planned only up to an intermediate stage. #113266 (alexey-milovidov). - Fixed rows never being deleted when a
MergeTreetable has two or moreTTL ... DELETE WHERErules whose time expression is identical and which differ only in theWHEREcondition. After a TTL delete merge the part’srows_where_ttl_infowas reset to zero, so no further TTL merge was ever scheduled for it and the expired rows were retained indefinitely with no error or log message. #113384 (groeneai). - Fixed
max_execution_timeandKILL QUERYbeing ignored while a query evaluatesgeohashesInBox,arrayFold,sleep,base58Decode,h3PolygonToCellsorh3PolygonToCellsWithContainmentinside an expression stored in table metadata, such as a sorting key, a skip index or aPARTITION BYkey. #113456 (groeneai). - Fixed a
LOGICAL_ERROR(Reading from materialized CTE '...' before its materialization completed - DelayedPortsProcessor gate is missing in the query plan) raised when aMergetable with more than one child reads a materialized CTE that the outer query references. #113558 (alexey-milovidov). - Fix
Logical error: 'Required output position N is out of range for pass-through inputs'inEXPLAIN ANALYZEover aMergetable with several children, a bare-columnPREWHEREand a separateWHERE. #113725 (groeneai). - Fix a logical error (
Unexpected return type) when a filter over a view whose column type differs from the underlying storage (e.g.information_schema.tables, whereengineisNullable(String), oversystem.tables, where it isString) is pushed down to the storage. #113984 (alexey-milovidov). - Fixed a
Not-ready Set is passed as the second argument for function 'in'error when a mutation whose predicate containsIN (subquery)is cancelled, for example byKILL MUTATIONorDETACH DATABASE, while the subquery’s set is still being built.. #114463 (groeneai). ALTER TABLE ... DROP COLUMNof a column that is used in the sorting, primary or partition key now fails withALTER_OF_COLUMN_IS_FORBIDDENand an explanation, the same asALTER TABLE ... CLEAR COLUMNdoes. Previously it failed with a confusingUNKNOWN_IDENTIFIER: Missing columnserror coming from the recalculation of the key expressions. The same applies to dropping or clearing a wholeNestedgroup by its common prefix when a column of the group is used in a key; previously that could silently destroy the group’s data or leave a mutation that never finishes. Dropping a column that is used only in aTTLexpression now also reports which TTL expression it breaks #114468 (m7kss1).- Fixed a column that was renamed, dropped and added again in one
ALTERreturning the dropped column’s values instead of its own default while the mutation was still pending. #114601 (tiandiwonder). - Fix reading a subcolumn (
.size0,.null, a tuple element,String.size) through aMergetable when the underlying table declares the parent column asALIAS. Such a read returned the type default, and when the parent was selected in the same query it could return the value of an unrelated column. #114673 (groeneai). - Fixed rows missing from the result of a
SELECTfiltered bypointInPolygonover a MergeTree primary key when the polygon argument is an invalid constant literal andvalidate_polygons = 0. Primary key analysis derived granule pruning from a polygon it never validated, and dropped granules holding matching rows. #114710 (groeneai). - Fixed the formatting of
ALTER TABLE ... MATERIALIZE INDEX,MATERIALIZE STATISTICSandMATERIALIZE PROJECTION, which dropped theIF EXISTSclause, so the formatted query was not the query that was parsed. Also rejected fourALTER ... STATISTICSAST JSON payloads that the SQL parser cannot produce and that formatted into a different command. #115099 (groeneai). - Fixed an out-of-bounds read of the cached dictionary hashes when aggregating or joining on a
LowCardinality(String)key whose dictionary grows while it is being aggregated, reachable fromTTL ... GROUP BY. It could yield a wrong hash, and a reported heap-buffer-overflow in sanitizer builds. #115604 (groeneai). CREATE TABLEandALTER TABLEnow reject a lossy codec such asSZ3on a column used in the partition key. #115626 (groeneai).- Reading through a
Mergetable that matches anAliastable whose target is a parameterized view no longer raisesLOGICAL_ERROR: Table has no columns.(which aborts the server in debug and sanitizer builds). Such a table now reportsSTORAGE_REQUIRES_PARAMETER, the same error the parameterized view itself reports. Any other matched table that cannot supply columns now reportsUNSUPPORTED_METHODinstead of an internal error. #115896 (groeneai). - Fixed the on-the-fly preview of a pending mutation whose expression references
_sample_factor. Withapply_mutations_on_fly = 1and aSAMPLEclause, the expression was evaluated with the reading query’s sample factor instead of the value the background materialization persists, so previewed rows disagreed with the eventual materialized result, and a pendingALTER TABLE t DELETE WHERE _sample_factor > 1could make aSAMPLEread return no rows at all. The query’s own_sample_factorcolumn is unchanged. #115906 (groeneai). - Statistics files with format version
V3are now read instead of rejected withILLEGAL_STATISTICS. Such files were produced only by builds ofmasterfrom a narrow window, but parts containing them were completely unreadable, and on a readonly diskALTER TABLE ... MATERIALIZE STATISTICScannot regenerate them. #116030 (alexey-milovidov). - Fixed
Invalid number of columns in chunk pushed to OutputPort(LOGICAL_ERROR) when aSELECT ... FROM t STREAM ... ORDER BY ... LIMIT nquery read a table with aPROJECTION. Lazy materialization is no longer applied to aSTREAMread. #116187 (groeneai). - Do not replicate
ALTER TABLE ATTACH PARTITION FROMfor replicated db. #94835 (MikhailBurdukov). - Fixed a server abort (in sanitizer builds) and silent data loss during table startup when a rolled-back transactional MergeTree part intersects a committed part.
PartLoadingTree::addnow correctly identifies which of the two intersecting parts was rolled back, evicts it, and re-parents its orphaned children under the committed part in the correct order. #100992 (tuanpach). - Fixed
indexOfAssumeSortedreturning incorrect results forArray(LowCardinality(String))columns in MergeTree tables. #101771 (Onyx2406). - Fix
LOGICAL_ERRORexceptions when readingIcebergorDeltaLakedata lake tables through paths that can reach the read pipeline without a pinneddatalake_table_state, such as concurrentIcebergmetadata updates ormergereads overDeltaLaketables. #102033 (groeneai). - Fixed
Logical error: Parsed partition value: ... doesn't match partition value for an existing part with the same partition ID: ...thrown byOPTIMIZE TABLE ... PARTITION ...(and other queries that resolve a partition value throughconvertFieldToTypeImpl) on tables with aTime-typed partition key. #106202 (groeneai). - Fixes a data part being incorrectly marked as having a broken projection after a lightweight delete leaves no projection part behind — either when the projection is rebuilt with zero output rows or when it is dropped (
lightweight_mutation_projection_mode = 'rebuild'/'drop'). The broken state previously disabled projection optimization for queries on that part. #106273 (tiandiwonder). - Fixed memory/CPU/mutation overload warnings in
system.warningslagging one asynchronous-metrics cycle behind (and being absent on the first cycle). #108035 (alexey-milovidov). - Fixed a
LOGICAL_ERRORexception (ScatterExchangeStep should have one source shard, got 8) and duplicated rows when querying aMergetable overDistributedtables or views with the experimental settingmake_distributed_plan = 1. #108401 (groeneai). - Fixed a server abort (
Sort order of blocks violated) during a merge, and incorrect reads, when a non-nullableLowCardinalityelement of aNullable(Tuple(...))column is used as a subcolumn (for example as a sort key). #108679 (groeneai). - Fix a crash in the vendored Azure SDK
Base64Decodereachable viaazureBlobStorage()(and related Azure table functions/storage) when the account key is not valid Base64. A byte>= 0x80caused an out-of-bounds read of the decode table, and any invalid byte caused an undefined left shift of a negative value (abort under sanitizers). Invalid keys are now rejected cleanly. #108716 (groeneai). - Fixed a
LOGICAL_ERROR(“New empty part is about to materialize but the directory already exist”) that could abort the server in debug and sanitizer builds when aDROP/DETACH/MOVE/REPLACE PARTITIONon a MergeTree table ran after a previous such operation was interrupted (for example a rolled-back transaction or a crash) and left a staletmp_empty_<part>directory behind. The stale directory is now reclaimed instead of failing. #108879 (groeneai). - Fix a projection returning a column’s type default (e.g.
0) instead of its DDLDEFAULT(e.g.-1) after the column’sTTLexpired on a wide part. #109013 (tiandiwonder). - Fixed
ALTERqueries failing forMergeTreetables created with a customdisksetting (SETTINGS disk = disk(...)). #109199 (Michicosun). - Fix stale and deleted rows appearing when reading tables of a
MaterializedPostgreSQLdatabase through aMergetable. #109339 (vdimir). - Fixed reading Paimon tables partitioned by a
BIGINTcolumn whose value does not fit intoInt32. Such partition values were truncated (for example9223372036854775807became-1), which produced a wrong partition path and failed with a filesystem error. #109510 (groeneai). - Fix a logical error (“Cannot determine row level filter; 0 columns deleted, 0 columns added”) when reading from a
Mergetable whose child table has a row policy whose filter is a bare existing column (for exampleCREATE ROW POLICY ... USING flag). #109843 (groeneai). - IcebergLocal was reporting Azure as the storage type. #109872 (PedroTadim).
CHECK TABLEnow reports the actual corruption for a projection part that failed to load its metadata, instead of a misleadingColumns doesn't match ... Expected: 0 columnserror. #110262 (Algunenano).- Fixed server startup failure for
MergeTreetables on object storage disks when a leftovertxn_version.txt.tmpfile has broken disk-level metadata. #110519 (alexey-milovidov). - Fixed a MergeTree table with a
_part_offsetor commit-order projection becoming permanently unattachable afterallow_part_offset_column_in_projectionsorallow_commit_order_projectionwas disabled and the table was detached or the server restarted. These two settings are CREATE-time gates, so they are no longer re-checked on ATTACH. #110570 (groeneai). - Fix a rare
LOGICAL_ERROR(“Unexpected number of parts to remove from parts_queue”) that could abort the server on aReplicatedMergeTreewhen two partition operations (for example twoMOVE PARTITION/REPLACE PARTITIONqueries, or one racing a backgroundDROP_RANGE) cancelled the background part check over overlapping ranges at the same time. #110738 (groeneai). - Fixed a
Hash is not set for serializationlogical error that could occur when a column with aQuantized(...)codec was nested inside a poolable serialization, for example aMergetable over sources whose same-named column has different types (merged into aVariant). #110776 (groeneai). - Fixed a
std::length_error(LOGICAL_ERROR) when reading a MergeTree table with a pathologicalmax_streams_for_merge_tree_readingvalue (near the maximum ofUInt64) together withallow_asynchronous_read_from_io_pool_for_merge_tree = 1. The setting is now clamped to the same ceiling asmax_threads. #110936 (groeneai). - Fix a logical error
Cannot calculate columns sizes when columns or checksums are not initializedduring a merge of a single-columnMergeTreetable whose only column has a fully-expiring columnTTL(withmin_bytes_for_wide_part = 1). #111169 (groeneai). - Fsync the text index files of a merged or materialized part when
min_rows_to_fsync_after_merge/min_compressed_bytes_to_fsync_after_merge/fsync_part_directoryare set. Previously these files were the only ones in the part left unsynced, so a power loss right after a merge could leave a committed but broken part and cause data loss. #111335 (groeneai). - Fix
OPTIMIZE TABLE ... MANIFESTforIcebergtables when manifest partition tuples do not match their partition spec #111368 (SmitaRKulkarni). - Fix an out-of-bounds read (release) / logical error (debug) in the lazy materialization optimization, and a logical error in
Pipe::unitePipesforMergetables, when the direct-read-from-text-index optimization leaves an unused index virtual column in the read header of aMergeTreetable with multipletextindexes. #111467 (groeneai). - Fix a
LOGICAL_ERROR(“Part level Min-Max index was constructed from unexpected columns set”) that could abort a background merge in debug/sanitizer builds afterpart_minmax_index_columnswas lowered (for example fromwith_block_number_offsetback topartition_key_only). #111511 (groeneai). - Fixed a crash when running a
STREAMread (enable_streaming_queries = 1) over a table with more than one partition and a row policy defined on it. In debug and sanitizer builds it failed with theFilter column ... not found in DAG outputslogical error; in release builds it could segfault. #111572 (groeneai). - Fixed a
LOGICAL_ERROR(Cannot create part with type Compact and storage type Full because table does not support polymorphic parts) raised when aMergeTreetable loads a pre-existingCompactpart while its current settings disable polymorphic parts (non-adaptive granularity). This could happen, for example, when a read-only table shares an on-disk path with another table and reloads its parts viaSYSTEM RESTART DISKor on server startup. In debug and sanitizer builds the exception aborts the server. #111626 (groeneai). - With
fsync_part_directory = 1, the rename of a part directory from its temporary to its final name is now itself made durable by fsyncing the parent directory (and, for moves across parents, the source parent too). Previously only the moved directory was fsynced, so a power loss right after a part was committed could leave the part missing on disk even though its files were already written, losing rows despite the setting being enabled. #111861 (groeneai). - Fixed wrong results and a
LOGICAL_ERRORwhen aNullable(Tuple(...))column is selected together with one of its null-carrying element subcolumns (Variant,DynamicorLowCardinality) from aMergeTreetable. Reading the subcolumn marked the rows that areNULLin the parent tuple asNULLin a column that had already been shared through the substreams cache, so the read of the whole column saw shifted values. #111949 (groeneai). - Fixed hash joins losing their post-build optimizations (conversion to a fixed hash table, the shared runtime filter, right table reranging) whenever an automatic spill-to-disk threshold was configured, which is the default since 26.5. #111972 (alexey-milovidov).
- Fixes wrong results (silently missing rows) for
hasAllTokensand other multi-tokentextindex searches on builds that do not usesimdcomp(every target except x86_64 Linux, e.g. aarch64) whentext_index_posting_list_apply_mode = 'lazy'. The portable bitpacking decoder did not write the decoded zeros for a block whose bit width is zero, so a posting living in a single-posting segment was dropped. The on-disk format is unchanged and existing data reads correctly after the fix. #112178 (groeneai). - Operations now fail when corrupt MergeTree statistics are loaded instead of ignoring the error. #112201 (skuznetsov-clickhouse).
- Fixes silent loss of acknowledged Iceberg writes on HDFS: an Iceberg commit publishes each new metadata file with a conditional write, but
HDFSObjectStoragedropped the condition and performed a plain overwrite, so concurrent writers were all acknowledged while one writer’s snapshot became unreachable. HDFS now refuses a conditional write instead, so storage-managed Iceberg writes on HDFS,CREATE TABLE ... ENGINE = IcebergHDFS(...)with an explicit column list included, now fail. Reads are unaffected. #112437 (groeneai). - Fix
DELETEonKeeperMaptables deleting only the firstmax_block_sizematched rows while reporting the mutation as successfully completed. Also fixDELETEwithkeeper_map_strict_mode = 1not being atomic: it could fall back to unversioned removals and delete rows that were concurrently updated, and it applied the matched rows block by block so a conflict could leave the delete partially applied. #112777 (alexey-milovidov). - Fixed a transient Azure Blob Storage error on the read or merge path — an RBAC permission-propagation
403 Forbidden, a credential authentication failure, or a connect timeout surfaced as408— being misclassified as non-retryable and reported asPOTENTIALLY_BROKEN_DATA_PART(Code 740), raising a false critical alert for a healthy part. These transient failures are now treated as retryable, and a genuinely persistent one surfaces as a plain Azure error instead of a phantom broken data part. #112871 (arsenmuk). - Fixed
listObjectson a prefixed-endpoint Azure Blob Storage disk returning wrong blob names on every page after the first, because pagination bypassed the client wrapper that strips the endpoint prefix. Pagination now re-enters the wrapper via the continuation token so every page’s blob names are correct. #112872 (arsenmuk). - Fix the per-part minmax index over
_block_numberand_block_offsetbeing lost after a table reload when the part was produced by a mutation that does not rewrite the whole part. #112878 (alexey-milovidov). - Fixed an exception (in release builds - an error that prevents the server from starting) when a mutation predicate uses the
mergetable function without an explicit database name. The database of the mutated table is now used, the same way as it is done for table names. #113185 (alexey-milovidov). - Fix wrong results and a
LOGICAL_ERRORwhen reading a subcolumn of a dropped-and-re-addedNestedmember together with a present member of the same group, on aMergeTreetable withshare_nested_offsets = 1. The present members returned values from a later block whenever the read spanned more than one block. #113225 (groeneai). - Fix an arithmetic exception in debug and sanitizer builds when reading from a
Mergetable with a very largemax_streams_to_max_threads_ratio: the number of streams was truncated to 32 bits, so multiples of2^32became zero. Also throwPARAMETER_OUT_OF_BOUNDinstead of invoking undefined behavior when multiplying stream counts for aMergetable exceedssize_t, avoid an overflow when limitingMergeTreestreams to the amount of data, and reject aMergetable read whose aggregate source count would exceed 65536. #113382 (alexey-milovidov). - Fixed writes to the
hdfsdisk: they failed withParent directory doesn't existbecause the generated object keys contain a nested directory prefix, but unlike blob storages HDFS has real directories and does not create the missing parent on file creation.. #113396 (alexey-milovidov). - Fixed the error reported when the
remote/remoteSecuretable functions or theRemote/RemoteSecuretable engines are given a named collection that does not exist together with akey = valueoverride. Such a call was silently reparsed as a positional call, so the override was analyzed as a data expression and the reported error named something unrelated (for example Function with namemergedoes not exist forremote(collection, database = merge(...))). The missing named collection is now reported. #113510 (groeneai). - Fixed an endless loop in background merges and
OPTIMIZEwhenmerge_max_block_size_byteswas set below thebyteSize()of an empty column. An emptyLowCardinalitycolumn still carries its dictionary, so the byte limit could be reached before a single row was merged; the merge then produced empty blocks forever, burning 100% of a CPU core and never releasing its background pool slot. #113585 (groeneai). - Fixed a mutation of a Compact part producing a different result depending on whether the part’s serialization info was still in memory or had been reloaded from
serialization.json, when the table usesserialization_info_version = 'basic'. OnReplicatedMergeTreethis made two replicas holding a byte-identical source part write different mutated parts, and the mutation failed withCHECKSUM_DOESNT_MATCH. #113588 (groeneai). - Backups now report the underlying lock-file access error instead of incorrectly reporting a concurrent backup when the destination storage cannot read the .lock file. #113637 (kirillshokhin).
- Fixed a data race between the background tasks of a
MergeTreetable (statistics refresh, parts refresh, outdated/unexpected parts loading) and the destruction of the table. #113722 (alexey-milovidov). - Fixes
UPDATEand lightweightDELETEreading the wrong tables when the database of the updated table differs from the database of the session. The expression of the mutation is now canonicalized against the database of the updated table, both for themergetable function, which uses the current database implicitly, and for unqualified table identifiers. Previously such names were resolved against the database of the session, so the statement silently updated or deleted rows selected from a different table. #113748 (groeneai). - Fixed an exception when reading a
MergeTreetable by primary-key range layers when a layer produces an empty pipe. #114177 (alexey-milovidov). BACKUP,RESTOREandENGINE = Backupnow authorize the backup location against the user’sSOURCESgrants, the same ways3(),file()andazureBlobStorage()do. Previously a user holdingBACKUPbut notSOURCEScould write a backup to, and read one from, any location the server could reach. Writing a backup now requires theWRITEdirection on the destination’s source (WRITE ON S3,WRITE ON AZURE,WRITE ON FILE) and reading one requiresREAD;Disk(...)destinations stay restricted bybackups.allowed_diskonly. #114405 (groeneai).- Fixed reading a table while a mutation is in progress: a
MATERIALIZEDcolumn could return a stale value, and selecting one together with an updated column could fail with an error. #114561 (tiandiwonder). - Fix a logical error (
Block structure mismatch) when reading aMergetable withmake_distributed_planenabled over children with internal set operations. #114753 (alexey-milovidov). OPTIMIZE TABLEon an object-storage table whose engine does not implement compaction reported success while doing nothing. It now raises an error, like every other engine that does not support the statement. This affectsDeltaLake,Paimon,Hudiand plain object-storage engines (S3,GCS,COSN,OSS,AzureBlobStorage,HDFS, and object-storage-backedURL);Iceberg, which does implement compaction, is unchanged. #114805 (groeneai).- Fix
BAD_DATA_PART_NAME(“Trying to get part name in new format for old format version”) on aReplicatedMergeTreecreated with the deprecated positional syntax underallow_deprecated_syntax_for_merge_tree. A mutation could be reported as done on a replica that had not applied it, and the table could stay readonly after a restart. #114980 (groeneai). - Fixed
Found patch part ... that intersects mutation with version ...(LOGICAL_ERROR) on tables with lightweight updates. A merge of patch parts could produce a patch whose range of data versions spans the data version of an existing part; that patch can then be neither applied nor skipped, so every later operation on the partition failed and the replication queue stalled. Such merges are no longer assigned. #115064 (alexey-milovidov). - Fixed the server failing to start when a
Backupdatabase engine refers to a backup that has become unavailable (for example, when its files were deleted or the underlying storage is inaccessible). The database is now loaded without tables instead of preventing the whole server from starting. #83188 (orloffv). - Fix logging recovery after disk full error. Previously, when the log disk became full, ClickHouse would enter a failed state and continuously spam error messages to syslog (potentially writing 100+ GB/hour), never recovering even after disk space was freed. Now the logging system automatically recovers once disk space becomes available, without requiring a server restart. #93127 (jaehanbyun).
- Fix crash in schema visitor in storage DeltaLake. #97112 (kssenii).
Data lake and external-storage fixes
- Fixed a transient
Code: 499 ... InvalidPart(S3_ERROR) failure on S3 multipart uploads (for exampleINSERT INTO ... hits_s3). MinIO can briefly report a just-uploaded part as missing onCompleteMultipartUpload; this error is now retried like the already-handledNoSuchKey, bounded bys3_max_unexpected_write_error_retries. #109364 (groeneai). - Reject
ALTER TABLE ... DELETEandALTER TABLE ... UPDATEon Iceberg tables whose data file format is notParquetwith a clearNOT_IMPLEMENTEDerror instead of crashing the server or silently corrupting the table. #105893 (groeneai). - Fixed the error
Cannot add column ...: column with this name already existsonINSERT SELECTfrom a table function such asfile,s3orinput, when the same source column is selected more than once. #105982 (crakjie). - Sensitive values passed as HTTP query-string parameters (for example the
param_*query-parameter binding such asparam_secret_key/param_aws_secret_access_key, thepasswordparameter, or S3-stylesignatureparameters) are no longer written verbatim tosystem.text_logor OpenTelemetry spans. TheRequest URIlog line now redacts the values of parameters whose names look sensitive, replacing them with[HIDDEN]. #108475 (groeneai). - Fixed the
_etagvirtual column for theS3QueueandAzureQueuetable engines: it was declared but never populated, soSELECT _etagalways returned an empty string. It now returns the object ETag, like theS3engine and thes3()table function. #108625 (groeneai). - A query reading from or writing to S3 that is cancelled (e.g. by
KILL QUERY) while an S3 request is in flight is now reported as cancelled instead of with a misleading network/S3 error. In particular, a cancelled backup or restore to S3 now reportsBACKUP_CANCELLED/RESTORE_CANCELLEDinsystem.backups. #108673 (jkartseva). - Fixed the
_tagsvirtual column for theS3Queuetable engine: it was declared but never populated, soSELECT _tagsalways returned an empty map. It now returns the object tags, like theS3engine and thes3()table function. #108676 (groeneai). - Fixed SHOW TABLES and SELECT … FROM system.tables returning different results for data lake catalog databases: tables that ClickHouse cannot read are now consistently hidden from both. #109273 (SmitaRKulkarni).
- Fixed a server abort (or, in release builds, a silently unfinalized data file) when an
INSERTinto a Delta Lake table (DeltaLakeLocal/deltaLake) fails after the sink has already started writing, for example when the source query throws mid-insert or the query is cancelled. #109328 (groeneai). - Fix
Not found column or subcolumn c.null in block(andBad cast from ColumnConst to ColumnNullablein debug/sanitizer builds) when runningWHERE col IS NULL/IS NOT NULLon a schema-evolved Iceberg column that was added after older data files were written, withoptimize_functions_to_subcolumns=1(the default). #109515 (groeneai). - Fix
Host is empty in S3 URIerror when a scalar subquery is used as the URL argument of thes3table function (or as an argument of any other table function) inCREATE TABLE ... AS SELECTqueries: scalar subqueries in table function arguments are now evaluated during query analysis. #110254 (CheSema). - Fix a logical error (server abort in debug/sanitizer builds) when a multi-command
ALTERsuch asUPDATE ..., DELETE ...was issued on an Iceberg table. Such mutations are now rejected with a clearNOT_IMPLEMENTEDerror. #110347 (groeneai). - Fixed Iceberg tables producing duplicate field IDs after
ALTER TABLE ... ADD COLUMNwhen the initial schema contains nested fields (Tuple/Array/Map).last-column-idnow records the maximum assigned field ID including nested children, so a subsequently added column no longer reuses a nested field ID and reads no longer fail withICEBERG_SPECIFICATION_VIOLATION(Duplicate field id). #110884 (groeneai). - Fix
DELETE/UPDATEon an Iceberg table silently removing the wrong rows (or throwingNOT_FOUND_COLUMN_IN_BLOCK) when the table contains data files written both before and afterALTER TABLE ... ADD COLUMNandoptimize_move_to_prewhereis enabled. #111693 (groeneai). - Fixed
made_current_atinsystem.iceberg_historybecoming1970-01-01for a retained snapshot afterALTER TABLE ... EXECUTE expire_snapshotson an Iceberg table. Also fixedsystem.iceberg_historyreturning no rows for a table whose metadata has nosnapshot-log. #111781 (groeneai). - Fixed data corruption when inserting into an Iceberg table reached through a
MATERIALIZED VIEW. The data file was written against the target’sCREATE-time column types instead of its live Iceberg schema, so aDateTimevalue was stored as a bare Avrointand read back as microseconds, and withDateTime64(0)orDateTime64(9)the file could not be read back at all. As a consequence,INSERT INTO <materialized view>against a target whose Iceberg schema is wider than the view’s declared columns now raisesTYPE_MISMATCHinstead of writing a corrupt row. #112359 (groeneai). - Fixes writing Iceberg tables whose schema contains a tuple nested inside a tuple. The published table metadata replaced the inner tuple’s field names with positional names
1,2, …, so with the defaultParquetformat theINSERTfailed withINCORRECT_DATAand withAvrothe column could not be read back. #112480 (groeneai). - Fixes a deadlock that permanently wedges a
FilesystemorHDFSdatabase when two queries resolve the same table name at the same time. Every later query against that database blocks forever and cannot be interrupted, so the server needs a restart to recover. #112495 (groeneai). - Fixes
DeltaLaketables returning too few rows when theWHEREpredicate contains a sub-expression the delta-kernel predicate translator cannot handle, positioned under aNOT. Such a sub-expression is now reported to delta-kernel as an explicit unknown predicate instead of as the end of the child iterator, which used to silently truncate the enclosing conjunction. #112652 (groeneai). - Fixed
ALTER TABLE ... EXECUTE remove_orphan_filesdeleting a livemetadata/version-hint.textof an Iceberg table. Its reachable-set entry was built by string concatenation assuming a trailing slash on a value which never has one, so the real hint file was never recognised as reachable and was removed once older than theolder_thanthreshold. Withiceberg_use_version_hint = 1the table then failed to read at all. #113548 (groeneai). - Fix
LOGICAL_ERRORwhen filtering an Iceberg column thatALTER TABLE ... MODIFY COLUMNmadeNullable. #114521 (groeneai). - Fixed a case where
ATTACH TABLEcarrying anENGINE = URL(...)definition did not check theTABLE ENGINEgrant of the engine the URL scheme dispatches to. A user holding onlyTABLE ENGINE ON URLcould attach and then read aFile,S3,AzureBlobStorageorHDFSbacked table, whichCREATE TABLEcorrectly rejects. #115914 (groeneai). - Fix outbound HTTP requests (e.g. `aiGenerate`, `url` table function, S3) failing with `No route to host` on hosts that advertise both IPv4 and IPv6 when only one address family is routable. The HTTP connection pool now falls back to the next resolved address when the first one fails with a network error, instead of propagating the error on the very first request. #103786 (alexey-milovidov).
- Allow reading an Avro
arrayof two-field{key, value}records into a ClickHouseMapcolumn. This is the encoding Iceberg and Spark produce for aMAP<K, V>with a non-string key (Avro native maps only support string keys). Previously such a file could be read asArray(Tuple(key, value))but not asMap(K, V), which failed withType Map(...) is not compatible with Avro array. #109289 (groeneai). - Fixed reading an Iceberg table with a metadata file whose version number is all digits but exceeds the 32-bit integer range (for example
v99999999999999999999.metadata.json). Such a name previously produced an opaquestd::out_of_range(STD_EXCEPTION) instead of a clearBAD_ARGUMENTSerror. #109619 (groeneai). - Fixed the file name of gzip-compressed Iceberg metadata files. ClickHouse wrote them as
v{N}.gzip.metadata.json(the HTTPContent-Encodingtoken), while the Iceberg spec expects thegzextensionv{N}.gz.metadata.json. As a result Spark and other Hadoop-catalog readers could not find the metadata written by ClickHouse. ClickHouse now writesv{N}.gz.metadata.jsonand still reads the legacygzipname for backward compatibility. #109812 (groeneai). - Fixes an Iceberg table whose write format is
Avroserializing a field declared"required": falsewhose type is alist,maporstructas required, with no["null", T]union, so that the data file’s own schema disagreed with the table metadata and other Iceberg readers saw a required field. Such a field is now written as the["null", T]union the Iceberg spec uses for an optional one. #112648 (groeneai). - Fixed
made_current_atinsystem.iceberg_historyreporting the earliest time a snapshot became current instead of the latest one, when the samesnapshot-idappears more than once in the Icebergsnapshot-log(as a rollback leaves it). #113598 (groeneai). - Fixed a hang of up to one hour in
BACKUP/RESTORE ... TO S3, GCS disks and thes3table function when the named collection setshttp_client = 'gcp_oauth'and the Google OAuth2 token endpoint accepts the connection but never answers. The bearer token request no longer inherits the caller’s data transfer request timeout; its response wait is bounded at 10 seconds, matching the STSAssumeRolerequest cap. #115531 (groeneai). - Fix a logical error in the filesystem cache (
Expected file ... not to exist) that could occur when a background cache download failed with a non-ErrnoException(for example whileOPTIMIZE-ing an Iceberg table), leaving an empty orphan cache file behind. #110549 (groeneai). - Fixed caches configured by a maximum number of entries (
SLRUpolicy) silently stopping to admit new entries once that many entries had been accessed at least twice. Affected caches include the Iceberg and Paimon metadata file caches, the Parquet metadata cache, the text index caches, the vector similarity index cache, the compiled expression cache and the ObjectStorageQueue file status cache. #115517 (groeneai). - Fixed Paimon table reads that could fail while the mutable
snapshot/LATESThint was being replaced concurrently. Invalid or stale hints now safely fall back to snapshot listing. #112010 (JiaQiTang98). - Surface the real underlying exception when a zip archive cannot be unpacked because the input buffer (e.g. an S3 read buffer with
restricted_seek = true) refuses a seek or read requested by minizip. Previously the actual error was hidden behind the genericCouldn't unpack zip archive: Code = -100/Couldn't open zip archivemessage. #105103 (groeneai). - Fix iceberg write for metadata without refs #106896 (AVMusorin).
- Fixes an exception when inserting into an Iceberg table whose metadata was created by an external engine and omits the optional
snapshots,metadata-log, orsnapshot-logarrays. Such inserts now succeed instead of failing. #107473 (tiandiwonder). - Fixed a
ReadBuffer is canceled. Can't read from it.error that could occur when an upload to S3 (for example a backup) is retried after a transient read error. #108573 (groeneai). - Fix a
std::future_error(“The associated promise has been destructed prior to the associated state becoming ready”) that could surface, and abort the server in debug/sanitizer builds, when scheduling the final asynchronous S3/Azure multipart-upload completion task failed (for example under thread-pool exhaustion). The real scheduling error is now reported instead. #108730 (groeneai). - Fixed reading ORC timestamps beyond ~year 2262 into a
DateTime64column with a scale coarser than 9. The native ORC reader used to convert every timestamp through a fixedDateTime64(9)intermediate, which overflowsInt64at nanosecond scale and rejected such values withVALUE_IS_OUT_OF_RANGE_OF_DATA_TYPE, even when the requestedDateTime64scale (for exampleDateTime64(6), as produced by Iceberg) can represent the value. Timestamps are now read directly at the requested scale, anddate_time_overflow_behavioris honored when even the target scale cannot hold the value. #109302 (groeneai). - Reading a Paimon table whose schema contains an unsupported nested type (for example a
ROWfield) now reports a clearBAD_ARGUMENTSerror naming the unsupported type, instead of a bareDB::Exception. (OK)with error code 0 and no message. #109762 (groeneai). - Fix reading Iceberg tables partitioned by the same source column more than once (for example
PARTITIONED BY (hours(ts), ts)). Such tables previously failed withCannot add column ...: column with this name already exists (ILLEGAL_COLUMN). #109895 (groeneai). - Data files written by ClickHouse into Iceberg tables now embed Iceberg field IDs for the Avro (
field-id) and ORC (iceberg.id/iceberg.requiredtype attributes) formats, and ORCStringcolumns are written as ORCstringinstead of binary. Previously only Parquet embedded field IDs, so spec-compliant readers (e.g. Spark/iceberg-java) silently returned NULLs from ClickHouse-written Avro rows after a column rename, and could not read ClickHouse-written ORC files at all. #109994 (groeneai). - Fixed a logical error
ChunkInfoRowNumbers does not existonOPTIMIZE TABLEof an Iceberg table containing a data file in a non-Parquet format (e.g.ORC) newer than all position delete files. #110107 (alexey-milovidov). - Fixed reading large
Parquetfiles from uncompressedSTOREDzip entries vias3andfile. #110495 (Gudauu). - Failing to start a background schedule pool (for example the
icebergpool, under global thread pool exhaustion) no longer aborts the server: the error is now a recoverable exception. Readingsystem.background_schedule_poolalso no longer creates those pools as a side effect. #111209 (groeneai). - Fix DLF authentication in the Paimon REST catalog: the request signature was computed once and reused, so every catalog request after the first one failed with HTTP 401 and SHOW TABLES returned an empty list. Also fix HTTP 404 detection in table existence checks. #112015 (JiaQiTang98).
- Fix a
LOGICAL_ERROR(partitions_count > 0) when partitioningIcebergdata chunks emptied by position deletes, e.g. during compaction of a partitionedIcebergtable with fully deleted data files. #112432 (PedroTadim). - Fixed Azure batch object deletion recording no
system.blob_storage_logDelete events when the batch request itself failed, leaving the whole batch unlogged. A Delete event is now recorded for each object on a batch-level failure. #112873 (arsenmuk). - Fixed reading
Paimontables partitioned by aTIMESTAMPcolumn with a precision higher than milliseconds, which failed withscale 6 is not supported, only support scale <= 3. #113401 (JiaQiTang98). - Fixes a crash when reading a data lake table whose schema declares a column with an empty name. Malformed Iceberg metadata is rejected with
ICEBERG_SPECIFICATION_VIOLATION; a schema supplied by a catalog, Delta Lake or Paimon is rejected withAMBIGUOUS_COLUMN_NAME. The check is unconditional, so an Iceberg table that merely retains an unused historical schema with an empty field name also becomes unreadable instead of aborting once that schema is read. #114394 (groeneai). - Reading an Iceberg table whose metadata describes a schema evolution that the Iceberg specification forbids no longer aborts the server. Four spec violations in
IcebergSchemaProcessorwere reported asLOGICAL_ERROR, which is treated as a failed assertion, or reached a fatal assertion unchecked; they now raiseICEBERG_SPECIFICATION_VIOLATION. #114518 (groeneai). - Fixed the
_timevirtual column of theurl/urlClustertable functions always beingNULLon musl-based builds, caused by non-portable parsing of the HTTPLast-Modifiedheader. #111619 (thevar1able). - Fixed a server that refuses to start after a table was created with
CREATE TABLE ... AS url('http://host/**/', ...)whileallow_experimental_url_wildcard_from_index_pageswas enabled. Loading such a table’s metadata re-evaluated the experimental check and failed withSUPPORT_IS_DISABLED, aborting startup. #115095 (groeneai). - A table of the
URLdatabase engine no longer discloses whether a local file exists to a user without the read source grant:EXISTS TABLE, which requires only theSHOW TABLESprivilege, and the resolution of a table no longer probe the filesystem before the grant is confirmed. #113029 (alexey-milovidov). - Apply
query_masking_rulesto messages appended viaException::addMessageso URL-encoded credentials in(in file/uri ...)suffixes fromjdbc()/odbc()errors are not leaked when masking rules are configured. #106916 (gaurav0107).
Formats and ingestion fixes
- Schema-less reads of Arrow
time32/time64(e.g.SELECT * FROM file(..., 'Arrow')) now inferTime64instead ofDateTime64Previously it inferredDateTime64. Anything downstream asserting the old inferred type will break on upgrade. Also adds support for exportingTime/Time64to Arrow with the appropriate Apache Arrow time type selected by precision. #104316 (Maoyao233). - Fixed possible wrong results or out-of-bounds reads when an
INSERTis rolled back after a mid-batch error (for example inBuffertables, asynchronous inserts, or theKafka/RabbitMQ/FileLogengines) while lazy column replication (enable_lazy_columns_replication) is in effect. #108935 (groeneai). - Fixed a parser inconsistency where an
INSERTcolumn list accepted a qualified column matcher written ast.* LIKE '<pattern>'/t.* ILIKE '<pattern>'but rejected its canonicalt.COLUMNS('<regexp>')form, breaking the query format round-trip (and aborting the server in debug/sanitizer builds). #109176 (groeneai). - Fixed
INSERTinto a Microsoft Fabric / OneLakeDataLakeCatalogtable failing withIncorrectEndpointError(HTTP 400) by routing ADLS Gen2 (DFS) writes to the.dfsendpoint host instead of the.blobhost. Note: withremote_url_allow_hosts, both the.blob(read) and.dfs(write) Fabric hosts must be allowlisted forINSERT. #110290 (zlareb1). - Fix writes through data-lake table functions (e.g.
INSERT INTO FUNCTION icebergS3(...)) producing parquet files whose string columns lost theStringtype annotation, making the written data unreadable for external readers such as Spark. Such writes now use the same format settings as writes through the table engine. #110465 (tiandiwonder). - Fixed
system.session_logbecoming unreadable after an Arrow Flight login: theinterfacecolumn is anEnum8, but theArrowFlightvalue was missing from its enumeration, so a login over the Arrow Flight protocol wrote the raw value10and anySELECTreading such a row threw the exceptionUnexpected value 10 in enum. #110758 (alexey-milovidov). - Fixes schema inference for the
TSVfamily of formats inferringStringfor a decimal value written with a leading zero, such as0.0or0.5, whereCSVcorrectly infersFloat64. A first row of such values was additionally consumed as a header byTSVheader auto-detection and disappeared from the result. Also fixesTSKVinferring a numeric type for a value written using an escape sequence, such asx=1\x2E5, which then failed to parse. Values whose leading zero is padding of an integer, such as007, keep inferringStringbecause theTSVvalue parser cannot read them back. #112226 (groeneai). - Fixed schema inference proposing a numeric type for text values the value parser cannot read back. Inference accepted a dangling exponent (
1e+), a missing mantissa (.) or a repeated sign (++inf), whichreadFloatTextPrecisethen rejected withCANNOT_PARSE_NUMBERwhen reading the inferred schema; on aDynamiccolumn this was an error at INSERT time. Inference now validates the number with the parser the reader actually uses. This also removes a case where the inferred type depended onmax_read_buffer_size, and lets valid leading-zero exponent forms such as0e5inferFloat64in TSV as they already do in CSV. It also fixes malformed exponent forms. #112453 (groeneai). - Fixes
KILL QUERYandmax_execution_timebeing ignored for minutes while a row-based input format was parsing one block. Row-based input formats now check for query cancellation every 8192 rows, so a killed insert (in particular a background async-insert flush, which parses up tomax_insert_block_sizerows per block) stops promptly instead of running to the end of the block. #112646 (groeneai). - Fixed the inferred column type depending on the order of previous queries for a source whose schema is cached. Settings that change an inferred type were missing from the schema inference cache key, so the first query’s value decided the type every later query saw:
input_format_try_infer_exponent_floats,max_parser_depth,input_format_json_infer_array_of_dynamic_from_array_of_different_types, and forParquetalsoinput_format_parquet_local_time_as_utc,input_format_parquet_allow_geoparquet_parser,input_format_parquet_skip_columns_with_unsupported_types_in_schema_inferenceandschema_inference_make_json_columns_nullable. Also registers the missing cache key getter forFormand makes theTemplategetter use its own row format’s escaping rule. #113533 (groeneai). - Fixed formatting of a subquery argument of the
viewandviewIfPermittedtable functions when the enclosing query has a trailingSETTINGSclause. Such a query was formatted asview((SELECT ...)), which cannot be parsed back, so the query failed the internal format-parse-format check and raisedInconsistent AST formatting. #114658 (groeneai). - Fix JSON data misdetected as TSKV during format auto-detection. #106009 (Avogar).
- Exporting
Time/Time64values outside a valid time-of-day (negative or>= 24h) to theArrowformat is now rejected with a clear error instead of writing invalid Arrowtime32/time64data. #107179 (tiandiwonder). - Fixed reading
Arrow,ArrowStreamand Arrow-basedORCfiles whose timestamp column carries a fixed numeric UTC offset (e.g.+05:30,-08:00,00:00) or the non-IANA markerfixedas its timezone, which previously failed withCannot load time zone ...(BAD_ARGUMENTS). #108633 (groeneai). - Serialize a
Nullable(Tuple(...))column as a single CSV field instead of flattening it into separate columns. Previously a non-null value was written as several CSV fields while aNULLwas a single\N, giving a row-dependent column count that brokeCSVWithNames/CSVWithNamesAndTypesheaders and round-trips. #108959 (groeneai). - Fixed an excessive memory allocation during schema inference of the
MsgPackformat: a corrupted or fuzzed input whose array/map/string/binary header declares a huge element count no longer drives a single multi-gigabyte allocation (it is now rejected as malformed input). This fixes anallocation-size-too-bigabort under sanitizers and an out-of-memory condition in release builds. #109019 (groeneai). - Fix a
LOGICAL_ERRORwhen writing aLowCardinality(Time)column to theArrowformat withoutput_format_arrow_low_cardinality_as_dictionary = 1. #109730 (groeneai). - Fixed reading a corrupted
Native-format stream whoseDynamictype count orJSON/Objectpath count is close toSIZE_MAX: such malformed input is now rejected with a clearINCORRECT_DATAerror instead of an uncaughtstd::length_error. #110590 (alexey-milovidov). - Fixed a
LOGICAL_ERROR: Cannot sum Bools(server abort in debug/sanitizer builds) when mergingsumMap/sumMapWithOverflow/sumMapFilteredaggregate states withBoolvalues that were serialized in the old (version 0) state format. The same fix also corrects two silent wrong-result cases with version-0Boolstates: map keys of typeBoolwere not deduplicated across states, and zero-value compaction dropped the wrong entries. #110922 (groeneai). - Fix the
formattable function throwingNOT_FOUND_COLUMN_IN_BLOCKwhen the input data parses to zero rows (for example an empty JSON, or a GeoJSONFeatureCollectionwith no features). It now returns an empty result with the correct columns. #111428 (groeneai). - You can now read GeoParquet, Arrow and ArrowStream files whose WKT-encoded geometry column uses a non-uppercase type keyword (
point(1 2)) or an empty container (LINESTRING EMPTY,POLYGON(),POLYGON(())). Such files previously failed to import withBAD_ARGUMENTSorCANNOT_PARSE_NUMBER, even though the same strings parse with thereadWKTfunction. This also fixes reading back a file that stores the output ofwktfor an empty geometry, which is spelledLINESTRING(). #112020 (groeneai). - Fixed
Unknown compression methodbeing thrown for theautoandnonecompression hints written in any letter case other than all-lowercase, for examplefile('data.csv.gz', 'CSV', 'x String', 'AUTO'). Codec names such asGZIPwere already accepted case-insensitively; the two special hints now behave the same way. #113631 (groeneai). - Fixed reading a
Tuplecolumn whose first element isNULLin theCustomSeparated,RegexpandTemplateformats with theCSVescaping rule. A bareTupleoccupies one field per element there, so the leading\Nfield is that element and not the whole column. Previously it was consumed as the whole column, shifting the remaining element fields onto the following columns, soCustomSeparatedcould not read back its own output. #114471 (groeneai). - Fixed the
Regexpinput format silently discarding the unparsed rest of a matched field, which stored a truncated or fabricated value instead of reporting an error:v=-1intoUInt64read0, andv=2020-01-01junkintoDateread1975-07-14under theJSONrule. Malformed fields are now rejected under theEscaped,CSVandJSONrules, matching the formats those rules are documented as behaving like. UnderRaw, a matched field containing a tab is now read as a whole instead of being truncated at the tab. #114520 (groeneai). - Fixed an
Inconsistent AST formattinglogical error (server abort in debug and sanitizer builds) forCREATE INDEX/CREATE HYPOTHETICAL INDEXexpressions that could not survive a format-parse-format round trip, for exampleCREATE HYPOTHETICAL INDEX i0 ON t0 ((a())) TYPE aandCREATE INDEX i0 ON t0 ((a, b).1) TYPE minmax. #109175 (groeneai). - Fix a logical error (
Logical error: 'KeyCondition uses PREWHERE output') in the Parquet v3 reader that occurred when a format-level key condition references a column that is not read from the Parquet file. #109267 (groeneai). - Fix exception in predefined HTTP handler on absent header #108324 (vitlibar).
- Fixed a possible
Logical error: 'ReadBuffer is canceled. Can't read from it.'when a row-based input format (e.g.TSV/CSV) failed to parse input and the underlying read had already been canceled (for example a malformed HTTP chunk or a truncated body). Building the verbose parse diagnostics no longer reads from a canceled buffer. #109708 (groeneai). - The
Valuesformat no longer records parse errors insystem.errorsandsystem.error_logwhen streaming parsing successfully falls back to SQL expression parsing. #111141 (pamarcos). - Fix
SYSTEM REFRESHon a stoppedRabbitMQtable sometimes failing to consume the queued backlog. A REFRESH grants exactly one out-of-order streaming cycle, but that cycle could return immediately with zero rows because the AMQP delivery loop had only just been started asynchronously, wasting the single permit and leaving the messages unconsumed until the nextSYSTEM START. #111480 (groeneai). - Fixes a
NATStable withnats_streamset silently consuming nothing after the NATS server is restarted. TheJetStreamsubscription is now re-established automatically instead of requiringDETACH TABLEandATTACH TABLE. #112828 (groeneai). - Reject an absurdly large
kafka_num_consumerswith a clear error instead of failing an allocation inside theKafkatable engine. Previously, withkafka_disable_num_consumers_limitenabled, such a value produced astd::length_errorexception. #114764 (alexey-milovidov). - Arrow Flight server: accept
Basicauthentication credentials encoded in Base64 without padding, as sent by Go-based Flight clients such as the ADBC Flight SQL driver, and return a proper authentication error instead of a genericUnexpected error in RPC handlingwhen theauthorizationheader is malformed. #115085 (alexey-milovidov). - Arrow Flight server: reject malformed Basic authentication credentials that omit the username/password separator. #115347 (alexey-milovidov).
Data type, function, and serialization fixes
- Fix a logical error
Stream <col>.variant_discr ... is not found(server abort in debug and sanitizer builds) whenALTER TABLE ... MODIFY COLUMNchanges a column into a type with dynamic subcolumns (for exampleVariantintoDynamic) on a table with Wide parts, and the part is later read or merged. #110204 (groeneai). - Fixed reading a subcolumn of a
Nullable(Tuple(...))column whose element is a non-nullableLowCardinality(T)when the same query also reads the parent subcolumn. On a Compact part the parent returned wrong values, or the query failed withUnexpected return type from assumeNotNull, or the server was killed by astring_viewhardening assertion, because the extracted subcolumn was deserialized into aLowCardinality(Nullable(T))buffer whose substreams were then shared with the parent’s read. #112232 (groeneai). - Fixed a
LOGICAL_ERROR“Not-ready Set is passed as the second argument for functionglobalNullIn” and the loss of the query’s real error message, which could happen when statistics part pruning speculatively executed anIN/GLOBAL INsubquery during index analysis and that subquery failed. #114121 (groeneai). - Fixed
Cannot read all data of type FixedStringwhen reading theproduct_quantization_codebooksubcolumn of a column with aQuantized('product', ...)codec. The per-part codebook is written after the data of all granules, so it is not delimited by marks, and a read whose mark range ended before the part’s final mark could read it short. #112818 (alexey-milovidov). - Stopped writing a part whose
Maplayout contradicts its own metadata, for aMapvalue inside aDynamiccolumn. Tables differing only inmap_serialization_versionshared one cached serialization object, so a part could get thebasicMap layout while its metadata declaredwith_buckets, and reading it later failed withLOGICAL_ERROR: Stream ...buckets_info ... is not found. Parts an earlier version already wrote this way stay unreadable and have to be dropped. #113514 (groeneai). - Fixed
ALP,FPCandGCDomitting their type-derived parameter from the codec hash. In a Compact part the writer shares one compressed stream per distinct codec hash, so two columns of different value width under the same codec collided and shared one codec object carrying the wrong width. This rejected valid inserts withCANNOT_COMPRESSwhen the widths disagreed, and otherwise compressed one of the columns worse than it should have. #114189 (groeneai). - Fix incorrect index pruning when a table’s sorting key wraps a
Datecolumn intoDateTime(for exampleORDER BY toDateTime(date_column)) and a query filters on the original column with a comparison likeWHERE date_column >= '...'.toDateTime(Date)overflows forDatevalues beyond theDateTimerange (after2106-02-07), so the stored key is non-monotonic; ClickHouse will no longer use primary key pruning for this key/predicate combination because doing so could drop granules that contain matching rows. #101814 (nihalzp). - Fixed
ALTER MODIFY COLUMNfailing when converting aNullablecolumn to a different type with aDEFAULT(e.g.Nullable(UInt8)→String,LowCardinality(String), orArray(UInt8)). #102156 (il9ue). - Fixed incorrect SQL literal escaping in
StorageSQLiteandsqlite()table function when pushingWHEREpredicates to SQLite: single quotes and control characters (\n,\r,\t,\) were escaped with backslashes, which SQLite does not interpret, causing syntax errors or wrong query results. Also fixed the escaping of string literals pushed down to PostgreSQL: strings nested insideINlists kept ClickHouse escaping, and control characters were sent as backslash sequences that PostgreSQL reads back as different bytes. #104217 (tiandiwonder). - Fix
NOT_FOUND_COLUMN_IN_BLOCKexception inINNER JOIN ... ON arrayJoin(...) = ...queries whenquery_plan_convert_join_to_inis enabled and the SELECT references the source array column. #104809 (tiandiwonder). - Fix IN and NOT IN expressions with non-constant right-hand side operands referencing columns from the current row, and align new analyzer tuple right-hand side handling with existing ClickHouse IN semantics. Under the old analyzer, a bare source column as the right-hand side (
x IN (arr)) still resolves as a table name and stays out of scope. #104993 (niyue). - Fixed
JOINagainstEmbeddedRocksDB,Redis, andKeeperMaptables that could silently return zero rows whenDirectKeyValueJoinis used withStringorNullable(String)keys. #105253 (vdimir). - Fix a
LOGICAL_ERROR(“Unexpected expression in JOIN ON section. Expected boolean (UInt8), got ‘Nothing’”) when a non-equiJOIN ... ONpredicate references aNothing-typed column, such as one produced byARRAY JOIN []. #106981 (groeneai). - Fixed an
Inconsistent AST formattinglogical error that could abort the server in debug and sanitizer builds when an aliased lambda was used as the operand of an access operator (.Ntuple element or[]array element) at a non-first position of an expression list, e.g.SELECT 1, ((p0, p1) -> p0 AS a7).4[3] FROM t. #107092 (groeneai). - A cluster table function (
urlCluster,fileCluster,s3Cluster, …) nested inside another distributed query, such asclusterAllReplicas(..., urlCluster(...)), is now rejected with aBAD_ARGUMENTSerror instead of failing with a logical error (Distributed task iterator is not initialized). #107107 (groeneai). - Fixed the
valuestable function failing withARGUMENT_OUT_OF_BOUNDwhen a decimal literal that is not exactly representable in a narrow floating-point column (such as0.1for aFloat32column) is used, e.g.SELECT * FROM values('x Float32', 0.1). Such values are now accepted and converted to the nearest representable value, consistent withCASTandINSERT ... VALUES. #108055 (alexey-milovidov). - Fixed
mapFilter,mapSort(and its variants) andmapConcatdroppingLowCardinalityfrom the key and value types of aMap. PreviouslymapFilterover aMap(LowCardinality(String), String)column returnedMap(String, String), which could corrupt the metadata of a table created viaCREATE TABLE ... AS SELECTand makeCHECK TABLEfail. #108057 (alexey-milovidov). - Fixed a cancelled or
KILLedINSERTcontinuing to run for a long time while building a skip index (for example an unboundedset(0)index on a high-cardinality column). The index build now stops promptly when the query is cancelled. #108351 (tiandiwonder). - Fixed a
SELECTfrom theprimestable function not responding to cancellation: with a largestep(orlimit) the query could keep running for a long time afterKILL QUERYor a timeout. It now stops promptly. #108353 (tiandiwonder). - Fixed parsing of data-type names whose name contains the substring
INT(for example a function-like name such asquantileInterpolatedWeightedused in a data-type position). Such names were mistaken for MySQL integer types and had their first(...)argument group silently consumed as a display-width modifier, which could break the query formatting round-trip. #108354 (groeneai). - Fixed an inconsistency where a malformed
ARRAY JOINfollowed by a comma and a parenthesized table list was misparsed as a cross join instead of being rejected. #108365 (Algunenano). - Fix a
LOGICAL_ERROR(“Invalid action query tree node …”) exception when a distributed query both deduplicated structurally-identical duplicate-ALIAScolumns and referenced a table function with akey = valuenamed-collection argument (for exampleoss(s3_conn, filename = '...')). #108435 (groeneai). - Fixed a logical error (an exception in release builds, a server abort in debug and sanitizer builds) that could happen for a function call with more than one
LowCardinalityargument over a distributed table, for exampleconcatAssumeInjective((SELECT toLowCardinality('p')), s)used as aGROUP BYkey withremote(). The messages wereDefault functions implementation for LowCardinality is supported only with a single LowCardinality argumentorExpected the argument ... to have N rows, but it has M. #108871 (groeneai). - Fixed
use_client_time_zonebeing ignored forDateTime/DateTime64string literals interpreted on the server (asynchronousINSERT,SELECTliterals). The client now propagates its local time zone assession_timezonewhenuse_client_time_zoneis enabled, so server-side parsing matches the synchronousINSERTpath. #109051 (groeneai). - Fix
max_execution_time(withtimeout_overflow_mode = 'throw') sometimes never cancelling a query: when the internal timeout watcher was already waiting for a query with a later deadline, a query with an earlier deadline registered afterwards could be missed entirely, letting it run long past its time limit. #109792 (tiandiwonder). - Fixed
NOT_FOUND_COLUMN_IN_BLOCKwhen a query over aDistributedtable uses_shard_num(orshardNum()) that becomesNullable(for example underFULL JOINwithjoin_use_nulls = 1) together withORDER BY, with the analyzer enabled. #109798 (groeneai). - Fixed
NOT_IMPLEMENTEDerror (“Method getDataAt is not supported for Nullable(String)”) that could be thrown by a join withenable_join_runtime_filters = 1(on by default since 26.2) when the build-side join key wasLowCardinality(Nullable(...))with NULLs and the runtime filter fell back to its bloom filter. #109824 (groeneai). - Fixed a server crash when a lambda expression was passed where a higher-order function expects a concrete value (for example the accumulator of
arrayFold, as inarrayFold(lambda, arr, another_lambda)). Such queries are now rejected withILLEGAL_TYPE_OF_ARGUMENTinstead of crashing whenenable_analyzer = 0. #109840 (groeneai). - Reject the
unnestalias ofarrayJoinin row policy filters, closing a gap where such a policy could raise acolumn->size() == num_rowslogical error at read time. #109973 (Algunenano). - Fixed skip indexes defined on
Tuplesubcolumns not being used when the field is accessed viatupleElement(t, 'name'),tupleElement(t, N), ort.Nwhile the full tuple is also read in the same query (e.g.SELECT *). All of these forms now prune granules the same way as the named subcolumn accesst.name. #110056 (groeneai). - Fixed
BAD_GETerror when comparing anArray(String)column with an array literal (arr = ['x']) when the column has atext,tokenbf_v1orngrambf_v1skip index. Such a comparison is now treated as a full scan by the index instead of failing the whole query. #110057 (groeneai). - Fixed a spurious
ILLEGAL_COLUMNerror fromALTER TABLE ... ADD COLUMN IF NOT EXISTSwhen the column already exists at apply time. This happened when the same column was added twice withIF NOT EXISTSin one statement, or when a concurrentALTERadded the column between the prepare and apply phases. #110080 (groeneai). - Fix a
LOGICAL_ERROR(Bad cast from type ColumnLowCardinality to ColumnString) whenidentity()(or a scalar subquery result) wraps a value containing a nestedLowCardinalityand the query usesWITH TOTALS/WITH ROLLUP. #110138 (groeneai). - Fixed a
NOT_FOUND_COLUMN_IN_BLOCKerror when usingGROUP BY ALLover a tuple expression together withORDER BY. #110206 (alexey-milovidov). - Fixed a
NOT_FOUND_COLUMN_IN_BLOCKerror onINSERTinto a table that has aCHECKconstraint referencing a subcolumn (such asx.nullof aNullablecolumn orarr.size0of anArray). #110208 (alexey-milovidov). - Fix wrong query results from the optimize_and_compare_chain optimization (enabled by default): a condition derived by transitivity from a < b AND b < c could use different comparison semantics than the original conditions when the chain mixes types with pair-specific conversion rules (e.g. Enum vs String, Decimal vs Float64, FixedString of different widths). Transitive conditions are now derived only within groups of types sharing a single comparison order. #110621 (yariks5s).
- Fixed
optimize_injective_functions_in_group_bychanging query results forGROUP BY GROUPING SETSandGROUP BY ... WITH TOTALS. The setting is documented as result-preserving, but eliminating an injective function of a grouping key produced a wrong value (or dropped a row) for rows where that key is absent from the aggregated set (aGROUPING SETSnon-member set) or for theWITH TOTALSrow. The wrong value could coincide with a genuine key value, making a super-aggregate row indistinguishable from a real group in the same result set. #110721 (groeneai). - Fix a server hang when multiplying a
median/quantile(ReservoirSampler-backed) aggregate function state by a huge integer constant (e.g.medianState(x) * 18446744073709551615). The query became unresponsive to cancellation and could run indefinitely. #110779 (groeneai). - Fixes a crash (null pointer dereference during query analysis) when
getSubcolumnis called on aJSONargument with an internal type-hint subcolumn name, that is one starting with a backtick type marker. #110940 (groeneai). - Fixed
map[key]returning the value of a later duplicate key instead of the first one. When aMapcolumn contained duplicate keys, the result ofmap[key]could depend on preceding rows in the same block and on theoptimize_functions_to_subcolumnssetting, so aSELECTand aWHEREover the same row could disagree. #111246 (groeneai). - Fix a logical error
!rhs_literal->getValue().isNull()in the logical-expression optimizer when anANDchain compares a column against a NULL-valued constant of a non-Nullabletype (for example a NULL-valuedVariant) withuse_variant_default_implementation_for_comparisons = 0. #111292 (groeneai). - Fixed wrong results when a query has a
setskip index and a predicate of the shape(x AND <null-value>) OR y, where the NULL is produced by a function (e.g.toInt64OrNull('x'),nullIf(1, 1),CAST(NULL AS Nullable(Int64))). Such a value was wrapped by__bitWrapperFuncand propagatedNULLinstead of the “unknown” mask, so the set index could prune granules that actually match and the query returned fewer rows than it should. #111604 (groeneai). - Fix
CREATE TABLE/ALTER TABLEwith the analyzer accepting a columnDEFAULT/MATERIALIZEDexpression that references a virtual column such as_tableor_database. Inserting into such a table failed withNOT_FOUND_COLUMN_IN_BLOCK, and aMATERIALIZEDcolumn (which cannot be supplied explicitly) made the table permanently un-insertable. These expressions are now rejected at definition time, matching the behavior without the analyzer.ALIAScolumns (read-time) andEPHEMERALcolumns (non-stored insert inputs) over virtual columns keep working. #111776 (groeneai). - Fixed
UNKNOWN_QUERY_PARAMETERwhen a parameterized view is called with a subquery-valued argument whose type isArray,TupleorLowCardinality, e.g.SELECT * FROM v(param = (SELECT groupArray(x) FROM t)). #111790 (groeneai). - Fixed three bugs in the analyzer rewrites that turn a chain of comparisons into
IN/NOT IN(x = c1 OR x = c2 OR ...,x != c1 AND x != c2 AND ..., andhas(const_array, x)). For aVariantexpression withuse_variant_default_implementation_for_comparisons = 0the rewrite changed the expression result type fromUInt8toNullable(UInt8), which raised aLOGICAL_ERRORfrom the query-tree validation check in debug and sanitizer builds. For an expression with a dynamic structure (Dynamic,Array(Dynamic),JSON) the rewrite made a working query fail withIllegal type ... of argument of function in, becauseINrejects such arguments. Both directions also returned wrong results when a constant does not convert losslessly to the expression’s type, such as aDateTimeconstant with a time of day compared against aDatecolumn. #111924 (groeneai). - Fix
quantileDeterministic,quantilesDeterministicandmedianDeterministicreturning different results for the same data on every run when the query uses parallel replicas, a distributed table, or external aggregation, or when the states are stored in a table (including inside aDynamicvalue). States written before this version under the unversioned type spelling keep the previous behavior, since their layout cannot change. #112052 (alexey-milovidov). - Fixes wrong results caused by a vector-search query poisoning the query condition cache. A
SELECT ... WHERE <condition> ORDER BY <distance function> LIMIT nquery over a table with avector_similarityindex could record granules as not matching<condition>, so a later ordinarySELECT ... WHERE <condition>on the same table silently returned fewer rows. Vector-search reads no longer write to the query condition cache. #112086 (groeneai). - Fixes
serialize_string_in_memory_with_zero_bytebeing dropped from the serialized query plan. Withserialize_query_plan = 1, a cluster running with the setting disabled could diverge on the in-memory string representation between the initiator and the node executing the deserialized plan, because the setting was read on deserialization but never written on serialization and therefore always resolved to its default on the executing node. #112131 (groeneai). - Fixed a wrong result for a
JOINwhoseONexpression contains a constant conjunct, for exampleON l.id = r.id AND CAST(NULL AS Nullable(UInt8)). The constant was silently dropped by query plan optimization, so the join ran as if it were not there:INNER,LEFT SEMIandRIGHT SEMIjoins returned matched rows where a never-true condition must return none, andRIGHT ANTIreturned no rows instead of all of them. #112155 (vdimir). - Fixes wrong results when a parameterized view is called with an argument that is not a
parameter = valueassignment. Withenable_analyzer = 0, and inEXPLAIN SYNTAXat default settings, calls such aspv(concat(name, '!')),pv(name, 'a'),pv(name != 'a')andpv(tuple(name = 'a'))silently bound a view parameter to an unrelated expression instead of reportingUNKNOWN_QUERY_PARAMETERas the analyzer does. Such calls are now rejected identically on both execution paths. #112194 (groeneai). - Fixes a bug where a
CREATE VIEWorCREATE FUNCTIONwhose definition callssubstr,midorbyteSlicein a form thesubstringgrammar cannot re-parse is accepted, but the metadata ClickHouse writes for it is not valid SQL. Reading that definition afterwards fails withSYNTAX_ERROR, and at server startup it aborts metadata loading, so the server cannot start at all. Such definitions now round-trip, and existing metadata files of the formsubstring(x, a, b, c)become readable again, so an affected server starts up without manual intervention. #112195 (groeneai). - Fixed wrong results, wrongly ordered
ORDER BYoutput and aBAD_TYPE_OF_FIELDexception when the primary key is anArrayorTupleand the query appliesplus,minus,multiply,divideorintDivto it. Arithmetic on a compound value is evaluated element-wise, while the comparison of a compound value is lexicographic, so primary key analysis no longer treats such an expression as monotonic. #112339 (groeneai). - Fixed
Code: 62 ... You must not specify ANY or ALL for PASTE JOIN. (SYNTAX_ERROR)raised by a remote server for a query containing aPASTE JOINthat is shipped to another server, for example when the left table is aDistributedtable or a*Clustertable function. The formatter printedALL PASTE JOIN, a spelling the parser rejects, so the receiving server could not parse the query it was sent. #112404 (groeneai). - Fixes a column alias list such as
(SELECT 1) AS t(x)orWITH t(x) AS (SELECT 1)not being visible inside theviewtable function, where a reference tot.xfailed withUNKNOWN_IDENTIFIERalthough the same construct works outsideview. #112690 (groeneai). - Fixes a logical error in query formatting when an
ARRAY JOINclause carries no expressions. Such a clause is now rejected while parsing withSYNTAX_ERROR, consistent with an emptyUSING, instead of being accepted and formatted into text that cannot be parsed back. #112699 (groeneai). - Fixed
connectionIdthrowingContext has expiredwhen expression actions prepared for a subquery run after their build context has been released. #112870 (clickgapai). - Fixes a logical error and wrong results when a non equi
JOIN ONcondition that is evaluated during the join containsarrayJoin. Ahash,parallel_hashorgrace_hashjoin now rejects such a condition withINVALID_JOIN_ON_EXPRESSION. Where the expansion depends on one side only, move it into anARRAY JOINin a subquery before the join; a condition whosearrayJoinargument reads columns from both sides has to be restructured. #112889 (groeneai). - Fixed
geohashesInBoxspending an unbounded amount of time on a box of zero area, such asgeohashesInBox(0., 0., 180., 0., 12). Such a query returned the correct single geohash but burned roughly 5e8 empty loop iterations per row and ignored bothmax_execution_timeandKILL QUERY. #112976 (groeneai). - Reject a
SETTINGSchange marked as written without a value when it carries a value other thantrue, and never elide the value of such a change when formatting a query. Previously a crafted AST JSON payload could execute aBoolsetting withfalsewhilesystem.query_logandformatQueryFromJSONshowed the valueless form. #113025 (alexey-milovidov). - Fixed
UNKNOWN_IDENTIFIERwhen selecting from aVIEWwhose declared column types differ from the types its inner query produces, for exampleSELECT sum(length(arr)) FROM vwherevdeclaresarr Array(UInt8)over aStringcolumn. With the defaultoptimize_functions_to_subcolumns = 1the optimizer rewrotelength(arr)into a read of thearr.size0subcolumn and forwarded that name into the inner query, which has no such subcolumn. #113057 (groeneai). - Fixed cancellation of
postgresqltable function and PostgreSQL engine reads. Cancelling while the read was still starting up could race on the transaction pointer, and the cancellation could be lost entirely, leaving the query running until PostgreSQL finished it. A cancel that reaches PostgreSQL before theCOPYstatement has begun executing can still be dropped. #113150 (groeneai). - Fixed a
LOGICAL_ERROR(Next task callback is not set for query) when aSQL SECURITY DEFINERorSQL SECURITY NONEview over a cluster table function (s3Cluster,urlCluster,fileCluster) was read as a secondary query, for example throughremoteorclusterAllReplicas. The unsupported nesting is now rejected with a normal query error instead of aborting the server in debug and sanitizer builds. #113371 (groeneai). - Fix a logical error
IDENTIFIER is not a table expressionwhen a remote-family table function with aview(...)argument is used on the left side of aJOIN ... USINGwhose key is aSELECTlist alias, withanalyzer_compatibility_join_using_top_level_identifier = 1. #113400 (groeneai). - Fixed
Not-ready Set is passed as the second argument for function 'in'when a query filterssystem.databaseson thenamecolumn with a subquery insideindexHint, for exampleSELECT count() FROM system.databases WHERE indexHint(name IN (SELECT 'foo')). Such a query was rejected with aLOGICAL_ERRORon release builds and aborted the server on debug and sanitizer builds. #113556 (groeneai). - Fixed a query over a
Variantcolumn ignoringmax_execution_timeandKILL QUERY. Calling a function with severalVariantarguments resolved the function once per alternative of every argument, and neither of the two loops doing that work checked for cancellation, so such a query could not be stopped until it finished. #113612 (groeneai). - Fixes
ALTER TABLE ... MODIFY COLUMNfailing withCannot specify codec for column type ALIASwhen one statement both turns anALIAScolumn into a physical one (DEFAULTorMATERIALIZED) and specifies aCODEC. #113853 (groeneai). - Fixed a server crash when joining with a constant
ONexpression against a right side that contains anARRAY JOIN. Reading a lazily replicated right-side column used a freedColumnReplicated. #113901 (groeneai). - Fixes a memory leak with
optimize_aggregation_in_order = 1when the table sorting key is a strict prefix of theGROUP BYkey and an aggregate function whose state owns heap memory is used, such asquantileDD. Server memory grows with every such query until restart. #114010 (groeneai). - Fixed
isProbablePrimeonUInt128/UInt256ignoringmax_execution_timeandKILL QUERY. A block of wide values ran to completion inside a single function call with no cancellation point, so a query could run for minutes past its deadline. #114032 (groeneai). - Fixed
Code: 46. DB::Exception: Unknown function exists. (UNKNOWN_FUNCTION)thrown when aPREWHEREclause contains anIN (subquery)predicate andrewrite_in_to_join = 1(ormake_distributed_plan = 1, which force-enables it) is set. The same query spelled withWHEREworked correctly. #114067 (RohithPariki). - Fixed a
LOGICAL_ERRORinarrayAutocorrelationwhen the argument is a non-empty array whose element type isNothing, for exampleSELECT arrayAutocorrelation([arrayMax([])]). Such an argument is now rejected withILLEGAL_COLUMN. The empty array literal[]keeps returning[]. #114249 (groeneai). CREATE TABLEandALTER TABLEnow reject lossy codec such asSZ3on a column used in the sorting key. #114531 (groeneai).- Fixed an infinite, uncancellable loop in functions
hopandwindowIDwhen the span of an interval argument in seconds is a multiple of 2^32 (for example,toIntervalDay(2147483648)): the wrapped subtraction dodged the time-overflow check, and with constant arguments the loop ran at analysis time, where the query could not even be killed. The same interval could also spin a background thread of aWINDOW VIEWforever; such a window view is now rejected at creation. #114607 (alexey-milovidov). - Fix the logical error
Not-ready Set is passed as the second argument for function 'in'for a query with anINsubquery reading from a remote cluster: the query now fails with the actual error from the subquery, such as a connection failure. #114983 (alexey-milovidov). - Fixed an
INSERTinto aLog,TinyLogorStripeLogtable that fails while committing the recorded file sizes. Such an insert could keep its rows in the table even though it reported an error, or leave the data files of an array column inconsistent with each other. Reading aLogorTinyLogtable whose array column holds no elements while its offsets claim some now raises an error naming the column instead of reading past the end of the elements. A partially written elements column was already rejected. #115027 (groeneai). - Fix
TYPE_MISMATCH(“CAST AS Array can only be performed between same-dimensional array types”) for a query with a constant of typeArray(Map(...)), such ashas([map('k', 'v')], m), executed over aDistributedtable or with parallel replicas. #115883 (alexey-milovidov). - Fixed wrong results when joining against a view or
view()whose projected column has the same name as a column of one of its own inner relations, for exampleSELECT expr(x.c0) AS c0 FROM a AS x, b AS y. The join order optimizer merged such an expression into the flattened join graph and then applied it twice, so the query silently returned skewed values. When the expression also changed the column type, the same cause producedAMBIGUOUS_COLUMN_NAMEat plan time instead. #116152 (groeneai). - Fixed an exception in correlated subqueries when outer columns become
Nullableundergroup_by_use_nullswith ROLLUP/CUBE. #100365 (alexey-milovidov). - Reject
max_replication_lag_to_enqueue = 0onReplicateddatabases. The value made the post-recovery unsynced check trivially true and aborted the server withLOGICAL_ERRORin debug and sanitizer builds.0is now rejected at parse time withBAD_ARGUMENTSfrom every source (CREATE DATABASE ... SETTINGS,<database_replicated>server-config block,ATTACHreplay, and upgrade-time replay of existing metadata). The smallest valid value is1. #106006 (groeneai). - Forbid creating minmax skip index on JSON (Object) columns to prevent NO_COMMON_TYPE exception when inserting mixed-type arrays. Use typed subcolumns instead (e.g.,
INDEX idx json.field TYPE minmax). #106094 (linhaojie). - Fix
JSON_QUERY/JSON_VALUE/JSON_EXISTSreturning Dynamic instead of String/UInt8 on Dynamic arguments. #106877 (Avogar). - Fixed a signed integer overflow when a wait-timeout setting, such as
interactive_delay, was set to a huge value: the wait could time out immediately instead of waiting, and the server could abort under the Undefined Behavior Sanitizer. #106961 (groeneai). - Fixed a
LOGICAL_ERROR(“Unsupported argument types”) in thegeohashesInBoxfunction when its coordinate arguments mixed constant and non-constant values, or when a coordinate was aBFloat16. Mixed const/non-constFloat32arguments now work, andBFloat16arguments are rejected with a clear error. #107063 (groeneai). - Fixed the
CSVWithNamesandCSVWithNamesAndTypesheader having fewer columns than the data whenoutput_format_csv_serialize_tuple_into_separate_columnsis enabled (the default). The header (and the types row) now flattensTuplecolumns into their leaf fields with dotted names (e.g.t.a,t.b), so the header column count matches the data. A new settingoutput_format_csv_header_serialize_tuple_into_separate_columns(default1) controls this and can be set to0to restore the previous single-name header. #107371 (groeneai). - Fixed a memory leak in the bundled
mongo-c-driverthat could occur when reading from MongoDB (for example via a MongoDB dictionary or themongodbtable function) if a retryable read error was followed by a failed retry server selection. #107448 (groeneai). - Fix a logical error (
WhichDataType(const_type).isArray()) and server abort during primary-key analysis whenpointInPolygonis called with a constant polygon argument of a wrapper type such asVariantorDynamic(for examplepointInPolygon((x, y), if(c, [(0, 0), ...], NULL))). #107589 (groeneai). - Fixed logical errors (
Bad cast) caused by inconsistent stripping ofLowCardinalitynested insideVariantandDynamiccolumns, for example inconcat,format, and primary-key analysis. #107773 (Avogar). - Fixed executable user-defined function command parameter parsing so placeholders with empty or invalid names are not accepted as parameters. #107983 (goutamadwant).
- Fix
accurateCastOrNullof aTuplewhose element isDynamic/Variant: a genuine sourceNULLwas treated as a conversion failure. For aNullabletarget element a sourceNULLnow stays an elementNULL(matching a plainTuple(Nullable(...))source); for a non-Nullabletarget element a sourceNULLnow nulls the whole tuple (matching the non-Dynamicreference) instead of producing the element default. Parse/overflow failures still null the whole tuple. #108023 (groeneai). - The
mongodbtable function now acceptsoid_columnspassed as a named argument (e.g.oid_columns='_id'), instead of rejecting it withBAD_ARGUMENTS. #108039 (alexey-milovidov). - Allow querying a
range_hashedorcomplex_key_range_hasheddictionary that uses aDateTime64,Decimalor floating-point range with a matching argument todictGet/dictHas. Previously such queries failed withmust be convertible to Int64, and open-ended intervals ofDecimal/DateTime64ranges incorrectly returned the default value. #108052 (alexey-milovidov). - Fix a server abort (
Logical errorinIColumn::insertFrom) when casting anArray(Dynamic)orArray(Variant)toQBitwithaccurateCastOrNull, e.g.accurateCastOrNull(CAST(range(114), 'Array(Dynamic)'), 'QBit(Float32, 114)'). #108288 (groeneai). - Fixed a server crash (
Received signal 4, illegal instruction) that could be triggered by a malformed or desynchronized native TCP protocol stream. Theinitial_addressfield ofClientInfois now validated to be a numerichost:portbefore it is parsed, instead of letting a non-numeric port reach the trappedgetservbynamelibc function. #108410 (groeneai). - Fixed excessive memory allocation when deserializing crafted aggregate function states for
mannWhitneyUTest,rankCorr,largestTriangleThreeBuckets,quantileGK,sequenceMatch/sequenceCountandgroupArrayIntersect. A malformed state could declare a huge element count and make the server try to allocate tens of gigabytes from a few bytes of input; such states are now rejected withTOO_LARGE_ARRAY_SIZE. #108465 (groeneai). - Fixed a logical error (
Bad cast from type DB::ColumnNullable to DB::ColumnString) and possible wrong results when usinggroup_by_use_nullswith aLowCardinalityconstant grouping key inGROUPING SETS/ROLLUP/CUBE. #108771 (alexey-milovidov). - Fix
TYPE_MISMATCHinmapFilter,mapSort,mapReverseSort,mapPartialSortandmapConcatwhen aMapvalue contains a nestedMapwithLowCardinality, e.g.mapFilter((k, v) -> 1, map('a'::LowCardinality(String), map('x'::LowCardinality(String), 'y'))). #108798 (groeneai). - Fixed a
LOGICAL_ERROR(“Inconsistent AST formatting”) that aborted the server in debug and sanitizer builds when formatting aTupledata type that mixes named and unnamed elements (for exampleTuple(a UInt8, UInt16)). #108915 (groeneai). JSONExtractnow honourscast_string_to_date_time_modewhen converting string JSON values toDateTime/DateTime64, consistently withCAST. #109252 (Utkal059).- Fixed
INwith a bare array column on the right argument silently returning a wrong (always-false) result or throwing an exception, so thatx IN arrbehaves likehas(arr, x). #109416 (alexey-milovidov). - Fix a
LOGICAL_ERRORinarrayFoldover a non-constArray(LowCardinality(T))argument (for examplearrayFold((acc, x) -> acc + x, materialize([1, 2, 3]::Array(LowCardinality(Int64))), toInt64(0))), which failed with “Arguments of ‘plus’ have incorrect data types” (a server abort in debug and sanitizer builds). #109462 (groeneai). - Fixed an
ILLEGAL_COLUMNexception inconvby correctly castingFixedStringarguments toString. #109771 (hp77-creator). - Fix wrong results for table-function reads with
FINAL/SAMPLEunderserialize_query_plan = 1, and fix the aggregation hash-table stats cache key for table functions (previously all table functions shared one preallocation entry). #109847 (azat). - Fix parsing of numeric
1/0asBoolvalues inside container types (e.g.Array(Bool),Tuple(Bool, ...)). Previously[1,0]failed withCANNOT_READ_ARRAY_FROM_TEXTwhile[true,false]worked. #109976 (groeneai). - Fixed
NOT_IMPLEMENTEDerror (Method getDataAt is not supported for Nullable(String) in case if value is NULL) when atextindex is built onArray(LowCardinality(Nullable(String)))(includingNestedfields stored that way) and an indexed array contains aNULLelement.NULLarray elements are now skipped during index construction, matchingArray(Nullable(String)). #110055 (groeneai). - Fixed incorrect results from
subBitmap,bitmapSubsetInRangeandbitmapSubsetLimitfor bitmaps still held in the small representation, includingUInt64andInt64element values above2^32, which were truncated to 32 bits. Comparisons in bitmap functions over signed element types now consistently use the unsigned value of the element type, so in anInt8bitmap the element-1is compared as255instead of as the sign-extended4294967295; this also fixesbitmapMin,bitmapMax,bitmapContainsandbitmapTransformon bitmaps that have grown past the small representation. Queries that passed sign-extended thresholds have to be adjusted: over anInt8bitmap,bitmapSubsetInRange(bm, 4294967168, 4294967296)becomesbitmapSubsetInRange(bm, 128, 256). Also fixedgroupNumericIndexedVectorreturning different results forInt8andInt16index columns than for wider index types, andnumericIndexedVectorGetValuereturning0for negative indexes. Corrected the bitmap function documentation, including the subset functions that were described as using 1-based indexing and the signedbitmapBuild/bitmapToArraysupport. #110072 (RamiDarwiche). - Fixed undefined signed-overflow paths in interval arithmetic for interval-kind
WITH FILL STEPand foradd*/subtract*date/time interval functions with extreme deltas, making extreme interval values deterministic instead of relying on signed overflow. Also fixed a stall of interval-kindWITH FILLwith a hugeYEARstep, which previously ran the calendar backward. #110158 (groeneai). - Fixed a quadratic-time blowup (and unresponsiveness to
max_execution_time) in aggregation in order when grouping by multiple keys whose sort order is only a prefix of the grouping key. #110159 (alexey-milovidov). - Fixed comparisons that previously threw
ILLEGAL_TYPE_OF_ARGUMENTwhen the operands have no least common supertype. Arrays such asArray(Int64)vsArray(UInt64)andArray(Int256)vsArray(UInt256)can now be compared lexicographically with=,!=,<,<=,>,>=,IS DISTINCT FROM, andIS NOT DISTINCT FROM. Null-safe comparisons such asisDistinctFrom/isNotDistinctFromnow also compare supported non-array numeric or decimal pairs by value instead of throwing. Equality comparisons also work for arrays with nestedTuple(Nullable(...))elements. #110245 (diegomestre2). - Fix a signed-integer overflow (UBSan) when converting an extreme
DateTime64value (e.g.INT64_MIN) toTime/Time64in a timezone with a negative offset. #110451 (groeneai). - Conversion of out-of-range floating-point values (
BFloat16,Float32,Float64) and ofUInt64values aboveInt64::max()toDateTimeandTimenow reliably saturates to the range boundaries on all architectures (previously the result of an out-of-range conversion was architecture-dependent and could wrap around on x86-64), and non-finite values (NaN,inf) now throw an exception instead of producing an arbitrary result. The same fix is applied to the float-to-Dateconversion path oftoDateand to the conversions ofUInt64and of wide integers ((U)Int128,(U)Int256) toDateandDate32, which could return a pre-epoch day instead of saturating. Conversion of a number toTimenow saturates to the range of the type for every numeric source type (previously values from narrow unsigned or wide integer sources were stored unclamped and clamped only when printed), and the accurate cast toTimeno longer rejects the negative values thatTimesupports, nor silently saturates the values that do not fit into it. The accurate cast of a number toDate32(accurateCast,accurateCastOrNull) no longer silently saturates an out-of-range or non-finite value, but throws or returnsNULLas it already did forDateandDateTime, and theOrDefaultflavours return the default value ofDate32for such an input. The accurate cast of a non-integral floating-point number toDate,Date32,DateTimeorTimeis now rejected instead of being silently truncated. Also fixed thetoDateTime32function documentation, which mistakenly showedtoDateTime64examples and described theDateTime64value range. #110459 (alexey-milovidov). - Fix schema inference failing with
Map cannot have a key of type LowCardinality(Nullable(String))when reading ORC files whose map keys are dictionary-encoded — including ORC files written by Spark and files written by ClickHouse itself withoutput_format_orc_dictionary_key_size_thresholdenabled. #110492 (tiandiwonder). - Fix a logical error (
Function node with name '...' is not resolved as ordinary function) that could occur withoptimize_or_like_chainenabled when an aggregate or window function appeared on the left-hand side of aLIKE/ILIKE/matchinside anORchain. #110641 (groeneai). - Fixed a signed integer overflow in
toStartOfIntervalwith an extremeMONTH,WEEK, orDAYinterval count onDate/Date32/DateTime64values. #110688 (groeneai). - Fixed
readWKTandreadWKTPointreturning uninitialized (scalar) or stale prior-row (vectorized) coordinates forPOINT EMPTY.POINT EMPTYis now rejected withCANNOT_PARSE_TEXT, since a ClickHousePointis a fixedTuple(Float64, Float64)with no empty representation. #110692 (groeneai). - Fixed
flipCoordinateslosing theGeometrytype: for aGeometryargument the result is now typedGeometryagain instead of the underlyingVariant(...), so it can be passed directly to functions likeareaCartesian. #110694 (groeneai). - Fixed non-canonical WKB serialization of an empty polygon:
wkb(POLYGON EMPTY)now emitsnumRings = 0instead of a spurious single zero-point ring, so a WKB round-trip of a standard empty polygon is identity-preserving. #110796 (groeneai). - Numeric
1/0in JSONBoolparsing now honors theallow_special_bool_valuessetting. Previously JSON input such as{"v":1}forVariant(Bool, UInt32)was read asBooleven withallow_special_bool_values_inside_variant = 0, because the JSONBoolparsers ignored the setting andBoolhas a higherVariantdeserialize priority than integer types. #110835 (groeneai). - Fixed
throwIf(notLike(col, pattern))(and otherthrowIfover a function) throwing unconditionally whencolisLowCardinality. The defaultLowCardinalityimplementation ranthrowIfon the whole dictionary, which always holds the reserved default value even when no row references it, so a non-zero value in that unused slot madethrowIfthrow for data that does not satisfy the condition. #110864 (groeneai). - Fixed a crash when an aggregate function with the
-Tuplecombinator was used withRESPECT NULLSand produced an intermediate state (-State, distributed aggregation,WITH ROLLUP/WITH CUBE). The combined function was named after the wrong state variant, so its serializedAggregateFunctiontype re-resolved to an incompatible in-memory layout on a round-trip. #110930 (groeneai). - Fixed
Nested type LineString cannot be inside Nullable type (ILLEGAL_TYPE_OF_ARGUMENT)when reading a nullable MySQL spatial column (LINESTRING,POLYGON,MULTILINESTRING,MULTIPOLYGON,MULTIPOINT, or the genericGEOMETRY) through themysqltable function or theMySQLtable/database engines. Such columns now fall back toNullable(String)holding the value as MySQL returns it (a 4-byte SRID prefix followed by the WKB payload).POINTkeeps mapping toNullable(Point). #110943 (groeneai). - Fixes
Nativeserialization ofSimpleAggregateFunctionwrappers containing versioned aggregate states for older peers. #110997 (groeneai). - Fix
CANNOT_CONVERT_TYPEerror for constants ofVarianttype (includingGeometry) in distributed queries and queries with parallel replicas. #111136 (fm4v). - Fix a
LOGICAL_ERROR(“Lambda resolved type … is not equal to type from actions DAG …”) when a higher-order function’s lambda body is a constant-conditionif/multiIfthat mixes aBoolliteral branch with aUInt8comparison branch, e.g.arrayFilter(p -> multiIf(false, true, p = 'ALL'), ['ALL']). #111245 (groeneai). - Fixed a signed integer overflow in
toStartOfIntervalwith subsecond intervals: forDateTime64values within one interval of the lower bound ofInt64, the rounded result silently wrapped to a positive garbage value; now the function throws aDECIMAL_OVERFLOWexception. #111370 (alexey-milovidov). - Fixed an exception (
CANNOT_CONVERT_TYPEor a logical error) in distributed queries using the-Tuplecombinator with aRESPECT NULLS/IGNORE NULLSmodifier under nested combinators, e.g.anyRespectNullsStateTuple(...) IGNORE NULLS. #111570 (alexey-milovidov). - Fixes a
FixedStringtext try-parse that leaves partially appended bytes in the column when parsing fails, which can cause aSizes of nested column and null map of Nullable column are not equallogical error during serialization ofVariant/Array/Nullablecolumns, or silently shifted result bytes. #111908 (groeneai). - Fix an exception and silently skipped data when reading a dynamic subcolumn (a path inside a
JSONor aDynamiccolumn) through aBuffertable. #111948 (alexey-milovidov). - Fixed quadratic complexity in
countSubstringsCaseInsensitiveUTF8, which made the function take minutes on a haystack of a few megabytes with many matches. The character offset of each match is now maintained incrementally instead of being recounted from the start of the row. #112003 (groeneai). - Fixed a signed integer overflow in the three-argument overload of
toStartOfInterval: for anoriginargument near the lower bound ofInt64the function returned a wrapped-around value instead of reporting an error. #112045 (alexey-milovidov). - Disallow aggregate states in table function parameters, because they are not supported at all. #112087 (PedroTadim).
- Fixed the
valuestable function rendering a non-literalBoolconstant (e.g.CAST(true, 'Nullable(Bool)')) inserted into aString/textual column as1/0instead oftrue/false. #112308 (yakov-olkhovskiy). - Fixes a logical error (which aborts in debug and sanitizer builds) when
finalizeAggregationis applied to a column whose aggregate states come from different functions that share a state representation but finalize to different types — for examplequantileStateandquantilesState(0.9)brought together by aUNIONor produced viaarrayReduce. Such queries now raise a normal error instead of crashing. #112662 (zainulabidin302). - Fixed parsing of integers that do not fit into the target type in
Poco::NumberParser, which is used to read JSON numbers. Such a number was silently parsed as a wrong value (for example,18446744073709551617became1) instead of being rejected, and aUInt64number above theInt64maximum did not survive an AST JSON round trip. #112904 (alexey-milovidov). - Fixed a
Boolconstant on the right-hand side ofINbeing converted to aStringleft-hand side as'1'/'0'instead of the canonical'true'/'false'(now consistent withCASTand thevaluestable function). #113051 (yakov-olkhovskiy). - Fix inconsistent AST formatting of
viewIfPermitted: a function normally written as an operator (e.g.not) in theELSEbranch of the table function form, and the expression formviewIfPermitted(...)being wrongly formatted withELSE. #113652 (alexey-milovidov). - Fix undefined behavior and wrong results in
avgoverDate/DateTime/DateTime64/Time/Time64: the average is now computed exactly in integer space, fixing both theInt64-boundary overflow (UB, wrong result on x86) andFloat64precision loss above 2^53 (visible at nanosecond scale). #113912 (alexey-milovidov). - Fix a memory leak in the experimental
SZ3compression codec: when compression of poorly-compressible or corrupted data failed with an exception, temporary scratch buffers were left allocated. #113916 (alexey-milovidov). - Fixes
hasAny,hasAll,has,indexOf,mapContainsKey,mapContainsValueandmapContainsreturning too few rows, or failing withTOO_LARGE_STRING_SIZE, when abloom_filterindex is queried with aFixedStringconstant. The index hashed the padded form of the constant while the function compares the unpadded one, so a matching granule was skipped. #114089 (alexey-milovidov). - Fix
minandmaxonDateTime64columns returning wrong results once the aggregate is JIT compiled and the data contains timestamps before 1970.is_signedwas not specialised forDateTime64andTime64, which derive fromDecimal64, so the generated code compared tick counts as unsigned integers and a pre-1970 timestamp wonmaxand lostmin. #114168 (groeneai). - Fixes
arrayExists(x -> x = needle, arr)returning a wrong result, or failing withTOO_LARGE_STRING_SIZE, when the array element and the needle are different string types (for example aStringneedle against anArray(FixedString(N))element). Theoptimize_rewrite_array_exists_to_hasoptimization, enabled by default, rewrote such a call tohas, which does not compare zero-padded the way=does. #114496 (groeneai). - Fix text index evaluation of
LIKE/ILIKEoperator built on Map or JSON containers. #114544 (ahmadov). - Fixed undefined behavior and incorrect boundary results in
quantileandquantilesforDateTime64and wideDecimalvalues. #114919 (alexey-milovidov). - Fixed
ifandmultiIfreturning a large positive value instead of a negative one when aTimebranch is combined withTime64,DateTimeorDateTime64and the expression is JIT-compiled. #115146 (groeneai). - Fixed
timezoneOffset(aliastimeZoneOffset) returning an offset wrong by exactly one day for time zones whose UTC offset differs from their 1970-01-01 offset by a whole day, such asPacific/KiritimatiandPacific/Apiaafter they crossed the international date line, and for most zones before 1970. This also fixesparseDateTime,parseDateTimeOrNull,parseDateTime64,parseDateTimeInJodaSyntax,EXTRACT(TIMEZONE_HOUR / TIMEZONE_MINUTE ...),formatDateTime’s%z,toUTCTimestampandfromUTCTimestamp, which consume that offset. #115332 (groeneai). - Fixed
Code: 349. Cannot convert NULL value to non-Nullable typewhen ahasornotHaspredicate carries a NULL array element and the primary key is aString,Array(String)orMap(String, String), and fixed wrong results when that element is aDynamicorVariantholding NULL. The NULL is now dropped from the set during key analysis, as it already was for other key types. #115647 (groeneai). - Fixed
cast_keep_nullablenot preserving nullability when the target type isLowCardinality. Casting a NULL-capable value toLowCardinality(T)threwCANNOT_INSERT_NULL_IN_ORDINARY_COLUMNinstead of producingLowCardinality(Nullable(T)). #115851 (groeneai).
Index and query-cache fixes
- Fix reading subcolumns of a column that has a
DEFAULTexpression and is not materialized in a part (e.g. afterALTER TABLE ADD COLUMNor duringALTER TABLE MATERIALIZE COLUMN): the subcolumn was filled with type default values instead of the evaluatedDEFAULTexpression, and the transposed vector distance functions (cosineDistanceTransposedand others) on suchQBitcolumns failed with theSIZES_OF_ARRAYS_DONT_MATCHerror. #110636 (alexey-milovidov). - Fix the
Not-ready Set is passed as the second argumentexception that could occur when building anINsubquery set during primary key analysis failed silently (for example, a subquery timeout withoverflow_mode = 'break'), leaving the set permanently unbuilt for the query pipeline. #107924 (alexey-milovidov). - Fixed a
SELECTfailing withNOT_FOUND_COLUMN_IN_BLOCKon a table that has asetdata-skipping index when a row policy filters it using an always-true condition combined with a check on a non-indexed column. #107971 (tiandiwonder). getClientHTTPHeaderis 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 the query condition cache (
use_query_condition_cache = 1) reused a skip-index-derived mark exclusion for a query that ran a different set of skip indexes, for example withuse_skip_indexes = 0,ignore_data_skipping_indices, or a differentuse_skip_indexes_for_disjunctionsmode. Skip-index-derived cache entries are now keyed by the effective set of skip indexes that ran, while row-level query-condition-cache entries stay reusable across all of these. #108548 (groeneai). - Fixed a hang where a query reading through the filesystem cache could keep waiting on an in-progress download after being cancelled with
KILL QUERY, and where dropping orSYSTEM STOP VIEW-ing a refreshable materialized view whose refresh was stuck in such a wait would block. #109116 (murphy-4o). - Fix wrong query results caused by the primary key index incorrectly pruning granules for tables with a reversed (
DESC) key column when parts have no final mark (non-adaptive granularity,index_granularity_bytes = 0). #109901 (nihalzp). - Fix the logical error
Expected CommonSubplanReferenceStep to reference CommonSubplanStep, the errorSubplan cannot be used to build pipeline, and the logical errorTrying to extract chunk from ChunkBuffer before all inputs are finishedforIN (subquery)where the subquery contains a correlated subquery and the set is built during index analysis. #110491 (alexey-milovidov). - Fix
NOT_FOUND_COLUMN_IN_BLOCKerror for the transposed distance functions overQBitcolumns (such ascosineDistanceTransposed) called with a scalar-subquery or constant reference vector when parallel replicas are enabled. #110729 (alexey-milovidov). - Fix text index not being used when its expression contains an empty string comparison such as
arrayFilter(s -> s != '', ...), because the settingoptimize_empty_string_comparisonsrewrites the comparison tonotEmptyon the query side only. #112115 (Ergus). - Do not remove columns when it’s referenced in both
PREWHEREandWHEREfilter conditions even if it’s rewritten into a text index virtual column. #114460 (ahmadov). - Fixed logical error
Multi-block postings must be compressedin queries over tables with a text index in the lazy posting-list apply mode, when the query was canceled during the read. #114636 (CurtizJ). - Fixed wrong results when a
WHEREclause contained aNULLexpression under aNOT, for exampleSELECT count() FROM t WHERE NOT (id >= 20 AND NULL). Index analysis treated such a condition as provably true for the whole range, so the filter was skipped:count()returned rows the query rejects, andALTER TABLE ... DELETEwith that condition removed rows it should have kept. #115152 (groeneai). - Fixed a
LOGICAL_ERROR(Join is supported only for pipelines with one output port) for afull_sorting_mergeJOIN withquery_plan_join_shard_by_pk_rangesenabled when one of the two sides is pruned to no parts at all - by the primary key, by column statistics, or because the table is empty. #115352 (alexey-milovidov). - Disable the query condition cache for filters over _part_starting_offset #115358 (azat).
- Fixed
LOGICAL_ERROR: Index with name auto_minmax_index_<column> already existswhen a singleALTER TABLEstatement combinedRENAME COLUMNwith another command, such asMODIFY SETTING, on a table with implicit minmax indices (add_minmax_index_for_numeric_columnsand friends). Renaming a plain column also no longer leaves a followingDROP COLUMNfailing withUNKNOWN_IDENTIFIER, nor a followingADD COLUMNwithout its implicit index. #116063 (groeneai). - A
LIKE/NOT LIKEpattern without wildcards (%,_) now uses an exact primary key range, so it reads the same number of granules as the equivalent=/!=predicate instead of a wider prefix range. #107077 (groeneai). - Fixed a
LOGICAL_ERROR(Invalid binary search result in MergeTreeSetIndex) and missed index pruning in release builds when anIN/NOT INcondition on the primary key wraps the key inintDivby a constant and the key is an unsigned integer whose values cross the signed boundary of theintDivresult type (for exampleintDiv(uint64_column, -9223372036854775807)). #107586 (groeneai). - Fixed
numericIndexedVectorPointwiseMultiplyreturning an empty result when multiplying by an all-ones vector that has a different BSI bit configuration. #108027 (alexey-milovidov). - Fixed a heap-buffer-overflow when building a text index with
positions = 1over many distinct short tokens. #108659 (groeneai). - Allow the
preprocessorandpostprocessorexpressions of a text index to referenceALIAScolumns. #110014 (Ergus). - Skips vector search optimization when
LIMIT ... WITH TIESis used, because the optimization bounds the ANN search to exactly n candidates and rows tied with the n-th row are dropped. #110453 (tamish560). - Fixed the text index not being used for
ILIKEwhen the index is defined over an expression rather than a bare column (for exampleassumeNotNull(col)with alower(...)preprocessor). Such queries now use the index instead of reading all granules. #110595 (ahmadov). - Fix a
LOGICAL_ERROR(LazyMaterializingTransform: Number of rows in lazy chunk N does not match number of offsets M) that could happen for vector search queries withvector_search_with_rescoring = 1combined with lazy materialization when several distances tie. #111003 (groeneai). - Reject vectors whose squared magnitude overflows to infinity when using a
vector_similarityindex withi8quantization (previously such vectors triggered undefined behavior in usearch and produced silent garbage). #111085 (groeneai). - Fixed stale reads on
plain_rewritabledisks with the page cache enabled. #111105 (Michicosun). - Fixed wrong
count()results and dropped rows when aString(or narrowerFixedString) key or minmax skip index is filtered by a comparison with a widerFixedStringconstant, e.g.toFixedString('abc', 257) = string_col. Primary-key and minmax pruning built the range from the NUL-padded constant and wrongly skipped matching granules. #111106 (groeneai). - Fixed reading Parquet files whose offset index does not start at row 0. Such a file could make the native Parquet reader return wrong rows, or report
LOGICAL_ERRORinstead ofINCORRECT_DATA. The offset index is now validated when it is read. #114530 (groeneai).
Security, access, backup, and restore fixes
RESTOREnow fsyncs the restored part files when the destination table hasfsync_after_insertenabled, so a restored part is as durable against power loss as an inserted one. Previously restored files were only written and closed (never fsynced), so a power loss right afterRESTOREreturnedRESTOREDcould leave the parts torn and the table empty. #111378 (groeneai).- Fix a security issue where an unauthenticated TCP client could probe table existence and replication status via the interserver port. #99854 (tiandiwonder).
- Fixed
DELETE FROM(lightweight delete) requiring theALTER UPDATEprivilege in addition toALTER DELETE. A user granted onlyALTER DELETEcan now runDELETE FROM, as documented. #107491 (tiandiwonder). - Fixed
CREATE TABLE ... AS SELECTon Atomic databases leaving an empty table behind when the query fails — for example when the user has no access to a table referenced from a subquery. Previously a retry reported that the table already exists instead of the original error. The table is now created via a temporary table and becomes visible only after it has been fully populated. #108048 (alexey-milovidov). - Fixed a
LOGICAL_ERROR(No available columns) when executingSELECT count()(or other trivial queries) on a table where the user is grantedSELECTaccess only on anALIAScolumn. #108056 (alexey-milovidov). - Fixes an issue where unauthenticated requests could cause ClickHouse to resolve hostnames supplied in forwarded client-address headers. ClickHouse now accepts only numeric IP addresses from these headers. Invalid values are ignored for forwarded-address quota attribution without DNS resolution. When
auth_use_forwarded_addressis enabled, invalid forwarded addresses are rejected during authentication. Rejections are logged at debug level. #111060 (otselnik). - Fix
SQL SECURITY DEFINER(andSQL SECURITY NONE) not being honored for parameterized views when the analyzer is disabled (enable_analyzer = 0). Previously selecting from such a view as a user without privileges on the underlying table failed withACCESS_DENIED, even though the same view works with the analyzer enabled. #111458 (groeneai). - Fixed a defect in the
MySQLintegrations where overriding a TLS credential of a named collection with an emptyssl_ca_pem/ssl_cert_pem/ssl_key_pemvalue was accepted when the collection stored the credential in the contents form, silently dropping the configured CA or client certificate instead of rejecting the override. #113947 (alexey-milovidov). - Fixed
EXPLAIN ANALYZElosing the join statistics of a view declaredSQL SECURITY DEFINERorSQL SECURITY NONEwhose body contains a JOIN. In debug and sanitizer builds the query aborted with the logical errorJoinStep analyzed without the analyze mode. #115156 (groeneai). - Fix
RESTORE ... AS <new name>leaving theREFRESH ... DEPENDS ONlist of a refreshable materialized view pointing at the original database. The restored view refreshed in response to a table it no longer read, ignored refreshes of its own parent, and stopped refreshing entirely once the original database was dropped. #115689 (groeneai). - Fixed a
LOGICAL_ERRORexception during backup of aReplicateddatabase that is being dropped and recreated concurrently; such backups now fail cleanly withCANNOT_GET_REPLICATED_DATABASE_SNAPSHOT. #100651 (alexey-milovidov). GRANT role TO roleon the same role is now rejected withBAD_ARGUMENTSinstead of silently creating a self-referential entry insystem.role_grants. #103315 (zxuhan).BACKUPof aMaterializedPostgreSQLdatabase no longer hangs forever with “Table … were created or changed its definition during scanning”, and now actually backs up the table data (delegated to the underlyingReplacingMergeTree), which can be restored as a standaloneReplacingMergeTree. #107433 (alexey-milovidov).- Allow
EXISTS <dictionary>for a user that has only theSHOW DICTIONARIESprivilege on the dictionary (previously it requiredSHOW TABLES). #108084 (alexey-milovidov). - Fixed cleanup of a
BACKUP ... TO Memory(...)that fails before finalization: the failed backup was left registered so its name could not be reused. #109947 (jkartseva). - Fixed a
ReadBuffer is canceled. Can't read from it.logical error (server abort in debug/sanitizer builds) that could occur while reading a zip archive (e.g. duringRESTOREfrom a zip backup) after a prior read from the same archive failed mid-stream. #110197 (groeneai). - Fixes heap corruption, which could crash the server at shutdown, caused by the
constmethodAccessRights::getFiltersmodifying an access-rights tree shared by concurrent queries. #112692 (groeneai).
Distributed execution, tables, views, and dictionaries
- Fixed a spurious
Inconsistent AST formattingerror forALTER TABLE ... MOVE PART ... TO SHARD '<path>'. The query formatter dropped theSHARDkeyword, so the reformatted query could not be parsed back, raising aLOGICAL_ERROR(a handled exception in release builds, an abort in debug and sanitizer builds). #109060 (groeneai). - Fixed a logical error (
!part.empty()) when aDistributedtable defined with an empty remote database name is used in a JOIN withdistributed_product_mode = 'local'. #110677 (groeneai). - Fixed
DISTINCTwith an orderedLIMIT/OFFSETreturning wrong rows when the DISTINCT early-stop limit hint could not bound the head of the result: a negativeLIMIT(returned the head instead of the tail), a fractionalLIMIT/OFFSET(the fraction is only resolved after a full read), and a bareOFFSETwith noLIMIT(the tail after the offset was dropped). For exampleSELECT DISTINCT intDiv(x, 100) FROM t ORDER BY intDiv(x, 100) LIMIT -1over a multi-part table returned the smallest distinct value instead of the largest. #111326 (groeneai). - Fixes the case where an alias after a subquery in DESCRIBE TABLE is not accepted by the parser and results in a syntax error. Fixes https://github.com/ClickHouse/ClickHouse/issues/100031. #100205 (yariks5s).
- Fixed a server abort (
Logical error: 'Block structure mismatch') that could occur during a concurrentINSERTwhile anALTER RENAME COLUMNis in flight in anAtomicdatabase withlazy_load_tables = 1afterDETACH DATABASE/ATTACH DATABASE. #104852 (groeneai). - Fix column-name corruption and
BAD_ARGUMENTSexception caused by the settinginject_random_order_for_select_without_order_by. With the setting enabled, multi-column SELECTs no longer fail and output column names (e.g. forCREATE TABLE AS SELECT,CREATE VIEW AS SELECT,JSONEachRow) are preserved instead of being replaced with internal__subquery_column_<UUID>aliases. #105896 (groeneai). - Fix confusing “Maybe you meant X?” hint after a server restart (or
DETACH/ATTACH DATABASE), where dropping an already-dropped table would suggest the just-dropped name as the alternative. The async-load tasks are now cleared fromDatabaseOrdinary::startup_tableandload_tableon detach. #106238 (groeneai). - Fix the
MaterializedPostgreSQLtable and database engines so that a single table or database can be replicated from a non-default PostgreSQL schema (materialized_postgresql_schema), including the case where tables with the same name exist in several schemas of the same database (previously they would share a publication and replication slot and cross-talk). #107425 (alexey-milovidov). - Fixed
NO_SUCH_COLUMN_IN_TABLEwhen a query with parallel replicas selects anALIAScolumn of a table shipped as aGLOBAL JOINtemporary table,MULTIPLE_EXPRESSIONS_FOR_ALIASwhen both sides of a distributedJOINdeclare anALIAScolumn with the same name, andUNKNOWN_IDENTIFIERwhen aDistributedtable declares anALIAScolumn over anotherALIAScolumn, including as aJOIN USINGkey. #107700 (yakov-olkhovskiy). - Fixed a signed integer overflow when a refreshable materialized view retries a failed refresh with
refresh_retriesset to a very large value (nearInt64max). The overflow could abort the server in builds with the undefined-behavior sanitizer. #108005 (groeneai). - Fixed an exception (
CANNOT_PARSE_TEXT) when loading a dictionary with a composite key whose key columns are not the first columns in the dictionary definition, with a localClickHousesource using an explicit query. Source columns are now matched to the dictionary structure by name instead of by position. #108053 (alexey-milovidov). - Fixed a crash (null pointer dereference) when querying a
Hivetable without aWHEREclause, e.g.SELECT * FROM hive_table. #108094 (alexey-milovidov). - Fixed a server crash (segmentation fault) that could occur when a distributed query referenced a not-yet-materialized
MATERIALIZEDCTE as an external table and the remote source was created lazily (delayed source). The crash was a nullLoggerdereference insideRemoteQueryExecutor::sendExternalTables(). #108547 (groeneai). - Fix a logical error (
Equal values are not contiguous within the range assumed to be sorted) when runningDISTINCTover aSTREAMread (SELECT DISTINCT ... FROM table STREAM). Read-in-order optimizations are no longer applied toSTREAMreads, which produce rows in commit order rather than sorting-key order. #108568 (groeneai). - Fix an exception (
Cannot find sharding key column, and in debug/sanitizer builds a server abort) when aDistributedtable’s sharding key is an expression the analyzer const-folds, for exampleif(1, toInt32(id), toInt32(id) + 1), withoptimize_skip_unused_shards = 1. #108737 (groeneai). - Fixed a server abort (
LOGICAL_ERROR/ assertion in debug and sanitizer builds) when anINSERT ... SELECTthrough anAliastable is cancelled without an exception, for example withtimeout_overflow_mode = 'break'. #108783 (groeneai). - Fixed a server abort (
LOGICAL_ERROR/ assertion in debug and sanitizer builds) when anINSERT ... SELECTinto aTimeSeriestable is cancelled without an exception, for example withtimeout_overflow_mode = 'break'. #108796 (tiandiwonder). - Fix distributed queries occasionally failing with
UNEXPECTED_PACKET_FROM_SERVER(“expected TablesStatusResponse, got ProfileInfo”) when a connection that a previous cancelled query left out of sync was reused from the pool. #108854 (alexey-milovidov). - Fix a
LOGICAL_ERROR(CTE '...' does not have query tree, but was not planned yet) when aMATERIALIZEDCTE is referenced from more than one join-tree position (for example two joined subqueries, or a joined subquery plus a scalar subquery inWHERE) in a query over aDistributedtable. The query now executes instead of raising the exception (it aborted the server in debug/sanitizer builds). #108924 (groeneai). - Fixes a memory safety issue where asynchronous work scheduled from materialized view processing could keep using query-level accounting after the query had finished. #108988 (filimonov).
- Fix a
LOGICAL_ERROR(“Cannot find__grouping_setcolumn in header of MergingAggregatedTransform with grouping sets”, or “Chunk info was not set for chunk in MergingAggregatedTransform”) that could occur, without the analyzer, for aUNION ALL/INTERSECT/EXCEPTwhere one branch usesGROUP BY GROUPING SETSwith parallel replicas and another branch usesFINAL. #109003 (groeneai). - Fix
CREATE TABLE ... AS SELECT FROM s3Cluster(...)(and other cluster table functions such asfileCluster/urlCluster) failing withNOT_FOUND_COLUMN_IN_BLOCKinside aReplicateddatabase. #109266 (groeneai). - Fixed an error (
NOT_FOUND_COLUMN_IN_BLOCK, orstd::bad_function_callin older versions) when runningCREATE TABLE ... ON CLUSTER ... AS SELECTreading aDistributedtable on a cluster with two or more shards. #109407 (alexey-milovidov). - Fixed a confusing internal error (
Method getResultType is not supported for TABLE query tree node) when a table expression was used as the left argument of theINoperator; such queries now produce a clear error message. #109412 (alexey-milovidov). - Fix a
Bad cast from type DB::FunctionNode to DB::ConstantNodelogical error (server abort in debug/sanitizer builds) when runningSELECT ... ORDER BY ... WITH FILLagainst aDistributedtable with a lowoptimize_const_name_size. #109938 (groeneai). - Fix a
LOGICAL_ERROR(Left and right columns have same names) in the join order optimizer that could be triggered by a comma join over a multi-table view whenquery_plan_merge_expression_into_joinis enabled. #110227 (groeneai). - Fix a rare
Logical error: 'No more packets are available.'raised for a valid distributed query with aLIMIT, which aborted the server in debug and sanitizer builds. #110334 (groeneai). - Fixed a parser bug where COMMENT was incorrectly consumed as an implicit alias when a SELECT query ended directly after a bare table identifier (e.g.
... FROM t COMMENT 'x'), causing a misleading syntax error instead of the comment being applied to the view/table. #110372 (adityaksolves). - Fixed a wrong result where
length()(and the QBit dimension) on aFixedStringcolumn returned0instead of the fixed sizeNunder parallel replicas when the query had a selective filter. #110427 (groeneai). - Fix
NOT_FOUND_COLUMN_IN_BLOCKerror for queries with a subquery inFROMwhen parallel replicas run incustom_key_sampling/custom_key_rangemode. Such queries (e.g.SELECT * FROM (SELECT id, k, v FROM t WHERE id < 20) ORDER BY k) previously failed withNot found column __table2.id in block .... #110690 (groeneai). - Fix
Logical error: 'clock'(in debug/sanitizer builds the server dies; an infinite hang in release builds) when runningEXPLAIN ANALYZEover a query with a streaming (FROM ... STREAM) read that is not a plain top-level table expression: nested in a subquery (for example insideWHERE ... IN (...)or a CTE), or hidden behind an ordinary or parameterized view.EXPLAIN ANALYZEnow rejects such queries withNOT_IMPLEMENTED, the same way it already rejects a top-level streaming read. #110935 (groeneai). - Fixes the resolution of fully qualified column names such as
db.table.columnin the analyzer for tables that have the same name as their database. #110976 (alexey-milovidov). - Fixed
NOT_FOUND_COLUMN_IN_BLOCK/THERE_IS_NO_COLUMNerrors (and, on some releases, silently wrong results) for aRIGHT JOINwhenparallel_replicas_min_number_of_rows_per_replicais set and a left-table column is projected. #111332 (groeneai). - Fix a race in executable UDFs, the
executabletable engine, and executable dictionaries where anfcntlon an already-closed file descriptor could corrupt an unrelated descriptor of a concurrent query (strippingO_NONBLOCK), which could make queries hang indefinitely. #111779 (Algunenano). - Fixed a
LOGICAL_ERROR(“Invalid number of rows in Chunk”) — and, under hardened builds, a related out-of-bounds abort — forORDER BY ... WITH FILL ... INTERPOLATEover aDistributedtable with two or more shards, overclusterAllReplicas, or over custom-key parallel replicas, including on empty results.WITH FILLis now applied only on the node producing the final result, which also fixes duplication of the generated fill rows across shards/replicas. #111919 (yakov-olkhovskiy). - Fixed
DROP TABLE ... SYNChanging forever when a query oversystem.replicashad been killed before its status requests were processed. #112164 (evillique). - Fixes a
LOGICAL_ERRORexception (Block structure mismatch in joined block stream) on a multi-table join whose two sub-joins both produce no output columns, for exampleSELECT count() FROM t1, t2, t3, t4 WHERE (t1.b = t2.b) AND (t3.a = t4.a)withquery_plan_optimize_join_order_limitset to0or1. #112205 (groeneai). - Fixes a logical error
Cannot add step Expression to QueryPlan because it has incompatible header with root step JoinLazyColumnsStepfor a query withFINAL, aPREWHEREfilter, a smallLIMITandmake_distributed_plan = 1, when the selected columns are listed in an order that differs from the table’s column order. Lazy materialization replaced a plan node without preserving that node’s output header, which made the query plan inconsistent. #112303 (groeneai). - Fixes wrong results and a logical error when an argument that must be constant, such as the scale of
toDecimal32or the timezone oftoDateTime, comes from a scalar subquery and the old analyzer is used. Such an argument was analyzed as if its value were zero or empty, so the declared result type disagreed with the computed value, andCREATE VIEWandCREATE MATERIALIZED VIEWpersisted a wrong column type. #112492 (groeneai). - Fixes a logical error and a wrong dependency record when an ordinary
VIEWbody references a table or dictionary through a query parameter, as inCREATE VIEW v AS SELECT x FROM {db:Identifier}.t. Such a view no longer records a referential dependency on a same-named table of the current database, so unrelated tables can be dropped again. #112704 (groeneai). - Fixes a logical error when a
Jointable engine is used on the right side of a JOIN whoseONsection contains a condition referencing columns of both tables, for exampleON (t.key = j.key) AND (t.a < j.a). Such a query is now rejected with a clear exception instead of raisingrequired columns: ... but not found any in left tableorstd::bad_variant_access. #112815 (groeneai). - Fixed undefined behavior and a silent scheduling error when a background task is scheduled with a very large delay, for example by a refreshable materialized view with a huge
REFRESH AFTERperiod. Delays are now bounded to the largest representable value instead of overflowing. #113041 (groeneai). - Fixed the error reported when an asynchronous
INSERTinto aDistributedtable needs a queue directory whose name exceeds the 255-byte filesystem limit (reachable withuse_compact_format_in_distributed_parts_names = 0). It was aLOGICAL_ERRORfor a shard withinternal_replicationand an unattributedCode: 1001. std::exceptionotherwise; both now reportARGUMENT_OUT_OF_BOUNDnaming the table, the cluster and the limit. #113083 (groeneai). UNDROP TABLEcan now be interrupted byKILL QUERYwhile it waits for running queries to release the dropped table. #113263 (alexey-milovidov).- Fixed a permanent
DROP TABLEhang and memory leak after a query using a materialized CTE failed. The table stayed stuck in the drop queue until the server was restarted. #113397 (tiandiwonder). - Fixes a segfault and a
LOGICAL_ERROR(No host found for exchange stream final_result__0_0) when a subquery setsdistributed_plan_execute_locallyin its ownSETTINGSclause undermake_distributed_plan. The distributed plan and the code executing it read the setting from two different contexts and could disagree, so the initiator built a self-contradictory pipeline. #113422 (groeneai). - Fixed a
CREATE TABLE ... AS SELECTwhose populatingSELECTfailed becoming unkillable whendatabase_atomic_wait_for_drop_and_detach_synchronouslyis enabled: the internal cleanupDROPof the temporary table waited for the background drop queue and ignored bothKILL QUERYandmax_execution_time. #113504 (groeneai). - Fix wrong results and, in a build with assertions enabled, an aborted assertion for a
JOINonto a dictionary whoseONclause has a non-equi condition over both tables, such asON (t.key = d.key) AND (t.a * 10 < d.a), whenjoin_use_nullsis enabled. The condition was evaluated over a column read through a mismatched type, so rows could match arbitrarily. #113534 (alexey-milovidov). - Fixed a parameterized view losing its database when a query referencing it unqualified is sent to a shard, so the shard resolved the name against its own default database. Depending on what that database contained, the query either failed with
UNKNOWN_FUNCTIONor silently returned rows from a different view. AffectsIN,JOINandremote()through aDistributedtable. #113550 (groeneai). - Fixed
Unsupported JOIN keys of type keys256 in StorageJoinwhen reading aJoin-engine table whose key is composite (several columns) or a single 16/32-byte value such asUUID,Int256orFixedString(4). Such a table could be created, written and joined against, but never read back withSELECT. #113578 (groeneai). - Fix a logical error
Sending a distributed query with unknown (zero) client versionwhen a structure-lessDistributedtable over a remote shard is attached or loaded from metadata written by an older server version. #113675 (alexey-milovidov). - Fixed a
LOGICAL_ERRORwhen aSELECTused theSTREAMmodifier on a named table together with parallel replicas in the read-tasks mode. With a materialized CTE used as anINset inPREWHERE, the server raisedReading from materialized CTE ... DelayedPortsProcessor gate is missing in the query plan(a server abort in debug and sanitizer builds). A table carryingSTREAMis now rejected when parallel replicas are requested: atenable_parallel_replicas = 2the query fails withSUPPORT_IS_DISABLED, at1it runs without them, exactly as already happens forFINAL. #113754 (groeneai). - Fix quadratic complexity of parsing the address argument of the
remote,mysql, and similar table functions: a very long address (e.g. a megabyte-sized zero-paddedFixedString) made the query hang during analysis, where it could not be cancelled. #113918 (alexey-milovidov). - Fixed a spurious
Failed to drop temporary table after refresh. Table ... is left behind and requires manual cleanup.error logged when a refreshable materialized view’s refresh is cancelled (for example by a server shutdown) whiledatabase_atomic_wait_for_drop_and_detach_synchronouslyis enabled. The temporary table was in fact dropped, so no manual cleanup was ever needed. #113957 (groeneai). - Fixes a missing
system.query_views_logrow for a materialized view that fails while its dependencies are being collected withmaterialized_views_ignore_errorsenabled. In debug and sanitizer builds this also aborts the server withstd::out_of_rangeinstead of logging the view. #114064 (groeneai). - Fix inconsistent results when a filter on a non-key column of the right table was pushed down below an ANY INNER JOIN. #114313 (kirillshokhin).
- Fixes
CREATE OR REPLACEleaking an internal_tmp_replace_*table, andCREATE OR REPLACE VIEWfailing withNOT_IMPLEMENTED, whenignore_drop_queries_probabilityis set. The DROPs it issues internally are steps of one user statement, so DROP fault injection no longer applies to them. #114420 (groeneai). - Fixed a
LOGICAL_ERRORwhen reading a Delta Lake table whosemetaData.partitionColumnsnames a column thatmetaData.schemaStringdoes not declare. Such metadata is now rejected withBAD_ARGUMENTSon the delta-kernel reader andINCORRECT_DATAon the legacy reader, whenever the table or its schema is read from a snapshot, not only when the query carries a predicate. #114538 (groeneai). - Fixed hash-table statistics — and with them aggregation hash-table preallocation — being silently and permanently disabled for the whole server when a query using the lazy
FINALoptimization (query_plan_optimize_lazy_final) was the first aggregation to run after server startup. #114597 (alexey-milovidov). - Fixed a refreshable materialized view leaking its rotated-out target table when
ignore_drop_queries_probabilityis enabled. TheDROPa refresh issues to clean up the previous target is a step of the refresh, not aDROPthe user asked for, so the fault injection no longer applies to it. #114622 (groeneai). - Fix
ANY RIGHT JOINandSEMI RIGHT JOINwith severalOR-ed conditions in theONsection returning some rows of the right table twice and losing the matches of others. A writtenANY LEFT JOINwas affected too, because the planner may swap the tables and execute it asANY RIGHT JOIN. #114676 (vdimir). - Fixes a
LOGICAL_ERROR(Cannot find input column ... on its position in inputs of expression actions DAG) when a query has aJOINabove a table whose text-indexed column is used by both aPREWHEREand aWHEREtext-search predicate. #114771 (groeneai). - Fixes
NOT_FOUND_COLUMN_IN_BLOCKon everyINSERTinto a table that declaresSTATISTICS(...)on anALIASorEPHEMERALcolumn, which made such tables read-only. #115231 (groeneai). - Fix
dictGetand its variations in distributed queries: an unqualified dictionary name is now bound to the current database of the initiator, instead of being resolved against the current database of each shard, where it either was not found or resolved to a different dictionary of the same name. #115541 (alexey-milovidov). - Fixed a bug where restoring a database or table under a new name dropped the table aliases from the bodies of stored views, so reading a restored view failed with
UNKNOWN_IDENTIFIER. #115645 (groeneai). - Fix
LOGICAL_ERROR“Reading from materialized CTE ‘X’ before its materialization completed - DelayedPortsProcessor gate is missing in the query plan” when aMATERIALIZEDCTE is read from inside anINsubquery over aDistributedtable. #115740 (groeneai). - Fix BAD_ARGUMENTS “Dictionary not found” on dictGet after a DETACH DICTIONARY … PERMANENTLY that was rejected by the dependency check (HAVE_DEPENDENT_OBJECTS). The dictionary now remains fully usable when the detach is rejected. #105259 (groeneai).
- Fixed a
Logical error: 'removed'(server abort in debug builds) in the background table-drop queue. It could be triggered when the same explicit UUID is reused across severalCREATE OR REPLACE TABLEqueries, which enqueues more than one dropped table sharing that UUID. #107031 (groeneai). - Fix
MaterializedPostgreSQLstopping replication of an entire database (withLOGICAL_ERROR: Columns number mismatch) when a single replicated table’s structure changed while the server was down. Now only the affected table is skipped and can be recovered withDETACH/ATTACH. #107427 (alexey-milovidov). - Fixed a severe work-distribution skew with parallel replicas when the cluster contains inactive replicas (for example, stale entries left after autoscaling). Such replicas are no longer counted by the reading coordinator, so work is balanced across the online replicas instead of piling onto a single one. #107805 (alexey-milovidov).
- Fixed a freeze of
SYSTEM RELOAD DICTIONARIES(andRELOAD DICTIONARY) when many MySQL dictionaries defined in XML shared a single connection pool viashare_connection. Such pools now honorconnection_pool_sizeandconnection_wait_timeout(default 5 seconds) from the configuration, matching dictionaries defined through named collections. #108083 (alexey-milovidov). - Prevent exhausted retries for leader-executed replicated distributed DDL from permanently blocking subsequent
ON CLUSTERDDL queries. #108505 (ahaanlimaye). - Fix a possible crash (use-after-free) when
SYSTEM FLUSH DISTRIBUTEDruns concurrently withDROP TABLEof the sameDistributedtable. #108684 (groeneai). - Fixed a
LOGICAL_ERROR(local_replica_plan_reading_step->getAnalyzedResult() == nullptr) in the automatic parallel replicas planner that could occur whenautomatic_parallel_replicas_modeis enabled together withparallel_replicas_min_number_of_rows_per_replicagreater than 0. #109011 (groeneai). - Fixed a
LOGICAL_ERROR(creation_csn is not set while removal_csn is set to 1) that could be thrown when a non-transactionalTRUNCATEran concurrently with an uncommitted transaction that had inserted into the same table. Such aTRUNCATEno longer removes parts created by not-yet-committed transactions. #109598 (tuanpach). - External database engines (e.g.
PostgreSQL) no longer push down range comparisons (>=,>,<=,<) onUUIDcolumns. ClickHouse and external databases order UUIDs differently, so pushing these down silently dropped rows from the result; such predicates are now evaluated by ClickHouse instead. #109833 (vismaytiwari). - Fixed a server crash in the
MaterializedPostgreSQLdatabase engine that could happen when a table was detached (or its structure changed) while it was still queued for synchronization. #110596 (alexey-milovidov). - Fixed the type of an
EPHEMERALcolumn being rewritten by the server’squery_masking_rules. A rule matching inside the type madeCREATE TABLEstore the wrong type, or fail. #112048 (alexey-milovidov). - Fixes an unsigned underflow in the table name length check that allowed creating a table which could never be dropped. When the escaped database name reached 214 bytes, the limit computed for the
metadata_droppedfilename wrapped around, soCREATE TABLEwas accepted and the subsequentDROP TABLEorDROP DATABASEfailed withFile name too long. Such a name is now rejected withARGUMENT_OUT_OF_BOUND, the same error already returned just below that boundary. #112527 (groeneai). - Fixed
RENAME TABLEchecking the maximum table name length against the source database instead of the destination one. Renaming a table into a database with a long name could produce a table thatDROP TABLEcould not remove, and renaming a table out of such a database was wrongly rejected. #113019 (groeneai). - Fixes
viewscolumn inquery_logfor inserts to anAliastable whose target triggers materialized views, which was always empty. #113076 (eclbg). - Invalid
parallel_replicas_custom_keyexpressions such astuple()or(x, y)are now rejected withILLEGAL_TYPE_OF_COLUMN_FOR_FILTERinstead of reading past the custom-key type list. #113359 (nickitat). - Fixed
RENAME DATABASEaccepting a target name long enough to make the database’s tables impossible to drop. The table name length check now runs regardless ofcheck_table_dependencies, and it also covers detached tables, which were never checked and so were affected even at default settings. #113557 (groeneai). - When using background inserts into tables with the
Distributedengine, delays caused by repeated errors when sending data to remote shards now decrease once the errors are resolved. Previously, the delay would only increase and never reset. #87378 (Blackmorse). - Reject native protocol handshakes that use the
USER_INTERSERVER_MARKERfor an unknown cluster, or for a cluster configured without a<secret>. Previously such a handshake would let an unauthenticated client enter interserver mode and exercise pre-auth protocol packets (e.g.TablesStatusRequest) backed by afake_interserver_context, learning information about local tables without proving knowledge of the cluster secret. #99675 (MillaFleurs).
SQL, analyzer, and JOIN fixes
- Fix substitution of query parameters inside the definitions of a named
WINDOWclause. Previously, parameters there were silently kept unsubstituted, and an unsetIdentifierparameter could lead to the exceptionLogical error: '!part.empty()'duringEXPLAIN SYNTAX. #110506 (alexey-milovidov). - Fix a rare server crash when refreshable materialized views are dropped or replaced concurrently. A null pointer was dereferenced in
RefreshTask::doSchedulingwhen another thread set the task’sviewtonullptrwhile the scheduling code had temporarily released its mutex. #105588 (groeneai). - Fix wrong result from a view defined with
EXCEPTorINTERSECTwhose operand is aUNIONchain afterDETACH/ATTACHor server restart. The formatter was missing parentheses aroundUNIONchildren ofINTERSECT/EXCEPT, so the stored SQL reparsed with reversed precedence. #105935 (groeneai). - Fixed wrong results (duplicate rows) for
SELECT DISTINCTover aGROUP BYwithWITH CUBE,WITH ROLLUP, orGROUPING SETSon the same keys. Thequery_plan_remove_redundant_distinctoptimization no longer removes theDISTINCTin these cases, because the grouping modifiers emit extra rows with the key columns defaulted, which can collide with real group values. #107072 (groeneai). - Fix a wrong result in an OUTER JOIN when the
WHEREfilter is a conjunction that contains a constant term (for examplep AND 1, orWHERE p QUALIFY NULLwhereQUALIFY NULLis merged intoWHERE). The constant conjunct could be pushed into the non-preserved side of the join and removed from the post-join filter, so the join produced non-matched rows with the side columns defaulted and those rows incorrectly passed the filter. #107084 (groeneai). - Fix a
LOGICAL_ERROR(“Cannot find input column … on its position in inputs of expression actions DAG”) that aborted the server for a correlated scalar subquery over a source projecting the same column identifier twice (e.g.SELECT number, *) whencorrelated_subqueries_default_join_kind = 'left'. #107112 (groeneai). - Fix a
LOGICAL_ERROR(server abort in debug/sanitizer builds) when aJOINuses the null-safe comparison operator (<=>/IS NOT DISTINCT FROM) and one of the keys is a scalar subquery, e.g.... JOIN t2 ON (SELECT x FROM t1) <=> t2.k, with the old analyzer (enable_analyzer = 0). Such a query now returns a regularNOT_FOUND_COLUMN_IN_BLOCKerror instead of a logical error. #108123 (groeneai). - Fixed
LOGICAL_ERROR(Join is supported only for pipelines with one output port) andNOT_IMPLEMENTED(MergeJoinAlgorithm is not implemented for strictness Semi) exceptions whenjoin_algorithm = 'full_sorting_merge'was used for aSEMI/ANTIjoin, including joins produced by decorrelating anEXISTScorrelated subquery.full_sorting_mergenow declines strictness/kind combinations it cannot execute, so the planner falls back to another enabled algorithm or reports that the join cannot be executed. #108658 (groeneai). - Fixed a bug where the
WITH TOTALSrow of aJOINcould contain default values (such as0) instead of the constant values coming from a constant subquery on one side of the join. The wrong result appeared only for some join orderings or with thequery_plan_join_swap_tablesetting. #108807 (vdimir). - Fixed a startup abort (
Cannot allocate ThreadStack,EINVAL) on glibc builds running on CPUs with a large signal-stack size (for example Intel Sapphire Rapids / Granite Rapids with an AMX-aware kernel), where the signal alt-stack size was not rounded up to a multiple of the page size beforealigned_alloc. #108813 (groeneai). - Fix a
Block structure mismatch in JoinSteplogical error (server abort in debug/sanitizer builds) when a correlated subquery is decorrelated into a join and the source relation carries a column name more than once, e.g.WITH t AS (SELECT number, * FROM numbers(3)) SELECT *, (SELECT t.number WHERE t.number >= 0) FROM twithcorrelated_subqueries_default_join_kind = 'right'. #109114 (groeneai). - Fixed
CREATE USER ... HOST LIKE 'a', 'b'(and the equivalentALTER USER) silently keeping only the first pattern. All specifiedHOST LIKEpatterns are now stored in the user’s host allow-list. #109187 (groeneai). - Fix unexpected result for
ANY JOINwith a constantONcondition (e.g.t1 ANY INNER JOIN t2 ON 1): the query returned the full cartesian product instead of the ANY join result #109207 (vdimir). - Fixed the global
max_rows_in_join/max_bytes_in_joinlimit not being enforced for the parallel_hash join algorithm, where a query withjoin_overflow_mode = 'throw'could silently succeed instead of raisingSET_SIZE_LIMIT_EXCEEDED. #109488 (m-selmi). - Fixed silent data loss when an
INSERT ... VALUESin a multi-query stream is followed by a trailing SQL comment (for exampleVALUES (1) -- comment) under the server-side inline insert parsing path (send_table_structure_on_insert_with_inline_data = 0). The trailing comment was scanned as row data past the terminating;, causing the following queries in the stream to be silently skipped. #109643 (groeneai). - Fixed deferred
PREWHEREwithoptimize_move_to_prewhere = 1whenPREWHEREruns afterFINAL:WHEREconditions are no longer moved ahead of a deferred row policy, which could change query results. #109703 (yariks5s). - Fix a
LOGICAL_ERROR(query tree node does not have valid source node) when a recursive CTE resolved an identifier from an outer scope as a correlated column (withallow_experimental_correlated_subqueries = 1). Such queries now return a clearUNSUPPORTED_METHODerror. #109863 (groeneai). - Fix a signed integer overflow when
ORDER BY ... WITH FILLskips a very large gap over anInt64/UInt64column (e.g. a step of 2 across a gap near 2^63). The overflow was undefined behavior under sanitizers and silently wrapped in release builds. #109937 (groeneai). - Fixed a
Invalid number of rows in Chunklogical error when anINTERPOLATEtarget is an alias of aWITH FILLcolumn with the old analyzer (enable_analyzer = 0). Such queries are now rejected with a clearINVALID_WITH_FILL_EXPRESSIONerror. #110103 (groeneai). - Fix a logical error (
Unexpected number of columns in result sample block) when a filter containing a correlated subquery (for exampleexists((SELECT ...))) is optimized withconvert_query_to_cnforoptimize_and_compare_chain. #110187 (alexey-milovidov). - A nested correlated
EXISTSsubquery that references a column from a scope beyond its immediate outer query (skipping an intermediate scope) now fails with a clearNOT_IMPLEMENTEDerror instead of an internalNOT_FOUND_COLUMN_IN_BLOCK. #110310 (groeneai). - Fixed wrong results from correlated scalar subqueries when the outer query has duplicate values for the correlated column and the subquery is decorrelated via the CROSS JOIN path. Previously aggregates such as
count()andsum()were multiplied by the duplicate count. This affects the experimentalallow_experimental_correlated_subqueriesfeature. #110311 (groeneai). - Fixed
ORDER BY ... WITH FILLnot respectingmax_execution_timeand being slow to cancel when generating a large fill range (especially withINTERPOLATE). #110332 (alexey-milovidov). - The lazy
FINALoptimization (query_plan_optimize_lazy_final, disabled by default) is no longer applied when the query reads in the order of the sorting key, because its replacement plan does not produce rows in sorting-key order, while the plan above may already rely on that after the read-in-order optimization, which could result in incorrectly ordered results. It is also no longer applied when aLIMITsmaller than the number of rows to read applies directly to the reading step, where the mandatory set-building pass would defeat early termination. #110576 (KochetovNicolai). - A background asynchronous insert flush now stops promptly when killed with
KILL QUERY; previously a flush of a large buffered payload kept parsing to the end and ignored the cancellation. #110652 (tiandiwonder). - Fixed wrong results from
GROUP BY ... optimize_aggregation_in_orderandDISTINCT ... optimize_distinct_in_orderplaced over apartial_mergeJOIN. The read-in-order optimization was propagated throughPartialMergeJoin, which re-sorts its left input by the join key and therefore does not preserve the left stream’s original order, so rows were grouped incorrectly. #110671 (groeneai). - Fixes a case when using CTE column aliases with
INTERSECTorEXCEPTqueries, e.g.WITH t (a, b) AS (SELECT 1, 2 INTERSECT SELECT 1, 2) SELECT a, b FROM t, the query was failing with UNKNOWN_IDENTIFIER error. #110885 (yariks5s). - Fixed a
LOGICAL_ERROR: ReadBuffer is canceled. Can't read from it.that could occur in agrace_hashjoin when reading spilled blocks failed mid-read while multiple threads shared the reader. #111056 (groeneai). - Fixes a bug where materializing the right-side columns during *_hash JOINs was involving the wrong timezone. #111074 (yariks5s).
- Fix
CREATEstatements with aCOMMENTclause to raise a syntax error when the comment string literal is missing, instead of silently accepting a misparsed query (e.g.CREATE VIEW v COMMENT AS SELECT 1no longer creates a view without a comment). #111159 (tiandiwonder). - Fix a
LOGICAL_ERROR(Reading from materialized CTE '...' before its materialization completed - DelayedPortsProcessor gate is missing in the query plan) when a reused materialized CTE (enable_materialized_cte) is read on a branch whoseWITH TOTALSorextremesoutput is discarded, including outer aggregation,INSERT ... SELECT, set operations andEXPLAIN ANALYZE. #111194 (groeneai). - Fix
join_algorithm = 'full_sorting_merge'filling the non-joined rows of aLEFT/RIGHT/FULL/ASOFJOINwith a raw zero instead of the column type default. For anEnumcolumn (whose default is its first element, not0) this produced wrong results (a filter on the padded value dropped the unmatched rows, so an outer join silently behaved like an inner one) and aUNKNOWN_ELEMENT_OF_ENUMerror when the column was selected. #111197 (groeneai). - Fix wrong results from
SummingMergeTree(andCoalescingMergeTree) withFINALwhenoptimize_move_to_prewhere_if_final = 1and theWHEREclause is an implicit-boolean condition on a sorting-key column (e.g.WHERE k). The key column was returned summed across the unmerged parts instead of preserved. #111342 (groeneai). - Fix a logical error (
Unexpected number of columns in result sample block) that could occur for aRIGHT/FULL JOINwhen the left side carries a duplicated same-named column (for example1 AS b, 1 AS b) that survives throughUNION ALL. #111554 (groeneai). - Fixes a client crash when formatting an
EXECUTE ASquery whose AST has a child replaced (as the AST fuzzer does).ASTExecuteAsQuerynow owns itstarget_userandsubquerychildren instead of holding non-owning pointers intochildren. #111725 (groeneai). - Fixed wrong results when
query_plan_join_swap_tableswaps anANY JOINwhilejoin_any_take_last_row = 1: ClickHouse now keeps the original join-side order soANY JOINstill returns the last matching row. #112458 (vdimir). - Fix the
Context has expiredexception whengetClientHTTPHeaderis used inside a subquery. #112533 (alexey-milovidov). - Fixes wrong results for a
JOINwhoseONclause carries a mixed condition, that is a cross-side non-equi residual such asON (t1.key = t2.key) AND (t1.a * 10 < t2.a). Only the hash family evaluates such a condition, butfull_sorting_merge,partial_mergeand the direct key-value join claimed they could run these joins and then silently ignored the residual, returning extra rows. They now decline, so a hash algorithm is used instead. #112831 (groeneai). - Fixed wrong results for a comma or
CROSSjoin whoseWHEREcompares columns of two tables through a non-deterministic expression such asrand. Such a query could return rows that contradict its ownWHEREclause, because rewriting the join into anINNERjoin moved the expression into the join key, where it is evaluated again per row of each side. #112938 (groeneai). - Fix a quadratic slowdown when parsing a PromQL query with a long tail of unrecognized characters (e.g. a
FixedStringvalue padded with NUL bytes): the parse of a megabyte-sized invalid input used to keep a thread busy for tens of minutes and was uncancellable, because it happens during query analysis. Now the parser stops at the first error. #112944 (alexey-milovidov). - Fixed
intDivon a non-constant column with the constant divisor -1 silently wrapping the division of the minimal signed number (e.g.intDiv(-9223372036854775808, -1)) instead of throwing theILLEGAL_DIVISIONexception like the other execution paths do. The silent wrap could also produce wrongORDER BY/DISTINCTresults, because the read-in-order optimization correctly treatsintDivby a negative constant as monotonic, and the wrapped value violated the resulting sort order. #113048 (alexey-milovidov). - Fixed
UNKNOWN_IDENTIFIERwithenable_analyzer = 0when aWHERErefers to a column produced byuntupleinside a subquery, for exampleSELECT t.keys FROM (SELECT untuple(arrayJoin(m)) AS t FROM ...) WHERE t.values > 0. Theenable_optimize_predicate_expressionrewrite pushed such a predicate into the subquery as aHAVING, where the name does not yet exist. #113760 (groeneai). - Fix wrong results in
JOINwithjoin_algorithm = 'partial_merge'when the right side carries totals: the right side could be left unmerged, soLEFTjoins substituted default values for real matches andINNER/RIGHTjoins returned no rows at all. In theRIGHT/FULLnon-joined path the same state dereferenced a null pointer, which crashed the server. #113936 (groeneai). - Fix wrong results for
SELECT DISTINCTwith aLIMITthat has to read its whole input:LIMIT n WITH TIESdropped tying rows,exact_rows_before_limit = 1reported a truncatedrows_before_limit_at_least, andGROUP BY ... WITH TOTALScomputed the totals row from a prefix of the stream. #114025 (groeneai). - Fixed a
LOGICAL_ERROR(“Unexpected number of kept output positions after removing unused columns fromReadFromMergeTree”) raised by aSELECT ... FINALwhosePREWHEREuses a column thatFINALneeds for merging (ver,is_deleted,sign) and which does not select that column. #114746 (groeneai). - Fixed a lifetime issue in asynchronous INSERT flushing, materialized-view processing, and
EXPLAIN ANALYZEthat could access expired query-tracking state. #114940 (azat). - Fixed
CREATE QUOTAandALTER QUOTAsilently discarding all but the lastFOR INTERVALblock when the separating comma between intervals was omitted. Such a statement succeeded while installing none of the earlier limits. The intervals now accumulate again, as they did before 2020. #115320 (groeneai). - Fixed
randConstantreturning two different values in one query when its calls carry different aliases.randConstantis documented to hold a single value for the whole query, andSELECT randConstant() = randConstant()already returned1, but adding aliases as inSELECT randConstant() AS a, randConstant() AS bproduced two unrelated values. #115384 (alexey-milovidov). - Fixed
SELECT count()never terminating, or returning an invented row count, on a corruptedMsgPackorProtobufListfile. Theoptimize_count_from_filesfast path counted rows without requiring the read position to advance, so a two byteProtobufListfile produced billions of rows and never returned. Such input is now rejected with the same error the ordinary read path gives. #115884 (groeneai). - Fix
broken_data_filesinsystem.distribution_queueand theBrokenDistributedFilesToInsertmetric always reporting0: when scanning the async-insert queue at startup,DistributedAsyncInsertDirectoryQueue::initializeFilesFromDiskcounted broken bytes but never incremented the broken-files counter. #92124 (squalfof).
Other bug fixes
- Secondary queries executed as part of internal queries will now be logged as internal queries. #108506 (mstetsyuk).
- Fix a DEFAULT column missing in a part being filled with the type default instead of the DEFAULT expression when the PREWHERE condition was split into multiple read steps. #111795 (yariks5s).
- Quotas keyed by
normalized_query_hashnow account all resources (read_rows,read_bytes,result_rows,result_bytes,execution_time,written_bytes,errors) per query pattern, like the query-count counters, instead of accounting them against a single shared per-user bucket. #107681 (alexey-milovidov). - Fixed the MySQL
connect_timeout, read and write timeouts not taking effect: a connection attempt to an unresponsive MySQL server could hang for more than two minutes even withconnect_timeout = 1, because the sampling query profiler’s periodic signals kept resetting the MySQL client’s internal poll deadline. #109592 (tiandiwonder). - Fixed the block-wait timeout of streaming inserts (
input_format_max_block_wait_ms) potentially never expiring while the query profiler is active: the file-descriptor poll restarted with the full timeout after every profiler signal, so a stalled input source could delay the partial-block flush indefinitely instead of flushing when the wait limit is reached. #109602 (tiandiwonder). - Fixed a
LOGICAL_ERROR(std::length_error) when a query with the experimental settingmake_distributed_plan = 1used a very largedistributed_plan_default_reader_bucket_countordistributed_plan_default_shuffle_join_bucket_count. Such values are now rejected withINVALID_SETTING_VALUE. #109770 (groeneai). - Fix
distinct_overflow_mode = 'break': DISTINCT now returns the partial result accumulated up to the limit and stops reading the source, as documented. Previously the chunk that crossedmax_rows_in_distinct/max_bytes_in_distinctwas discarded (truncated or empty results) and the query kept reading and inserting into the hash set to the end of the input, which could end inMEMORY_LIMIT_EXCEEDED. #110075 (skuznetsov-clickhouse). - Fix
optimize_aggregation_in_orderignoring query cancellation.AggregatingInOrderTransformnow checks for cancellation while aggregating a chunk, so a query stopped byKILL QUERYor bymax_execution_time(in the defaulttimeout_overflow_mode = 'throw') stops promptly instead of running the whole chunk to completion. #110104 (groeneai). - Fixed a server abort (in debug and sanitizer builds) when a value from a
mysql/postgresql/sqlite-family PostgreSQL source is read into a declared type it cannot be parsed into (for example atextcolumn declared asInt32). Such a type mismatch is now reported as a query error instead of aborting the server. #110264 (groeneai). - Fixed a server abort when parsing certain PRQL queries (
SET dialect = 'prql'). A panic inside theprqlccompiler was aborting the process instead of being reported as a query error. #110316 (groeneai). - Fix
NOT_FOUND_COLUMN_IN_BLOCKwhen a row policy andadditional_table_filtersreference the same column that is not otherwise selected by the query. #111099 (groeneai). - Fixed a hang in
randChiSquared,randStudentT,randFisherFandrandBinomialwith extreme parameters. The query could not be interrupted bymax_execution_timeorKILL QUERY. Such parameters are now rejected even whenmax_rand_distribution_parameterormax_rand_distribution_trialsis set to 0, andmax_rand_distribution_trialscan no longer be raised above its default of 10^9 forrandBinomial. #111909 (alexey-milovidov). - Fixes
connection_pool_max_wait_msso that its documented and default value0means an infinite timeout. A query that found the connection pool full used to retry in a tight loop, consuming CPU and loggingNo free connections in pool. Waiting 0 ms.on every iteration. It now waits until a connection is returned to the pool. #112380 (groeneai). - Fixes
replaceOne,replaceAll,replaceRegexpOneandreplaceRegexpAllignoringmax_execution_timeandKILL QUERY. A single call now checks for cancellation while it works, so a long-running replacement over a large value or a large number of rows stops instead of holding a server thread until it finishes. Such a call has no partial result to return, so undertimeout_overflow_mode = 'break'it raises the deadline as an error: always when constant-folded, and in the pipeline whenever this checkpoint is reached before the executor’s between-block check, which still stops the query silently. #112483 (groeneai). - Fixed
countSubstrings,countSubstringsCaseInsensitiveandcountSubstringsCaseInsensitiveUTF8ignoringKILL QUERYandmax_execution_time: a call over many rows or many matches could run for minutes after the query was cancelled. #113369 (groeneai). - Fix possible query hanging (for the
receive_timeoutseconds) in case it very quickly finishes on the initiator. #113551 (nickitat). - Fixes
countMatchesandcountMatchesCaseInsensitiveignoringmax_execution_timeandKILL QUERYwhile counting the matches of one large value, which kept a cancelled query running for minutes. With a constant argument, the call happens during query analysis, so no pipeline exists to cancel it. #113886 (groeneai). - A query could be cancelled up to 1 ms before its
max_execution_timehad passed, failing with a self-contradictoryTimeout exceeded: elapsed 999.672 ms, maximum: 1000 ms. #114559 (alexey-milovidov). - Fixed an out-of-bounds read in
pointInPolygonwith a constant polygon whose bounding box is unbounded, for example when a coordinate reaches+-DBL_MAX. Such a polygon is now rejected withBAD_ARGUMENTS, and a polygon with an empty ring is treated as having no interior. Previously the query could return a wrong result, or abort the server in builds with hardening or sanitizers enabled. #115035 (groeneai). - Fix a race that could make a
STREAM BOUNDEDquery return fewer rows than were committed before it started, usually none. #115543 (alexey-milovidov). - Fix
Unknown identifierraised during query plan optimization undermake_distributed_plan = 1when a window query’s sort column passes through anExpressionstep that does not mention it. #115890 (groeneai). - Fixed
h3kRing,h3HexRing,h3Lineandh3ToChildrenignoringmax_execution_timeandKILL QUERYwhile expanding a block of rows. #115893 (alexey-milovidov). - Fix
NOT_FOUND_COLUMN_IN_BLOCKin the native Parquet V3 reader (after a previous regression) when aPREWHEREhad conjuncts sharing a common intermediate expression. #107059 (groeneai). - Fixes a hang when writing Parquet with
output_format_parquet_parallel_encodingenabled (the default) andmax_threadsgreater than 1. If the encoder failed to schedule an additional thread, its live thread counter underflowed and the write could stop making progress permanently instead of finishing or reporting an error. #112959 (groeneai). - Fixed unbounded memory allocation when reading a Parquet file whose Thrift metadata declares a container or string length larger than the metadata itself. Such a length is now rejected instead of being allocated for. #113212 (groeneai).
- Fixes a
std::out_of_rangeexception (Logical error: 'std::exception. Code: 1001, type: std::out_of_range', which also aborts the server in debug and sanitizer builds) in the Parquet v3 reader when a column needed only for filter evaluation was scheduled for decoding after its output slot had already been dropped. #113956 (groeneai). - Fix data races in profile events and memory tracker. #112466 (mstetsyuk).
- Fix a theoretical data race in profile events when using the
trace_profile_events_listsetting to write stack traces of certain profile events tosystem.trace_log. #113553 (mstetsyuk). - Fixed
intDivOrNull,moduloOrNullandpositiveModuloOrNullreturning0instead ofNULL, andintDivOrNullandintDivOrZeroraising an exception instead of returningNULL/0, when the division leads to a floating-point exception (division by zero orINT_MIN / -1), including for mixed signed/unsigned arguments. #101976 (yariks5s). - Fixed
IS DISTINCT FROMforArrayandMapvalues compared withNULL. #103162 (ylw510). - Fixed
groupConcatwhen the parametric and two-argument spellings are mixed, for examplegroupConcat(',', 2)(x, '/'): the row-limit parameter was silently dropped, so every row was returned instead of the requested number; the delimiter from the second argument now correctly overrides the parameter. #104882 (yariks5s). - Fixes bugs when using CAST from smaller to larger interval units. #105058 (yariks5s).
- Fixed a logical error (
Assertion 'row < chunk.getNumRows()' failed) inLIMIT ... WITH TIESqueries running with the read-in-order pipeline (optimize_read_in_order = 1andread_in_order_use_virtual_row_per_block = 1). #105102 (groeneai). - Fix
Logical error: 'Pipeline stuck'in queries that useenable_sharding_aggregator = 1together with aUNION ALLandmax_streams_for_union_stepsmaller than the pipeline width.BufferedShardByHashTransformnow finishes empty-queue output ports as soon as its input is exhausted, and keeps pulling input when a demanded empty port has nothing drainable even if a sibling queue hit the back-pressure cap. #106251 (groeneai). - Fixed
removeDirectoryonplainobject-storage disks (such ass3_plain) removing all files inside a non-empty directory as if the removal were recursive. Removing a non-empty directory now fails withCANNOT_RMDIR, and recursive removal handles the contents explicitly. #106281 (RinChanNOWWW). - Fixed
executable_pooluser-defined functions configured with<lifetime>not picking up changes to the underlying script. Previously, onlySYSTEM RELOAD FUNCTIONSwould re-read an edited script; periodic<lifetime>reloads kept executing the old version. #107087 (groeneai). - Fixed a data race on the server-global trace collector between a worker thread starting its profiler and server shutdown. #107307 (groeneai).
- Fixed a rare server abort during shutdown (
std::future_error, “The associated promise has been destructed prior to the associated state becoming ready”) caused by a task scheduled throughthreadPoolCallbackRunnerUnsafebeing dropped from the thread pool queue before it ran. #107383 (groeneai). - Fixed a
FileLoglogical error (Last stored last_written_position in meta file ... is bigger than current last_written_pos) that could happen when a watched file was deleted and recreated reusing the same inode. #107617 (groeneai). - Fixed
toTime64andCAST(... AS Time64)not clamping out-of-range values to theTime64range insaturateandignoreoverflow modes, which could produce values that display identically but compare as different. #108028 (alexey-milovidov). - Fixed incorrect handling of zero-width assertions (
\b,^,$) in the regexp “match all” functionsextractAll,extractAllGroupsVertical,extractAllGroupsHorizontal,countMatchesandsplitByRegexp. For example,extractAll('new york is the greatest', '\b(\w)')now correctly returns the first letter of each word instead of every letter. #108047 (alexey-milovidov). - Fix incorrect results when using
hasTokenon text indexes with non-splitByNonAlphatokenizer. #108066 (rschu1ze). - Fix
toStartOfIntervalanddateTruncreturning a rounded value instead of the start of the containing interval when the input has finer precision than the interval unit, e.g.toStartOfInterval(toDateTime64('2023-10-09 10:11:12.000999', 6), INTERVAL 1 millisecond)returned10:11:12.001instead of10:11:12.000. The overload with an explicit origin was affected too. #108186 (yariks5s). - Fixed
replaceRegexpOneandreplaceRegexpAllso.matches newline characters by default, consistently with other regular expression functions. #108265 (linjiayu1025-collab). - Fixes lightweight UPDATE/DELETE conditions being evaluated twice. #108323 (ofeliacode).
- Fix
arrayPartialSort()with a non-constant limit argument. #108327 (vitlibar). - Fixed regular expression functions (
match,extract,extractAll,replaceRegexpOne,replaceRegexpAll, etc.) silently returning wrong results when the pattern contained a NUL (\0) byte. The NUL is now treated as an ordinary literal byte, consistent with RE2. #108427 (alexey-milovidov). - Fix
changeYear/changeMonth/changeDay/changeHour/changeMinute/changeSecondover aDateTime64(N)argument creating the result column with the hardcoded default scale (3) instead ofN. The values were computed at scaleNbut the column object declared scale 3, leaving it structurally inconsistent with its type. #108551 (groeneai). - Fix a
LOGICAL_ERROR(“Trying to extract chunk from ChunkBuffer before all inputs are finished”) when a set operation (INTERSECT/UNION ALL/EXCEPT) combines branches that use correlated subqueries andcorrelated_subqueries_default_join_kind = 'left'. #108554 (groeneai). - Fixed
DateTime64bugs inchangeYear,changeMonth,changeDay,changeHour,changeMinuteandchangeSecond: nanosecond-precision (scale 9) inputs no longer throwDECIMAL_OVERFLOW, and pre-epoch sub-second inputs now return the correct calendar second. #108681 (takumihara). - Avoid excessive server log output and an oversized error message when compiling a very large regular expression (for example a
LIKEormatchpattern with hundreds of thousands of wildcards); such patterns now fail with a clearCANNOT_COMPILE_REGEXPerror. #108821 (Algunenano). - Fix
reinterpret(x, 'Decimal128(scale)')(andDecimal32/Decimal64/Decimal256/DateTime64targets) producing a result column whose internal scale was the source scale instead of the requested target scale when the source and target had the same physical type. The values were correct but the column object was structurally inconsistent with its declared type. #108878 (groeneai). - Fixed undefined behaviour when stringifying an out-of-range protocol packet type (e.g. in the “Unexpected packet from server” / “Received … packet” error messages) for a desynced or fuzzed connection. #108885 (groeneai).
- Fix
changeYear/changeMonth/changeDay/changeHour/changeMinute/changeSecondoverDateTime64(N)building the result column at the hardcoded default scale 3 instead ofN, andtimeSlotsoverDateTime64ignoring the optionalSizeargument scale in its declared return type. Both produced a column whose physical scale diverged from its declared type, which led to aLOGICAL_ERROR(writeSlice expects same column types) when the result was passed toarrayPushBack/arrayPushFront/arrayConcat, andtimeSlotswith the largest scale inSizereturned wrong timestamps. #108994 (groeneai). - Fix non-monotonic ProfileEvents increments #109180 (azat).
- Fix
SAMPLEratios with an exponent whose magnitude overflowsInt32(e.g.SAMPLE 1e-3000000000) being silently treated asSAMPLE 1. #109197 (Algunenano). - Fixed two bugs on the
DateTime64path oftoUTCTimestamp/fromUTCTimestamp(and theirto_utc_timestamp/from_utc_timestampaliases) andtoTime64. First, a signed integer overflow near theInt64boundary (now computed inInt128and clamped to the representable range). Second, a wrong result for negative fractional values near a timezone-offset boundary: the seconds split truncated toward zero, sotimezoneOffset()inspected the next second and could pick the wrong side of a DST/offset change. #109738 (groeneai). - Fixed a logical error (
column->size() == num_rows) when a row policy filter expression usesarrayJoin; such filters are now rejected. #109753 (Algunenano). - Fixed
generateRandomStructure()occasionally producing an invalid structure string with two data types concatenated (for exampleDecimal32(7)IPv4) when type nesting exceeded the internal depth limit, which made the result unparseable. #109928 (groeneai). - Fixed query-setting propagation in
accurateCastOrDefaultand preservation of sourceNULLs encoded byDynamicandVariant. #109946 (Avogar). #114912 (alexey-milovidov). - Fix
max_bytes_in_distinctandmax_bytes_in_set: string keys are stored in an arena that was not counted by the limit checks, so DISTINCT / IN over string keys could hold memory exceeding the byte limit by the whole key payload (unbounded in the key length). The limits now account for the arena, matching their documentation; queries with string keys close to a byte limit may now trip it earlier (correctly). #110120 (skuznetsov-clickhouse). - Fixed reading Npy files with zero-sized inner dimensions so materialized rows and optimized and non-optimized
count()results agree. #110146 (qiuyanjun888). - Fix a logical error (server abort in debug/sanitizer builds) when the
indexHint/ignore/isZeroOrNullfunctions are given an argument whose type resolves toNothing, e.g. inside expressions likeindexHint(assumeNotNull(materialize(NULL))). #110192 (groeneai). - Fix reading files on
plain_rewritabledisks after an existing path was rewritten with content of a different size: the in-memory metadata kept the stale file size, which could fail reads withUNEXPECTED_END_OF_FILE. #110304 (thevar1able). - Rejects
vector_search_index_fetch_multipliervalues below 1.0 to prevent empty ANN search results when the computed fetch count truncates to zero. #110452 (tamish560). - Fix a sporadic
Cannot parse string ... as UInt64 ... While executing ValuesBlockInputFormaterror that could occur when inserting values whose expressions contain a numeric cast such asinitializeAggregation('sumState', 0::UInt64). #110637 (groeneai). - Fixed
readWKTrejecting WKT strings with leading whitespace (e.g.readWKT(' POINT(1 2)')), which are accepted by the typedreadWKTPoint/readWKTPolygon/… readers and by the WKT grammar. #110706 (groeneai). - Fix a server crash (integer division by zero) when a quota with a zero-length interval (for example CREATE QUOTA q FOR INTERVAL 0 SECOND MAX queries = 1000) was consumed. A non-positive quota interval duration is now rejected at quota creation. #110846 (PedroTadim).
- Fixed a crash in FIPS builds when using an Ed25519 SSH key (
CREATE USER ... IDENTIFIED WITH ssh_key ... TYPE 'ssh-ed25519', or such a key inusers.xml). Ed25519 is not FIPS-approved; such keys are now rejected with a clearLIBSSH_ERRORerror. #110891 (thevar1able). - The transposed distance functions over
QBit(cosineDistanceTransposed,L2DistanceTransposed,dotProductTransposed) now use a bounded low-precision reconstruction of a value truncated toprecisionbit planes, instead of reconstructing it to the coarse cell’s lower edge (dropped bits zero-filled). Zero-filling biased every reconstructed value towards zero and was degenerate at low precision: forQBit(BFloat16)at precision 1 only the sign bit survives, so every value reconstructed to±0.0and the distance was the same constant for every row, carrying no ranking information. The reconstruction now depends on which bits are dropped: rawInt8codes are reconstructed to their cell centre (matching theLloyd-Maxreconstruction the...TransposedQuantizedpath already used); a floating-point value at precision 1 becomes a proper sign quantization; a floating-point value that keeps its whole exponent and drops only mantissa bits is reconstructed to the bounded midpoint within its own binade (while a genuine zero or±0.0is left at exact zero and the non-finite cell is carved out so±infstays exactly infinite; see the note below on the ambiguousNaNcell); and while exponent bits are still being truncated the bounded lower edge is kept, so a smallerprecisiontrades accuracy for speed without blowing a magnitude up by orders of magnitude. Full-precision results are unchanged. #110911 (alexey-milovidov). - Fixed an out-of-range read in the native ORC reader during schema inference of a corrupt ORC file whose type tree declares more columns than the stripe footer has column encodings. It is now rejected with
CANNOT_EXTRACT_TABLE_STRUCTUREinstead of aborting the server in debug/sanitizer builds. #110967 (groeneai). - Each SSH key now counts individually toward the
max_authentication_methods_per_userlimit, so the limit applies consistently regardless of syntax. Previously, multiple keys in a singlessh_keymethod counted as one. #111181 (TheMC47). - Iceberg REST catalogs now report stale credentials with the underlying error instead of silently reporting
UNKNOWN_TABLE. #111379 (alesapin). - Fix
readWKTPoint/readWKTreturning uninitialized (scalar) or previous-row (vectorized) coordinates for a dimension-tagged empty point such asPOINT M EMPTY. Such inputs are now rejected withCANNOT_PARSE_TEXT, consistent withPOINT EMPTY. #111517 (groeneai). - Creating
minmaxstatistics now emits a warning instead of throwing an exception. #111785 (hanfei1991). - Keep
randBinomialusable with the degenerate probabilities0and1for any number of trials. #112021 (alexey-milovidov). - Fixed
system.s3_queue_settingsreporting incorrect values forbucketing_mode,partitioning_mode,partition_regex, andpartition_component. #112300 (bharatnc). - Report the correct position for errors in PromQL queries. #112494 (fallintoplace).
SYSTEM DISABLE FAILPOINTnow rejects a fail point name that does not exist, raisingBAD_ARGUMENTSlikeSYSTEM ENABLE FAILPOINTalready did. Previously a mistyped name reported success while leaving the intended fail point enabled, with no indication that nothing had been disabled. #112680 (groeneai).- Fixes a wrongly typed read of a
Nestedelement subcolumn whose column was dropped and re-added. Selecting such a subcolumn together with its own parent column returned the whole element in the subcolumn’s slot while the block still declared the element type, which produced arbitrary values in release builds and a logical error in debug and sanitizer builds. #112769 (groeneai). - Reject PromQL durations whose units are repeated or not ordered from longest to shortest. #112954 (fallintoplace).
- Fixed undefined behavior (null pointer passed to
memcpy) inreplaceRegexpOne/replaceRegexpAllwhen the replacement string substitutes a capturing group that did not participate in the match, e.g.replaceRegexpAll('abc', '(a)|(b)', '\\2'). #113012 (alexey-milovidov). - Fix an incompatibility that text indexes created in ClickHouse 26.3 with
unicode_wordtokenizer cannot be loaded in later versions #113061 (rschu1ze). - Reject invalid Unicode surrogate code points in PromQL string escapes. #113203 (fallintoplace).
- Reject invalid cross-delimiter escapes in PromQL strings. #113587 (fallintoplace).
- Fix
theilsUover a window frame returning an arbitrary value instead of 0 when the first argument is constant within the frame. #113691 (alexey-milovidov). - Fixed the error code reported for some malformed
Replicated-serialized columns received over the Native protocol: these are now rejected withINCORRECT_DATAinstead ofLOGICAL_ERROR(which aborted the server in debug and sanitizer builds). #114186 (Avogar). - Fixes incorrect, often sign-flipped,
Time64literals andIN-list constants produced when rescaling a lower-scaleDecimal64overflowedInt64. Such a conversion now reportsDECIMAL_OVERFLOW, matching theDateTime64branch and explicitCAST. #114546 (groeneai). - Support quoted metric and label names in PromQL selectors. #114551 (fallintoplace).
- Fixed
ReplicatedMergeTreetables remaining read-only indefinitely after a failed startup attempt that ran through the attach thread. The restart retry is now scheduled correctly, restoring automatic recovery and its session checks. #114802 (azat). timeSeriesRangeandtimeSeriesFromGridthrew a falseDECIMAL_OVERFLOWfor timestamps before 1970 because the start/end timestamps were read as unsigned values. Also, forDateTime/UInt32timestamps the step was silently truncated to its lower 32 bits. Now all the calculations are done inInt64. #114815 (vitlibar).- Fixed a possible exception in asynchronous inserts after a query fails with
MEMORY_LIMIT_EXCEEDED, and fixedasync_insert_queue_flush_on_shutdownso queued inserts are flushed during server shutdown. #114839 (azat). - Fixed an
out_of_rangeexception raised as an internalCode: 1001error when asequenceMatch,sequenceCountorsequenceMatchEventspattern contains the event number0, for examplesequenceMatch('(?0)'). Such a pattern is now rejected withBAD_ARGUMENTS. A temporal condition holding a lone sign, such assequenceMatch('(?1)(?t>+)(?2)'), was silently treated as(?t>0)and is now rejected withSYNTAX_ERROR. #115056 (groeneai). - Fixed the error message of
geoToH3naming the wrong argument position and the wrong type when a coordinate is notFloat64. Under the defaultgeotoh3_argument_order = 'lat_lon'the message pointed at the other argument and printed that argument’s type. #115503 (groeneai). - Fix invalid backslash escape characters in text, tokenbf_v1 and ngrambf_v1 indexes. #115634 (ahmadov).
- Fix an issue: the server couldn’t start when the
TZenvironment variable is empty. #68921 (ardenwick).