github StarRocks/starrocks 4.0.14

2 hours ago

4.0.14

Release Date: August 11, 2026

Behavior Changes

  • A static partition clause in an INSERT into an Iceberg or Hive table is now validated even when a target column list is given. A clause that names a column which is not a partition column of the table is rejected with a clear error instead of being silently ignored. Every INSERT shape that was accepted before is still accepted. #76659
  • ARRAY and MAP constructors now fail with a CapacityLimitExceed error when the flattened result of a single chunk exceeds the addressable byte limit, instead of silently returning corrupted values that CREATE TABLE AS SELECT, INSERT, or a materialized view refresh could persist. For affected queries, reduce chunk_size or split the constructor expression. #76419
  • Division expressions whose divisor is not constant (for example, 10 DIV c) are no longer treated as monotonic, which stops unsafe Zone Map pruning from dropping rows that satisfy the predicate. Queries filtering on such an expression may return more rows than in earlier versions; the previous results were incorrect. Monotonic forms such as c DIV 10 still benefit from the pruning. #76744
  • On a column with a GIN inverted index, NOT MATCH no longer returns rows whose value is NULL, which brings it in line with SQL three-valued logic. Queries that depended on the previous behavior return fewer rows. #75578
  • Under "compression" = "zstd", flat JSON sub-columns and the synthetic null and offset sub-columns of ARRAY, MAP, and STRUCT columns are now compressed. They were previously written as raw pages, which could make a ZSTD table larger on disk than the same table compressed with LZ4. Only segments written after the upgrade are affected, so existing tables shrink gradually as their segments are rewritten. #76949
  • The Iceberg partition cache is now bounded by memory instead of by entry count, through the new catalog property iceberg_partition_cache_memory_usage_ratio (default 0.1), and its footprint is reported in /api/memory_usage and the minute-level memory logs. Under memory pressure the cache may hold fewer entries than before; raise the ratio to restore the previous footprint. #76165
  • FE memory estimation now also counts the internal overhead of maps and collections — per-entry nodes and the backing table — instead of only the sampled keys, values, and elements. Weight-bounded caches such as the Iceberg metadata caches therefore report their true footprint and evict earlier for the same *_memory_usage_ratio, which lowers FE memory usage but may increase cache misses. #75971
  • When the keep-alive GC reaps an expired external scan context — left behind by a Spark or Flink connector reader that died without calling close_scanner — it now cancels the corresponding pipeline fragment. The buffered scan memory is released within keep_alive_min plus one GC interval instead of staying pinned until query_timeout. #76535
  • The audit log of a statement forwarded to the Leader FE now records the relations resolved by the Leader, so QueriedRelations holds fully qualified table names with CTE references excluded, matching what the Leader logs. A Follower falls back to local collection only when the statement ran locally or the Leader did not return the list, for example during a rolling upgrade. #76387
  • The Operation column of SHOW ALTER TABLE OPTIMIZE now shows a readable description of the optimize operation instead of an internal object address such as com.starrocks.sql.ast.OptimizeClause@b5dc069. #75948

Improvements

  • Supported complex types (ARRAY, MAP, and STRUCT) in Paimon tables; querying such columns could previously crash the BE. #66784
  • Added the LAST_REFRESH_TIME column to information_schema.materialized_views and SHOW MATERIALIZED VIEWS. It reports the data-freshness timestamp used by the mv_rewrite_staleness_second check, which is distinct from the existing LAST_REFRESH_FINISHED_TIME (when the refresh job finished running). #71642
  • Added the mutable BE configuration item object_storage_client_cache_size (default 8), which replaces the hard-coded capacity of the S3 and Azure Blob client caches. #75851
  • Metadata refresh of a filesystem-backed external table triggered by INSERT ... SELECT no longer runs while FE internal metadata locks are held, so slow remote metadata access no longer stalls unrelated work on the same path. #73391
  • Multi-statement (multi-table) transaction Stream Load now dispatches all per-table channels before waiting for them, instead of dispatching and waiting for each table in turn, which shortens commit time for CDC pipelines that write many tables under one label. #76715
  • Reduced lock contention across the INSERT OVERWRITE code path by relaxing or shortening the table-level locks held in each phase. #75828
  • Error messages for the large-column capacity limit no longer embed internal diagnostics such as the driver address and the operator chain, and the Capaticy typo in the shared status string is corrected. #76303
  • Shared-data Primary Key publish now applies the per-segment delete vectors carried in op_write.seg_delvecs, so a BE running this version consumes such metadata correctly instead of leaving duplicate primary-key rows live. #76474
  • Addressed security vulnerabilities (CVE): upgraded Thrift to 0.24.0 and Netty to 4.1.136.Final, bumped the PostgreSQL JDBC driver to 42.7.12, and removed vulnerable transitive dependencies that shipped alongside their fixed counterparts, namely bcprov-jdk15on, the end-of-life OkHttp 2.x line, avro-ipc (which bundles jQuery 1.4.2), and the Jetty client and security jars. #76922 #76555 #76783 #76097 #76270

Bug Fixes

The following issues have been fixed:

  • The query cache could store an incomplete per-tablet result and serve it to later queries, returning wrong results. #77066 #77404
  • COUNT(DISTINCT) on an Iceberg table partitioned by bucket() returned an over-count when enable_bucket_aware_execution_on_lake was enabled and the GROUP BY list was a superset of the bucket column. #76601
  • JSON subfield pushdown returned wrong results when two subfield keys of the same column differed only in case (for example, get_json_string(c, 'Campaign') and get_json_string(c, 'campaign')), because the generated column names are resolved case-insensitively. Such collisions are now excluded from the pushdown. #76594 #76593
  • array_difference on integer input computed each adjacent difference in the 32-bit input type before widening to the BIGINT result type, so a difference outside the INT range overflowed and returned a wrong value. #76569
  • Aggregating a whole STRUCT column together with ROLLUP, CUBE, or GROUPING SETS failed at plan time with StructType SlotRef must have an non-empty usedStructFiledPos. #76804
  • Rebuilding a logical window operator in the optimizer dropped the inputIsBinary flag, losing the binary-input merge behavior selected by ranking-window pre-aggregation. #77058
  • A query could fail with Expr evaluate meet error: slot_id N not found because the partition-by columns of a PARTITION TOP-N node were rewritten to a dictionary slot that had already been decoded below the node. #75956
  • Query planning, including EXPLAIN, aborted with an IllegalStateException when a contradictory range predicate such as col > X AND col < X collapsed to an empty value set and the same column was also referenced by a column-to-column join predicate. #75011
  • Planning aborted when an aggregate over a multi-branch CASE with a non-null constant ELSE clause was considered for push-down below a join. #75037
  • Selecting from views whose definitions form a cycle — which ALTER VIEW can create — failed with an opaque Unknown error caused by a StackOverflowError. Cyclic definitions are now detected and reported. #75033
  • Grouping key columns of GROUP BY ROLLUP, CUBE, and GROUPING SETS were reported as non-nullable at analysis time, which produced an incorrect result schema for Arrow Flight SQL clients. #76149
  • array_contains and array_position failed with class com.starrocks.type.NullType cannot be cast to class com.starrocks.type.ArrayType when the first argument was an untyped NULL literal. #76970
  • For an OR predicate with more than 16 disjuncts, the merged null fraction of a column was always estimated as 1 instead of the average of the operands' null fractions, which distorted cardinality estimation. #75864
  • Loading column statistics failed for a column whose values are all NULL when the global sql_mode includes ERROR_IF_OVERFLOW, because the stored empty min/max string could not be cast to the column type. #76684
  • The FE scan-range heap-safety check ran once per physical partition instead of once per scan node, which burned minutes of FE CPU per plan attempt on a table with tens of thousands of physical partitions. #76978
  • A predicate on a wildcard CHAR column, such as CAST(json_col->'$.x' AS char), threw std::length_error because the column's declared length of -1 was used for zero-padding. #77444
  • The BE crashed when scan predicates were pulled up through an array_map lambda, because the rewrite descended into the lambda body. #76380
  • The STRUCT output of UNNEST could be pruned narrower than the element type the BE materializes for the input array, when another consumer keeps that input array fully materialized. #76002
  • Analyzing __iceberg_transform_truncate or __iceberg_transform_bucket mutated the shared builtin function object in place, stamping its wildcard decimal signature with the precision and scale of the first query analyzed. #76777
  • Rewriting a query with two aggregates on the same base column of a synchronous materialized view or rollup, such as min(c) and max(c), failed during cost estimation with missing statistic of col: ... mv_min_c. #75528
  • A materialized view could serve stale results after rollback_to_snapshot on its Iceberg base table, because a negative staleness was accepted as within the mv_rewrite_staleness_second budget. #75924
  • With mv_rewrite_staleness_second set and query_rewrite_consistency = checked, a chained partial refresh of one recently committed partition kept renewing the freshness of the whole materialized view while another partition lagged far beyond the tolerance. The staleness baseline is now the time the view was last confirmed fresh. #76758
  • A materialized view defined on an un-partitioned Delta Lake or Kudu table was never used for query rewrite, even after a successful refresh. #76359
  • The FE Iceberg manifest data-file cache could serve a cached file set that was missing a live data file while still passing the read-side completeness check, so scan planning silently dropped the file and the query returned a short result. #76215
  • Queries on an Iceberg V1 table failed after a partition field was dropped, because a dropped V1 partition field is retained in the spec with a void transform and keeps the name of its source column. #75149
  • An Iceberg REST catalog configured with an OAuth2 client credential failed every request until the catalog was recreated, once its background token refresh had exhausted its retry budget. The catalog now rebuilds its session and retries the request once, at most one rebuild per 60 seconds; catalogs using jwt security or a static token are not affected. #76457
  • Closing an Iceberg incremental scan-range iterator while the executor thread was still consuming it, which happens on query cancellation, was unsafe. #75953
  • Iceberg REST catalog vended credentials for GCS were ignored and FE metadata reads failed with 403 Forbidden, because the gcs-connector 3.x configuration keys were renamed. #75979
  • Querying a table in a Hive catalog intermittently failed with out of sequence response and then Unknown table, because the getTable() fallback reused the same Thrift connection after a read timeout without reconnecting. #76456
  • A Paimon predicate that cannot be converted, such as a comparison on a CASE expression, discarded the whole conjunction instead of keeping the convertible conjuncts. #66038
  • BOOLEAN columns were excluded from page-level predicate pushdown in Parquet files, because the Parquet column index min/max decoder had no BOOLEAN case. #74752
  • A streaming pre-aggregation running with enable_spill = true could run out of memory, because the memory budget latched when the sink downgraded to limited-memory mode was computed only once and could be frozen at 0. #76702
  • The data load path and the column-mode partial update path could build a chunk whose ARRAY or string column exceeded the addressable size, leaving the offsets no longer describing the buffer they belong to. Both paths now bound and check the chunk capacity. #77163
  • Columns missing from a Parquet file were null-padded to the whole Arrow batch size instead of the bounded chunk size, so a batch processed across several chunks produced unequal column lengths within one chunk and crashed. #75981
  • A replica in DECOMMISSION state could be elected as the primary replica of a load. Because it is the replica most likely to be removed while the load is still running, the whole load then failed with Fail to get tablet ... instead of the write quorum absorbing a single-replica failure. Such a replica is now skipped unless no other healthy candidate exists. #77035
  • The BE crashed during a load spill because LoadChunkSpiller used an unsynchronized null check as its initialization flag, letting a racing memtable-flush thread use a spiller whose serde was not ready yet. #76098
  • A multi-character CSV delimiter that straddled a buffer expansion caused a heap use-after-free in the CSV reader, reachable through the FILES() table function, Broker Load, and the Hive text connector. #76718
  • Loading malformed JSON crashed the BE with a heap buffer overflow, because the data-quality error message was built from an unbounded raw JSON pointer. #76752
  • Cancelling a load crashed the BE or CN with a SIGSEGV when tracing was enabled through jaeger_endpoint, because the sink's close_wait was not idempotent. #76869
  • Aborting a failed INSERT INTO FILES() threw a swallowed NPE, because the abort path looked up a database that a table function table does not have. #75983
  • INSERT OVERWRITE garbage collection could journal a failed-state change for a table that had already been dropped, because the target table was resolved before the table write lock was acquired. #77212
  • SHOW CREATE ROUTINE LOAD emitted the jsonpaths value without escaping its double quotes, producing DDL that cannot be parsed or replayed. #75755
  • On a Primary Key table with file bundling, retrying an aggregate publish could probe remote storage for a version that exists only in the metadata cache and leave a dangling prev_garbage_version. Durable metadata is now read when calculating the new base version. #75904
  • A shared-data publish could write a bundle tablet metadata file that was missing tablet pages, which left the partition's publish permanently stuck with can not find tablet ... from shared tablet metadata. Such a file is now refused, so the failure stays transient and retryable. #76850
  • A CN crashed on a physical-split scan of an empty tablet, because SparseRangeIterator::has_more() was not null-safe and a transient lake segment-load failure was swallowed instead of being surfaced as a retryable error. #75985
  • A CN crashed while building a segment for a materialized view carrying a legacy NGRAMBF index that older FE versions persisted without its gram_num property. #76989
  • The BE crashed when the build side of an aggregation IN runtime filter was a constant column. #74941
  • Cancelling a query could cause a use-after-free in the spillable hash-join build operator, because its set_finishing ran the spill-start path even when the runtime state was already cancelled. #76633
  • A BE or CN could abort during shutdown with std::bad_weak_ptr, because the global runtime-filter timer of an abandoned pipeline driver was never unscheduled. #76252
  • Memory leaked because the bRPC stub cache cleanup timer task was unscheduled but never removed. #75973
  • A query's ConnectContext, together with the whole ExecPlan object graph it references, stayed pinned in the thread-local of a pooled query-deploy worker. #76366
  • PipeObservable::defer_notify_sink() emitted a source event instead of a sink event, so a driver blocked on OUTPUT_FULL could stay blocked until an unrelated event arrived. #76782
  • An exception thrown by a ThreadPool task was swallowed by default and the task was still counted as finished. #76863
  • The BE kept running with the crashing flag set, and therefore kept reporting SHUTDOWN to the FE, when the fatal-signal handler stalled before re-raising the signal. The process is now forced to exit. #76491
  • Dropping a catalog verified its existence under a read lock and removed it under a separate write lock, so two concurrent drops of the same catalog could both pass the check. #76778
  • Java UDFs failed on JDK 21 and later with NoSuchMethodException: java.nio.DirectByteBuffer.(long,int), because that private constructor was removed in JDK 21. #75666
  • Behind a load balancer, every ADBC client that opens with a prepared statement failed, because a CreatePreparedStatement or ClosePreparedStatement forwarded to another FE carried the protobuf message name instead of the Flight SQL action type. #76310
  • The Arrow LZ4 and ZSTD IPC codecs were missing from the FE classpath of the Arrow Flight SQL service. #76921

Don't miss a new starrocks release

NewReleases is sending notifications on new releases.